개요
FreeRTOS는 임베디드 업계에서 가장 널리 사용되는 실시간 운영체제(RTOS) 중 하나입니다. 비교적 작은 메모리를 가진 MCU에서도 동작할 수 있도록 설계되어 있으며, ARM Cortex-M 계열을 포함한 다양한 마이크로컨트롤러를 지원합니다. 또한 Task, Queue, Semaphore, Mutex 등 멀티태스킹 시스템 구현에 필요한 다양한 기능을 제공하여 복잡한 임베디드 소프트웨어를 보다 체계적으로 개발할 수 있도록 도와줍니다.
최근에는 STM32CubeMX와 같은 도구를 이용하여 FreeRTOS를 손쉽게 프로젝트에 추가할 수 있지만, 실제 개발 현장에서는 RTOS의 구조와 동작 원리를 이해하는 것이 중요합니다. 단순히 자동 생성된 코드를 사용하는 것만으로는 FreeRTOS가 어떻게 동작하는지, 어떤 파일들이 필요한지, 그리고 MCU에 어떻게 포팅되는지를 이해하기 어렵기 때문입니다.
본 시리즈에서는 인터넷에 많이 소개되어 있는 단순 사용법 위주의 예제보다는 FreeRTOS 공식 GitHub 저장소에서 제공하는 FreeRTOS-Kernel V11.3.0을 직접 프로젝트에 추가하고, STM32C0316-DK 보드에서 단계적으로 실험해 보면서 FreeRTOS를 학습하는 것을 목표로 합니다. 특히 STM32CubeMX의 FreeRTOS 미들웨어를 사용하지 않고 수동으로 설치하는 과정을 통해 실제 포팅 과정을 이해해 보고자 합니다.
실험 대상 MCU는 32KBytes Flash Memory와 12KBytes RAM을 가진 STM32C031C6입니다. 메모리 자원이 넉넉하지 않은 소형 MCU에서도 FreeRTOS를 충분히 활용할 수 있는지 확인해 보는 것도 이번 실험의 중요한 목적 중 하나입니다. 다행히 STM32C0316-DK 개발 보드를 보유하고 있어 이를 이용하여 실제 동작을 확인하면서 실험을 진행할 예정입니다.
실험환경
본 실험에서는 FreeRTOS-Kernel V11.3.0을 STM32C0316-DK 개발보드에 수동으로 설치하고 동작을 확인합니다. 실험 초기 단계에서는 외부 센서나 통신 모듈과 같은 주변장치를 연결하지 않고, 개발보드에 기본적으로 탑재된 LED를 이용하여 FreeRTOS의 동작을 확인할 예정입니다.
또한 FreeRTOS 자체의 포팅 과정에 집중하기 위해 최소한의 하드웨어 구성만 사용합니다. 따라서 아래 그림과 같이 STM32C0316-DK 개발보드와 STLINK-V3 디버거만 연결하여 실험을 진행합니다.

