I recently came across this piece of code in an interrupt service routine (ISR):
#define MAX_CHANNELS 4
static uint16_t volatile* ADCVALS[MAX_CHANNELS] = {
&ADC1BUF0, &ADC1BUF1, &ADC1BUF2, &ADC1BUF3
};
static uint8_t CHANNELS = 0;
static uint16_t volatile* volatile BUFFER_IDX[MAX_CHANNELS];
void __attribute__((interrupt, no_auto_psv)) _AD1Interrupt(void) {
*(BUFFER_IDX[0]++) = *ADCVALS[0];
if (CHANNELS >= 1) {
*(BUFFER_IDX[1]++) = *ADCVALS[1];
if (CHANNELS >= 2) {
*(BUFFER_IDX[2]++) = *ADCVALS[2];
if (CHANNELS >= 3) {
*(BUFFER_IDX[3]++) = *ADCVALS[3];
}
}
}
}
It copies between 1-4 register values into memory, depending on the value of CHANNELS
, which is a value between 0-3 which is set elsewhere in the program via a setter function.
I found the nested if's extremely ugly and changed it to this:
int i;
for (i = 0; i <= CHANNELS; i++) {
*(BUFFER_IDX[i]++) = *ADCVALS[i];
}
which promptly broke the ISR. This is an embedded system, PIC24 architecture, 64 MHz clock. The ISR is severely time constrained and must finish within 1 µs. The for loop is apparently too slow, while the nested if is fast enough.
My question, then, is two-fold:
- Is there a less ugly way to do what the nested if clauses do, without slowing down the ISR?
- Why is the for loop so much slower? I would have expected the compiler (xc16) to be smart enough to generate similar asm for both (at
-O2
).