应用于STM32单片机的消息队列程序

应用于STM32单片机的消息队列程序

一、设计概述

本消息队列程序专为STM32单片机设计,支持裸机环境FreeRTOS环境,具有以下特性:

特性 说明
线程安全 支持中断和任务并发访问
阻塞/非阻塞 支持超时等待和立即返回
优先级 支持消息优先级(高/中/低)
内存管理 静态内存分配,避免碎片
错误处理 完整的返回值判断
多队列 支持创建多个独立队列

二、核心数据结构

2.1 消息队列结构体

/**
 * @file msg_queue.h
 * @brief STM32消息队列头文件
 */

#ifndef __MSG_QUEUE_H
#define __MSG_QUEUE_H

#include "stm32f10x.h"
#include <string.h>
#include <stdbool.h>

// 消息优先级定义
typedef enum {
    MSG_PRIO_LOW = 0,
    MSG_PRIO_MEDIUM = 1,
    MSG_PRIO_HIGH = 2,
    MSG_PRIO_CRITICAL = 3
} MsgPriority;

// 消息类型定义
typedef enum {
    MSG_TYPE_KEY = 0x01,      // 按键消息
    MSG_TYPE_UART = 0x02,     // 串口消息
    MSG_TYPE_ADC = 0x03,      // ADC消息
    MSG_TYPE_TIMER = 0x04,    // 定时器消息
    MSG_TYPE_SYSTEM = 0x05,   // 系统消息
    MSG_TYPE_USER = 0x10      // 用户自定义消息
} MsgType;

// 消息结构体
typedef struct {
    MsgType type;             // 消息类型
    MsgPriority priority;     // 消息优先级
    uint16_t id;              // 消息ID
    uint16_t length;          // 数据长度
    uint8_t data[32];         // 消息数据
    uint32_t timestamp;       // 时间戳
} Message;

// 队列状态
typedef enum {
    QUEUE_OK = 0,
    QUEUE_FULL = 1,
    QUEUE_EMPTY = 2,
    QUEUE_ERROR = 3,
    QUEUE_TIMEOUT = 4
} QueueStatus;

// 消息队列结构体
typedef struct {
    Message *buffer;          // 消息缓冲区
    uint16_t capacity;        // 队列容量
    uint16_t head;            // 头指针
    uint16_t tail;            // 尾指针
    uint16_t count;           // 当前消息数
    uint8_t initialized;      // 初始化标志
    uint8_t overflow_count;    // 溢出计数
    // FreeRTOS相关(如果使用)
    void *os_queue;           // OS队列句柄
    uint8_t use_os;           // 是否使用OS
} MessageQueue;

// 队列句柄
typedef MessageQueue* QueueHandle;

// 函数声明
QueueHandle MsgQueue_Create(uint16_t capacity, uint8_t use_os);
QueueStatus MsgQueue_Send(QueueHandle queue, const Message *msg, uint32_t timeout_ms);
QueueStatus MsgQueue_Receive(QueueHandle queue, Message *msg, uint32_t timeout_ms);
QueueStatus MsgQueue_SendFromISR(QueueHandle queue, const Message *msg);
uint16_t MsgQueue_GetCount(QueueHandle queue);
uint8_t MsgQueue_IsFull(QueueHandle queue);
uint8_t MsgQueue_IsEmpty(QueueHandle queue);
void MsgQueue_Clear(QueueHandle queue);
void MsgQueue_Delete(QueueHandle queue);

#endif /* __MSG_QUEUE_H */

三、裸机版本实现(无RTOS)

3.1 队列管理函数

/**
 * @file msg_queue_bare.c
 * @brief 裸机版本消息队列实现
 */

#include "msg_queue.h"

// 全局队列数组(最多支持8个队列)
#define MAX_QUEUES 8
static MessageQueue queues[MAX_QUEUES];
static uint8_t queue_count = 0;

// 临界区保护(关中断)
static inline uint32_t EnterCritical(void) {
    uint32_t primask = __get_PRIMASK();
    __disable_irq();
    return primask;
}

static inline void ExitCritical(uint32_t primask) {
    __set_PRIMASK(primask);
}

