Polling for an external pulse - logic question

I'm using a polling loop to wait for a pulse from an external system that lasts 1-2 us. There's a good 5000us between pulses.

I'm polling rather than interrupting because I need to get the data in very quickly.

The following logic woks if I disable interrupts:

  noInterrupts();
  while(true){ //loop for 1802 program output
    while (PINB & 0x10); //make sure 1802 signal is low
    while ((PINB&0x10)==0); //wait for it to come high
    char1802=busin(); //grab the character **this will take several us**
    Serial.print(char1802); //running at 19200 - maybe as much as 1ms
  }

I don't think the delays in grabbing or forwarding the character are the problem. It's something like the millis() service routine happening during or over the pulse.

I don't really want to disable interrupts - anybody got a better idea? I keep thinking maybe the polling logic is wrong.

Oh, I should have said, the problem seems to be missing the positive going pulse about one time in 50 or so. Also. this processor is only running at 8mhz.

run1802
Hello World!
Hello Word!
Hello World!
Hello World!
Hello World!
Hello World
Hello World!
Hello World!
Hello World!
Hello orld!
Hell World!
Hello World!
Hello World!
Hello World!

apropos of the polling logic, I realized that given my timing, it wasn't necessary to check for the low condition so I pulled that out completely. I'm now only missing one in 500 or so pulses/characters. The target processor will be 16mhz so the problem may go away completely if the millis update routine completes in <my pulse duration.

It's something like the millis() service routine happening during or over the pulse.

Yup.

This strikes me as a good candidate for (pin) change interrupts. How much time can your application take to call busin after the pulse?

Is there any way the pulse can be changed to a toggle?

bill2009:
I'm polling rather than interrupting because I need to get the data in very quickly.

How quickly? Give us a number.

Well, here's the results of my research ...

I am just generating a test signal using Timer 1, and plugging D9 into D2 for testing how quickly we can see the transition.


First, using external interrupts. That is the slowest because of the way the ISR gets called (the overhead):

void gotData ()
 {
 PORTB = _BV (5);  // toggle pin 13
 PORTB = 0;
 }
 
void setup ()
  {
  pinMode (9, OUTPUT);
  pinMode (13, OUTPUT);
  
  // set up Timer 1
  TCCR1A = _BV (COM1A0);  // toggle OC1A on Compare Match
  TCCR1B = _BV(WGM12) | _BV(CS10) | _BV (CS12);   // CTC, scale to clock / 1024
  OCR1A =  4999;       // compare A register value (5000 * clock speed / 1024)
  
  attachInterrupt (0, gotData, RISING);
  }
void loop () 
{
}

3.6875 uS before we do something useful.


Next, polling:

void setup ()
  {
  pinMode (9, OUTPUT);
  pinMode (13, OUTPUT);
  
  // set up Timer 1
  TCCR1A = _BV (COM1A0);  // toggle OC1A on Compare Match
  TCCR1B = _BV(WGM12) | _BV(CS10) | _BV (CS12);   // CTC, scale to clock / 1024
  OCR1A =  4999;       // compare A register value (5000 * clock speed / 1024)
  }
void loop () 
{
  while (PIND & _BV (2)); //make sure 1802 signal is low
      
  while (true)
    {
     while ((PIND & _BV (2)) == 0); //wait for it to come high  
 
     PORTB = _BV (5);  // toggle pin 13
     PORTB = 0;
     return;     
    }
}

That took 0.4375 uS.


Now, using a pin change interrupt:

ISR (PCINT2_vect)
 {
 PORTB = _BV (5);  // toggle pin 13
 PORTB = 0;
 }
 
void setup ()
  {
  pinMode (9, OUTPUT);
  pinMode (13, OUTPUT);
  
  // set up Timer 1
  TCCR1A = _BV (COM1A0);  // toggle OC1A on Compare Match
  TCCR1B = _BV(WGM12) | _BV(CS10) | _BV (CS12);   // CTC, scale to clock / 1024
  OCR1A =  4999;       // compare A register value (5000 * clock speed / 1024)
  
  // pin change interrupt
  PCMSK2 = _BV (PCINT18);  // only want pin 2
  PCIFR  = _BV (PCIF2);   // clear any outstanding interrupts
  PCICR |= _BV (PCIE2);   // enable pin change interrupts for PCINT23..16
  }
void loop () 
{
}

Reasonably fast: 1.375 uS. Less overhead before we hit the ISR.


Of course with the polling you can turn interrupts off so you don't get the jitter from Timer 0 interrupt being called. But you lose track of the elapsed time. The other methods require interrupts to be enabled, naturally. With a large gap between incoming bytes you could conceivably use Timer 1 as a timer with a suitable prescaler, as long as you catch any overflow before it wraps around.

