Skip to content

FreeRTOS 驱动开发

FreeRTOS 驱动架构

FreeRTOS 本身不提供统一的驱动框架,驱动通常由三部分组成:

应用任务
    │  队列 / 信号量 / 事件组

驱动任务(可选)
    │  直接调用

底层驱动(寄存器操作 + ISR)


硬件外设

UART 驱动完整示例(STM32 + FreeRTOS)

c
/* uart_driver.h */
#ifndef UART_DRIVER_H
#define UART_DRIVER_H

#include "FreeRTOS.h"
#include "queue.h"
#include "semphr.h"
#include <stdint.h>
#include <stddef.h>

#define UART_RX_QUEUE_SIZE  256
#define UART_TX_QUEUE_SIZE  256

typedef struct {
    UART_HandleTypeDef *huart;
    QueueHandle_t       rx_queue;
    QueueHandle_t       tx_queue;
    SemaphoreHandle_t   tx_done_sem;
} uart_drv_t;

int  uart_drv_init(uart_drv_t *drv, UART_HandleTypeDef *huart);
int  uart_drv_send(uart_drv_t *drv, const uint8_t *data,
                   size_t len, uint32_t timeout_ms);
int  uart_drv_recv(uart_drv_t *drv, uint8_t *data,
                   size_t len, uint32_t timeout_ms);
void uart_drv_rx_isr_callback(uart_drv_t *drv, uint8_t byte);
void uart_drv_tx_isr_callback(uart_drv_t *drv);

#endif
c
/* uart_driver.c */
#include "uart_driver.h"
#include "stm32f4xx_hal.h"

int uart_drv_init(uart_drv_t *drv, UART_HandleTypeDef *huart)
{
    drv->huart = huart;

    /* 使用静态分配避免堆碎片 */
    static StaticQueue_t rx_queue_buf, tx_queue_buf;
    static uint8_t rx_storage[UART_RX_QUEUE_SIZE];
    static uint8_t tx_storage[UART_TX_QUEUE_SIZE];

    drv->rx_queue = xQueueCreateStatic(UART_RX_QUEUE_SIZE, sizeof(uint8_t),
                                        rx_storage, &rx_queue_buf);
    drv->tx_queue = xQueueCreateStatic(UART_TX_QUEUE_SIZE, sizeof(uint8_t),
                                        tx_storage, &tx_queue_buf);

    static StaticSemaphore_t tx_sem_buf;
    drv->tx_done_sem = xSemaphoreCreateBinaryStatic(&tx_sem_buf);

    if (!drv->rx_queue || !drv->tx_queue || !drv->tx_done_sem)
        return -1;

    /* 使能 UART 接收中断 */
    __HAL_UART_ENABLE_IT(huart, UART_IT_RXNE);
    return 0;
}

int uart_drv_send(uart_drv_t *drv, const uint8_t *data,
                   size_t len, uint32_t timeout_ms)
{
    /* 使用 DMA 发送 */
    HAL_StatusTypeDef ret = HAL_UART_Transmit_DMA(drv->huart,
                                                    (uint8_t *)data, len);
    if (ret != HAL_OK)
        return -1;

    /* 等待 DMA 发送完成 */
    if (xSemaphoreTake(drv->tx_done_sem,
                        pdMS_TO_TICKS(timeout_ms)) != pdTRUE) {
        HAL_UART_AbortTransmit(drv->huart);
        return -2;   /* 超时 */
    }
    return (int)len;
}

int uart_drv_recv(uart_drv_t *drv, uint8_t *data,
                   size_t len, uint32_t timeout_ms)
{
    TickType_t deadline = xTaskGetTickCount() + pdMS_TO_TICKS(timeout_ms);
    size_t received = 0;

    while (received < len) {
        TickType_t now = xTaskGetTickCount();
        if (now >= deadline)
            break;

        if (xQueueReceive(drv->rx_queue, &data[received],
                           deadline - now) == pdTRUE) {
            received++;
        }
    }
    return (int)received;
}

/* 在 UART ISR 中调用(接收一个字节) */
void uart_drv_rx_isr_callback(uart_drv_t *drv, uint8_t byte)
{
    BaseType_t woken = pdFALSE;
    xQueueSendFromISR(drv->rx_queue, &byte, &woken);
    portYIELD_FROM_ISR(woken);
}

