지노랩 /JinoLab
STM32Cube HAL의 타임베이스 소스를 Systick → TIM6으로 변경하기 본문
이전 강의까지 FreeRTOS 커널을 프로젝트에 성공적으로 통합했지만, STM32Cube HAL과 FreeRTOS가 동시에 SysTick 타이머를 타임베이스(timer tick)로 사용할 경우 충돌이 발생할 수 있습니다.
따라서, HAL은 SysTick 대신 TIM6(Timer 6)을 타임베이스 소스로, FreeRTOS는 원래대로 SysTick을 사용하도록 설정해야 합니다.
아래 단계대로 진행하면 됩니다.
1. 왜 타임베이스 소스를 변경해야 하는가?
- FreeRTOS
- 기본적으로 ARM Cortex-M 계열 프로세서의 내장 SysTick 타이머를 “RTOS 틱”으로 이용하여
- vTaskDelay() 처리,
- 주기적 스케줄러 실행(시스템 틱 발생 시)
등 다양한 시간 기반 기능을 수행합니다.
- 기본적으로 ARM Cortex-M 계열 프로세서의 내장 SysTick 타이머를 “RTOS 틱”으로 이용하여
- STM32Cube HAL(HAL_Delay(), HAL_GetTick(), Timeout 처리 등)
- HAL 라이브러리 또한 기본적으로 “SysTick 타이머”를 사용해 시스템 틱을 생성하고,
HAL_Delay(), HAL_GetTick() 함수나
통신 모듈(예: UART 통신에서 Rx 타임아웃, SPI 타임아웃 등)
타임아웃 처리에 SysTick을 사용합니다.
- HAL 라이브러리 또한 기본적으로 “SysTick 타이머”를 사용해 시스템 틱을 생성하고,
🚨 문제:
두 개의 서로 다른 모듈(FreeRTOS와 HAL)이 동시에 SysTick을 타임베이스로 쓰면,
- FreeRTOS가 틱 인터럽트를 잡아서 스케줄러를 돌려야 할 때,
- HAL도 “1ms마다 tick 증가”를 원하지만, 이미 FreeRTOS가 SysTick을 독점하고 있으므로 HAL의 지연 함수(HAL_Delay())나 타임아웃 기능이 오작동합니다.
따라서, HAL 쪽 타이머 소스를 TIM6(또는 다른 일반 타이머)으로 변경하여,
FreeRTOS가 SysTick을 그대로 사용하도록 두고, HAL만 별도의 타이머로 시간을 관리하게 해야 서로 충돌이 나지 않습니다.
2. STM32CubeMX 설정에서 타임베이스 소스 변경하기
- .ioc 파일 열기
- 프로젝트 루트에서 MyProject.ioc (STM32CubeMX 설정 파일)을 더블클릭해 엽니다.
- 좌측 메뉴에서 “System Core → SYS” 선택
- 좌측 패널 트리뷰에서 **“System Core”**를 펼치고, “SYS”(System Configuration) 메뉴를 클릭합니다.
- 우측 창이 SYS 설정 화면으로 전환됩니다.
- “Time Base Source”를 SysTick → TIM6으로 변경
- SYS 설정 화면에서 중간쯤에 “Timebase Source” 옵션이 있습니다.
- 기본값은 **“Time Base Source = SysTick”**으로 설정되어 있는데,
- 이 드롭다운을 클릭하여 **“TIM6”**으로 변경합니다.
- NVIC(우선순위 그룹) 설정 (선택 사항이지만 권장)
FreeRTOS와 HAL이 공통 인터럽트 우선순위 체계를 사용하므로,
NVIC 우선순위 그룹 설정을 미리 조정하면 예기치 않은 선점(preemption) 충돌을 방지할 수 있습니다.- 좌측 메뉴 **“System Core → NVIC”**를 선택
- 우측 상단의 “Priority Group” 드롭다운에서 “4 bits for pre-emption priority, 0 bits for subpriority” (Group 4))를 선택
- 즉, “Preempt Priority 4 bits / Sub Priority 0 bits” 로 변경
15)만 지원하므로,15 우선순위**만 사용합니다.
“Preempt Priority 4비트, Sub Priority 0비트”로 설정하면 **순수하게 0
실질적으로 “HAL 예외” vs. “FreeRTOS 예외” 간 우선순위 충돌을 최소화할 수 있습니다. - 코드 재생성(Generate Code)
- 우측 상단 또는 하단의 “GENERATE CODE” 버튼(돋보기 모양 옆) 또는
화면 상단 메뉴바의 **“Project → Generate Code”**를 누릅니다. - “Yes”를 눌러 변경사항을 수락하면, CubeMX가 *.ioc 파일에 맞게 코드(특히 main.c, stm32f4xx_hal_conf.h, it.c, 새 타임베이스 소스 파일 등)를 자동으로 재생성합니다.
- 우측 상단 또는 하단의 “GENERATE CODE” 버튼(돋보기 모양 옆) 또는
- 타임베이스 소스 코드 확인
- 재생성 후 Project Explorer에서 Src/ 폴더에파일이 새로 생겼는지 확인합니다.
- stm32f4xx_hal_timebase_tim.c
- 이 파일은 HAL이 더 이상 SysTick이 아니라 TIM6를 이용하여 “1ms 틱”을 발생시키도록 설정해 주는 드라이버 코드입니다.
/* stm32f4xx_hal_timebase_tim.c */ /* * HAL library에서 * “1ms마다 HAL_GetTick가 증가하도록 하는 타이머로 TIM6을 사용”하도록 구성된 코드 */ #include "stm32f4xx_hal.h" #if (USE_HAL_DRIVER) #include "stm32f4xx_hal_tim.h" #endif TIM_HandleTypeDef htim6; /* * HAL_InitTick() 함수 구현 예시 * → HAL이 내부적으로 호출해 TIM6를 초기화하고 1ms마다 인터럽트를 발생시키도록 설정 */ HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority) { /* TIM6 clock enable */ __HAL_RCC_TIM6_CLK_ENABLE(); /* TIM6 설정: 1 MHz 카운터 → 프리스케일러(APB1(42MHz) 기준, prescaler = 42-1 → 1 MHz), → 1 MHz / 1000 → 1 kHz(1ms) */ htim6.Instance = TIM6; htim6.Init.Prescaler = (uint32_t)(SystemCoreClock / 1000000) - 1; htim6.Init.CounterMode = TIM_COUNTERMODE_UP; htim6.Init.Period = (uint32_t)(1000 - 1); htim6.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; if (HAL_TIM_Base_Init(&htim6) != HAL_OK) { return HAL_ERROR; } HAL_NVIC_SetPriority(TIM6_DAC_IRQn, TickPriority ,0); HAL_NVIC_EnableIRQ(TIM6_DAC_IRQn); HAL_TIM_Base_Start_IT(&htim6); return HAL_OK; } /* * TIM6의 인터럽트 핸들러 예시 * → 인터럽트가 발생할 때마다 HAL_IncTick()을 호출하여 HAL tick 값을 갱신 */ void TIM6_DAC_IRQHandler(void) { HAL_TIM_IRQHandler(&htim6); } /* * HAL_TIM_PeriodElapsedCallback() 예시 * → Timer tick 인터럽트가 발생할 때마다 HAL_GetTick()에 사용되는 ‘uwTick’ 증가 */ void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if (htim->Instance == TIM6) { HAL_IncTick(); } }- CubeMX가 자동 생성하므로, 직접 수정할 필요는 없고 “Timebase = TIM6” 옵션만 바꾸면 자동으로 코드가 추가됩니다.
3. 프로젝트 빌드 및 확인
- 변경사항 저장(Ctrl+S)
- 프로젝트를 Build
- Project → Build Project
- 빌드 성공 메시지 확인
- 이전에는 SysTick 충돌 등으로 에러가 났지만,
- 지금은 HAL이 TIM6를 타임베이스로 사용하므로 SysTick 충돌 없이 FreeRTOS가 SysTick을 독점하여 사용할 수 있습니다.
4. 정리 및 다음 단계
- 결론:
- FreeRTOS → SysTick 사용
- STM32Cube HAL → TIM6 사용
로 타임베이스를 분리하였으므로, 두 모듈 간의 타이머 충돌이 사라지고
HAL_Delay(), HAL_GetTick() 등 HAL 타이밍 기능도 정상 동작합니다.
이로써 FreeRTOS 커널 + HAL 환경에서 타임베이스(Tick) 자원을 올바르게 분리할 수 있었습니다.
다음 강의부터는 FreeRTOS에서 **Task(태스크)**를 생성하고 사용법을 익히면서,
“Hello World” 예제를 실제 보드에서 실행해보겠습니다.
'임베디드 시스템 > RTOS' 카테고리의 다른 글
| FreeRTOS에서의 태스크(Task)란 무엇인가? (0) | 2025.06.11 |
|---|---|
| STM32CubeIDE GUI를 사용해 FreeRTOS(CMSIS-RTOS v2) 통합하기 (1) | 2025.06.11 |
| FreeRTOSConfig.h 생성 및 통합 (STM32CubeIDE 기준) (1) | 2025.06.10 |
| FreeRTOSConfig.h 추가 및 설정 방법 (0) | 2025.06.09 |
| STM32CubeIDE 프로젝트에 FreeRTOS 커널을 수동 추가하는 방법 (1) | 2025.06.09 |