01
program
事情的起因
事情的起因还是来自于前段时间自制的STM32H5板子,成功的移植完ST7789的驱动以及用DMA优化完。对这块屏幕的刷新率非常的满意。
于是想着,这不得为屏幕安排上GUI界面!(这也是为什么我选了一块触摸屏)于是打算装上TouchGFX框架。
装TouchGFX时,顺带开启了FreeRTOS来方便任务管理。
但是就在安装完FreeRTOS框架之后,程序死机了!(主要是一段时间没用FreeRTOS这次一次踩了不少坑)。于是就开启了一晚上的排查~
02
Frist
可恶的系统定时器
通过Debug,发现程序会在FreeRTOS完成初始化之后卡在HAL_Delay,而如果在FreeRTOS初始化前调用屏幕的显示,则是并没有问题的。那么显然是HAL_Delay函数卡住了。
这里我们讲解一下HAL_Delay函数的运行逻辑。
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){ /* USER CODE BEGIN Callback 0 */ /* USER CODE END Callback 0 */ if (htim->Instance == TIM12) { HAL_IncTick(); } /* USER CODE BEGIN Callback 1 */ /* USER CODE END Callback 1 */}
当我们使用时基函数(默认是系统Systick滴答定时器这里我换成了硬件定时器)当系统定时器触发中断的时候,会让计数值递增。
__weak void HAL_Delay(uint32_t Delay){ uint32_t tickstart = HAL_GetTick(); uint32_t wait = Delay;
/* Add a freq to guarantee minimum wait */ if (wait < HAL_MAX_DELAY) { wait += (uint32_t)(uwTickFreq); }
while ((HAL_GetTick() - tickstart) < wait) { }}
Delay的作用则是死循环等待计数值到达预期。可见这是一种非常低效的,并不适合于FreeRTOS框架。
FreeRTOS框架提供了一个vTaskDelay来实现非阻塞延时。
而且最最关键的是,并非在FreeRTOS中禁用HAL_Delay函数,这实际上是FreeRTOS自身的机制。
FreeRTOS 的调度器本身并不直接管理硬件中断的优先级,而是依赖于底层硬件平台(来实现中断的优先级处理。某些中断的优先级 需要高于
FreeRTOS 的 中断嵌套优先级,以防止任务调度器受到影响。这里应该是Freertos调度出现了问题。
FreeRTOS 会根据 configMAX_SYSCALL_INTERRUPT_PRIORITY
这个宏来设置一个 “ 最大系统调用中断优先级 ”
。任何低于这个优先级的(数字越大越低)都是属于临界中断。而BaseTime优先级默认恰恰是15最低的优先级(这也是为什么中断函数中不能用
HAL_Delay 的原因,因为优先级太低了会被打断),因此当我们提高系统定时器的优先级即可解决这个问题。
当我们将任务TimeBase的优先级修改到5之上,即可通过拔高优先级来解决这个问题。
不过这只是权宜之计,正确的做法应该是修改屏幕驱动,将HAL_Delay修改为非阻塞式延时vTaskDelay以适配FreeRTOS的需要。
02
another
一波刚平一波又起
我们刚刚解决完HAL_Delay的问题,屏幕初始化可以正常的进行下去,可是等到屏幕刷新的时候,又出现了新的问题。
while(sent_bytes < total_bytes) { chunk_size = ((total_bytes - sent_bytes) > 65534) ? 65534 : (total_bytes - sent_bytes); HAL_Delay(1); HAL_SPI_Transmit_DMA(&ST7789_SPI_PORT, (uint8_t*)data + sent_bytes, chunk_size); while(!dma_transfer_done) { //等待传输完成 } dma_transfer_done = 0; sent_bytes += chunk_size; HAL_Delay(1); }
由于我们使用轮询的方式等待传输完成,这个标志位由我们定义,在SPI_DMA的回调函数中赋值。但是抽象的事情又发生了。
DMA的中断也失效了!
DMA的传输中断刚好是5,又是恰好处在临界中断,然后又是导致了这个中断失效。所以导致屏幕传输不能使用。然而CubeMX中不能修改这个优先级为更高的优先级。原因是FreeRTOS前面的宏定义设定,0~4的优先级都可以打断FreeRTOS进入中断,这会导致FreeRTOS的实时性受到影响。
因此CubeMX中只能设置5~15的优先级。
不过让程序跑下去也行,我们进配置文件把优先级限制改一改,改成6,让我们的DMA能触发中断。
03
End
反思
其实上述修改中断的方法是断断不可取的,应该是我的程序中有一些问题导致FreeRTOS在某些地方卡死了。FreeRTOS 本身不会直接禁用低优先级的中断,但
低优先级的中断 在某些情况下可能会受到影响,尤其是当这些中断的优先级与 FreeRTOS 的任务调度机制或 SysTick
中断发生冲突时。可能是某些地方发生了优先级翻转这个问题,导致各个函数中断无法进入,暂时也没有找到问题的根源。
不过大家在编写代码的过程中还是需要对这些可能导致潜在问题的基本知识掌握清楚,以免出现问题的时候无法判断情况。
不过经过仔细研究,虽然这个做法能暂时的解决问题,但是实际上引起这个问题的原因还有别的,疑似是选择的heap堆有问题。 下期再详谈。