STM32C031C6는 32KB Flash Memory와 12KB RAM을 가진 비교적 작은 사양의 MCU입니다. 최근에는 수백 KB 이상의 Flash와 RAM을 가진 MCU가 많이 사용되고 있지만, 실제 산업 현장에서는 여전히 소형 MCU가 널리 사용되고 있습니다. 따라서 제한된 메모리 환경에서도 FreeRTOS가 충분히 동작할 수 있는지 확인해 보는 것은 의미가 있다고 생각합니다.
본 시리즈의 모든 실험은 아래 환경에서 진행하였습니다.
- 개발도구 : STM32CubeIDE Version 1.15.1
- RTOS : FreeRTOS-Kernel V11.3.0
- MCU : STM32C031C6
- 개발보드 : STM32C0316-DK
- Debugger & Programmer : STLINK-V3
참고로 본 시리즈에서는 STM32CubeMX의 FreeRTOS 미들웨어 기능을 사용하지 않고, FreeRTOS 공식 GitHub 저장소에서 제공하는 Kernel 소스를 직접 프로젝트에 추가하는 방식으로 진행합니다. 이를 통해 FreeRTOS가 실제로 어떤 파일들로 구성되어 있으며 MCU에 어떻게 포팅되는지를 함께 살펴볼 예정입니다.
FreeRTOS 포팅
포팅 과정
FreeRTOS-Kernel V11.3.0을 STM32C0316-DK에 포팅하기 위해 아래와 같은 순서로 진행합니다.
- STM32CubeIDE에서 기본 프로젝트 생성 (Non-FreeRTOS)
- FreeRTOS-Kernel V11.3.0 다운로드
- FreeRTOS 소스 파일 프로젝트에 추가
- Include Path 및 Source Locations 설정
- FreeRTOSConfig.h 생성 및 설정
- Build 및 오류 수정
- LED Blink Task 생성 및 Scheduler 시작
초기 실험의 목표는 FreeRTOS가 STM32C0316-DK에서 정상적으로 포팅되어 Task가 동작하는지 확인하는 것입니다.
먼저 가장 단순한 LED Blink Task를 구현하여 Scheduler가 정상적으로 동작하는지 확인한 후, 이후 실험에서는 Queue, Semaphore,
Mutex, Software Timer 등 FreeRTOS의 주요 기능을 단계적으로 살펴볼 예정입니다.
STM32CubeIDE에서 기본 프로젝트 생성(Non FreeRTOS)
가장 먼저 해야 할 일은 STM32CubeIDE에서 새 프로젝트를 생성하고 MCU가 정상적으로 동작하는지 확인하는 것입니다. FreeRTOS를 추가하기 전에 LED 점멸 프로그램을 다운로드하여 기본 프로젝트가 정상적으로 빌드되고 실행되는지 확인합니다.
이 단계에서는 FreeRTOS를 사용하지 않고 일반적인 Super Loop 구조의 프로그램으로 진행합니다.
STM32CubeIDE에서 File → New → STM32 Project 메뉴를 선택하면 아래와 같이 보드 또는 MCU를 선택하는 화면이 나타납니다. 여기서 STM32C0316-DK를 선택한 후 우측 하단의 Finish 버튼을 눌러 프로젝트를 생성합니다.

현재 STM32C0316-DK는 STM32CubeIDE에서 NRND (Not Recommended for New Design) 상태로 표시됩니다. 이는 신규 제품 설계에 권장되지 않는다는 의미이며, 향후 단종(EOL)을 고려하여 새로운 설계에는 다른 제품 사용을 검토하라는 의미입니다.
그러나 본 실험의 목적은 FreeRTOS 포팅 및 동작 확인이므로, 보유하고 있는 개발보드를 활용하여 실습을 진행하도록 하겠습니다. 또한 STM32C031C6 MCU 자체는 여전히 학습 및 소규모 프로젝트에 활용하기에 충분한 성능을 제공합니다.
참고로 FreeRTOS 포팅 과정은 특정 STM32 보드에만 국한되는 내용이 아니므로, 본 글에서 설명하는 대부분의 과정은 다른 Cortex-M 계열 STM32 MCU에도 동일하게 적용할 수 있습니다.
디바이스 선택이 완료되면 프로젝트 이름을 지정합니다. 본 예제에서는 프로젝트 이름을 C9SW-FreeRTOS-Manual-v1.0으로 지정하였습니다.
STM32C0316-DK는 STM32CubeIDE에 등록된 개발보드이므로 프로젝트 생성 과정에서 보드에 맞는 기본 주변장치 설정을 사용할 것인지 확인하는 창이 나타납니다.
여기서는 기본 설정을 사용하기 위해 Yes 버튼을 선택합니다.

그러면 STM32CubeIDE가 새로운 프로젝트를 생성하며, 생성이 완료되면 자동으로 프로젝트 화면이 열립니다.
기본 프로젝트 생성이 완료되었으므로 먼저 LED를 점멸시켜 프로젝트가 정상적으로 동작하는지 확인해 보겠습니다.
FreeRTOS를 포팅하기 전에 MCU의 클럭 설정, GPIO 설정, 다운로드 환경 및 디버깅 환경이 모두 정상적으로 동작하는지 확인하는 과정입니다. 만약 이 단계에서 LED가 정상적으로 점멸하지 않는다면 이후 FreeRTOS를 추가하더라도 문제의 원인을 찾기가 어려워질 수 있습니다.
main.c의 while() 루프에 아래와 같이 LED를 토글하는 코드를 추가합니다. 지연 함수는 HAL_Delay()를 사용하였으며 500ms 주기로 LED 상태를 변경하도록 하였습니다.

