지노랩 /JinoLab
FreeRTOSConfig.h 생성 및 통합 (STM32CubeIDE 기준) 본문
지난 강의에서 FreeRTOS 커널 소스를 프로젝트에 복사하고 헤더 검색 경로를 추가했지만,
FreeRTOSConfig.h 파일이 없어서 컴파일 에러가 발생했습니다. 이 강의에서는 FreeRTOSConfig.h를 직접 가져와서 프로젝트에 추가하고,
나머지 설정을 마무리하여 빌드가 정상적으로 완료되도록 하는 과정을 단계별로 정리합니다.
목차
- FreeRTOSConfig.h의 필요성
- FreeRTOSConfig.h 가져오기
- 헤더 검색 경로(include path) 설정
- 컴파일 에러 해결: SystemCoreClock 외 정의되지 않은 심볼 처리
- 컴파일 에러 해결: 중복된 예외 핸들러 제거
- 컴파일 에러 해결: Hook 관련 옵션 비활성화
- 최종 빌드 확인
1. FreeRTOSConfig.h의 필요성
- FreeRTOSConfig.h는 FreeRTOS 커널을 사용자가 원하는 동작으로 설정(커스터마이즈)하기 위해 반드시 필요한 헤더 파일입니다.
- FreeRTOS 소스 패키지에는 포함되어 있지 않으며, 프로젝트마다 반드시 하나를 만들어야 합니다.
- 주요 역할:
- configUSE_PREEMPTION, configTICK_RATE_HZ, configMINIMAL_STACK_SIZE, configMAX_PRIORITIES 등 사용자 정의 매크로를 통해
FreeRTOS 커널 동작(선점형/비선점형, 틱 주기, 스택 크기, 우선순위 개수 등)을 설정 - configCPU_CLOCK_HZ 같은 프로세서 의존 매크로를 통해 CPU 클럭 정보를 커널에 전달
- 메모리 할당 방식, 타이머 사용 여부, 각종 훅(Hook) 함수 사용 여부를 켜고 끄는 역할
- configUSE_PREEMPTION, configTICK_RATE_HZ, configMINIMAL_STACK_SIZE, configMAX_PRIORITIES 등 사용자 정의 매크로를 통해
2. FreeRTOSConfig.h 가져오기
2.1 FreeRTOS 데모 디렉터리 확인
- 로컬에 다운로드된 FreeRTOS 커널 소스 폴더(FreeRTOSvXXXXXX)를 엽니다.
- 그 안에 있는 FreeRTOS/Demo 폴더 → STM32F407 (혹은 “STM32F407VG-SK” 등) 데모 경로로 이동
- [FreeRTOS 소스]/FreeRTOS/Demo/ARM_CM4F_STM32F407ZG-SK/FreeRTOSConfig.h
- 해당 FreeRTOSConfig.h 파일을 복사합니다.
2.2 STM32CubeIDE 프로젝트에 붙여넣기
- STM32CubeIDE에서 Project Explorer 창에 있는 프로젝트를 우클릭 → Refresh
(소스 폴더 변경 후 동기화) - FreeRTOSConfig.h를 보관하려는 위치(예: Core/Inc/)를 우클릭 → New → File
- 파일 이름: FreeRTOSConfig.h
- 복사해 온 FreeRTOSConfig.h 내용을 방금 생성한 파일에 붙여넣기 → 저장(Ctrl+S)
Tip: 일반적으로 FreeRTOSConfig.h는 프로젝트의 Inc/ 폴더(헤더 전용) 안에 두는 편이 편리합니다.
다른 위치에 두려면 해당 경로를 헤더 검색 경로에 추가해야 합니다.
3. 헤더 검색 경로(include path) 설정
만약 FreeRTOSConfig.h를 Core/Inc/가 아닌 다른 경로에 두었다면,
빌드 설정에 포함 경로를 반드시 추가해야 합니다.
이 예제에서는 Core/Inc/에 넣었으므로 추가 작업은 불필요합니다.
하지만 혹시 ThirdParty/FreeRTOS/include나 ThirdParty/FreeRTOS/portable/GCC/ARM_CM4F를 아직 등록하지 않았다면,
다음 단계를 따라 헤더 검색 경로를 다시 확인합니다.
- 프로젝트를 우클릭 → Properties
- C/C++ Build → Settings
- 탭에서 MCU GCC Compiler → Includes 선택
- 아래 Add... 버튼 클릭 →
- 경로 1: FreeRTOS 헤더 모음이 있는 폴더
- ${ProjDirPath}/ThirdParty/FreeRTOS/include
- 경로 2: 포팅용 헤더(*.h)가 있는 폴더
- ${ProjDirPath}/ThirdParty/FreeRTOS/portable/GCC/ARM_CM4F
- Apply → “Changes made will not be reflected in index until it is rebuilt. Continue?” → Yes → OK
ProjDirPath는 프로젝트 루트(예: C:/Workspace/MyFreeRTOSProject)를 가리킵니다.
폴더 위치를 프로젝트 구조에 맞춰 적절히 바꿔주세요.
4. 컴파일 에러 해결: SystemCoreClock 미정의
빌드 시 다음과 같은 에러가 남을 수 있습니다.
fatal error: 'FreeRTOSConfig.h' : No such file or directory ← 해결 후
undefined reference to `SystemCoreClock' ← 새로 발생
SystemCoreClock은 system_stm32f4xx.c(또는 .h)에 정의된 전역 변수입니다.
하지만 FreeRTOSConfig.h 내부에서 SystemCoreClock을 참조할 때, 컴파일러는 아직 이를 알지 못해 에러가 발생합니다.
4.1 FreeRTOSConfig.h 내 SystemCoreClock 참조 부분 찾기
- FreeRTOSConfig.h 파일을 열고, configCPU_CLOCK_HZ 아래에 다음 코드가 있을 수 있습니다.또는
- extern uint32_t SystemCoreClock; /* 직접 extern 선언한 형태 */
- #ifdef __ICCARM__ /* IAR 보드용 예시 */ extern uint32_t SystemCoreClock; #endif
- 문제: 위 #ifdef __ICCARM__ 블록은 IAR 컴파일러 전용으로 작성되었기 때문에,
GCC로 컴파일할 경우 SystemCoreClock이 extern 선언되지 않습니다. - 해결: GCC, ARMCC(Keil) 등에서도 SystemCoreClock을 extern으로 선언하도록 조건문을 수정합니다.
4.2 조건부 컴파일 매크로 수정
- FreeRTOSConfig.h에서 SystemCoreClock 관련 부분을 찾아, 다음과 같이 변경하세요.
- #if defined(__ICCARM__) /* IAR 컴파일러 */ extern uint32_t SystemCoreClock; #elif defined(__GNUC__) /* GCC 컴파일러 */ extern uint32_t SystemCoreClock; #elif defined(__CC_ARM) /* Keil(ARMCC) 컴파일러 */ extern uint32_t SystemCoreClock; #else extern uint32_t SystemCoreClock; /* 기타 툴체인에서도 사용 가능 */ #endif
- 저장(Ctrl+S) 후 프로젝트를 Rebuild:
- SystemCoreClock 관련 에러가 사라지고, 다음 단계로 넘어갑니다.
- Project → Build Project
5. 컴파일 에러 해결: 중복된 예외 핸들러 제거
다음으로, 포팅 레이어(portable/GCC/ARM_CM4F/port.c)에서 이미 정의된 PendSV_Handler, SysTick_Handler, SVC_Handler 등이
CubeMX가 자동 생성한 it.c에도 동일 이름으로 함수를 제공하기 때문에 “중복 정의” 에러가 발생할 수 있습니다.
예시 에러:
“_PendSV_Handler” redefined “_SysTick_Handler” redefined “_SVC_Handler” redefined
5.1 원인
- **FreeRTOS 포팅 코드(port.c)**는 FreeRTOS 커널이 동작하기 위해 PendSV, SysTick, SVC 예외 핸들러를 직접 구현합니다.
- 반면, STM32CubeIDE가 생성한 Core/Src/it.c 파일에도 기본적으로 빈(weak) 핸들러 구현이 들어있어 충돌이 발생합니다.
5.2 해결 방법: STM32CubeMX 설정 변경
it.c에서 핸들러를 지우기보다는, CubeMX 설정을 통해 “해당 예외 핸들러를 코드 생성에서 제외”시키는 편이 안전합니다.
- 프로젝트 루트에 있는 .ioc 파일(예: MyFreeRTOSProject.ioc)을 더블클릭하여 STM32CubeMX 설정 창을 엽니다.
- 좌측 메뉴에서 System Core → NVIC(또는 “NVIC 설정” 탭)을 클릭
- NVIC 관련 설정 화면 우측 상단 탭에서 “Configuration” 아이콘(톱니바퀴 모양) 클릭
- 아래와 같이 핸들러 생성을 끕니다:
- PendSV interrupt 우측 체크박스 “Generate Interrupt Handler” → 해제
- SysTick interrupt 우측 “Generate Interrupt Handler” → 해제
- SVC interrupt 우측 “Generate Interrupt Handler” → 해제

- 상단 툴바에서 “Generate Code”(또는 Ctrl+S)를 눌러 코드 재생성
- it.c 파일에서 더 이상 PendSV_Handler, SysTick_Handler, SVC_Handler 정의가 나타나지 않습니다.
5.3 빌드 확인
- 다시 Project → Build Project 수행
- 포팅 코드에서 구현된 예외 핸들러만 존재하므로 더 이상 “중복 정의” 에러가 발생하지 않습니다.
6. 컴파일 에러 해결: Hook 관련 옵션 비활성화
빌드 후에도 몇 가지 Hook 함수(스택 오버플로우 검사, 메모리 할당 실패 등)가 설정되어 있어
다음과 같은 에러가 나타날 수 있습니다.
undefined reference to `vApplicationTickHook'
undefined reference to `vApplicationMallocFailedHook'
undefined reference to `vApplicationStackOverflowHook'
…
이들은 FreeRTOSConfig.h에서 Hook 함수 사용 여부 매크로가 활성화되어 있기 때문에 발생합니다.
6.1 FreeRTOSConfig.h 수정
Core/Inc/FreeRTOSConfig.h를 열고, Hook 사용 매크로를 찾아 0으로 설정해 끕니다.
/* --------------------------------------------------- */
/* Hook 함수 사용 여부 (1: 활성, 0: 비활성) */
#define configUSE_MALLOC_FAILED_HOOK 0 /* malloc 실패 훅 비활성 */
#define configCHECK_FOR_STACK_OVERFLOW 0 /* 스택 오버플로우 검사 비활성 */
#define configUSE_IDLE_HOOK 0 /* Idle 훅 비활성 */
#define configUSE_TICK_HOOK 0 /* Tick 훅 비활성 */
- 이전 기본값 예제에서 이들 매크로를 1로 두었다가 Hook 함수 정의(vApplicationMallocFailedHook 등)가 없어서 에러가 생긴 것입니다.
- 위와 같이 모두 0으로 수정하면, Hook 함수 호출이 컴파일 타임에 제거되어 심볼 미정의 에러가 사라집니다.
- 저장(Ctrl+S) 후 Build Project 수행 → Hook 관련 에러가 없어집니다.
7. 최종 빌드 확인
- 모든 수정사항 저장(Ctrl+S)
- Project → Clean… → “Clean all projects” 체크 → OK
- Project → Build Project
- 더 이상 헤더 파일 누락, 중복 정의, Hook 함수 미정의 등 에러 없이 빌드 성공 상태여야 합니다.
Build finished successfully (0 errors, 0 warnings) - ST-Link 연결 후 Run → Run 또는 Debug → Start를 눌러 타겟에 다운로드
- 정상적으로 실행된다면, FreeRTOS 스케줄러가 시작된 상태에서 Cortex-M 예외 핸들러들이 Hot Swap 되며 동작 중입니다.
부록: Hello World 태스크 예제
이제 빌드가 성공적으로 되었다면, 간단한 “Hello World” FreeRTOS 태스크를 시험해 볼 수 있습니다.
Core/Src/main.c를 다음과 같이 수정해 보세요.
#include "main.h"
#include "FreeRTOS.h"
#include "task.h"
/* 태스크 함수 prototype */
static void vHelloTask(void *pvParameters);
int main(void)
{
/* HAL 초기화 및 시스템 클럭 설정 (CubeMX 자동 생성 코드) */
HAL_Init();
SystemClock_Config();
MX_GPIO_Init(); /* 필요 시, GPIO 설정 */
MX_USART2_UART_Init(); /* 필요 시, UART 설정 */
/* “헬로 월드” 태스크 생성 */
xTaskCreate(
vHelloTask, /* 태스크 함수 */
"HelloTask", /* 태스크 이름(디버깅용) */
configMINIMAL_STACK_SIZE, /* 스택 크기(워드 단위) */
NULL, /* 파라미터 */
tskIDLE_PRIORITY + 1, /* 우선순위 (IDLE+1) */
NULL /* 태스크 핸들 */
);
/* FreeRTOS 스케줄러 시작 */
vTaskStartScheduler();
/* 스케줄러가 시작되면 절대 이곳으로 오지 않습니다. */
while (1)
{
}
}
/*-----------------------------------------------------------*/
/* 헬로 월드 태스크: 1초마다 문자열 출력 */
static void vHelloTask(void *pvParameters)
{
(void) pvParameters;
for (;;)
{
/* 디버그 콘솔로 “Hello FreeRTOS World!” 출력 */
printf("Hello FreeRTOS World!\r\n");
/* 1000ms 딜레이 */
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
/*-----------------------------------------------------------*/
/** SystemClock_Config, MX_GPIO_Init, MX_USART2_UART_Init 등은
* CubeMX가 자동 생성한 함수이며, UART printf 출력용 HAL_UART_Transmit 구현이 필요합니다.
* 또는 LED 깜빡임으로 동작 확인 가능. **/
위 예제에서 printf를 사용하려면, UART2 설정과 함께 syscalls.c에 write()가 구현되어 있어야
UART로 출력이 나갑니다. 만약 번거롭다면, 다음과 같이 GPIO LED 토글만 작성해도 됩니다.
/* vHelloTask 내부 예시: PD13 LED 깜빡임 */
for (;;)
{
HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_13);
vTaskDelay(pdMS_TO_TICKS(500));
}
- 빌드 후 다운로드 → Run 시
- UART로 주기적으로 “Hello FreeRTOS World!” 메시지가 찍히거나,
- **PD13(녹색 LED)**가 반초 간격으로 깜빡이면
→ FreeRTOS 스케줄러 및 태스크가 정상 동작하는 것입니다.
요약
- FreeRTOSConfig.h는 사용자가 직접 만들어야 하는 “커널 설정” 헤더입니다.
- 데모 프로젝트(Demo/.../FreeRTOSConfig.h)에서 STM32F407용 샘플을 복사해 사용하는 것이 가장 빠릅니다.
- 헤더 검색 경로에 ThirdParty/FreeRTOS/include와 ThirdParty/FreeRTOS/portable/GCC/ARM_CM4F를 추가했는지 확인합니다.
- SystemCoreClock extern 선언을 컴파일러에 맞춰 수정하여 정의되지 않음 에러 해결.
- NVIC 설정에서 PendSV/SysTick/SVC 핸들러 생성을 비활성화하여 중복 정의 오류 해결.
- Hook 관련 매크로를 0으로 변경해 미정의 함수 오류를 제거.
- 위 과정을 마치면 빌드 성공 후 FreeRTOS 스케줄러가 실행됩니다.
이제 FreeRTOSConfig.h가 포함된 정상 빌드 환경이 갖춰졌습니다.
다음 강의에서는 큐, 세마포, 메모리 관리, 소프트웨어 타이머 등 FreeRTOS 주요 API 사용을 배우겠습니다.
'임베디드 시스템 > RTOS' 카테고리의 다른 글
| STM32CubeIDE GUI를 사용해 FreeRTOS(CMSIS-RTOS v2) 통합하기 (1) | 2025.06.11 |
|---|---|
| STM32Cube HAL의 타임베이스 소스를 Systick → TIM6으로 변경하기 (1) | 2025.06.10 |
| FreeRTOSConfig.h 추가 및 설정 방법 (0) | 2025.06.09 |
| STM32CubeIDE 프로젝트에 FreeRTOS 커널을 수동 추가하는 방법 (1) | 2025.06.09 |
| RTOS와 GPOS의 우선순위 역전(Priority Inversion) 이해하기 (4) | 2025.06.08 |