/**
 * @brief 创建消息队列
 * @param capacity 队列容量
 * @param use_os 是否使用操作系统(裸机版本设为0)
 * @return 队列句柄
 */
QueueHandle MsgQueue_Create(uint16_t capacity, uint8_t use_os) {
    if (queue_count >= MAX_QUEUES || capacity == 0) {
        return NULL;
    }
    
    MessageQueue *queue = &queues[queue_count];
    
    // 分配消息缓冲区(静态内存)
    queue->buffer = (Message *)malloc(sizeof(Message) * capacity);
    if (queue->buffer == NULL) {
        return NULL;
    }
    
    // 初始化队列参数
    queue->capacity = capacity;
    queue->head = 0;
    queue->tail = 0;
    queue->count = 0;
    queue->initialized = 1;
    queue->overflow_count = 0;
    queue->use_os = 0;  // 裸机版本不使用OS
    
    queue_count++;
    return queue;
}

/**
 * @brief 发送消息(非中断安全)
 * @param queue 队列句柄
 * @param msg 消息指针
 * @param timeout_ms 超时时间(毫秒,0表示立即返回)
 * @return 队列状态
 */
QueueStatus MsgQueue_Send(QueueHandle queue, const Message *msg, uint32_t timeout_ms) {
    if (queue == NULL || msg == NULL || !queue->initialized) {
        return QUEUE_ERROR;
    }
    
    uint32_t start_time = HAL_GetTick();
    
    while (MsgQueue_IsFull(queue)) {
        if (timeout_ms == 0) {
            return QUEUE_FULL;
        }
        if ((HAL_GetTick() - start_time) >= timeout_ms) {
            return QUEUE_TIMEOUT;
        }
        // 等待队列有空位
    }
    
    // 进入临界区
    uint32_t primask = EnterCritical();
    
    // 拷贝消息到队列尾部
    memcpy(&queue->buffer[queue->tail], msg, sizeof(Message));
    queue->tail = (queue->tail + 1) % queue->capacity;
    queue->count++;
    
    // 退出临界区
    ExitCritical(primask);
    
    return QUEUE_OK;
}

/**
 * @brief 从中断发送消息(中断安全)
 * @param queue 队列句柄
 * @param msg 消息指针
 * @return 队列状态
 */
QueueStatus MsgQueue_SendFromISR(QueueHandle queue, const Message *msg) {
    if (queue == NULL || msg == NULL || !queue->initialized) {
        return QUEUE_ERROR;
    }
    
    // 检查队列是否已满
    if (MsgQueue_IsFull(queue)) {
        queue->overflow_count++;
        return QUEUE_FULL;
    }
    
    // 拷贝消息到队列尾部
    memcpy(&queue->buffer[queue->tail], msg, sizeof(Message));
    queue->tail = (queue->tail + 1) % queue->capacity;
    queue->count++;
    
    return QUEUE_OK;
}

/**
 * @brief 接收消息
 * @param queue 队列句柄
 * @param msg 消息缓冲区
 * @param timeout_ms 超时时间(毫秒,0表示立即返回)
 * @return 队列状态
 */
QueueStatus MsgQueue_Receive(QueueHandle queue, Message *msg, uint32_t timeout_ms) {
    if (queue == NULL || msg == NULL || !queue->initialized) {
        return QUEUE_ERROR;
    }
    
    uint32_t start_time = HAL_GetTick();
    
    while (MsgQueue_IsEmpty(queue)) {
        if (timeout_ms == 0) {
            return QUEUE_EMPTY;
        }
        if ((HAL_GetTick() - start_time) >= timeout_ms) {
            return QUEUE_TIMEOUT;
        }
        // 等待队列有消息
    }
    
    // 进入临界区
    uint32_t primask = EnterCritical();
    
    // 从队列头部取出消息
    memcpy(msg, &queue->buffer[queue->head], sizeof(Message));
    queue->head = (queue->head + 1) % queue->capacity;
    queue->count--;
    
    // 退出临界区
    ExitCritical(primask);
    
    return QUEUE_OK;
}

