I need to monitor asynchronously if the INPUT pin is active for a period of time. The current practice is to set up an interrupt handler that sets a flag bool to true as soon as the pin is active. The main thread then periodically checks this flag and decides what to do based on its value. For example:
bool Flag;
static void SetFlag() {
Flag = true;
}
void setup() {
Flag = false;
pinMode(18, INPUT);
attachInterrupt(digitalPinToInterrupt(18), SetFlag, RISING);
}
void loop() {
if (Flag) {
Flag = false;
//Do some time-consuming tasks
} else {
Flag = false;
//Do something else
}
}
However, I found that the function of this Flag variable and the hardware register EIFR are duplicative: they are both set to true when the pin is active. So I thought, can we just replace this flag with EIFR? Let loop() check the EIFR directly to decide what to do.
But the problem is, it doesn't seem like EIFR continues to monitor pins without enabling interrupts? However, if interrupts are enabled, the CPU will immediately call the interrupt handler when it sees that the EIFR is set, and then return the EIFR to false. At the same time, I can't use noInterrupts() to disable all interruptions, because I also need to use a timer. So is there some intermediate state in this system that allows the EIFR to keep an eye on the pin but not trigger the interrupt handler?
Writing flags for each pin activity adds a lot of code complexity and degrades performance. If the hardware provides ready-made registers, I'd like to make the most of it.
That's quite an extraordinary claim, and does not match my experience. So I will not be losing any sleep over it or spending any time worrying about it.
Have you checked how many cycles the INTx ISR takes? I did count 41 for a 328P processor (interrupt on pin 2) so that's roughly 2.5 microseconds. You might be using a Mega or something else, not sure.
IMPLEMENT_ISR(INT0_vect, EXTERNAL_INT_0)
400: 1f 92 push r1
402: 0f 92 push r0
404: 0f b6 in r0, 0x3f ; 63
406: 0f 92 push r0
408: 11 24 eor r1, r1
40a: 2f 93 push r18
40c: 3f 93 push r19
40e: 4f 93 push r20
410: 5f 93 push r21
412: 6f 93 push r22
414: 7f 93 push r23
416: 8f 93 push r24
418: 9f 93 push r25
41a: af 93 push r26
41c: bf 93 push r27
41e: ef 93 push r30
420: ff 93 push r31
422: e0 91 00 01 lds r30, 0x0100 ; 0x800100 <__data_start>
426: f0 91 01 01 lds r31, 0x0101 ; 0x800101 <__data_start+0x1>
42a: 09 95 icall
42c: ff 91 pop r31
42e: ef 91 pop r30
430: bf 91 pop r27
432: af 91 pop r26
434: 9f 91 pop r25
436: 8f 91 pop r24
438: 7f 91 pop r23
43a: 6f 91 pop r22
43c: 5f 91 pop r21
43e: 4f 91 pop r20
440: 3f 91 pop r19
442: 2f 91 pop r18
444: 0f 90 pop r0
446: 0f be out 0x3f, r0 ; 63
448: 0f 90 pop r0
44a: 1f 90 pop r1
44c: 18 95 reti
Reading a port using direct port manipulation is faster.
An instruction like uint8_t pins = PINC which reads pins A0 to A5 in one go translates to a single machine instruction (328P processor) and hence 62.5 nanoseconds.
If I remember correctly,on the ATmega328P, EIFR flags are set by pin events according to EICRA even if EIMSK is cleared, allowing you to poll them in loop() without triggering interrupts, while other enabled interrupts like timers keep working.
Did you try?
EDIT: I did not remember correctly. EIFR flags can only be cleared when the interrupt is actually triggered, and this happens automatically during the interrupt handling process
If you can do without interrupts then you can call cli(). The flags will then be set (I think) without any service routine being called. I'm not sure this is a practical solution though.
Yeah, EIFR latches that there was an interrupt until the bits are reset.
They get reset automagically when entering the ISR, but if you have the ISR disabled, you'll need to reset it manually by writing a one to the appropriate register bit...