STM32HAL + FreeRTOS:改时基进入HardFault_Handler

1、前言

当使用 STM32Cubemx 生成 FreeRTOS 代码时,如果将 SYS > Timebase Source 默认设置为 SysTick ,那么在生成代码时,STM32Cubemx 就会报警 HAL 库与 FreeRTOS 不要共用时基 SysTick 。一般忽略这个警告继续生成代码,也能继续使用。

但是,如果一个工程通过 STM32Cubemx 实现了共用 SysTick ,过一段时间后,通过 STM32Cubemx 修改 SYS > Timebase Source 为 TIMx (HAL 库使用 TIMx 作为时基),再生成代码,那么该工程运行后大概率进入 HardFault_Handler。

为了能够实现" HAL 库使用 TIMx 作为时基, FreeRTOS 使用 SysTick 作为时基",接下来进行探究。这时我使用的是 STM32G473RCT6 。

2、调试过程

2.1、谁直接导致了 HardFault_Handler

系统进入 HardFault_Handler 后暂停,拿到以下信息,如图1。
图1

依据 调试方向 可知,复制 MSP 地址 0x20002840 到 Memory 窗口中得到以下信息,如图2。从开头往后数,数到第6、7个,得到 0x0800AB8D 和 0x0800E34E 。
图2

在 Disassembly 窗口中 Show disassembly at Address : 0x0800AB8D 得到图3,接着 Show disassembly at Address : 0x0800E34E 得到图4。图4就是图3中 xTaskIncrementTick 函数内部。接下来在 Watch 窗口查看涉及到的变量。
图3
图4

在问题语句的上一句 pxTCB = listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList ) 中,重点是 pxDelayedTaskList ,它的定义是指针 PRIVILEGED_DATA static List_t * volatile pxDelayedTaskList 。为了查看代码,在问题语句和它上一行都打上断点,重启系统后全速运行到问题语句上一句,得到图5。pxDelayedTaskList 是空指针。
图5

2.2、空指针为何没有被相关检查阻挡

精简一下问题语句所在的函数 xTaskIncrementTick :

cpp 复制代码
if( listLIST_IS_EMPTY( pxDelayedTaskList ) != pdFALSE )
{
	/* The delayed list is empty.  Set xNextTaskUnblockTime
	to the maximum possible value so it is extremely
	unlikely that the
	if( xTickCount >= xNextTaskUnblockTime ) test will pass
	next time through. */
	xNextTaskUnblockTime = portMAX_DELAY; /*lint !e961 MISRA exception as the casts are only redundant for some ports. */
	break;
}
else
{
	/* The delayed list is not empty, get the value of the
	item at the head of the delayed list.  This is the time
	at which the task at the head of the delayed list must
	be removed from the Blocked state. */
	pxTCB = listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too.  Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */
	xItemValue = listGET_LIST_ITEM_VALUE( &( pxTCB->xStateListItem ) );	// 问题语句
}

为什么空指针 pxDelayedTaskList 可以通过 listLIST_IS_EMPTY 检查呢?查看 listLIST_IS_EMPTY 定义(图6)以及执行语句 if( listLIST_IS_EMPTY( pxDelayedTaskList ) != pdFALSE ) 前的指针 pxDelayedTaskList 的值(图7)。
图6 图7

此时尚未执行 listLIST_IS_EMPTY 检查,下一步就会执行,此时的 pxDelayedTaskList > uxNumberOfItems 非零,所以 listLIST_IS_EMPTY 检查可以正常通过,从而执行 else 分支。

当将要执行问题语句时,如图8,listGET_LIST_ITEM_VALUE 想要访问 pxTCB > xStateListItem ,但是其地址是 0xB1270A4A ,该地址落在 0xA0000000 ~ 0xDFFFFFFF 的外部设备区域,但在 STM32G473 中,该区域未连接任何物理存储器或外设,属于空的保留区。该地址没有实际对应的寄存器或存储器,AHB 总线矩阵未将其路由到任何目标。当内核发起对该地址的访问,AHB 矩阵会返回一个错误响应(slave error),触发总线故障异常(BusFault)或硬件错误(HardFault)。
图8

2.3、如何解决问题

由上述分析可得,是 pxDelayedTaskList 没有被赋值导致它是空指针,从而其成员值是非法的(地址位于保留区,无法读数据)。那么接下来理清它的调用链,在这个过程中保证它能够被赋值,就会解决问题。

