【发布时间】:2021-07-08 08:53:27
【问题描述】:
SysTick 使用说明
系统提示
我们有一个基于 STM32G483 MCU (Cortex M4) 的定制板。我们使用 SysTick 作为软件定时器的参考。 SysTick 重载寄存器设置为 0x00FFFFFF 以使中断最少。 SysTick 的 CPU 时钟频率为 128MHz,这意味着每 131ms 左右就有一个 SysTick 中断。中断将滴答计数器增加负载值 + 1。
#define SYSTICK_LOAD_VALUE 0x00FFFFFFU
static volatile uint64_t _ticks;
void
systick_interrupt(void)
{
_ticks += SYSTICK_LOAD_VALUE + 1;
}
然后我们使用当前值寄存器获取当前计数周期中经过的时钟周期数来计算当前时间。
uint64_t
systick_get_ticks(void)
{
return _ticks - SysTick->VAL;
}
软件计时器
然后,我们可以将此值用于不同的软件定时器,这些定时器理论上可以按几个时钟周期的数量级计数。
void
timer_start(struct timer *timer)
{
timer->_start_tick = systick_get_ticks();
}
bool
timer_check_ticks(const struct timer timer, uint64_t duration)
{
uint64_t difference = systick_get_ticks() - timer._start_tick;
return (difference >= duration);
}
由于函数调用开销,不可能精确到滴答声,但这在更长的时间段内仍然应该是准确的,例如 1us(128 个滴答声)或 1ms(128 000)。当然,软件定时器可能会超出一些时钟周期,具体取决于主循环频率,但它不应该低于。
测试
我们看到这些计时器出现了一些奇怪的行为,因此我们决定通过最简单的主循环来切换我们可以探测的 GPIO 来测试它们。
int
main(void)
{
// Clock, NVIC, SysTick and GPIO initialisation
struct pin test_gpio;
struct timer test_timer;
timer_start(&test_timer);
while (TRUE) {
if (timer_check_ticks(test_timer, 128000U)) { // 128000 ticks @ 128MHz ==> 1ms
gpio_toggle(test_gpio);
timer_start(&test_timer);
}
}
}
有了这个,我们期待一个具有 50% 占空比和 2ms 周期 (500Hz) 的方波,这是我大部分时间得到的。然而,有些脉冲有时更短,例如 185us。在试图找到问题的根源时,我们也注意到,在编译后任何修改都会改变较短脉冲的长度,但在代码执行时,这个持续时间似乎没有改变。
我们检查了核心时钟确实以 128MHz 运行,SysTick 配置为我们想要的,我们编写了一个 sn-p 来检查 SysTick 中断是否以正确的频率触发,并且 @ 987654325@ 函数返回一个可靠的数字。这让我们相信问题出在计时器代码本身,但我们似乎无法找到问题所在。
使用 clang (--target=arm-none-eabi) 编译代码,不使用 STM32 HAL 库
【问题讨论】:
标签: c timer arm embedded stm32