Hi,
Thanks for the interrupt timing comparison, I was going to have to do this myself sooner or later but without access to a scope.

Thanks for sharing

Duane B

rcarduino.blogspot.com

No problem. It's interesting that pin change interrupts are so fast. Well, that's until you start working out exactly which pin caused it, but if you don't need to, it is pretty responsive.

Huh. I would have expected the hardware interrupts to be faster with their dedicated vector.

The problem with the external interrupt is the extra code generated.

Unlike the pin change interrupt, which directly goes to your own function the external interrupts (when you use attachInterrupt) "helpfully" slow you down like this:

SIGNAL(INT0_vect) {
  if(intFunc[EXTERNAL_INT_2])
    intFunc[EXTERNAL_INT_2]();
}

So it does an "if" to see if you declared an interrupt handler, and if so, calls it. And since the compiler can't know what registers that function (which is supplied at runtime) uses, it has to push and pop the lot. So between the test, the array lookup, and extra function call, and all the extra register pushing, it's somewhat slower.

Can that be worked around somehow?
Or is the solution to use PCINTs on the hardware interrupt lines?

If you don't use the core functions (attachInterrupt, detachInterrupt) you can provide your own interrupt service routine. That makes the two techniques interchangeable in terms of speed and size. But you have to go "low level". But you can copy-and-paste the code from the core so it isn't too painful.

OK, then that gives another figure again ...

We will manually handle external interrupts:

ISR (INT0_vect)
 {
 PORTB = _BV (5);  // toggle pin 13
 PORTB = 0;
 }
 
void setup ()
  {
  pinMode (9, OUTPUT);
  pinMode (13, OUTPUT);
  
  // set up Timer 1
  TCCR1A = _BV (COM1A0);  // toggle OC1A on Compare Match
  TCCR1B = _BV(WGM12) | _BV(CS10) | _BV (CS12);   // CTC, scale to clock / 1024
  OCR1A =  4999;       // compare A register value (5000 * clock speed / 1024)
  
  // turn on external interrupt 0 (D2)
  EICRA |= _BV (ISC00) | _BV (ISC01);  // interrupt on leading edge
  EIMSK |= _BV (INT0);
  } // end of setup

void loop () 
{
}

This time it is 1.5 uS to handle it.

The difference of 125 nS between the two different sorts of interrupt handlers is probably not significant. Since interrupts cannot occur mid-instruction, and instructions are from 1 to 3 clock cycles (where each cycle is 62.5 nS) you could probably say that both pin change interrupts, and external interrupts, are comparable.

Whether or not interrupts are faster than polling depends, of course, on how quickly you can poll. Usually, unless the application is extremely basic, its busy doing the stuff that it needs to do. The interrupt gives some measure of guarantee that whatever needs attention gets it within x time frame. Using an ISR to handle your time critical processing makes it easier to write the rest of your code, since you won't have to worry about making sure your polling loop completes within that same x time frame.

I am a big fan of using interrupts where they are the right tool. Some people go out of their way to avoid them, and each approach has its benefits. Personally, when there is data coming in that needs to be handled or sampled quickly, I think an ISR is almost always the way to go. A good example is serial data, which even at relatively low baud rate, and even with a hardware buffer, is difficult to deal with by polling.

Nick, I'm surprised to see there is so much overhead added by the Arduino library. I had read other comparisons between hardware interrupts and pin change interrupts, and usually the hardware win because there is overhead in checking to see which pin in the port triggered the interrupt.

skyjumper:
I had read other comparisons between hardware interrupts and pin change interrupts, and usually the hardware wins ...

Well, they are both hardware. I think my figures show that, other things being equal, the ISRs are entered in approximately the same amount of time. And if you mask off the pin change interrupt to a single pin, which you can do, then you don't need to check which pin it was.

As for the overhead, the external interrupts have the burden of the attachInterrupt intermediate function, which you can work around if you want to. The problem there is that you defeat compiler optimisation by using the attachInterrupt, because it doesn't know which registers to save.

I agree with the other stuff you said, generally speaking, and in appropriate situations, interrupts make life easier.

Yeah, bad phrasing on my part... All I meant was that with the non-pin change interrupts (native??? Dedicated???) you don't have the overhead of doing the masking inside the ISR to see which one triggered it. If I understand correctly, a pin change interrupt is fired when any pin in the entire port is toggled, and you then have to go see which one it was...

True.

Thanks very much for the hard numbers and the revised int0 times. I'm writing a midterm today but i'll be retrying an interrupt tomorrow.