【Bug】FreeRTOS滴答定时器初始化及中断服务函数卡死

Bug1

在导入FreeRTOS库下,滴答定时器初始化时钟源为**SysTick_CLKSource_HCLK_Div8(即HCLK的八分频)**时,任务延时时间不准确,任务中vTaskDelay(1000)了1s,但是实际PC上通过串口助手查看接收到的串口数据时间间隔了8s


Reason1

CTRL寄存器的Bit2用于控制SysTick定时器的时钟源,0=AHB/8,1=AHB

通过Debug查看发现程序运行后SysTick的控制及状态寄存器CTRL 的Bit2位被置一,而我本来是想要初始化为0的,然后发现在开启任务调度器函数中会重新对SysTick定时器进行初始化,具体代码如下:

c 复制代码
// 626行对CTRL进行了设置
void vPortSetupTimerInterrupt( void )
	{
		/* Calculate the constants required to configure the tick interrupt. */
		#if configUSE_TICKLESS_IDLE == 1
		{
			ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ );
			xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick;
			ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ );
		}
		#endif /* configUSE_TICKLESS_IDLE */

		/* Configure SysTick to interrupt at the requested rate. */
		portNVIC_SYSTICK_LOAD_REG = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL;
		portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT );
	}

在port.c的626行处对SysTick控制寄存器重新进行了写入,并根据宏portNVIC_SYSTICK_CLK_BIT来设置SysTick的时钟源选择,而该宏又根据下面的宏判断来设置值,代码如下:

c 复制代码
// 89行确定宏的值
#ifndef configSYSTICK_CLOCK_HZ
	#define configSYSTICK_CLOCK_HZ configCPU_CLOCK_HZ
	/* Ensure the SysTick is clocked at the same frequency as the core. */
	#define portNVIC_SYSTICK_CLK_BIT	( 1UL << 2UL )
#else
	/* The way the SysTick is clocked is not modified in case it is not the same
	as the core. */
	#define portNVIC_SYSTICK_CLK_BIT	( 0 )
#endif

依然是在port.c的第89行,此处根据是否定义了宏configSYSTICK_CLOCK_HZ 来确定宏portNVIC_SYSTICK_CLK_BIT 的值,如果定义了第一个宏,则给宏portNVIC_SYSTICK_CLK_BIT 赋值为0 ,即0x00,Bit2的值为0,如果没有定义该宏,则赋值为**( 1UL << 2UL ),即0x04,Bit2的值为1,最后该宏赋值给了上面的CTRL寄存器,以此来设置滴答定时器时钟源的选择
如果想设置SysTick的时钟源为HCLK/8,则必须定义
宏configSYSTICK_CLOCK_HZ**,这样宏portNVIC_SYSTICK_CLK_BIT的值才为0x00,才不会在开启调度器后CTRL寄存器的Bit2的值设置为1


Resolution1

SysTick的时钟源设置为HCLK/8,则需要定义宏configSYSTICK_CLOCK_HZ,定义为HCLK则不要定义该宏


Bug2

初始化完SysTick后程序卡死在中断服务函数SysTick_Handler


Reason2

SysTick_Handler中断服务函数执行代码:

c 复制代码
void SysTick_Handler(void)
{
    if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED)//系统已经运行
    {
        xPortSysTickHandler();	
    }
}

我是直接在stm32f10x_it.c中直接在中断服务函数中调用FreeRTOS中的中断处理函数,在编译时有一个警告内容如下:

...\USER\stm32f10x_it.c(153): warning: #223-D: function "xPortSysTickHandler" declared implicitly

当时没怎么在意这个警告,就没有去管了,并且之前的创建动静态时也有这个警告,不过在重新编译了一下后面就没再提示了,而且之前的程序也能正常实现任务调度

在这次做中断管理实验时发现,我只要在该函数上面include函数所在的头文件,并且extern该函数,程序又能正常运行了,具体添加代码如下:

c 复制代码
#include "FreeRTOS.h"
#include "task.h"	
extern void xPortSysTickHandler(void);
void SysTick_Handler(void)
{
		if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED)//系统已经运行
		{
				xPortSysTickHandler();	
		}
}

这样程序又不会卡死在里面了,但是一但注释掉前三行,程序又卡死了,在之前的程序中没有前三行程序也正常调度,但在这次的实验中又必须添加进去程序才不会卡死,这个具体的原因还未知,猜测可能是编译器问题把有的时候能找到该函数有的时候找不到


Resolution2

在SysTick的中断服务函数所在的文件中,一定要导入FreeRTOS相关的头文件,防止程序找不到该函数导致卡死,在SysTick_Handler前加入以下三行代码:

c 复制代码
#include "FreeRTOS.h"
#include "task.h"	
extern void xPortSysTickHandler(void);

或者直接在FreeRTOSConfig.h中宏定义一下:

c 复制代码
#define xPortSysTickHandler		SysTick_Handler

这样当程序调用中断服务函数时会直接跳到FreeRTOS为我们写好的中断服务函数中,更加方便,但需要注意注释掉stm32f10x_it.c中的函数,防止冲突


相关推荐
深念Y1 天前
Opencode Event 表写入优化方案
数据库·人工智能·ai·node.js·bug·优化·opencode
姚青&2 天前
Bug基本概念
bug
AI多Agent协作实战派2 天前
AI多Agent协作系统实战(三十九):AI修对了Bug,却越了权——自动化团队的第一条铁律
人工智能·自动化·bug
深念Y4 天前
AMD 芯片组驱动触发高频 SSD 写入的问题及解决方案
windows·bug·ssd·日志·芯片·驱动·amd
加油码4 天前
探秘 volatile关键字:编译器的过度优化
linux·bug·信号处理
鹿屿二向箔4 天前
keil5.39版本使用AC6编译器不能位域操作的BUG使用STM32U385系列MCU
stm32·单片机·bug
angryshan5 天前
goLang配置断点流程
go·bug
吨吨ai6 天前
# ChatGPT Plus / Pro + Codex Debug 实战:如何让 AI 从日志、Trace、异常堆栈自动定位线上 Bug
人工智能·chatgpt·bug
灰暗世界%7 天前
飞牛bug修复
bug
凌云拓界9 天前
NodeVerdict | 性能门禁:把追踪数据变成 CI 规则
ci/cd·信息可视化·开源·node.js·bug·数据可视化·安全架构