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

相关推荐
sramdram1 天前
基于MCU微控制器的冰箱压缩机系统解决方案
单片机·嵌入式硬件·mcu·mcu微控制器
小黄蚁1 天前
使用状态机实现按键消抖
stm32·单片机
集芯微电科技有限公司1 天前
16V/5A高效率COT模式同步降压转换器集成高低功率MOSFET
人工智能·单片机·嵌入式硬件·生成对抗网络·计算机外设
巴巴媛6661 天前
5. 智能小车
单片机·嵌入式硬件
longxingiot1 天前
单片机数传终端助力物联网数据稳定采集传输
单片机·嵌入式硬件·物联网
合兴软件@1 天前
芯片适配快讯:ISDT 完成对意法半导体(ST)Stellar MCU 平台的适配
单片机·嵌入式硬件·嵌入式实时数据库
KaifuZeng1 天前
电源面试问题汇总二
单片机·嵌入式硬件·面试
科芯创展1 天前
1.8VIN,3.3/5VOUT,电荷泵升压芯片,XZ3110
stm32
老k一步步来1 天前
CAN通信知识汇总,软件和硬件部分相关问题。
单片机·嵌入式硬件·人机交互