/**
 * @brief 获取队列中消息数量
 * @param queue 队列句柄
 * @return 消息数量
 */
uint16_t MsgQueue_GetCount(QueueHandle queue) {
    if (queue == NULL || !queue->initialized) {
        return 0;
    }
    return queue->count;
}

/**
 * @brief 检查队列是否已满
 * @param queue 队列句柄
 * @return 1表示已满,0表示未满
 */
uint8_t MsgQueue_IsFull(QueueHandle queue) {
    if (queue == NULL || !queue->initialized) {
        return 1;
    }
    return queue->count >= queue->capacity;
}

/**
 * @brief 检查队列是否为空
 * @param queue 队列句柄
 * @return 1表示为空,0表示非空
 */
uint8_t MsgQueue_IsEmpty(QueueHandle queue) {
    if (queue == NULL || !queue->initialized) {
        return 1;
    }
    return queue->count == 0;
}

/**
 * @brief 清空队列
 * @param queue 队列句柄
 */
void MsgQueue_Clear(QueueHandle queue) {
    if (queue == NULL || !queue->initialized) {
        return;
    }
    
    uint32_t primask = EnterCritical();
    queue->head = 0;
    queue->tail = 0;
    queue->count = 0;
    ExitCritical(primask);
}

/**
 * @brief 删除队列
 * @param queue 队列句柄
 */
void MsgQueue_Delete(QueueHandle queue) {
    if (queue == NULL || !queue->initialized) {
        return;
    }
    
    free(queue->buffer);
    queue->initialized = 0;
}

四、FreeRTOS版本实现

4.1 FreeRTOS兼容层

/**
 * @file msg_queue_freertos.c
 * @brief FreeRTOS版本消息队列实现
 */

#include "msg_queue.h"
#include "FreeRTOS.h"
#include "queue.h"
#include "semphr.h"
#include "task.h"

/**
 * @brief 创建消息队列(FreeRTOS版本)
 * @param capacity 队列容量
 * @param use_os 是否使用操作系统(设为1)
 * @return 队列句柄
 */
QueueHandle MsgQueue_Create(uint16_t capacity, uint8_t use_os) {
    if (!use_os) {
        // 调用裸机版本
        return MsgQueue_Create_Bare(capacity, 0);
    }
    
    if (queue_count >= MAX_QUEUES || capacity == 0) {
        return NULL;
    }
    
    MessageQueue *queue = &queues[queue_count];
    
    // 创建FreeRTOS队列
    queue->os_queue = xQueueCreate(capacity, sizeof(Message));
    if (queue->os_queue == NULL) {
        return NULL;
    }
    
    queue->capacity = capacity;
    queue->head = 0;
    queue->tail = 0;
    queue->count = 0;
    queue->initialized = 1;
    queue->overflow_count = 0;
    queue->use_os = 1;
    
    queue_count++;
    return queue;
}

/**
 * @brief 发送消息(FreeRTOS版本)
 * @param queue 队列句柄
 * @param msg 消息指针
 * @param timeout_ms 超时时间(毫秒)
 * @return 队列状态
 */
QueueStatus MsgQueue_Send(QueueHandle queue, const Message *msg, uint32_t timeout_ms) {
    if (queue == NULL || msg == NULL || !queue->initialized) {
        return QUEUE_ERROR;
    }
    
    if (!queue->use_os) {
        // 调用裸机版本
        return MsgQueue_Send_Bare(queue, msg, timeout_ms);
    }
    
    BaseType_t result;
    TickType_t ticks = pdMS_TO_TICKS(timeout_ms);
    
    result = xQueueSend((QueueHandle_t)queue->os_queue, msg, ticks);
    
    if (result == pdPASS) {
        return QUEUE_OK;
    } else if (result == errQUEUE_FULL) {
        return QUEUE_FULL;
    } else {
        return QUEUE_TIMEOUT;
    }
}

/**
 * @brief 从中断发送消息(FreeRTOS版本)
 * @param queue 队列句柄
 * @param msg 消息指针
 * @return 队列状态
 */
