基于CMSIS-OS V2/FreeRTOS的STM32队列传结构体触发HardFault问题
我基于STM32CubeMX生成的CMSIS-OS V2/FreeRTOS框架开发,在任务间通过队列传递结构体时出现HardFault错误。调试发现,执行osMessageQueuePut相关代码时会跳转到HardFault错误处理函数。
任务代码
void pmt_task_RX_uart_func(void *argument) { /* USER CODE BEGIN pmt_task_RX_uart_func */ char msg[60]; Fuente_MPPC_C11204 Pmt_AUX; osStatus_t eventPMT_buffer; /* Infinite loop */ for(;;) { eventPMT_buffer = osMessageQueueGet(PMT_Buffer_QueueHandle, &msg, NULL, osWaitForever); // 等待消息 if(eventPMT_buffer==osOK){ // 消息已从队列取出 osStatus_t eventPMT_struct = osMessageQueueGet(PMT_Struct_QueueAUXHandle, &Pmt_AUX, NULL, osWaitForever); if(eventPMT_struct==osOK){ // 结构体已从队列取出 strcpy((char*) Pmt_AUX.cmd_recieved_buffer, msg); Pmt_AUX = hps_recivecmd(&Pmt_AUX); osStatus_t eventSen = osMessageQueuePut(PMT_RX_QueueHandle, &Pmt_AUX, 0U, osWaitForever); if(eventPMT_struct==osOK){ // 消息已入队 osThreadYield(); } else if(eventPMT_struct==osErrorResource){ // 队列已满 } else if(eventPMT_struct==osErrorTimeout){ // 消息入队超时 } else if(eventPMT_struct==osErrorParameter){ // 参数mq_id为空或无效,ISR中指定了非零超时时间 } } else if(eventPMT_struct==osErrorResource){ // 队列已满 } else if(eventPMT_struct==osErrorTimeout){ // 消息入队超时 } else if(eventPMT_struct==osErrorParameter){ // 参数mq_id为空或无效,ISR中指定了非零超时时间 } } else if(eventPMT_buffer==osErrorResource){ // 队列已满 } else if(eventPMT_buffer==osErrorTimeout){ // 消息入队超时 } else if(eventPMT_buffer==osErrorParameter){ // 参数mq_id为空或无效,ISR中指定了非零超时时间 } } /* USER CODE END pmt_task_RX_uart_func */ }
触发错误的代码段
调试确认,执行以下代码时触发HardFault:
osStatus_t eventSen = osMessageQueuePut(PMT_RX_QueueHandle, &Pmt_AUX, 0U, osWaitForever); if(eventPMT_struct==osOK){ // 消息已入队 osThreadYield(); }
结构体定义
/*Typedef start*/ typedef enum { HST = 0, HRT, HPO, HGS, HGV, HGC, HGT, HFI, HGN, HOF, HON, HRE, HCM, HSC, HRC, HBV }hps_cmd_t; typedef struct Fuente_MPPC_C11204{ UART_HandleTypeDef * uart; uint8_t cmd_recieved_buffer[60]; uint8_t buffer_PMT_INDEX; char data2send[32]; hps_cmd_t comSend; // 发送的命令(hps_cmd_t格式) char cmdAwait[5]; // 等待接收的命令 uint8_t statusUart; // 命令响应接收状态:0成功,1失败 char error[5]; // 电源错误信息 char secondCoefT1[5]; // 温度系数 char secondCoefT2[5]; // char FirstCoefT1[5]; // char FirstCoefT2[5]; // char RefVoltage[5]; // 电源基准电压Vb char RefTemp[5]; // 电源基准温度Tb char status[5]; // 电源状态 char OutVoltage[5]; // 输出电压 char OutCurrent[5]; // 输出电流 char MPPCTemp[5]; // 电源温度 char deviceName[17]; // 电源设备名称 char Version[17]; // 版本号 char BuildDate[12]; // 构建日期 char SerialNum[17]; // 序列号 uint8_t mode; // 温度补偿模式:启用/禁用 char FunctionSettings[5]; // 电源功能设置 }Fuente_MPPC_C11204;
队列创建代码
comman_tQueueHandle = osMessageQueueNew (1, sizeof(command_t), &comman_tQueue_attributes); /* 创建PMT_Struct_Queue */ PMT_Struct_QueueHandle = osMessageQueueNew (1, sizeof(Fuente_MPPC_C11204), &PMT_Struct_Queue_attributes); /* 创建PMT_Struct_QueueAUX */ PMT_Struct_QueueAUXHandle = osMessageQueueNew (1, sizeof(Fuente_MPPC_C11204), &PMT_Struct_QueueAUX_attributes); /* 创建PMT_RX_Queue */ PMT_RX_QueueHandle = osMessageQueueNew (1, sizeof(Fuente_MPPC_C11204), &PMT_RX_Queue_attributes); TxRaspBuffQueueHandle = osMessageQueueNew (1, sizeof(uint8_t[100]), &TxRaspBuffQueue_attributes); /* 创建RxRaspBuffQueue */ RxRaspBuffQueueHandle = osMessageQueueNew (1, sizeof(uint8_t[100]), &RxRaspBuffQueue_attributes); /* 创建PMT_Buffer_Queue */ PMT_Buffer_QueueHandle = osMessageQueueNew (1, sizeof(uint8_t[100]), &PMT_Buffer_Queue_attributes);
环境信息
所有任务优先级相同,栈大小均为500字(即2000字节)。我怀疑问题与队列或栈大小有关,但未找到具体原因,希望得到排查方向与解决方案。
排查方向与解决方案
修复变量判断逻辑错误
代码中osMessageQueuePut执行后,错误地使用eventPMT_struct(之前osMessageQueueGet的返回值)来判断入队结果,应该使用新定义的eventSen。这个逻辑错误可能导致后续分支执行异常,甚至访问非法内存触发HardFault。修正后的代码:osStatus_t eventSen = osMessageQueuePut(PMT_RX_QueueHandle, &Pmt_AUX, 0U, osWaitForever); if(eventSen==osOK){ // 消息已入队 osThreadYield(); }检查任务栈溢出
虽然任务栈配置为2000字节,但仍需确认是否存在栈溢出情况:- 临时增大任务栈至3000字节(750字),测试是否还会触发HardFault
- 调用FreeRTOS API
uxTaskGetStackHighWaterMark(pmt_task_RX_uart_Handle)获取任务栈的剩余空间最小值,判断是否接近栈极限
验证队列句柄有效性
确认PMT_RX_QueueHandle是否被正确初始化,未被意外篡改。可以在osMessageQueuePut前添加断言检查:#include <assert.h> // ... assert(PMT_RX_QueueHandle != NULL); osStatus_t eventSen = osMessageQueuePut(PMT_RX_QueueHandle, &Pmt_AUX, 0U, osWaitForever);排查
hps_recivecmd函数的内存操作
检查hps_recivecmd函数是否存在非法内存访问:比如越界修改结构体成员、返回无效的结构体实例等。可以单步调试该函数,确认结构体Pmt_AUX在执行前后的内存布局是否正常。处理内存对齐问题
ARM Cortex-M架构对多字节数据类型有对齐要求,若结构体存在对齐异常可能导致HardFault。可以强制结构体按4字节对齐:typedef struct __attribute__((aligned(4))) Fuente_MPPC_C11204{ // 原有结构体成员 }Fuente_MPPC_C11204;
内容的提问来源于stack exchange,提问作者Ignacio Islas

