지노랩 /JinoLab

FreeRTOSConfig.h 추가 및 설정 방법 본문

임베디드 시스템/RTOS

FreeRTOSConfig.h 추가 및 설정 방법

지노랩/JinoLab 2025. 6. 9. 11:45

이전 강의에서 FreeRTOS 관련 헤더 경로를 포함시켜 빌드는 대부분 성공했지만,
여전히 FreeRTOSConfig.h 파일을 찾을 수 없어 컴파일 에러가 발생합니다.
이 강의에서는 FreeRTOSConfig.h를 직접 작성하고 프로젝트에 추가하는 과정을 단계별로 설명하겠습니다.


목차

  1. FreeRTOSConfig.h의 역할
  2. 예제 기본값으로 FreeRTOSConfig.h 작성
  3. 프로젝트에 FreeRTOSConfig.h 추가
  4. 빌드 테스트 및 예제 태스크 코드 작성(헬로 월드)

1. FreeRTOSConfig.h의 역할

  • FreeRTOSConfig.hFreeRTOS 커널을 사용자가 원하는 동작으로 커스터마이즈하기 위해 필요한 설정(매크로)들을 모아놓은 파일입니다.
  • FreeRTOS 소스 자체에 포함되어 있지 않으며, 사용자가 직접 프로젝트 내에 만들어야 합니다.
  • 주요 설정 예시:
    • CPU 클럭 주파수(configCPU_CLOCK_HZ)
    • 최소 스택 크기(configMINIMAL_STACK_SIZE)
    • 최대 우선순위(configMAX_PRIORITIES)
    • 메모리 관리 방식(configSUPPORT_DYNAMIC_ALLOCATION, configTOTAL_HEAP_SIZE 등)
    • 각종 기능(태스크 통계, 타이머, 통신 큐, 서브 우선순위 등) 활성화/비활성화 여부

2. 예제 기본값으로 FreeRTOSConfig.h 작성

STM32F407 Discovery 보드 기준(주 클럭 168 MHz)에 맞게, 가장 간단히 동작할 수 있는 최소 설정 예시를 아래에 제시합니다.
FreeRTOSConfig.h라는 이름으로 새 파일을 만들고, 프로젝트에 다음 내용을 복사하세요.

#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H

/*-----------------------------------------------------------
 * Application specific definitions.
 *
 * Adjust these definitions for your hardware and application requirements.
 *----------------------------------------------------------*/

/*-----------------------------------------------------------*/
/* Processor clock frequency (Hz). STM32F407은 168MHz */
#define configCPU_CLOCK_HZ              ( ( unsigned long ) 168000000UL )

/* Tick interrupt 주기 설정 (Hz) – 1 ms마다 tick 발생 */
#define configTICK_RATE_HZ              ( ( TickType_t ) 1000 )  

/* 최소 태스크 스택 크기(워드 단위) */
#define configMINIMAL_STACK_SIZE        ( ( unsigned short ) 128 )

/* 태스크 우선순위 개수(0 ~ (configMAX_PRIORITIES−1)) */
#define configMAX_PRIORITIES            ( 5 )

/* FreeRTOS 스케줄링 옵션 */
#define configUSE_PREEMPTION            1
#define configUSE_PORT_OPTIMISED_TASK_SELECTION 0

/* Time slicing (라운드 로빈) 사용 여부 */
#define configUSE_TIME_SLICING          1

/* 인터럽트 우선순위 비트 수 (Cortex-M4F 대부분 4비트) */
#define configPRIO_BITS                 4        /* STM32F4디폴트: 4비트 우선순위 */

/* Kernel interrupt priorities */
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY       15
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY  5

/* FreeRTOS NVIC 우선순위 정의 */
#define configKERNEL_INTERRUPT_PRIORITY        ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
#define configMAX_SYSCALL_INTERRUPT_PRIORITY   ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )

/*-----------------------------------------------------------*/
/* 동적 할당 사용 */
#define configSUPPORT_DYNAMIC_ALLOCATION      1
#define configSUPPORT_STATIC_ALLOCATION       0
#define configTOTAL_HEAP_SIZE                 ( ( size_t ) ( 20 * 1024 ) )  /* 20 KB 힙 공간 */

