最好的方法是使用片上定时器。Systick、RTC 或外围定时器。这些具有时序精确、确定性的优点,并且可以在 CPU 时钟速度发生变化时轻松调整。或者,您甚至可以让 CPU 休眠并使用唤醒中断。
另一方面,脏的“忙延迟”循环很少是准确的,并且会出现各种问题,例如与特定 CPU 指令集和时钟的“紧密耦合”。
一些注意事项:
- 反复切换 GPIO 引脚是个坏主意,因为这会不必要地消耗电流,如果引脚连接到走线,还可能导致 EMC 问题。
- 使用 NOP 指令可能不起作用。许多架构(如 Cortex M、iirc)可以在 CPU 级别上随意跳过 NOP 而实际上不执行它们。
如果您想坚持生成脏忙循环,那么只需volatile限定循环迭代器就足够了。例如:
void dirty_delay (void)
{
for(volatile uint32_t i=0; i<50000u; i++)
;
}
这保证会生成各种废话代码。例如 ARM gcc-O3 -ffreestanding给出:
dirty_delay:
mov r3, #0
sub sp, sp, #8
str r3, [sp, #4]
ldr r3, [sp, #4]
ldr r2, .L7
cmp r3, r2
bhi .L1
.L3:
ldr r3, [sp, #4]
add r3, r3, #1
str r3, [sp, #4]
ldr r3, [sp, #4]
cmp r3, r2
bls .L3
.L1:
add sp, sp, #8
bx lr
.L7:
.word 49999
从那里你理论上可以计算每条指令需要多少滴答声,并相应地更改幻数 50000。流水线、分支预测等将意味着代码的执行速度可能比时钟周期的总和更快。由于编译器决定涉及堆栈,因此数据缓存也可以发挥作用。
我的重点是准确计算这段代码实际需要多少时间是很困难的。与尝试进行理论计算相比,使用范围进行试错基准测试可能是一个更明智的想法。