2.3.1、pxDelayedTaskList 赋值调用链

搜索 FreeRTOS 资料后发现, pxDelayedTaskList 是要执行 pxDelayedTaskList = &xDelayedTaskList1 的,这一语句所在函数是 prvInitialiseTaskLists ,这个函数的调用链如图9。
图9

2.3.2、pxDelayedTaskList 访问调用链

问题语句所在的函数是 xTaskIncrementTick ,该函数的调用链是 xTaskIncrementTick > xPortSysTickHandler ,全局搜索 xPortSysTickHandler 发现在 FreeRTOSConfig.h 中有 #define xPortSysTickHandler SysTick_Handler 宏定义。

由于我们想让 HAL 库使用 TIMx 作为时基, FreeRTOS 使用 SysTick 作为时基,当 main 函数执行 HAL_Init 后,SysTick_Handler就会每隔1ms执行一次。

2.3.3、解决办法

由小节 2.3.1 和 2.3.2 可知,在 pxDelayedTaskList 被赋值之前, SysTick_Handler 中断已经开始触发并访问 pxDelayedTaskList 。为了在 pxDelayedTaskList 没赋值时跳过 SysTick_Handler 中断,需要在 xPortSysTickHandler 外面套一层检查 pxDelayedTaskList 。

因此,最合适的函数是 cmsis_os.c 的 osSystickHandler 函数。它会先执行 xTaskGetSchedulerState 检查,如果 pxDelayedTaskList 未被赋值,则 if 检查不通过,直到 pxDelayedTaskList 被赋值。

cpp 复制代码
/*************************** Additional specific APIs to Free RTOS ************/
/**
* @brief  Handles the tick increment
* @param  none.
* @retval none.
*/
void osSystickHandler(void)
{

#if (INCLUDE_xTaskGetSchedulerState  == 1 )
  if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED)
  {
#endif  /* INCLUDE_xTaskGetSchedulerState */  
    xPortSysTickHandler();
#if (INCLUDE_xTaskGetSchedulerState  == 1 )
  }
#endif  /* INCLUDE_xTaskGetSchedulerState */  
}

需要在 FreeRTOSConfig.h 中做如下修改:

cpp 复制代码
// 修改前
/* IMPORTANT: This define is commented when used with STM32Cube firmware, when the timebase source is SysTick,
              to prevent overwriting SysTick_Handler defined within STM32Cube HAL */

#define xPortSysTickHandler SysTick_Handler

// 修改后
/* IMPORTANT: This define is commented when used with STM32Cube firmware, when the timebase source is SysTick,
              to prevent overwriting SysTick_Handler defined within STM32Cube HAL */

#define osSystickHandler SysTick_Handler

end

相关推荐
糖糖单片机设计8 小时前
基于 STM32 的小型鸟类宠物繁殖系统
stm32·嵌入式硬件·宠物
电子元器件选型哥10 小时前
一文讲清LLC谐振电路工作原理
单片机·嵌入式硬件·硬件工程师·电子元器件·器件选型
HUI-47410 小时前
EG1162|SOP‑16 高压同步整流降压控制器,最高 600V 输入,大电流 5V 输出优选
嵌入式硬件·硬件工程
POMAGTOR10 小时前
【5G 通讯 POGOPIN 弹簧顶针连接器解决方案】
网络·嵌入式硬件·5g
ZLG_zhiyuan11 小时前
测试方案 | 从“000”到“111”,高效完成100BASE-TX以太网眼图测试
单片机·嵌入式硬件
恒锐丰科技林技术员11 小时前
EG1198:120V 宽压内置 MOS 降压 DC‑DC 电源芯片解析
经验分享·嵌入式硬件·硬件工程
数字新视界12 小时前
能耗管理系统的综合方案与优化实战解析
嵌入式硬件·物联网·数据中心·数据中心基础设施管理·dcim管理系统
深圳市恒锐丰科技杨生12 小时前
EG3525S 推挽 / 半桥 PWM 控制器|屹晶 EGmicro
嵌入式硬件·硬件工程
H_oRIZoN_12 小时前
Linux入门DAY39(51 单片机入门笔记|最小系统、位运算、处理器、数码管基础梳理)
单片机·嵌入式硬件
12.=0.12 小时前
【电学基础】
单片机·嵌入式硬件