/*-----------------------------------------------------------*/
/* API 기능 활성화 (필요 시 1, 미사용 시 0) */
#define INCLUDE_vTaskDelay                     1
#define INCLUDE_vTaskDelayUntil                1
#define INCLUDE_vTaskPrioritySet               1
#define INCLUDE_vTaskDelete                    1
#define INCLUDE_vTaskSuspend                   1
#define INCLUDE_xTaskGetSchedulerState         1

/* Queue, Semaphore, Mutex 관련 */
#define configUSE_MUTEXES                      1
#define configUSE_RECURSIVE_MUTEXES            0
#define configUSE_COUNTING_SEMAPHORES          1
#define configUSE_QUEUE_SETS                   0

/* Software timer 사용 여부 */
#define configUSE_TIMERS                        1
#define configTIMER_TASK_PRIORITY               ( 3 )
#define configTIMER_QUEUE_LENGTH                ( 10 )
#define configTIMER_TASK_STACK_DEPTH            ( configMINIMAL_STACK_SIZE * 2 )

/* 태스크 통계 사용 여부 */
#define configGENERATE_RUN_TIME_STATS           0
#define configUSE_STATS_FORMATTING_FUNCTIONS    0

/* Stack overflow hook, malloc 실패 훅 등 */
#define configCHECK_FOR_STACK_OVERFLOW          2
#define configUSE_MALLOC_FAILED_HOOK            1

/* Assertion 매크로 정의 (무한 루프 진입) */
#define configASSERT( x ) if( ( x ) == 0 ) { taskDISABLE_INTERRUPTS(); for( ;; ); }

/* Cortex-M 인터럽트 핸들러 매크로 연결 (CMSIS 이름 지정) */
#define vPortSVCHandler     SVC_Handler
#define xPortPendSVHandler  PendSV_Handler
#define xPortSysTickHandler SysTick_Handler

#endif /* FREERTOS_CONFIG_H */

주요 설정 해설

  • configCPU_CLOCK_HZ: CPU 클럭 속도를 정확히 입력해야 타이머 틱이 올바르게 계산됩니다.
  • configTICK_RATE_HZ: 1 ms 틱을 원하면 1000으로 설정.
  • configMINIMAL_STACK_SIZE: 태스크 스택 최소 크기(워드 단위, 4 byte씩 계산).
  • configMAX_PRIORITIES: 최대 태스크 우선순위 수. (예: 5 계층 = 0~4)
  • configSUPPORT_DYNAMIC_ALLOCATION: malloc 방식 동적 태스크/큐 할당 사용 여부.
  • configTOTAL_HEAP_SIZE: FreeRTOS 전용 힙 영역 크기(바이트 단위).
  • configCHECK_FOR_STACK_OVERFLOW: 스택 오버플로우 감지 방식 (0 : 비사용, 1 : 스택 포인트 만 검사, 2 : 태스크 컨텍스트 검사)
  • CMSIS 인터럽트 핸들러 이름 정의: Cortex-M의 PendSV, SysTick, SVC 핸들러 연결

이 예제는 가장 기본적인 설정이며, 실제 응용에 따라 configUSE_TIMERS, configUSE_MUTEXES 등 필요에 따라 조정합니다.


3. 프로젝트에 FreeRTOSConfig.h 추가

  1. STM32CubeIDE에서 Project Explorer로 돌아갑니다.
  2. FreeRTOS_Project/Inc 폴더를 우클릭 → New → File
  3. 파일 이름을 FreeRTOSConfig.h로 입력 → Finish
  4. 자동 생성된 FreeRTOSConfig.h를 더블클릭 후 위 예제 코드를 복사해 붙여넣기 → 저장(Ctrl+S)

Tip: 파일을 다른 경로(예: ThirdParty/FreeRTOS/include)에 두어도 되지만, 일반적으로 STM32CubeIDE 표준처럼 헤더는 Inc/ 폴더에 두는 편이 편리합니다.


4. 빌드 테스트 및 간단한 “헬로 월드” 태스크 작성

4.1. Include 경로 재확인

  • 이미 이전 강의에서 ThirdParty/FreeRTOS/include와 ThirdParty/FreeRTOS/portable/GCC/ARM_CM4F 두 경로를 MCU GCC Compiler → Includes에 추가했는지 확인
  • Inc/FreeRTOSConfig.h는 기본 Inc/ 경로에 있으므로 별도 추가 불필요

4.2. 빌드

  1. Project → Build Project 실행
  2. 에러 없이 빌드 성공 확인 (Bin/Hex 파일 생성)

