Interrupt / Timer advice: Driving LED matrix while reading serial data

Hi all,

I'm a bit stuck with setting up a TIMER1 configuration to drive a LED matrix. Right now I have an interrupt configuration that gets triggered every ~1250us to do an full update of my LED matrix:

TCCR1A = 0;
TCCR1B = _BV(WGM13);
ICR1 = 10000;
TIMSK1 = _BV(TOIE1);
TCNT1 = 0;
TCCR1B |= _BV(CS10);

My code inside the ISR(TIMER1_OVF_vect) method takes up to ~620us. Sadly this takes too long to allow the Arduino build-in interrupt to handle incoming serial data so that I'm experiencing data loss during the time the interrupt is running. Since I'm able to break down my interrupt code into pieces where to longest running one is about ~80us I've changed the ICR1 value to:

ICR1 = 715;

so that it gets triggered every ~90us. Every call of the interrupt method is only executing parts of the code needed to update the LED matrix and the ~10ns buffer between the runtime of my code fragments and the next interrupt call seems to be enough to give the internal Arduino interrupt some time to handle incoming serial data so that I don't suffer from data loss. Also the later interrupt calls in the original 1250us duration are doing nothing to fit the initial timer configuration. Everything works so far but here's my question:

How do I implement such an interrupt in a non-hacky way? :wink:

What I'm actually searching for is an interrupt configuration where I can set a duration at the end of every interrupt method call until the next call of the interrupt should take place - basically the buffer between the interrupt calls to allow the serial interrupt to do its thing. I know that there are Timer Overflow and Output Compare Match timer configurations available but all my attempts to use them for my purpose have failed so far - apparently I'm to stupid to use them :wink:

Also if someone knows if there's a mechanism to query the internal Arduino serial interrupt or some magic register or something if there's work to do to handle incoming serial data than I could save the waiting time until the next interrupt call would kick in and could continue updating my LED matrix without any delay.

Greetings,
Markus

Couldn't you use to if command lines?

Like this kinda,

int round;

round = 0

if(round = 0)
{
TCCR1A = 0;
TCCR1B = _BV(WGM13);
ICR1 = 10000;
TIMSK1 = _BV(TOIE1);
TCNT1 = 0;
TCCR1B |= _BV(CS10);
round = 1;
}

If(round = 1)
{
read data info here
round = 0;
}

My current code looks like this:

static const int ISR_STEPS = 14;
static int currentIsrStep = 0;

ISR(TIMER1_OVF_vect) {
  switch (currentIsrStep) {
    case 0:
      // do first part of the LED matrix update code
      break;
    case 1:
      // second part of the LED matrix update code
      break;
    [...]
    case 9:
      ...
      break;
    case 10:
      ...
      break;
  }
  currentIsrStep++;
  if (currentIsrStep == ISR_STEPS) {
    currentIsrStep = 0;
  }
}

Steps 0-10 are each calling parts of the code needed to update the LED matrix - steps 11-13 are doing nothing so that the entire update code is running as close as possible at the beginning of my original ~1250us interval.

Instead of having a fixed interrupt call rate I'd like to set at the end of every interrupt method call the duration until the TIMER1 will again call the interrupt method. If I would be able to query somehow if the serial interrupt has to do some work than this would be a plus.

Greetings,
Markus

Way over my head.......

I ran into a similar problem recently, and overcame it using 'nested interrupts'. I don't know if this would be the best method for you, but here it is..
Basically, every time an interrupt is triggered, global interrupts are disabled while it completes, so other interrupts cannot run.

The way I used around that, the main steps would be something like follows:

(I posted some info on my blog too: TMRh20s Project Blog: July 2012)

a: COMPB interrupt triggered
b: Disable COMPB interrupt from within the COMPB interrupt vector
c: Enable 'Global Interrupts'
d: Let COMPB do its thing, OVF will interrupt it when it needs to read Serial data
e: Last step before COMPB completes is to re-enable itself

It would require two interrupts the way I see it, but the code might be something like this:

ISR(TIMER1_COMPB_vect){
  
 TIMSK1 &= ~_BV(OCIE1B);  //Disable this interupt, otherwise seems like it will try to trigger again next cycle even if not complete
 sei();  //Now enable global interupts before this interrupt is finished
   
 // take as long as you like to do what you need here, this interrupt will keep trying to complete while other interrupts... interrupt it
 // while(x < yourMom){delay{10)}

  TIMSK1 = ( _BV(OCIE1B) | _BV(TOIE1) ); //Re-enable this interrupt
}


ISR(TIMER1_OVF_vect){ 

//check for serial data at a defined rate and have your way with it

}

Then you just have to work out the timing period and configuration on timer1 that works best with your code. You can also modify the duty cycle on your timer on the fly, which would affect the timing of COMPB and possibly OVF depending on configuration.

Edit to add: I just remembered this partial implementation of Serial.bufferUntil() I found a while back: Added methods buffer(size) and bufferUntil(char) to Serial, WIP · cmaglie/Arduino@86ae0f9 · GitHub and Google Code Archive - Long-term storage for Google Code Project Hosting. this may be closer to what you are looking for

Hi TMRh20,

TMRh20:
sei(); //Now enable global interupts before this interrupt is finished

Thanks for pointing me into the right direction with that line of code. Before I haven't really thought about re-enabling global interrupts within an interrupt call since that sounds a bit like a bad idea I'd say :slight_smile:

Nevertheless I was able to solve my problem with going back to my initial interrupt interval of ~1250us and re-enabling global interrupts at the beginning of every interrupt call. Luckily the interruptions of the internal serial interrupt are that short that they don't mess around with the timing of updating my LED array so that I was able to get this running with just using one interrupt.

Thanks again!

Greetings,
Markus