因此,我已经使用utils / delay.h中的标准功能实现了自定义延迟功能。
inline void delay_us(uint16_t time) {
while (time > 0) {
_delay_us(1);
time--;
}
}
在主函数的循环内调用它:
#define F_CPU 16000000UL
...
int main() {
pin_mode(P2, OUTPUT);
while (1) {
pin_enable(P2);
delay_us(1);
pin_disable(P2);
delay_us(1);
}
}
使用示波器,我可以知道该引脚保持1.120us高和1.120us低,其中1为参数。将参数增加到6,示波器显示6.120us。但是,有了7,它就保持了9 us。与10,大约14我们。
我知道循环会带来开销,但是为什么在1到6 us之间没有开销(或者为什么开销不会改变)?
OBS:我正在使用Arduino UNO(16 MHz)
答案 0 :(得分:2)
对于小参数,gcc-avr将展开while循环,有效地将多个1µs延迟串在一起:
delay_us(5):
ldi r24,lo8(5)
mov r25,r24
1: dec r25
brne 1b
mov r25,r24
1: dec r25
brne 1b
mov r25,r24
1: dec r25
brne 1b
mov r25,r24
1: dec r25
brne 1b
1: dec r24
brne 1b
然而,在某些时候,编译器将策略从占用空间的展开更改为通过while循环实际分支:
delay_us(6):
ldi r24,lo8(6)
ldi r25,hi8(6)
ldi r19,lo8(5)
.L2:
mov r18,r19
1: dec r18
brne 1b
sbiw r24,1
brne .L2
那时,精心设计的_delay_us()
函数或多或少会被击败。与单个_delay_us(1)
所需的16个时钟周期相比,分支开销非常大,并将在每次循环迭代中支付。
您描述的运行时突然增加基本上就是编译器停止展开循环的时候。
将此与直接调用_delay_us(6)
进行比较:
_delay_us(6):
ldi r24,lo8(32)
1: dec r24
brne 1b
上面显示的程序集可能与编译器的操作有所不同,因为编译器的输出随版本和标志的不同而有很大差异,但清单应合理地接近。
对于示例,我假定了优化级别为-O2
的gcc-avr 4.6.4。
Try it out