프로젝트를 빌드한 후 STM32C0316-DK 보드에 다운로드하여 실행하였습니다. 그 결과 LED가 500ms 간격으로 정상적으로 점멸하는 것을 확인할 수 있었습니다.
이로써 기본 프로젝트가 정상적으로 동작하고 있음을 확인하였으며, GPIO 설정, 시스템 클럭, 다운로드 환경 및 디버깅 환경 또한 정상적으로 구성되었음을 확인하였습니다.
다음 단계에서는 FreeRTOS-Kernel V11.3.0 소스를 프로젝트에 추가하여 본격적인 FreeRTOS 포팅 작업을 진행하겠습니다.
FreeRTOS-Kernel V11.3.0 다운로드
FreeRTOS 공식 사이트에서는 FreeRTOS 202604.00 LTS(Long Term Support) 버전을 다운로드할 수 있습니다. LTS 버전은 장기간 유지보수를 목적으로 하는 안정화 버전으로, 산업용 제품 개발에 적합합니다.
그러나 이번 실험에서는 FreeRTOS 전체 패키지가 아닌 FreeRTOS-Kernel만 사용합니다. FreeRTOS-Kernel에는 Task, Queue, Semaphore, Mutex, Software Timer 등의 핵심 RTOS 기능이 포함되어 있으며, 본 시리즈의 목적은 STM32C0316-DK에 RTOS 커널을 직접 포팅하는 것이기 때문입니다.
따라서 아래 GitHub Release 페이지에서 최신 버전인 FreeRTOS-Kernel V11.3.0을 다운로드하겠습니다.
링크를 열면 아래와 같은 GitHub Release 화면이 나타납니다.

화면 하단의 Assets 항목에서 FreeRTOS-KernelV11.3.0.zip 파일을 다운로드합니다.

압축을 해제하면 FreeRTOS Kernel 소스와 다양한 포터블(Portable) 파일들을 확인할 수 있습니다. 다음 단계에서는 STM32C0316-DK 프로젝트에 필요한 파일만 선별하여 추가해 보겠습니다.
FreeRTOS 소스 파일 프로젝트에 추가
이제 다운로드한 FreeRTOS-Kernel V11.3.0 소스를 프로젝트에 추가해 보겠습니다.
FreeRTOS 관련 파일은 프로젝트의 다른 소스 파일과 구분하여 관리하는 것이 좋습니다. 따라서 먼저 FreeRTOS 전용 폴더를 생성한 후 관련 파일들을 이 폴더에 복사하도록 하겠습니다.
STM32CubeIDE의 Project Explorer 창에서 프로젝트 이름(C9SW-FreeRTOS-Manual-v1.0)을 선택한 후 마우스 오른쪽 버튼을 클릭합니다.
이후 New → Folder 메뉴를 선택하여 새로운 폴더를 생성합니다.

폴더 이름은 FreeRTOS로 지정하였습니다. 앞으로 FreeRTOS-Kernel에서 필요한 소스 파일과 헤더 파일들은 이 폴더 아래에 추가할 예정입니다.
다음 단계에서는 다운로드한 FreeRTOS-Kernel V11.3.0 압축 파일을 해제한 후 필요한 파일들을 확인해 보겠습니다.

다운로드한 FreeRTOS-Kernel V11.3.0 압축 파일을 해제하면 위와 같은 폴더와 파일들을 확인할 수 있습니다.
FreeRTOS-Kernel에는 다양한 MCU와 컴파일러를 지원하기 위한 많은 파일들이 포함되어 있습니다. 그러나 STM32C0316-DK에서 FreeRTOS를 실행하기 위해서는 모든 파일이 필요한 것은 아닙니다.
우선 아래 항목들을 앞에서 생성한 FreeRTOS 폴더로 복사합니다.
include(폴더)
portable(폴더)
tasks.c
queue.c
list.c
timers.c
stream_buffer.c
event_groups.c
복사가 완료되면 프로젝트 구조는 아래와 같은 형태가 됩니다.

