Go Down

Topic: Due attachInterrupt is a bit slow: example code with less overhead? (Read 3359 times) previous topic - next topic

jimbles

Hey,

I'm afraid I am another person only comfortable with the standard arduino functions struggling to make the leap into the more advanced low level stuff....

I am trying to speed up the code I have to set a pin high when another goes high using interrupts on the Due. So far I am merely using basic attachInterrupt and the faster digitalWriteDirect which floats around in this forum.

Code: [Select]

const int strt = 7;
const int ind=9;
const int intrpin = 6; // pin to detect change in state

void InterIn()
{
  digitalWriteDirect(ind);
  digitalWriteDirect(ind);
}

void setup() {
Serial.begin(115200);

pinMode(strt ,OUTPUT);
digitalWrite(strt ,0);
pinMode(ind,OUTPUT);
digitalWrite(ind);
pinMode(intrpin,INPUT);

attachInterrupt(intrpin,InterIn,RISING);

}

void loop() {
    char c = '#';
  //read serial byte if one is available
  if (Serial.available() > 0)
  {
   // delay(1);
    c = Serial.read(); 
  digitalWriteDirect(pin,1);
  digitalWriteDirect(pin,0);
     
  }

}



the pin7 "strt " is connected to pin6 "intrpin", and I am checking the voltage on pin7 and pin9 with a scope. Everything works as expected - when I enter something on the serial monitor pin6 goes high followed by pin9. However the delay between pin7 going high - when I expect the ISR InterIn() to be called - and pin9 going high 0 the pin9 set within InterIn() is just over 3.2 us.

This is much slower than I would expect and I suspect using attachInterrupt is the culprit in this case. Could someone point me in the direction of a good arduino due specific example of how to speed this up with the lower level functions?

Cheers
Jimbles

schwingkopf

Hi,

the interrupt latency on the Due depends on the exact pin you use. Let me explain this..

The ARM detects interrupts only for a whole PORT and sends a 32bit word with the information on which pins on this PORT an interrupt was triggered. The code that handles this word and decides which ISR should be called, is in the "WInterrupts.c" file (here shown for PORT A only):


Code: [Select]

void PIOA_Handler(void) {
uint32_t isr = PIOA->PIO_ISR;
uint32_t i;
for (i=0; i<32; i++, isr>>=1) {
if ((isr & 0x1) == 0)
continue;
if (callbacksPioA[i])
callbacksPioA[i]();
}
}



As you can see, it scans through each bit of the 32bit word "isr" that corresponds to the 32 pins on port A and checks if an interrupt was triggered on this pin or not (pin corresponding bit either 1 or 0). Then it calls the interrupt service routine that was previously stored for this pin in "callbacksPioA".

The problem is that this routine always loops over all 32 pins of the PORT which actually keeps the processor busy for about 4us (measured) every time an interrupt on a pin is triggered. The delay for the ISR to be called actually depends on which position the pin is on the corresponding PORT. For example your chosen pin number 6 is on PORT C at position 24. This means that the loop scans over pins 1 to 23 first before "finding" your interrupt. The according latency is about 24/32 * 4us = 3us, consistent with what you have measured.

If you want to speed up the latency you can simply use a pin that is at a lower position of any of the PORTs A,B,C or D of the DUE. By choosing for example pin 26 (corresponding to position 1 of PORT D) your latency will be as small as about 150ns. However this will not circumvent the problem that your processor will be busy for 4us without being able of handling other interrupts. If this is a problem for your application you have to recode the part in the "WInterrupts.c" to make it more efficient (for example getting rid of the loop over all pins and only looping over those you have already defined an interrupt for).

Hope this helps you a bit!

jimbles

Hey!

Thank you so much for your helpful and detailed reply! Exactly the sort of thing one hopes for when asking for help on a forum. I'll fiddle around with my pin config and bring the pin to position 1 on a port like you suggest.

The idea for this is that when a pulse is detected, another wider pulse (variable in width) is generated using another timer ISR (a bit like this one). So it would perhaps be best if I did alter the WInterrupts.c. Do you know of a way to point my code at a different file when it compiles? rather than overwrite the one in the arduino core and thus affect all future projects?

Thanks again mate!

MorganS

If it's important that both pins go high at the same time, then wire them together with a diode. Then use the timer to decide when to switch off, after subtracting the known delay caused by the interrupt.
"The problem is in the code you didn't post."

jimbles

Thanks for the advice MorganS, unfortunately I neglected to mention that there needs to be a controllable delay between the pins going high, preferably from (as close to) 0 to ~170 uS. The reason being the initial pulse is a "zero-phase" marker from an AC current source, and the resultant pulse from the Due needs to begin at a programmable phase.

So my plan was:
 phase marker pulse -> ISR sets "go" variable to true -> Timer interrupt with "go" now true, waits a programmable number of ticks, sets output pin high, waits another bunch of programmable ticks and sets pin low. -> await next phase marker pulse.

If getting the delay less than 1 uS is unrealistic then I can just make it wait for a complete cycle in addition to programmable delay

MorganS

There's probably a better way to do this. The hardware supports this kind of AC phase manipulation directly.

I can't suggest how to do it as I'm not using my Due for this purpose, so I haven't delved deep into the waveform modes of the timer units. Maybe look at using the zero phase marker as an enable for a timer counter that's pre-programmed with the delays you want?
"The problem is in the code you didn't post."

MarkT

You can almost certainly get a timer to be triggered directly on an external pin,
though I am guessing about this.  There's a lot of datasheet to read alas!

Then you arrange for the various compare registers to drive output pins at
programmable delays after start.  Well, that's my hope...  Anyone know for
definite (haven't played with Due for a while now, getting rusty).
[ I will NOT respond to personal messages, I WILL delete them, use the forum please ]

Go Up