안녕하세요, '소프트웨어 공장'에 오신 것을 환영합니다! 오늘은 STM32F429ZI를 이용하여 실시간 운영체제인 FreeRTOS를 설정하고, 기본적인 RTOS 프로젝트를 구성해보는 시간을 갖겠습니다. FreeRTOS는 임베디드 시스템에서 멀티태스킹 기능을 구현할 수 있는 강력한 도구입니다. 이번 포스팅에서는 FreeRTOS의 개념부터 태스크 생성, 세마포어와 큐를 이용한 멀티태스킹 구현까지 다뤄보겠습니다.
1. FreeRTOS란 무엇인가?
FreeRTOS는 오픈 소스 실시간 운영체제로, 임베디드 시스템에서 멀티태스킹을 구현하기 위해 자주 사용됩니다. FreeRTOS는 태스크(task)라는 단위를 통해 여러 작업을 병렬로 수행할 수 있도록 도와줍니다. 태스크의 우선순위에 따라 작업을 스케줄링하고, 세마포어, 큐, 타이머 등 다양한 IPC(Inter-Process Communication) 도구를 제공합니다. 이로 인해 마이크로컨트롤러 기반의 시스템에서도 복잡한 동작을 효율적으로 처리할 수 있게 해줍니다.
FreeRTOS는 주로 다음과 같은 이유로 많이 사용됩니다:
- 경량: 메모리 사용량이 적어 제한된 자원에서 실행할 수 있습니다.
- 확장성: 다양한 하드웨어 플랫폼을 지원하며, 쉽게 이식할 수 있습니다.
- 타이밍 보장: 정해진 시간 내에 작업을 완료해야 하는 상황에서, 태스크의 우선순위를 설정해 정확한 타이밍 제어가 가능합니다.
2. FreeRTOS 설정하기
STM32CubeIDE에서 STM32F429ZI에 FreeRTOS를 설정하는 방법에 대해 설명하겠습니다. STM32CubeMX를 통해 FreeRTOS를 설정하고 코드를 자동 생성할 수 있습니다.
2.1 프로젝트 생성 및 FreeRTOS 활성화
- STM32CubeIDE를 실행하고 새로운 프로젝트를 생성합니다. STM32F429ZI MCU를 선택합니다.
- 프로젝트 설정 화면에서 Middleware 탭으로 이동한 후 FreeRTOS를 활성화합니다.
- Configuration 버튼을 눌러 FreeRTOS 설정 화면으로 이동합니다. 여기에서 태스크의 수, 스택 크기, 타이머 설정 등을 설정할 수 있습니다.
2.2 기본적인 설정
- Heap Size 설정: FreeRTOS에서 동적 메모리 할당을 위해 힙(Heap) 크기를 설정해야 합니다. 프로젝트의 요구사항에 맞게 힙 크기를 늘려줍니다.
- 시스템 클럭 설정: RTOS가 원활히 동작하기 위해서는 시스템 클럭이 적절히 설정되어야 합니다. HCLK와 SysTick 인터럽트의 클럭 주파수를 확인합니다.
3. 태스크(Task) 생성하기
FreeRTOS의 주요 기능 중 하나는 태스크를 생성하고 관리하는 것입니다. 태스크는 독립적인 실행 단위이며 각각의 태스크는 고유한 우선순위를 가집니다. 예제를 통해 태스크를 생성해보겠습니다.
#include "FreeRTOS.h"
#include "task.h"
#include "stm32f4xx_hal.h"
void Task1_Handler(void *pvParameters);
void Task2_Handler(void *pvParameters);
int main(void)
{
// HAL 초기화
HAL_Init();
SystemClock_Config();
// Task 생성
xTaskCreate(Task1_Handler, "Task1", 128, NULL, 1, NULL);
xTaskCreate(Task2_Handler, "Task2", 128, NULL, 1, NULL);
// FreeRTOS 스케줄러 시작
vTaskStartScheduler();
// 메인 루프는 여기 도달하지 않음
while (1)
{
}
}
void Task1_Handler(void *pvParameters)
{
while (1)
{
// Task 1 작업 수행
HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_12);
vTaskDelay(pdMS_TO_TICKS(1000)); // 1초 대기
}
}
void Task2_Handler(void *pvParameters)
{
while (1)
{
// Task 2 작업 수행
HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_13);
vTaskDelay(pdMS_TO_TICKS(500)); // 0.5초 대기
}
}
위 코드에서는 두 개의 태스크(Task1, Task2)를 생성하고, 각 태스크는 LED를 깜박이는 역할을 합니다. vTaskDelay()
함수를 이용해 각 태스크의 실행 간격을 조정합니다.
4. 세마포어와 큐를 이용한 멀티태스킹 구현
RTOS에서 여러 태스크가 자원을 공유할 때는 세마포어와 큐를 이용하여 동기화와 통신을 할 수 있습니다. 이번에는 세마포어를 사용해 자원 접근을 제어하고, 큐를 이용해 데이터를 교환하는 방법을 알아보겠습니다.
4.1 세마포어 사용하기
세마포어는 여러 태스크가 동시에 접근할 수 없는 자원을 보호하는 역할을 합니다. 아래 예제에서는 바이너리 세마포어를 사용하여 LED 자원 접근을 보호합니다.
#include "semphr.h"
SemaphoreHandle_t xSemaphore;
void Task1_Handler(void *pvParameters)
{
while (1)
{
if (xSemaphoreTake(xSemaphore, portMAX_DELAY) == pdTRUE)
{
// LED 토글 (자원 사용)
HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_12);
vTaskDelay(pdMS_TO_TICKS(1000));
xSemaphoreGive(xSemaphore);
}
}
}
void Task2_Handler(void *pvParameters)
{
while (1)
{
if (xSemaphoreTake(xSemaphore, portMAX_DELAY) == pdTRUE)
{
// LED 토글 (자원 사용)
HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_13);
vTaskDelay(pdMS_TO_TICKS(500));
xSemaphoreGive(xSemaphore);
}
}
}
int main(void)
{
HAL_Init();
SystemClock_Config();
// 세마포어 생성
xSemaphore = xSemaphoreCreateBinary();
xSemaphoreGive(xSemaphore);
// Task 생성
xTaskCreate(Task1_Handler, "Task1", 128, NULL, 1, NULL);
xTaskCreate(Task2_Handler, "Task2", 128, NULL, 1, NULL);
// 스케줄러 시작
vTaskStartScheduler();
while (1)
{
}
}
위 코드에서는 xSemaphoreTake()
와 xSemaphoreGive()
를 통해 LED 자원에 대한 접근을 제어합니다. 이렇게 함으로써 두 태스크가 동시에 LED를 제어하지 않도록 보장합니다.
4.2 큐 사용하기
큐는 태스크 간 데이터를 주고받는 용도로 사용됩니다. 예제에서는 Task1에서 데이터를 생성하고 Task2에서 해당 데이터를 처리하는 간단한 큐 사용 예제를 살펴보겠습니다.
#include "queue.h"
QueueHandle_t xQueue;
void Task1_Handler(void *pvParameters)
{
uint32_t count = 0;
while (1)
{
count++;
xQueueSend(xQueue, &count, portMAX_DELAY);
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
void Task2_Handler(void *pvParameters)
{
uint32_t receivedValue;
while (1)
{
if (xQueueReceive(xQueue, &receivedValue, portMAX_DELAY) == pdTRUE)
{
// 수신된 값 처리
printf("Received: %lu\n", receivedValue);
}
}
}
int main(void)
{
HAL_Init();
SystemClock_Config();
// 큐 생성
xQueue = xQueueCreate(5, sizeof(uint32_t));
// Task 생성
xTaskCreate(Task1_Handler, "Task1", 128, NULL, 1, NULL);
xTaskCreate(Task2_Handler, "Task2", 128, NULL, 1, NULL);
// 스케줄러 시작
vTaskStartScheduler();
while (1)
{
}
}
위 코드에서 xQueueSend()
는 Task1에서 데이터를 큐에 넣는 역할을 하고, xQueueReceive()
는 Task2에서 데이터를 큐로부터 받아오는 역할을 합니다. 이를 통해 Task1과 Task2 간의 통신을 구현할 수 있습니다.
5. 간단한 RTOS 프로젝트 구성 및 테스트
위에서 설명한 것들을 종합하여 간단한 RTOS 프로젝트를 구성하고 STM32F429ZI 보드에서 테스트할 수 있습니다. 태스크를 생성하고, 세마포어와 큐를 사용하여 태스크 간의 동기화 및 통신을 구현하면 다양한 임베디드 시스템에서 실시간으로 동작하는 기능을 손쉽게 구현할 수 있습니다.
마무리
이번 포스팅에서는 STM32F429ZI와 FreeRTOS를 이용하여 기본적인 RTOS 프로젝트를 구성해 보았습니다. FreeRTOS는 효율적인 멀티태스킹을 가능하게 하며, 세마포어와 큐를 사용해 태스크 간의 동기화와 통신을 쉽게 처리할 수 있습니다. 실제 프로젝트에 적용해보면서 RTOS의 강력함을 경험해 보시기 바랍니다.
'STM32' 카테고리의 다른 글
STM32 [STM32F429ZI] Ethernet 설정 및 간단한 HTTP 서버 구현하기 (0) | 2024.12.01 |
---|---|
STM32 USB 호스트/디바이스 설정 및 사용법 (0) | 2024.11.30 |
STM32 인터럽트와 EXTI 설정 (0) | 2024.11.28 |
STM32 아날로그 신호 출력 DAC 설정하기 (0) | 2024.11.27 |
STM32 ADC 설정 및 사용하기 (0) | 2024.11.26 |