FreeRTOS

FreeRTOS 실전 예제

임베디드 친구 2025. 1. 18. 10:10
반응형

FreeRTOS는 실시간 운영체제(RTOS)로서, 임베디드 시스템 개발자들에게 효율적인 태스크 관리와 자원 제어를 제공합니다. 이번 포스팅에서는 FreeRTOS를 활용한 간단한 IoT 프로젝트를 구현해보겠습니다. 프로젝트는 온도 및 습도 센서 데이터 수집 및 전송, OLED 디스플레이 제어, 태스크 및 자원 관리 최적화를 중심으로 구성됩니다.

프로젝트 목표

  • 온도 및 습도 센서 데이터 수집 및 전송: 센서를 통해 데이터를 읽고 이를 UART 또는 BLE를 통해 전송합니다.
  • OLED 디스플레이 제어: 수집된 데이터를 OLED 디스플레이에 출력합니다.
  • 태스크 및 자원 관리 최적화: FreeRTOS의 태스크 우선순위, 큐, 세마포어 등을 활용해 시스템 성능을 최적화합니다.

프로젝트 준비물

  • MCU: STM32F429ZI 또는 유사 마이크로컨트롤러
  • 온습도 센서: DHT22 또는 BME280
  • 디스플레이: SSD1306 기반 OLED 디스플레이
  • 개발 환경: STM32CubeIDE
  • 라이브러리: FreeRTOS CMSIS v1, HAL 라이브러리

프로젝트 설계

주요 태스크 정의

  1. Sensor Task: 온습도 데이터를 주기적으로 읽어와 큐에 저장합니다.
  2. Display Task: 큐에서 데이터를 읽어와 OLED 디스플레이에 출력합니다.
  3. Communication Task: 큐에서 데이터를 읽어와 UART 또는 BLE로 전송합니다.

시스템 구조도

            +-------------------+
            | Sensor Task       |
            |   센서 데이터 수집  |
            +-------------------+
                      |
                      V
            +-------------------+
            | Queue             |
            |   센서 데이터 저장  |
            +-------------------+
                 |         |
                 V         V
+-------------------+    +-------------------+
| Display Task      |    | Communication Task|
|   데이터 출력      |    |   데이터 전송      |
+-------------------+    +-------------------+

주요 FreeRTOS 개념 활용

  • 태스크 우선순위: Sensor Task > Display Task > Communication Task
  • : 태스크 간 데이터 교환
  • 세마포어: OLED 디스플레이와 UART 자원 보호

코드 구현

1. 프로젝트 초기화

#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
#include "stm32f4xx_hal.h"
#include "ssd1306.h"
#include "bme280.h"

#define QUEUE_LENGTH 5
#define QUEUE_ITEM_SIZE sizeof(SensorData)

typedef struct {
    float temperature;
    float humidity;
} SensorData;

QueueHandle_t sensorQueue;
SemaphoreHandle_t displaySemaphore;

void SystemClock_Config(void);
void MX_GPIO_Init(void);
void MX_I2C1_Init(void);
void MX_USART2_UART_Init(void);

2. 태스크 구현

Sensor Task

void SensorTask(void *pvParameters) {
    SensorData data;
    while (1) {
        // 센서 데이터 읽기
        data.temperature = BME280_ReadTemperature();
        data.humidity = BME280_ReadHumidity();

        // 큐에 데이터 저장
        xQueueSend(sensorQueue, &data, portMAX_DELAY);

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

Display Task

void DisplayTask(void *pvParameters) {
    SensorData data;
    char buffer[32];

    while (1) {
        // 큐에서 데이터 수신
        if (xQueueReceive(sensorQueue, &data, portMAX_DELAY) == pdPASS) {
            // 세마포어 획득
            if (xSemaphoreTake(displaySemaphore, portMAX_DELAY) == pdPASS) {
                // OLED 디스플레이 업데이트
                snprintf(buffer, sizeof(buffer), "Temp: %.2f C", data.temperature);
                SSD1306_WriteString(0, 0, buffer);

                snprintf(buffer, sizeof(buffer), "Hum: %.2f %%", data.humidity);
                SSD1306_WriteString(0, 10, buffer);

                SSD1306_UpdateScreen();

                // 세마포어 반환
                xSemaphoreGive(displaySemaphore);
            }
        }

        // 500ms 대기
        vTaskDelay(pdMS_TO_TICKS(500));
    }
}

Communication Task

void CommunicationTask(void *pvParameters) {
    SensorData data;
    char buffer[64];

    while (1) {
        // 큐에서 데이터 수신
        if (xQueueReceive(sensorQueue, &data, portMAX_DELAY) == pdPASS) {
            // 데이터 UART로 전송
            snprintf(buffer, sizeof(buffer), "Temp: %.2f C, Hum: %.2f %%\r\n", data.temperature, data.humidity);
            HAL_UART_Transmit(&huart2, (uint8_t *)buffer, strlen(buffer), HAL_MAX_DELAY);
        }

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

3. 메인 함수

int main(void) {
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    MX_I2C1_Init();
    MX_USART2_UART_Init();

    // 큐와 세마포어 생성
    sensorQueue = xQueueCreate(QUEUE_LENGTH, QUEUE_ITEM_SIZE);
    displaySemaphore = xSemaphoreCreateMutex();

    // 태스크 생성
    xTaskCreate(SensorTask, "SensorTask", 128, NULL, 2, NULL);
    xTaskCreate(DisplayTask, "DisplayTask", 128, NULL, 1, NULL);
    xTaskCreate(CommunicationTask, "CommTask", 128, NULL, 1, NULL);

    // 스케줄러 시작
    vTaskStartScheduler();

    while (1) {}
}

실행 결과

  • OLED 디스플레이: 온도와 습도 데이터를 주기적으로 업데이트
  • UART 터미널 출력: 센서 데이터를 텍스트 형식으로 출력

프로젝트 최적화

  1. 태스크 우선순위 조정: Sensor Task를 최우선으로 설정
  2. 큐 길이 조정: 데이터 손실 방지를 위해 충분한 큐 크기 확보
  3. 전력 관리: 저전력 모드 활성화로 에너지 절약

결론

이번 프로젝트를 통해 FreeRTOS를 활용한 IoT 시스템 구현의 기초를 배웠습니다. 태스크, 큐, 세마포어를 조합해 효과적인 태스크 관리와 자원 활용이 가능함을 확인했습니다. 이를 바탕으로 더 복잡한 IoT 프로젝트를 설계해보세요!

반응형