Electronic – arduino – AVR Interrupt “Interference”

arduinoatmegaavrinterruptsremote control

I'm currently working on receiving signals from an RC remote. I have used pins A8 to A15 on my Arduino Mega board.

The ports and interrupts are setup with the help of this code:

DDRK = 0b00000000;      // Set port A8-A15 to input
PCICR |= (1<<PCIE2);    // Enable interrupt on PCMSK2 containing receiver pins
PORTK = 0b11111111;     
PCMSK2 = 0b11111111;    // Enable all interrupts on receiver pins

Then I have set up an interrupt which finds the pin which interrupted and times the pulse.

// Interrupts whenever a signal is received.
ISR(PCINT2_vect) {  
static uint32_t startTime[8];
static uint8_t lastState;
uint8_t mask;
uint8_t changedPin;

mask = PINK ^ lastState;
lastState = PINK;

if(mask & (1 << 0)) changedPin = 0;
if(mask & (1 << 1)) changedPin = 1;
if(mask & (1 << 2)) changedPin = 2;
if(mask & (1 << 3)) changedPin = 3;
if(mask & (1 << 4)) changedPin = 4;
if(mask & (1 << 5)) changedPin = 5;
if(mask & (1 << 6)) changedPin = 6;
if(mask & (1 << 7)) changedPin = 7; 

// Check if the changed pin is high
if(lastState & (1 << changedPin)) {
    // Start counting if it is
    startTime[changedPin] = micros();
} else {
    // Save if it has gone down again
    rcValue[changedPin] = micros() - startTime[changedPin];
}
}

This is actually working really good, except for the part when I connect a total of 4 or more pins.

Example:
A8: Working fine
A8 to A9: Working fine
A8 to A10: Working fine
A8 to A11: A9 and A11 working fine, A10 doesn't register any input, A8 jumps up from normal values (1000-2000) to values which are approximately 17000.

My guess is that it may have something to do with the masking and detection of the pin from there, but I have searched all over the almighty internet and it has not given me a clear answer.

Best Answer

I see three issues with your code:

Variable volatility and scope

The variables below will get corrupted due to the fact that you define them only in the ISR. But you clearly want to retain their value between interrupts. Make the following variables volatile and define them globally, not in the ISR:

  • uint8_t lastState;
  • uint32_t startTime[8];

Accurate timing

Run timing critical code as soon a possible after the interrupt occured. Read micros() into a temporary variable at the start of the ISR so the all the conditional code doesn't influence timing. Then assign the temporary variable to startTime[changedPin] once your code decided where to put it.

Simultaneous edges

Notice that your code does not cope with simultaneous pin changes, the highest pin will take precedence. Then when the those pins do not have simultaneous falling edges, the timing for the pin with lowest number will be corrupt.