/* 在 DMA 发送完成 ISR 中调用 */
void uart_drv_tx_isr_callback(uart_drv_t *drv)
{
    BaseType_t woken = pdFALSE;
    xSemaphoreGiveFromISR(drv->tx_done_sem, &woken);
    portYIELD_FROM_ISR(woken);
}

SPI 驱动(基于事件组)

c
#include "event_groups.h"

#define SPI_TX_DONE_BIT  BIT(0)
#define SPI_RX_DONE_BIT  BIT(1)
#define SPI_ERROR_BIT    BIT(2)

typedef struct {
    SPI_HandleTypeDef *hspi;
    EventGroupHandle_t events;
    SemaphoreHandle_t  bus_mutex;   /* 总线互斥 */
} spi_drv_t;

int spi_drv_transfer(spi_drv_t *drv, const uint8_t *tx,
                      uint8_t *rx, size_t len, uint32_t timeout_ms)
{
    /* 获取总线互斥锁(多任务共享 SPI 总线时) */
    if (xSemaphoreTake(drv->bus_mutex, pdMS_TO_TICKS(timeout_ms)) != pdTRUE)
        return -EBUSY;

    /* 清除事件位 */
    xEventGroupClearBits(drv->events, SPI_TX_DONE_BIT | SPI_ERROR_BIT);

    /* 启动 DMA 全双工传输 */
    HAL_SPI_TransmitReceive_DMA(drv->hspi, (uint8_t *)tx, rx, len);

    /* 等待完成 */
    EventBits_t bits = xEventGroupWaitBits(
        drv->events,
        SPI_TX_DONE_BIT | SPI_ERROR_BIT,
        pdTRUE,    /* 清除等待到的位 */
        pdFALSE,   /* 任一位满足即返回 */
        pdMS_TO_TICKS(timeout_ms)
    );

    xSemaphoreGive(drv->bus_mutex);

    if (bits & SPI_ERROR_BIT)
        return -EIO;
    if (!(bits & SPI_TX_DONE_BIT))
        return -ETIMEDOUT;

    return (int)len;
}

任务通知(最轻量的同步)

c
/* 驱动任务句柄 */
static TaskHandle_t uart_task_handle;

/* ISR 中通知任务 */
void USART1_IRQHandler(void)
{
    BaseType_t woken = pdFALSE;
    /* 发送通知值(可携带状态信息) */
    vTaskNotifyGiveFromISR(uart_task_handle, &woken);
    portYIELD_FROM_ISR(woken);
}

/* 驱动任务 */
static void uart_task(void *param)
{
    while (1) {
        /* 等待中断通知,超时 100ms */
        uint32_t notif = ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(100));
        if (notif > 0) {
            /* 处理接收数据 */
            process_rx_data();
        } else {
            /* 超时:检查硬件状态 */
            check_hardware_status();
        }
    }
}

低功耗驱动模式

c
/* 在 FreeRTOS idle 钩子中进入低功耗 */
void vApplicationIdleHook(void)
{
    /* 关闭不需要的外设时钟 */
    __HAL_RCC_SPI2_CLK_DISABLE();

    /* 进入 STOP 模式(等待中断唤醒) */
    HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);

    /* 唤醒后重新配置时钟 */
    SystemClock_Config();
    __HAL_RCC_SPI2_CLK_ENABLE();
}

/* tickless idle(减少定时器中断) */
/* 在 FreeRTOSConfig.h 中启用 */
#define configUSE_TICKLESS_IDLE  1

常见陷阱

陷阱说明解决
ISR 中使用非 FromISR API导致断言失败或死锁所有 ISR 中的 FreeRTOS 调用必须用 FromISR 版本
忘记 portYIELD_FROM_ISR高优先级任务不能及时运行ISR 末尾检查 xHigherPriorityTaskWoken
栈溢出任务栈太小启用 configCHECK_FOR_STACK_OVERFLOW,用 uxTaskGetStackHighWaterMark 监控
优先级反转低优先级任务持有高优先级任务需要的互斥锁使用 xSemaphoreCreateMutex(支持优先级继承)

褚成志的笔记