4.3. main.c 수정: 간단한 태스크 생성 예제

Core/Src/main.c를 열고, 다음과 같이 수정하여 “Hello World” 태스크를 하나 실행해 보겠습니다.
예시 코드:

/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "FreeRTOS.h"
#include "task.h"

/* Function prototype */
static void vHelloTask( void *pvParameters );

int main( void )
{
    /* HAL 초기화 */
    HAL_Init();

    /* 시스템 클럭 설정 (CubeMX 기본 코드) */
    SystemClock_Config();

    /* GPIO, UART 등 필요한 하드웨어 초기화는 여기서… 
       (IDE가 생성한 MX_GPIO_Init(), MX_USART2_UART_Init() 등 호출) */

    /* “헬로 월드” 태스크 생성 */
    xTaskCreate(
        vHelloTask,            /* 태스크 함수 */
        "Hello",               /* 태스크 이름 (디버깅용) */
        configMINIMAL_STACK_SIZE, /* 스택 크기 (워드 단위) */
        NULL,                  /* 태스크 파라미터 */
        tskIDLE_PRIORITY + 1,  /* 우선순위 (Idle보다 1단계 높음) */
        NULL                   /* 태스크 핸들 */
    );

    /* 스케줄러 시작. 반환되지 않음 */
    vTaskStartScheduler();

    /* 스케줄러가 실행되면 이 아래 코드에는 절대 도달하지 않습니다 */
    while(1)
    {
    }
}

/*-----------------------------------------------------------*/
/* “헬로 월드” 태스크 함수 구현 */
static void vHelloTask( void *pvParameters )
{
    ( void ) pvParameters;

    for( ;; )
    {
        /* 여기서는 디버거 콘솔(Printf)로 출력하거나 LED 토글 등으로 대체 가능 */
        printf("Hello FreeRTOS World!\r\n");

        /* 1초(1000 t) 대기 */
        vTaskDelay( pdMS_TO_TICKS(1000) );
    }
}

/*-----------------------------------------------------------*/
/** 시스템 클럭 설정 함수(IDE 자동 생성 코드) **/
void SystemClock_Config(void)
{
    ...  /* CubeMX가 생성한 기본 설정 코드 */
}

/*-----------------------------------------------------------*/
/* assert_failed, HAL_MspInit 등 나머지 IDE 자동 생성 코드는 그대로 둠 */
  • 주의: printf 사용 시, UART/ITM 등의 구현이 필요합니다.
    • 예시에서는 기본적으로 syscalls.c에 구현된 write() 함수가 잇으면
      • UART2를 통해 printf가 출력됩니다.
    • 또는, 단순히 LED 토글로 “정상 동작” 확인 가능
    • HAL_GPIO_TogglePin( GPIOD, GPIO_PIN_13 ); vTaskDelay( pdMS_TO_TICKS(500) );

4.4. 태스크 실행 확인

  1. 빌드 후 ST-Link로 프로그램 다운로드
  2. 디버깅 모드 진입 → 실행
    • UART를 통해 “Hello FreeRTOS World!”가 1초마다 출력되면 성공
    • 또는 PD13(녹색 LED)을 0.5초 간격으로 깜빡이면 성공

5. 정리

  • FreeRTOSConfig.h를 직접 작성하여 프로젝트에 포함시키면,
    IDE나 MCU 종류에 상관없이 동일한 FreeRTOS 커널을 사용할 수 있습니다.
  • 다음 단계로는
    1. 다양한 FreeRTOS API: 태스크 우선순위 변경, 큐/세마포/뮤텍스 활용, 소프트웨어 타이머 등
    2. 정밀한 FreeRTOSConfig.h: 실제 애플리케이션 요구사항에 맞춘 세부 조정
    3. FreeRTOS의 메모리 관리 방식(Heap_4.c) 이해 및 활용
    4. CMSIS-RTOS 레이어 사용 예제
  • 위 예제를 바탕으로 기본적인 FreeRTOS 동작 방식을 익히고, 이후 응용 프로젝트에 확장 가능합니다.

이상으로 “FreeRTOSConfig.h 추가 및 Hello World 태스크” 강의를 마칩니다.
다음 강의에서는 **FreeRTOS 주요 API(큐, 세마포, 메모리 관리 등)**를 다룹니다.