Windows 탐색기에서 파일을 복사하여 붙여넣은 경우, STM32CubeIDE의 Project Explorer에는 새로 추가된 파일이 즉시 표시되지 않을 수 있습니다.
이 경우 FreeRTOS 폴더를 선택한 후 마우스 오른쪽 버튼을 클릭하고 Refresh(F5) 를 실행하면 새로 복사한 파일들이 Project Explorer에 나타납니다.
처음 FreeRTOS를 수동으로 추가하는 경우 파일이 보이지 않아 복사가 실패한 것으로 오해할 수 있는데, 대부분은 Refresh를 수행하면 정상적으로 표시됩니다.
참고로 tasks.c, queue.c, list.c는 FreeRTOS의 핵심 기능을 구현하는 파일입니다.
tasks.c: Task 생성 및 스케줄링 기능queue.c: Queue, Semaphore, Mutex 기능list.c: FreeRTOS 내부 리스트 관리 기능
또한 아래 파일들은 추가 기능을 제공합니다.
timers.c: Software Timer 기능stream_buffer.c: Stream Buffer 및 Message Buffer 기능event_groups.c: Event Group 기능
현재 구현할 LED Blink 예제에서는 일부 파일만 사용되지만, 이후 Queue, Semaphore, Mutex, Software Timer 등의 실험도 진행할 예정이므로 처음부터 함께 추가하도록 하겠습니다.
필요한 파일은 모두 복사하였지만 portable 폴더에는 우리가 사용하는 환경 외에도 다양한 MCU와 컴파일러를 지원하기 위한 포터블 파일들이 포함되어 있습니다.
따라서 STM32C0316-DK에서 사용하는 Cortex-M0+ 코어와 GCC 컴파일러에 필요한 파일만 남기고 나머지는 삭제하겠습니다.
남겨둘 파일은 다음과 같습니다.
portable -> GCC -> ARM_CM0
portable -> MemMang -> heap_4.c
ARM_CM0는 STM32C031C6의 Cortex-M0+ 코어를 지원하기 위한 포터블 계층이며, FreeRTOS-Kernel V11.3.0에서는 Cortex-M0와 Cortex-M0+가 동일한 포터블 파일을 사용합니다.
또한 FreeRTOS의 메모리 관리 방식으로는 heap_4.c를 선택하였습니다. heap_4.c는 동적 메모리 할당과 해제를 모두 지원하며, 실제 프로젝트에서도 가장 널리 사용되는 메모리 관리 방식 중 하나입니다.
ARM_CM0와 heap_4.c를 제외한 나머지 포터블 파일들은 현재 실험 환경에서는 사용되지 않으므로 삭제하였습니다. 불필요한 파일을 제거하면 프로젝트 구조를 단순하게 유지할 수 있고, 이후 FreeRTOS 포팅 과정을 이해하기도 쉬워집니다.
아래는 정리 작업이 완료된 후의 FreeRTOS 폴더 구조입니다.

현재까지는 FreeRTOS 관련 소스 파일만 프로젝트에 추가한 상태입니다. 하지만 아직 STM32CubeIDE는 FreeRTOS 헤더 파일과 포터블 파일의 위치를 알지 못하기 때문에 이 상태에서는 정상적으로 컴파일되지 않습니다.
다음 단계에서는 Include Path와 Source Location을 설정하여 FreeRTOS 관련 파일들을 컴파일 대상에 포함시키도록 하겠습니다.
Include Path 및 Source Locations 설정
앞 단계에서 FreeRTOS 관련 소스 파일을 프로젝트에 추가하였습니다. 그러나 아직 STM32CubeIDE는 FreeRTOS 헤더 파일과 소스 파일의 위치를 알지 못하기 때문에 이 상태에서는 정상적으로 컴파일되지 않습니다.
예를 들어 tasks.c, queue.c, timers.c 등은 FreeRTOS.h, task.h, queue.h 등의 헤더 파일을 참조하고 있습니다. 컴파일러가 이러한 헤더 파일의 위치를 찾지 못하면 컴파일 과정에서 오류가 발생하게 됩니다.
따라서 FreeRTOS 관련 헤더 파일의 위치를 Include Path에 등록하고, FreeRTOS 소스 파일이 저장된 폴더를 Source Locations에 추가하여 컴파일 대상에 포함시켜야 합니다.
Include Path 설정
Include Path를 설정하기 위해 Project Explorer에서 C9SW-FreeRTOS-Manual-v1.0 프로젝트를 선택한 후 마우스 오른쪽 버튼을 클릭합니다.
메뉴에서 Properties를 선택한 후 아래 경로로 이동합니다.
C/C++ Build
└─ Settings
└─ Tool Settings
└─ MCU GCC Compiler
└─ Include paths
아래와 같은 설정 화면이 나타납니다.