QueueStatus MsgQueue_SendFromISR(QueueHandle queue, const Message *msg) {
    if (queue == NULL || msg == NULL || !queue->initialized) {
        return QUEUE_ERROR;
    }
    
    if (!queue->use_os) {
        // 调用裸机版本
        return MsgQueue_SendFromISR_Bare(queue, msg);
    }
    
    BaseType_t higher_priority_task_woken = pdFALSE;
    BaseType_t result;
    
    result = xQueueSendFromISR((QueueHandle_t)queue->os_queue, 
                              msg, &higher_priority_task_woken);
    
    // 如果需要上下文切换
    portYIELD_FROM_ISR(higher_priority_task_woken);
    
    if (result == pdPASS) {
        return QUEUE_OK;
    } else {
        return QUEUE_FULL;
    }
}

/**
 * @brief 接收消息(FreeRTOS版本)
 * @param queue 队列句柄
 * @param msg 消息缓冲区
 * @param timeout_ms 超时时间(毫秒)
 * @return 队列状态
 */
QueueStatus MsgQueue_Receive(QueueHandle queue, Message *msg, uint32_t timeout_ms) {
    if (queue == NULL || msg == NULL || !queue->initialized) {
        return QUEUE_ERROR;
    }
    
    if (!queue->use_os) {
        // 调用裸机版本
        return MsgQueue_Receive_Bare(queue, msg, timeout_ms);
    }
    
    BaseType_t result;
    TickType_t ticks = pdMS_TO_TICKS(timeout_ms);
    
    result = xQueueReceive((QueueHandle_t)queue->os_queue, msg, ticks);
    
    if (result == pdPASS) {
        return QUEUE_OK;
    } else if (result == errQUEUE_EMPTY) {
        return QUEUE_EMPTY;
    } else {
        return QUEUE_TIMEOUT;
    }
}

五、应用示例

5.1 主程序示例

/**
 * @file main.c
 * @brief STM32消息队列应用示例
 */

#include "msg_queue.h"
#include "stm32f10x.h"

// 全局队列句柄
QueueHandle key_queue;
QueueHandle uart_queue;
QueueHandle system_queue;

// 任务函数声明
void Key_Task(void *arg);
void UART_Task(void *arg);
void System_Task(void *arg);
void LED_Task(void *arg);

int main(void) {
    // 系统初始化
    System_Init();
    
    // 创建消息队列
    #ifdef USE_FREERTOS
    key_queue = MsgQueue_Create(10, 1);    // 按键队列,使用FreeRTOS
    uart_queue = MsgQueue_Create(20, 1);   // 串口队列,使用FreeRTOS
    system_queue = MsgQueue_Create(5, 1);  // 系统队列,使用FreeRTOS
    #else
    key_queue = MsgQueue_Create(10, 0);    // 按键队列,裸机版本
    uart_queue = MsgQueue_Create(20, 0);   // 串口队列,裸机版本
    system_queue = MsgQueue_Create(5, 0);  // 系统队列,裸机版本
    #endif
    
    if (key_queue == NULL || uart_queue == NULL || system_queue == NULL) {
        Error_Handler();
    }
    
    #ifdef USE_FREERTOS
    // 创建FreeRTOS任务
    xTaskCreate(Key_Task, "Key", 128, NULL, 2, NULL);
    xTaskCreate(UART_Task, "UART", 256, NULL, 1, NULL);
    xTaskCreate(System_Task, "System", 512, NULL, 3, NULL);
    xTaskCreate(LED_Task, "LED", 128, NULL, 1, NULL);
    
    // 启动调度器
    vTaskStartScheduler();
    #else
    // 裸机主循环
    while (1) {
        // 处理系统消息
        Message msg;
        if (MsgQueue_Receive(system_queue, &msg, 0) == QUEUE_OK) {
            Process_SystemMessage(&msg);
        }
        
        // 处理按键消息
        if (MsgQueue_Receive(key_queue, &msg, 0) == QUEUE_OK) {
            Process_KeyMessage(&msg);
        }
        
        // 处理串口消息
        if (MsgQueue_Receive(uart_queue, &msg, 0) == QUEUE_OK) {
            Process_UARTMessage(&msg);
        }
        
        // 其他任务
        Task_LED();
        Task_KeyScan();
        Task_UARTReceive();
    }
    #endif
}

