应用于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() |
| 内存溢出 | 动态内存分配 | 使用静态内存池 |
| 消息乱序 | 优先级未设置 | 为重要消息设置高优先级 |
| 响应延迟 | 接收任务优先级低 | 提高接收任务优先级 |