화면 우측의 + 아이콘을 클릭하여 아래 두 개의 경로를 추가합니다.
FreeRTOS/Include
FreeRTOS/portable/GCC/ARM_CM0
첫 번째 경로는 FreeRTOS의 공통 헤더 파일이 저장된 위치이며, 두 번째 경로는 Cortex-M0/M0+용 포터블 계층 헤더 파일이 저장된 위치입니다.
참고로 Core/Inc 경로는 STM32CubeIDE가 프로젝트를 생성할 때 자동으로 등록하므로 별도로 추가하지 않아도 됩니다. 이후 생성할 FreeRTOSConfig.h 파일도 이 폴더에 저장할 예정입니다.
아래 화면이 Include Paths를 추가한 화면입니다.

Source Locations 설정
Include Path 설정이 완료되었다면 다음은 FreeRTOS 소스 파일이 실제로 빌드 대상에 포함되도록 Source Locations를 설정해야 합니다.
다시 프로젝트를 선택한 후 마우스 오른쪽 버튼을 클릭하여 Properties 메뉴를 선택합니다.
이번에는 아래 경로로 이동합니다.
C/C++ General
└─ Paths and Symbols
└─ Source Location
FreeRTOS 폴더를 Add하면 아래와 같은 화면이 됩니다.’

이렇게 설정하면 FreeRTOS 커널 소스 파일과 포터블 계층 파일, 그리고 메모리 관리 모듈이 모두 컴파일 대상에 포함됩니다.
현재까지 진행한 작업은 다음과 같습니다.
- FreeRTOS-Kernel V11.3.0 다운로드
- FreeRTOS 관련 소스 파일 복사
- ARM_CM0 포터블 계층 선택
- heap_4.c 메모리 관리 모듈 선택
- Include Path 설정
- Source Locations 설정
이제 STM32CubeIDE는 FreeRTOS 관련 헤더 파일과 소스 파일의 위치를 모두 알 수 있게 되었습니다.
다음 단계에서는 FreeRTOS의 핵심 설정 파일인 FreeRTOSConfig.h를 생성하고 STM32C031C6 환경에 맞게 설정해 보겠습니다.
FreeRTOSConfig.h 파일 생성
FreeRTOS를 사용하기 위해서는 반드시 FreeRTOSConfig.h 파일이 필요합니다.
FreeRTOS 커널은 다양한 MCU와 프로젝트 환경에서 사용할 수 있도록 설계되어 있기 때문에 스케줄러 동작 방식, Tick 주기, Heap 크기, 최대 우선순위 수 등의 설정을 사용자가 직접 지정해야 합니다.
이러한 설정 정보는 모두 FreeRTOSConfig.h 파일에 저장되며, FreeRTOS 커널은 컴파일 시 이 파일을 참조하여 동작합니다.
따라서 수동 포팅 과정에서 가장 중요한 작업 중 하나가 FreeRTOSConfig.h 파일을 생성하는 것입니다.
Project Explorer에서 다음 폴더를 선택합니다.
Core
└─ Inc
마우스 오른쪽 버튼을 클릭한 후
New -> Header File
을 선택합니다.
파일 이름은 아래와 같이 입력합니다.
FreeRTOSConfig.h
생성이 완료되면 아래와 같이 파일이 추가됩니다.

FreeRTOSConfig.h 파일은 FreeRTOS.org에서 샘플을 제공하고 있습니다. 그 샘플을 이용하여 해당 MCU에 맞게 커스터마이징 해야 합니다.
아래 링크에서 FreeRTOSConfig.h를 커스터마이징 하는 방법을 설명하고 있습니다.
FreeRTOSConfig.h Customization
본 실험에 맞게 커스터마이징 된 FreeRTOSConfig.h 파일의 내용은 아래와 같습니다.

