STM32 µC:SysTick 延迟在中断处理程序中不起作用
STM32 µC: SysTick delay doesn't work inside interrupt handler
所以我用 C 语言为 STM32F103C8T6
微控制器编写了一个程序,使用了 RTC(实时时钟)和一个显示模块。
RTC 和显示器都工作正常,但是当我尝试从 RTC 中断处理程序内部更新显示器时,它不起作用。
当我从 main()
中向显示器写入内容时,它工作得很好。
中断处理程序也能正常工作,所以我认为问题出在写入显示器的函数中。
此函数使用小延迟来对与显示控制器的通信进行位爆炸。
我之前使用 SysTick
生成这样的延迟:
void delay(uint32_t n){
uint32_t start = systick_count;
while (systick_count - start < n);
return;
}
但不知何故,在 RTC 的中断处理程序中它不起作用。所以我用这个替换了我的延迟函数,而不是使用 SysTick:
for (; i>0; i--) {
for (int j = 0; j < 72; ++j) {
__asm__ __volatile__("nop\n\t":::"memory");
}
}
现在一切正常。
我试图理解为什么 SysTick
显然在 RTC 中断处理程序中不起作用。
我想可能是中断优先级引起的,但根据数据表,默认情况下 SysTick Interrupt
的优先级高于 RTC 中断优先级。
也许有人可以解释,为什么会这样?
EDIT1:好的,所以我已经阅读了更多关于中断优先级的内容,看来我需要正确配置 NVIC_IRQChannelPreemptionPriority。我会尽快尝试这个...
关于中断内部的延迟,我知道这不是正确的做法,但我仍然想了解程序的行为
EDIT2:我刚刚尝试通过以下方式更改中断优先级:
// set RTC interrupt priority to 15 (lowest)
NVIC_SetPriority(RTC_IRQn, 15);
// set interrupt priority of SysTick to 0 (highest)
NVIC_SetPriority(SysTick_IRQn, 0);
现在 SysTick 延迟在 RTC 中断处理程序中起作用。
您使用SysTick 实现延迟的方式与来自ST 的HAL 库中的HAL_Delay
相同。它的工作方式是 SysTick 不断递增(通常以 1 毫秒为间隔)一个变量 - systick_count
在你的例子中 - 与延迟函数内部的局部变量进行比较 - start
在你的例子中。
这里要注意两点:
systick_count
需要设置为 volatile 以强制在每次比较之前重新读取其值。它是由中断修改的,而不是在该函数内部修改的。您的编译器可能会选择优化这部分代码。
SysTick中断在调用delay函数时需要"running"。如果不是,例如因为您处于更高优先级(数值较低)的中断中,所以您处于死锁状态并且延迟功能将永远不会完成,因为计数器永远不会被从未发生过的 SysTick 递增。关于这一点——不要假设,只依赖数据表。继续并在延迟时实际验证它是 运行ning。要么在调用延迟函数时检查 NVIC 寄存器值,要么在 RTC 中断时简单地在 SysTick 中设置一个断点——当你在延迟函数中时,它应该会到达该断点。如果不是-您可能需要调整中断优先级。
关于延迟中断的整个想法 - 它通常被认为是不好的做法。中断应该用于非常快速地完成非常小的部分工作,例如设置一个标志,表明需要在主程序代码中完成某些事情,或者将接收到的字节放入队列中以供以后处理。虽然这在一个只有一个中断(如果算上 Systick 的话是两个)的简单程序中可能看起来并不明显,但一旦添加更多中断,它很快就会出现问题,并且 运行 会出现丢失信息等问题,因为您的中断相互阻塞,并且调用速度不够快。
所以我用 C 语言为 STM32F103C8T6
微控制器编写了一个程序,使用了 RTC(实时时钟)和一个显示模块。
RTC 和显示器都工作正常,但是当我尝试从 RTC 中断处理程序内部更新显示器时,它不起作用。
当我从 main()
中向显示器写入内容时,它工作得很好。
中断处理程序也能正常工作,所以我认为问题出在写入显示器的函数中。
此函数使用小延迟来对与显示控制器的通信进行位爆炸。
我之前使用 SysTick
生成这样的延迟:
void delay(uint32_t n){
uint32_t start = systick_count;
while (systick_count - start < n);
return;
}
但不知何故,在 RTC 的中断处理程序中它不起作用。所以我用这个替换了我的延迟函数,而不是使用 SysTick:
for (; i>0; i--) {
for (int j = 0; j < 72; ++j) {
__asm__ __volatile__("nop\n\t":::"memory");
}
}
现在一切正常。
我试图理解为什么 SysTick
显然在 RTC 中断处理程序中不起作用。
我想可能是中断优先级引起的,但根据数据表,默认情况下 SysTick Interrupt
的优先级高于 RTC 中断优先级。
也许有人可以解释,为什么会这样?
EDIT1:好的,所以我已经阅读了更多关于中断优先级的内容,看来我需要正确配置 NVIC_IRQChannelPreemptionPriority。我会尽快尝试这个...
关于中断内部的延迟,我知道这不是正确的做法,但我仍然想了解程序的行为
EDIT2:我刚刚尝试通过以下方式更改中断优先级:
// set RTC interrupt priority to 15 (lowest)
NVIC_SetPriority(RTC_IRQn, 15);
// set interrupt priority of SysTick to 0 (highest)
NVIC_SetPriority(SysTick_IRQn, 0);
现在 SysTick 延迟在 RTC 中断处理程序中起作用。
您使用SysTick 实现延迟的方式与来自ST 的HAL 库中的HAL_Delay
相同。它的工作方式是 SysTick 不断递增(通常以 1 毫秒为间隔)一个变量 - systick_count
在你的例子中 - 与延迟函数内部的局部变量进行比较 - start
在你的例子中。
这里要注意两点:
systick_count
需要设置为 volatile 以强制在每次比较之前重新读取其值。它是由中断修改的,而不是在该函数内部修改的。您的编译器可能会选择优化这部分代码。SysTick中断在调用delay函数时需要"running"。如果不是,例如因为您处于更高优先级(数值较低)的中断中,所以您处于死锁状态并且延迟功能将永远不会完成,因为计数器永远不会被从未发生过的 SysTick 递增。关于这一点——不要假设,只依赖数据表。继续并在延迟时实际验证它是 运行ning。要么在调用延迟函数时检查 NVIC 寄存器值,要么在 RTC 中断时简单地在 SysTick 中设置一个断点——当你在延迟函数中时,它应该会到达该断点。如果不是-您可能需要调整中断优先级。
关于延迟中断的整个想法 - 它通常被认为是不好的做法。中断应该用于非常快速地完成非常小的部分工作,例如设置一个标志,表明需要在主程序代码中完成某些事情,或者将接收到的字节放入队列中以供以后处理。虽然这在一个只有一个中断(如果算上 Systick 的话是两个)的简单程序中可能看起来并不明显,但一旦添加更多中断,它很快就会出现问题,并且 运行 会出现丢失信息等问题,因为您的中断相互阻塞,并且调用速度不够快。