In this step, we design a structure to manage Tasks and Timers across the entire project. To achieve this, we create a manager object called BasicFunctions, which integrates and controls both Tasks and Timers. As an example, we add a RUNLEDTask to verify the operation by making the LED blink at 1-second intervals.
1. Concept of Task and Timer
Task Object
A Task represents a unit of repetitive execution in the system. Every Task is derived from the base class BaseTask.
class BaseTask { protected: BOOL bEnabled; BOOL bModuleInitialized; BOOL bFinished; BaseTimer timer; string name; public: BaseTask(); virtual ~BaseTask(); void setEnable(BOOL flag); BOOL getEnable(void); void setModuleInitialize(BOOL flag); BOOL getModuleInitialized(void); void setFinish(BOOL flag); BOOL getFinish(void); void setTimer(BaseTimer timer); BaseTimer * getTimerHandle(void); void registerTimer(TimerManager *pTM); void setName(string name); string getName(void); void virtual run(void); };
- A Task manages its enable flag (bEnabled), initialization state (bModuleInitialized), and completion state (bFinished).
- Each Task internally contains a BaseTimer object to support time-based operation.
- The registerTimer() function allows the Task to register its timer with the TimerManager.
In other words, a Task is not simply a structure that executes run(). It can also work in combination with a Timer and operate only when the specified condition is met.
Timer Object
A Timer is a general-purpose object used for time-based operations within the project.
class BaseTimer { protected: BOOL bEnable; BOOL bTrigger; TIMEDEF timetype; uint16_t timecriteria; uint16_t timecount; string name; public: BaseTimer(); BaseTimer(string name, BOOL flag, TIMEDEF type, uint16_t criteria, uint16_t count); virtual ~BaseTimer(); void setName(string name); string getName(void); void setEnable(BOOL flag); BOOL getEnable(void); void setTrigger(BOOL flag); BOOL getTrigger(void); void setTimeType(TIMEDEF type); TIMEDEF getTimeType(void); void setTimeCriteria(uint16_t value); uint16_t getTimeCriteria(void); void setTimeCount(uint16_t value); uint16_t getTimeCount(void); uint16_t updateTimeCount(void); };
- BaseTimer manages properties such as enable flag (bEnable), trigger flag (bTrigger), time type (TIMEDEF, ms or sec), and timeout condition (timecriteria).
- The timer count (timecount) is updated based on the activated Timer2 interrupt and the configured timertype.
- When timecount reaches timecriteria, the bTrigger flag becomes True, allowing the desired operation to be executed.
Thus, a Timer can be used to control Task execution intervals, but it can also be applied more broadly for various time-based features throughout the project.
2. BasicFunctions Object
The BasicFunctions class serves as the central management object of the project and includes two managers:
class BasicFunctions { protected: ConsoleTask * CTask; uint16_t ms_count; uint32_t sec_count; TimerManager timerMGR; TaskManager taskMGR; public: BasicFunctions(); BasicFunctions(ConsoleTask * pTask); virtual ~BasicFunctions(); void TimeoutHandler(void); TimerManager * getTimerManagerHandle(void); TaskManager * getTaskManagerHandle(void); void RegisterTask(BaseTask * pTask); void PrintTasksAndTimers(void); void run(); };
- TimerManager: registers and manages all Timer objects in the project
- TaskManager: registers and manages all Task objects in the project
Tasks are registered with the RegisterTask() function, while Timers are registered using each Task’s registerTimer() function.
In short, BasicFunctions acts as the control hub that manages both Tasks and Timers.
3. Project Setup
Add the Tasks Folder and Configure Properties
- Create a new folder named Tasks.
- Update project settings:
- C/C++ Build → Settings → MCU/MPU G++ Compiler → Include paths → ../Tasks
- C/C++ General → Paths and Symbols → Source Location → Tasks 폴더
Now place BasicFunctions.cpp and BasicFunctions.h in the Tasks folder.
4. Creating and Running the BasicFunctions Object
Declare the Object
In main.cpp:
/* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ #include <ConsoleTask.h> #include <BasicFunctions.h> /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ /* USER CODE BEGIN PTD */ /* USER CODE END PTD */ /* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ /* USER CODE END PD */ /* Private macro -------------------------------------------------------------*/ /* USER CODE BEGIN PM */ /* USER CODE END PM */ /* Private variables ---------------------------------------------------------*/ I2C_HandleTypeDef hi2c3; RTC_HandleTypeDef hrtc; /* USER CODE BEGIN PV */ ConsoleTask CTask; __attribute__ ((section(".ccmram"))) volatile uint8_t consoleBuf[2048]; BasicFunctions basic;
Add Initialization Function
/* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); static void MX_GPIO_Init(void); static void MX_DMA_Init(void); static void MX_TIM2_Init(void); static void MX_SPI1_Init(void); static void MX_USART6_UART_Init(void); static void MX_I2C3_Init(void); static void MX_RTC_Init(void); /* USER CODE BEGIN PFP */ void InitConsoleTask(void); void InitBasicFunctions(); /* USER CODE END PFP */ ... /* USER CODE BEGIN 4 */ /* * */ void InitConsoleTask(void) { CTask = ConsoleTask(USART6, DMA2, LL_DMA_STREAM_6, DMA2, LL_DMA_STREAM_1); CTask.setBufPtr((uint8_t *)consoleBuf); CTask.uart.DMARxEnable(); LL_USART_EnableIT_IDLE(USART6); CTask.PRINTF((char *)"\r\n\r\n"); CTask.PRINTF((char *)"=====================================\r\n"); CTask.PRINTF((char *)"Hello. This is TW100PCTest Application\r\n"); CTask.PRINTF((char *)"\r\nBuild Date: %s, %s\r\n", __DATE__, __TIME__); CTask.PRINTF((char *)"=====================================\r\n"); CTask.flushTxBuf(); } /* * */ void InitBasicFunctions() { basic = BasicFunctions(&CTask); LL_TIM_EnableCounter(TIM2); LL_TIM_EnableIT_UPDATE(TIM2); CTask.PRINTF((char *)"InitBasicFunctions\r\n"); CTask.flushTxBuf(); } /* USER CODE END 4 */
Run in main()
/* Initialize all configured peripherals */ MX_GPIO_Init(); MX_DMA_Init(); MX_TIM2_Init(); MX_SPI1_Init(); MX_USART6_UART_Init(); MX_I2C3_Init(); MX_RTC_Init(); /* USER CODE BEGIN 2 */ InitConsoleTask(); InitBasicFunctions(); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { CTask.run(); basic.run(); /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ }
5. Creating and Registering RUNLEDTask
Once the structure is ready, we add the RUNLEDTask to confirm that everything works properly.
Add the Common Folder
Create Tasks/Common and add RUNLEDTask.cpp and RUNLEDTask.h. Also, add ../Tasks/Common to the project’s include paths.
Declare and Register RUNLEDTask
/* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ #include <ConsoleTask.h> #include <BasicFunctions.h> #include <RUNLEDTask.h> /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ /* USER CODE BEGIN PTD */ /* USER CODE END PTD */ /* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ /* USER CODE END PD */ /* Private macro -------------------------------------------------------------*/ /* USER CODE BEGIN PM */ /* USER CODE END PM */ /* Private variables ---------------------------------------------------------*/ I2C_HandleTypeDef hi2c3; RTC_HandleTypeDef hrtc; /* USER CODE BEGIN PV */ ConsoleTask CTask; __attribute__ ((section(".ccmram"))) volatile uint8_t consoleBuf[2048]; BasicFunctions basic; RUNLEDTask runledTask; /* USER CODE END PV */ ... /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); static void MX_GPIO_Init(void); static void MX_DMA_Init(void); static void MX_TIM2_Init(void); static void MX_SPI1_Init(void); static void MX_USART6_UART_Init(void); static void MX_I2C3_Init(void); static void MX_RTC_Init(void); /* USER CODE BEGIN PFP */ void InitConsoleTask(void); void InitBasicFunctions(); void InitRunLEDTask(void); /* USER CODE END PFP */ ... /* * */ void InitBasicFunctions() { basic = BasicFunctions(&CTask); LL_TIM_EnableCounter(TIM2); LL_TIM_EnableIT_UPDATE(TIM2); CTask.PRINTF((char *)"InitBasicFunctions\r\n"); CTask.flushTxBuf(); } /* * */ void InitRunLEDTask(void) { runledTask = RUNLEDTask(&CTask); runledTask.setEnable(True); basic.RegisterTask(&runledTask); runledTask.registerTimer(basic.getTimerManagerHandle()); }
Run in main()
/* Initialize all configured peripherals */ MX_GPIO_Init(); MX_DMA_Init(); MX_TIM2_Init(); MX_SPI1_Init(); MX_USART6_UART_Init(); MX_I2C3_Init(); MX_RTC_Init(); /* USER CODE BEGIN 2 */ InitConsoleTask(); InitBasicFunctions(); InitRunLEDTask(); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { CTask.run(); basic.run(); /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ }
At this point, the LED will blink every 1 second, demonstrating that the Task and Timer structure is functioning correctly.
Conclusion
In this step, we designed a management structure for Tasks and Timers and integrated it using the BasicFunctions
object. We also registered a RUNLEDTask
to verify the design by blinking an LED.
In the next step, we will extend the system by adding more complex Tasks and integrating them with the Console.
0 Comments