FreeRTOSConfig.h에는 수십 개의 설정 항목이 존재합니다. 본 실험에서는 LED Blink를 시작으로 Queue, Semaphore, Mutex, Software Timer 실험까지 진행할 예정이므로 이에 필요한 기능들을 미리 활성화하였습니다.
주요 설정 항목은 다음과 같습니다.
Tick 주기
#define configTICK_RATE_HZ 1000
FreeRTOS Tick 인터럽트를 1ms 주기로 발생시키는 설정입니다.
최대 우선순위 개수
#define configMAX_PRIORITIES 5
Task 우선순위를 0~4까지 사용할 수 있도록 설정합니다.
본 실험에서는 5단계 정도면 충분하므로 이 값으로 설정하였습니다.
Heap 크기
#define configTOTAL_HEAP_SIZE (4 * 1024)
FreeRTOS가 사용할 Heap 영역을 4KB로 설정합니다.
STM32C031C6은 총 12KB RAM을 가지고 있으며, 현재 실험 규모에서는 4KB 정도면 충분합니다.
Mutex 활성화
#define configUSE_MUTEXES 1
향후 UART 공유 자원 보호 실험을 위해 Mutex 기능을 활성화합니다.
Counting Semaphore 활성화
#define configUSE_COUNTING_SEMAPHORES 1
Semaphore 관련 실험을 진행하기 위해 활성화합니다.
Software Timer 활성화
#define configUSE_TIMERS 1
향후 Software Timer 실험에 사용됩니다.
현재 설정은 STM32C031C6에서 FreeRTOS를 학습하기 위한 최소 설정입니다.
실제 제품 개발에서는 Heap 크기, Tick 주기, 우선순위 개수 등을 프로젝트 특성에 맞게 조정해야 합니다. 또한 사용하지 않는 기능은 비활성화하여 Flash와 RAM 사용량을 줄이는 것이 일반적입니다.
Build 및 오류 수정
FreeRTOSConfig.h 파일까지 생성하였으므로 이제 프로젝트를 빌드해 보겠습니다.
STM32CubeIDE에서 Project → Build Project 메뉴를 선택하거나 단축키 Ctrl + B를 눌러 빌드를 수행합니다.
그러나 첫 번째 빌드에서는 아래와 같이 오류가 발생하였습니다.

오류 내용을 보면 다음과 같습니다.
multiple definition of `PendSV_Handler’
multiple definition of `SVC_Handler’
multiple definition of `SysTick_Handler’
이는 같은 인터럽트 핸들러 함수가 두 곳에서 중복 정의되었기 때문에 발생한 오류입니다.
STM32CubeIDE에서 생성한 기본 프로젝트에는 이미 stm32c0xx_it.c 파일 안에 PendSV_Handler, SVC_Handler, SysTick_Handler가 정의되어 있습니다. 그런데 FreeRTOS의 port.c 파일에도 동일한 핸들러가 정의되어 있으므로 링커 단계에서 중복 정의 오류가 발생합니다.
FreeRTOS를 사용할 경우 이 세 가지 핸들러는 FreeRTOS 포터블 계층에서 처리해야 합니다.
따라서 Core/Src/stm32c0xx_it.c 파일을 열고 아래 함수들을 주석 처리합니다.
아래는 주석처리된 그림 입니다.

주석처리 후 다시 Build을 하니 해당 오류는 사라 졌으나 다른 오류가 발생하였습니다.

이것은 FreeRTOSConfig.h에서 Stack Overflow 검출 기능을 활성화 하였지만 , 실제 Hook 함수가 구현되어 있지 않기 때문에 발생하는 오류입니다.
![]()
이것을 “0”만들고 다시 Build하면 오류는 사라집니다. 그러나 Stack Overflow 검출은 디버깅 시 매우 유용함으로 이 함수를 구현하여 오류를 없애도록 하겠습니다.
main.c 에서 “task.h”를 include하고 아래 코드를 삽입합니다.

위 함수는 Task Stack Overflow가 발생했을 때 호출됩니다. 현재는 오류 발생 시 무한 루프에 머물도록 하였습니다. 실제 제품에서는 LED 점멸, UART 메시지 출력, 오류 로그 저장 등의 방식으로 처리할 수 있습니다.
다시 빌드한 결과 오류는 없어졌지만, 아래와 같이 경고가 1개 발생하였습니다.

경고 내용은 다음과 같습니다.
unused variable ‘pxVectorTable’
이 경고는 port.c 내부 변수와 관련된 것으로 현재 동작에는 영향을 주지 않으므로 무시해도 됩니다.
수정하지 않고 다시 Build하면 모든 에러와 경고가 사라지고 정상적으로 Build가 된 것을 확인 할 수 있습니다.

이로써 FreeRTOS-Kernel V11.3.0의 소스 파일, Include Path, Source Location, FreeRTOSConfig.h 설정이 모두 정상적으로 적용되었음을 확인하였습니다.
현재 상태에서는 FreeRTOS 커널이 컴파일 가능한 상태가 되었지만, 아직 Task를 생성하거나 Scheduler를 시작하지는 않았습니다.
다음 단계에서는 LED Blink Task를 생성하고 FreeRTOS Scheduler를 시작하여 실제 Task 동작을 확인해 보겠습니다.
LED Blink Task 생성 및 Scheduler 시작
이제 FreeRTOS 커널이 정상적으로 빌드되는 것을 확인하였으므로, 실제 Task를 생성하고 Scheduler를 시작해 보겠습니다.
이번 단계의 목표는 기존 Super Loop 방식의 LED Blink 코드를 FreeRTOS Task 구조로 변경하는 것입니다.
먼저 main.c 상단에 FreeRTOS 관련 헤더 파일을 추가합니다.

그리고 LED를 점멸할 Task 함수를 작성합니다.

함수의 원형을 main.c 의 앞부분에 추가 합니다.
![]()
다음으로 main() 함수에서 초기화 코드가 끝난 후 Task를 생성하고 Scheduler를 시작합니다.

Build를 하면 아래와 같은 오류가 발생합니다.

undefined reference to `vApplicationMallocFailedHook’ 라는 오류 메세지 인데 이 오류 역시 FreeRTOSConfig.h에서 아래 설정을 활성화 했기 때문에 발생합니다.
#define configUSE_MALLOC_FAILED_HOOK 1
이 옵션이 1이면 FreeRTOS의 pvPortMalloc()이 메모리 부족으로 실패했을 때 아래 함수를 호출합니다.
void vApplicationMallocFailedHook(void);
하지만 현재 프로젝트에는 해당 함수가 구현되어 있지 않으므로 링커 오류가 발생한 것입니다.
main.c에 아래 함수를 추가합니다.