// 按键扫描任务(裸机)
void Task_KeyScan(void) {
    static uint8_t key_state = 0;
    uint8_t key_value = Read_Key();
    
    if (key_value != 0 && key_state == 0) {
        key_state = 1;
        
        // 发送按键消息
        Message msg;
        msg.type = MSG_TYPE_KEY;
        msg.priority = MSG_PRIO_MEDIUM;
        msg.id = key_value;
        msg.length = 0;
        msg.timestamp = HAL_GetTick();
        
        #ifdef USE_FREERTOS
        MsgQueue_Send(key_queue, &msg, 0);
        #else
        MsgQueue_SendFromISR(key_queue, &msg);  // 中断安全版本
        #endif
    } else if (key_value == 0) {
        key_state = 0;
    }
}

// 串口接收任务
void Task_UARTReceive(void) {
    if (UART_ReceiveComplete()) {
        uint8_t *rx_data = Get_UARTBuffer();
        uint16_t length = Get_UARTLength();
        
        // 发送串口消息
        Message msg;
        msg.type = MSG_TYPE_UART;
        msg.priority = MSG_PRIO_LOW;
        msg.id = 0;
        msg.length = length > 32 ? 32 : length;
        memcpy(msg.data, rx_data, msg.length);
        msg.timestamp = HAL_GetTick();
        
        MsgQueue_Send(uart_queue, &msg, 100);  // 等待100ms
    }
}

// 系统消息处理
void Process_SystemMessage(Message *msg) {
    switch (msg->id) {
        case SYS_MSG_RESET:
            NVIC_SystemReset();
            break;
        case SYS_MSG_SLEEP:
            Enter_StandbyMode();
            break;
        case SYS_MSG_CALIBRATE:
            Start_Calibration();
            break;
        default:
            break;
    }
}

// 按键消息处理
void Process_KeyMessage(Message *msg) {
    switch (msg->id) {
        case KEY_UP:
            Increase_Parameter();
            break;
        case KEY_DOWN:
            Decrease_Parameter();
            break;
        case KEY_ENTER:
            Confirm_Parameter();
            break;
        default:
            break;
    }
}

// 串口消息处理
void Process_UARTMessage(Message *msg) {
    // 解析串口命令
    if (msg->length >= 2 && msg->data[0] == 'S' && msg->data[1] == 'T') {
        // 设置命令
        uint8_t value = msg->data[2];
        Set_SystemParameter(value);
    }
}

5.2 中断中使用示例

/**
 * @file interrupt_example.c
 * @brief 中断中使用消息队列示例
 */

#include "msg_queue.h"

// 定时器中断处理函数
void TIM2_IRQHandler(void) {
    if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) {
        TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
        
        // 发送定时器消息(中断安全)
        Message msg;
        msg.type = MSG_TYPE_TIMER;
        msg.priority = MSG_PRIO_HIGH;
        msg.id = TIM2_IRQn;
        msg.length = 0;
        msg.timestamp = HAL_GetTick();
        
        MsgQueue_SendFromISR(system_queue, &msg);
    }
}

// 外部中断处理函数
void EXTI0_IRQHandler(void) {
    if (EXTI_GetITStatus(EXTI_Line0) != RESET) {
        EXTI_ClearITPendingBit(EXTI_Line0);
        
        // 发送外部中断消息
        Message msg;
        msg.type = MSG_TYPE_SYSTEM;
        msg.priority = MSG_PRIO_CRITICAL;
        msg.id = EXTI0_IRQn;
        msg.length = 0;
        msg.timestamp = HAL_GetTick();
        
        MsgQueue_SendFromISR(system_queue, &msg);
    }
}

参考代码 应用于STM32单片机的消息队列程序 www.youwenfan.com/contentcsu/60273.html

六、优化与调试

6.1 内存优化

// 使用内存池替代动态分配
#define MSG_POOL_SIZE 100
static Message msg_pool[MSG_POOL_SIZE];
static uint8_t msg_pool_used[MSG_POOL_SIZE];

