我有一些代码,其中中断抖动是交易杀手。只有一个中断源,并且需要尽可能精确。对于 ATMEGA,最大中断响应是当前指令的长度 + 调用中断例程。
但我的代码表现不稳定。中断延迟看起来要大得多。终于我找到了问题所在。 GCC自己禁用中断!这是一个简单的例子。源代码中根本没有中断使用。
#include <avr/io.h>
void DoJob(void){
uint8_t buf[256];
for (uint8_t i = 0; i < 255; i++){
PORTB=buf[i];
buf[i]=PORTB;
}
}
int main(void){
for(;;){
DoJob();
}
}
现在在汇编器列表中出现了惊喜。有 CLI 指令可以禁用中断。
000000cc <DoJob>:
cc: cf 93 push r28
ce: df 93 push r29
d0: cd b7 in r28, 0x3d ; 61
d2: de b7 in r29, 0x3e ; 62
d4: da 95 dec r29
d6: 0f b6 in r0, 0x3f ; 63
d8: f8 94 cli
da: de bf out 0x3e, r29 ; 62
dc: 0f be out 0x3f, r0 ; 63
de: cd bf out 0x3d, r28 ; 61
e0: fe 01 movw r30, r28
e2: 31 96 adiw r30, 0x01 ; 1
e4: ce 01 movw r24, r28
e6: 93 95 inc r25
e8: 21 91 ld r18, Z+
ea: 25 b9 out 0x05, r18 ; 5
ec: 25 b1 in r18, 0x05 ; 5
ee: df 01 movw r26, r30
f0: 11 97 sbiw r26, 0x01 ; 1
f2: 2c 93 st X, r18
f4: e8 17 cp r30, r24
f6: f9 07 cpc r31, r25
f8: b9 f7 brne .-18 ; 0xe8 <DoJob+0x1c>
fa: d3 95 inc r29
fc: 0f b6 in r0, 0x3f ; 63
fe: f8 94 cli
100: de bf out 0x3e, r29 ; 62
102: 0f be out 0x3f, r0 ; 63
104: cd bf out 0x3d, r28 ; 61
106: df 91 pop r29
108: cf 91 pop r28
10a: 08 95 ret
0000010c <main>:
10c: 0e 94 66 00 call 0xcc ; 0xcc <DoJob>
110: fd cf rjmp .-6 ; 0x10c <main>
在这个例子中,解决方案很简单,只需将缓冲区声明为全局即可解决问题。但在实际代码中,无法将所有缓冲区和变量声明为全局,因为没有空间。那么如何避免这种情况,是否还有其他情况编译器在不知道的情况下禁用中断?至少警告编译器这样做应该受到欢迎。我的系统可能每天都会出现一次错误,并且需要很长时间才能发现问题。
需要中断锁,因为SP的设置必须是原子的。锁在 OUT 0x3d 之前一直有效。
当你确定该函数运行时不会触发任何IRQ,然后将其放入自己的编译单元并添加选项
-mno-interrupts
。