다시 Build를 하면 오류가 사라지고 정상적으로 Building이 되는 것을 확인할 수 있습니다.

포팅의 모든 과정이 종료되었습니다.
이제 타겟보드에 다운로드하여 실행을 시켜 보겠습니다.
다운로드가 성공한 후 실행한 결과 LED가 정상적으로 점멸하는 것을 확인할 수 있었습니다.
이는 FreeRTOS Scheduler가 정상적으로 동작하며 생성한 Task가 실행되고 있음을 의미합니다.
결론
이번 글에서는 STM32C0316-DK에 FreeRTOS-Kernel V11.3.0을 수동으로 포팅하는 과정을 살펴보았습니다. STM32CubeIDE 기본 프로젝트 생성부터 FreeRTOS 소스 추가, Include Path 설정, FreeRTOSConfig.h 작성, 컴파일 오류 수정, 그리고 첫 번째 Task 실행까지의 전 과정을 단계별로 진행하였습니다.
실험 결과 FreeRTOS Scheduler가 정상적으로 동작하는 것을 확인하였으며, LED 점멸 역시 기존 Super Loop 방식이 아닌 FreeRTOS Task에 의해 수행되는 것을 확인하였습니다.
수동 포팅 과정에서는 인터럽트 핸들러 중복 정의 문제와 Hook 함수 관련 링크 오류가 발생하였지만, 이를 해결하면서 FreeRTOS의 내부 구조를 이해할 수 있었습니다. 이러한 경험은 단순히 CubeMX의 미들웨어를 사용하는 것보다 FreeRTOS를 깊이 있게 이해하는 데 도움이 될 것으로 생각합니다.
최종적으로 생성된 프로젝트는 STM32C031C6의 32KB Flash와 12KB RAM 환경에서 정상적으로 동작하였으며, FreeRTOS-Kernel V11.3.0을 사용하는 최소 실행 환경을 구축할 수 있었습니다.
다음 글에서는 LED Task를 하나 더 추가하여 두 개의 Task가 서로 다른 주기로 동작하는 모습을 확인하고, FreeRTOS Scheduler가 CPU 시간을 어떻게 분배하는지 살펴보겠습니다.