// 从内存池分配消息
Message* Allocate_Message(void) {
    for (int i = 0; i < MSG_POOL_SIZE; i++) {
        if (!msg_pool_used[i]) {
            msg_pool_used[i] = 1;
            return &msg_pool[i];
        }
    }
    return NULL;
}

// 释放消息回内存池
void Free_Message(Message *msg) {
    if (msg >= msg_pool && msg < msg_pool + MSG_POOL_SIZE) {
        uint32_t index = msg - msg_pool;
        msg_pool_used[index] = 0;
    }
}

6.2 调试支持

// 队列调试信息
void Print_QueueInfo(QueueHandle queue) {
    if (queue == NULL || !queue->initialized) {
        printf("Queue not initialized!\r\n");
        return;
    }
    
    printf("Queue Info:\r\n");
    printf("  Capacity: %d\r\n", queue->capacity);
    printf("  Count: %d\r\n", queue->count);
    printf("  Head: %d\r\n", queue->head);
    printf("  Tail: %d\r\n", queue->tail);
    printf("  Overflow: %d\r\n", queue->overflow_count);
    printf("  Usage: %.1f%%\r\n", (float)queue->count / queue->capacity * 100);
}

// 消息内容打印
void Print_Message(const Message *msg) {
    printf("Message:\r\n");
    printf("  Type: 0x%02X\r\n", msg->type);
    printf("  Priority: %d\r\n", msg->priority);
    printf("  ID: %d\r\n", msg->id);
    printf("  Length: %d\r\n", msg->length);
    printf("  Timestamp: %lu\r\n", msg->timestamp);
    
    if (msg->length > 0) {
        printf("  Data: ");
        for (int i = 0; i < msg->length; i++) {
            printf("%02X ", msg->data[i]);
        }
        printf("\r\n");
    }
}

七、使用建议

7.1 队列设计建议

队列类型 容量建议 用途
按键队列 5~10 存储按键事件
串口队列 20~50 存储串口数据
系统队列 3~5 存储系统命令
ADC队列 10~20 存储采样数据
日志队列 50~100 存储调试日志

7.2 优先级设计

// 消息优先级使用示例
void Send_UrgentMessage(void) {
    Message msg;
    msg.type = MSG_TYPE_SYSTEM;
    msg.priority = MSG_PRIO_CRITICAL;  // 最高优先级
    msg.id = EMERGENCY_SHUTDOWN;
    
    // 立即发送,不等待
    MsgQueue_Send(system_queue, &msg, 0);
}

void Send_LogMessage(char *log) {
    Message msg;
    msg.type = MSG_TYPE_SYSTEM;
    msg.priority = MSG_PRIO_LOW;  // 最低优先级
    msg.id = LOG_MESSAGE;
    strcpy((char*)msg.data, log);
    msg.length = strlen(log);
    
    // 等待100ms,允许丢弃
    MsgQueue_Send(system_queue, &msg, 100);
}

7.3 错误处理

// 健壮的错误处理
QueueStatus Safe_SendMessage(QueueHandle queue, const Message *msg, uint32_t timeout) {
    QueueStatus status;
    int retry_count = 3;
    
    while (retry_count-- > 0) {
        status = MsgQueue_Send(queue, msg, timeout);
        
        switch (status) {
            case QUEUE_OK:
                return QUEUE_OK;
                
            case QUEUE_FULL:
                // 队列满,等待一下再重试
                HAL_Delay(10);
                break;
                
            case QUEUE_TIMEOUT:
                // 超时,直接返回
                return QUEUE_TIMEOUT;
                
            default:
                // 其他错误
                return QUEUE_ERROR;
        }
    }
    
    return QUEUE_FULL;  // 重试失败
}

八、常见问题排查

问题 原因 解决方案
消息丢失 队列容量不足 增大队列容量或加快处理速度
系统死机 中断中发送消息阻塞 使用MsgQueue_SendFromISR()
内存溢出 动态内存分配 使用静态内存池
消息乱序 优先级未设置 为重要消息设置高优先级
响应延迟 接收任务优先级低 提高接收任务优先级

 

专注于matlab/simulink,电子电路,编程