AVR SEI 指令 ( http://www.atmel.com/webdoc/avrassembler/avrassembler.wb_SEI.html ) 在启用中断之前等待下一条指令完成。
如果我使用另一条指令设置 SREG 中的 I 标志,这也会等待 1 条指令吗?
换句话说:等待是 SEI 指令的特性还是状态寄存器的特性?
如果它是 SEI 指令的一个特性,那么在执行 SEI 的周期中还是在下一条指令的周期中,该标志实际上是在什么时候设置的?
AVR SEI 指令 ( http://www.atmel.com/webdoc/avrassembler/avrassembler.wb_SEI.html ) 在启用中断之前等待下一条指令完成。
如果我使用另一条指令设置 SREG 中的 I 标志,这也会等待 1 条指令吗?
换句话说:等待是 SEI 指令的特性还是状态寄存器的特性?
如果它是 SEI 指令的一个特性,那么在执行 SEI 的周期中还是在下一条指令的周期中,该标志实际上是在什么时候设置的?
虽然其他答案是经过深思熟虑且有充分理由的,但它们都是不完整的或只是推测。在文档不明确的地方,我们必须进行试验,并且必须测试每个案例。
这个问题值得一个结论性的答案,所以让我们拿出一个 AVR 并开始设置一些位!
为了测试,我做了一个小 Arduino (ATMEGA328P) 程序,它可以...
while (1)
)INT0
低)我使用了一个测试台,它会在启用中断后在单条指令中打开 LED。通过尝试在测试台中启用中断并检查 LED 的不同方法,我可以判断启用指令之后的指令是否已执行。
如果 LED 没有亮起,那么我知道 ISR 在启用中断后立即执行(并锁定)。
如果 LED 确实亮了,那么我知道在调用 ISR 之前允许执行下一条指令。
SEI
说明(基本情况)代码:
sei
结果:LED 亮起。执行以下指令。
OUT
操作说明代码:
in r16,0x3f // Get SREG
ori r16,128 // Set I bit
out 0x3f,r16 // Save back to SREG
结果:
带领。执行以下指令。
ST
操作说明代码:
clr r29 // Clear Y high byte
ldi r28,0x5f // Set Y low byte to point to SREG
ld r16, Y // Get SREG
ori r16,128 // Set I bit
st Y,r16 // Put SREG
结果:
带领。执行以下指令。
问:等待是 SEI 指令的特性还是状态寄存器的特性?
A:看起来,将I
位SREG
从 a更改0
为 a1
将允许下一条指令执行,即使存在未决中断,也不管使用什么指令设置该位。
这实际上变成了一个非常有趣的问题,有很多复杂性。如果您对他的详细信息感兴趣,请查看...
http://wp.josh.com/2016/01/05/different-ways-to-set-i-bit-in-avr-sreg-besides-sei/
我从文档中了解到,执行sei
指令与直接将 1 写入 SREG 的 I 位没有什么不同。该指令的优点是您无需先将值加载1<<I
到工作寄存器中即可更改 SREG,从而节省时间。
详细说明,使用sei
:
sei ; One cycle
使用设置位sbi
(仅当 SREG 位于寄存器映射的低 32 字节时才有效,但似乎大多数情况下不是全部。)
sbi SREG,7 ; Two cycles
直接在 SREG 中写入 I 位:
in r24,SREG ;
ori r24,0x80 ;
out SREG,r24 ; Three cycles
I
一旦sei
指令(或sbi
或out
)完成,该位应在 SREG 中设置。但是,直到下一条指令完成后,才会处理任何挂起的中断- 该位将被设置,但需要一个额外的周期才能启用中断。因为中断不能在指令中间处理,并且有些指令需要超过一个周期才能执行,所以它们将启用它所需的时间指定为一条指令。这应该适用于所有版本的代码 - 即上述每个版本都会导致指令延迟。
经过一番搜索,我在 Arduino 论坛上找到了这个帖子,其中执行了几个不同的测试来验证行为。好像和我上面说的一样。
此外,根据该线程,如果I
已设置标志,则不会导致中断sei
的延迟响应,这意味着延迟响应不是由指令本身引起的,而是由I
标志控制的内部硬件引起的-因此,任何改变 SREG 中标志的操作,无论是或sei
将具有完全相同的行为。out
sts
恕我直言,写入 SREG 仍然延迟 1 条指令可以这样测试(伪代码):
ISR() { PORTA = 0; while(1); }
main()
{
cli();
DDRA = 0xff;
configure_isr_for_level_interrupt_that_will_trigger_immediately();
SREG = 0xff;
cli();
PORTA = 0xff;
while(1);
}
不幸的是我没有时间去做:(
这不是它所说的。文件说
SEI 之后的指令将在任何未决中断之前执行。
并不是说它等待下一条指令。我读到这个标志是立即设置的,但即使启用,在执行下一条指令之前不会处理任何中断。