이번 단계에서는 프로젝트 전체에서 동작하는 Task와 Timer를 관리하는 구조를 설계합니다. 이를 위해 BasicFunctions라는 관리 객체를 만들고, 이 객체 안에서 Task와 Timer를 통합적으로 제어할 수 있도록 구성합니다. 또한 예제로 RUNLEDTask를 추가하여 LED가 1초 간격으로 점멸(blinking)되는 동작을 확인해보겠습니다.
1. Task와 Timer 개념
Task 객체
Task는 시스템에서 반복적으로 실행되는 단위 동작을 의미합니다. 기본적으로 모든 Task는 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); };
- Task는 활성화 여부(bEnabled), 초기화 상태(bModuleInitialized), 완료 상태(bFinished) 등을 관리합니다.
- 또한, 내부적으로 BaseTimer 객체를 포함하여 시간 기반 동작을 수행할 수 있습니다.
- registerTimer() 함수를 통해 TimerManager에 등록할 수 있습니다.
즉, BaseTask는 단순히 run()을 실행하는 구조가 아니라, Timer와 결합되어 조건이 충족될 때 동작하는 구조를 가질 수도 있습니다.
Timer 객체
Timer는 프로젝트 내에서 시간 기반 동작을 제어하기 위한 범용 객체입니다.
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는 타이머 활성화 여부(bEnable), 조건 충족 여부(bTrigger), 시간 타입(TIMEDEF, ms 단위 / sec 단위), timeout 조건(timecriteria) 등을 속성으로 가집니다.
- Timer의 시간 카운트(timecount)는 활성화된 Timer2 interrupt와 timertype에 의해 증가하는 구조로 동작합니다.
- timecount 값이 timecriteria에 도달하면 bTrigger 플래그가 True로 바뀌고, 이를 통해 원하는 조건에서 동작을 수행할 수 있습니다.
즉, Timer는 Task 실행 주기를 제어하는 데 사용할 수도 있지만, 프로젝트 전반에서 다양한 시간 기반 기능을 구현할 때 활용할 수 있습니다.
2. BasicFunctions 객체
BasicFunctions는 프로젝트의 중앙 관리 객체로, 내부적으로 다음 두 매니저를 포함합니다.
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 : 프로젝트 내 모든 Timer를 등록하고 관리
- TaskManager : 프로젝트 내 모든 Task를 등록하고 관리
RegisterTask() 함수를 이용해 Task를 등록할 수 있으며, Timer 등록은 각 Task의 registerTimer() 함수를 통해 처리됩니다.
즉, BasicFunctions는 Task와 Timer를 한 곳에서 관리하는 컨트롤 허브 역할을 합니다.
3. 프로젝트 설정
Tasks 폴더 추가 및 설정
- Tasks 폴더를 생성합니다.
- 프로젝트 속성에서 다음과 같이 경로를 추가합니다.
- C/C++ Build → Settings → MCU/MPU G++ Compiler → Include paths → ../Tasks
- C/C++ General → Paths and Symbols → Source Location → Tasks 폴더
이제 BasicFunctions.cpp와 BasicFunctions.h를 Tasks 폴더에 추가합니다.
4. BasicFunctions 객체 생성 및 실행
객체 선언
main.cpp에서 다음과 같이 BasicFunctions 객체를 선언합니다.
/* 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;
초기화 함수 추가
/* 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 */
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. RUNLEDTask 생성 및 등록
Task 구조가 준비되면, 실제 동작을 확인하기 위해 RUNLEDTask를 추가합니다.
Common 폴더 추가
Tasks/Common 폴더를 생성하고, RUNLEDTask.cpp, RUNLEDTask.h 파일을 작성합니다.
프로젝트 속성에서 Include Path에 ../Tasks/Common을 추가합니다.
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()); }
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 */ }
이제 LED가 1초 주기로 깜박이면서, Task와 Timer 구조가 정상적으로 동작하는 것을 확인할 수 있습니다.
마무리
이번 단계에서는 Task와 Timer의 관리 구조를 설계하고, BasicFunctions 객체를 통해 이를 통합적으로 제어하는 방법을 살펴보았습니다. 또한, RUNLEDTask를 등록하여 LED 점멸을 확인함으로써 구조가 올바르게 동작함을 검증했습니다.
다음 단계에서는 더 복잡한 Task를 추가하고, Console과 연동하여 시스템 동작을 확장해보겠습니다.
0개의 댓글