timer not waiting period to fire interrupt (solved!)

Arduino Mega2650 using TimerThree library.

I am writing a sequencer that accepts an external sync signal from a BPM clock. This signal is attached to an ISR called isrStepAdv().

Within the isrStepAdv() routine, a byte is read and a set of 8 relays are activated based on their associated bits within the byte. A MIDI message is sent as a SysEx to a Novation Launchpad Pro via a MIDI shield using the MIDI.h library to indicate the active step. The final goal of the isrStepAdv() routine is to start a timer (attached to isr_PortKill) which is intended to close the valves (via a direct port write,) after a desired period of 320000 microseconds (in the setup() routine via Timer3.intialize(320000);, Timer3.attachInterrupt(isr_PortKill); ).

void isr_StepAdv() {  
  // when the beat signal is detected, increment the step count
  if (!isrBeatGuard) {   // unless the guard is true simply ignore this interrupt
    return ;
  }
  
  if (step_num++ > 7 ){
    step_num = 0;
  } 

  digitalWrite(SEQLED, HIGH);
  
  byte boosh_out = 0;
 
  for (int col = 1; col < 9 ; col++){
    // BOOSH on Mega2560 = Pin22MSB - Pin29LSB
    boosh_out = (lpadWorkingMatrix[8-step_num][col] == clrOOOO) ? boosh_out + 0 : boosh_out + (1 << (col-1));   
   }

   // turn OFF Launchpad step indicator LEDs
  byte sysexColArray[16] = {0,32,41,2,16,12,9
                          , clrBLAK
                          , clrBLAK
                          , clrBLAK
                          , clrBLAK
                          , clrBLAK
                          , clrBLAK
                          , clrBLAK
                          , clrBLAK
                          , clrBLAK};
  sysexColArray[(8-step_num) + 7] = clrSTEP;     // turn ON appropriate Launchpad step indicator LED
  MIDI.sendSysEx(16, sysexColArray, false); 

  BOOSH = boosh_out;   // output byte on BOO
    
  stepTrigger = true;

  Timer3.start();
 
}

//%%%%%%%%%%%%%%%%%%%%%%%%%%%

void isr_PortKill() { 
  // when the timer interrupt fires, shut off all boosh output
  stepTrigger = false;
  BOOSH = B00000000; // All Booshes OFF
  digitalWrite(SEQLED, LOW);
  Timer3.stop();
}

The problem is that Timer3 fires immediately rather than waiting for its period to end. I have a digital scope set up and can watch the relationship of the sync signal and the valve output. The valve line goes high (as desired,) but within 8 microseconds of the sync, goes low. If I comment out the BOOSH = B00000000 line in isr_PortKill, the valves never go low, so I have evidence that the timer ISR is firing. (Also the SEQLED is being driven LOW which only occurs in the isr_PortKill() routine.)

I did try ditching the timer and switching to a millis() compare in the main loop approach and it almost worked as desired. However, while the sequencer is operating, I need to be able to select from various patterns stored in eeprom (and displayed on a u8g2 driven OLED screen.) Using the millis() in the main loop approach, the active step sequence is screwed up (hesitates,) if I preview patterns while one is actively running. (because it delays the return to the millis() compare in the main loop while reading the eeprom and writing to the OLED.) This is a non-starter for the project.

I need a solution that sets the appropriate valve lines when the external sync signal arrives and shuts them off after a set period, but is not impacted by other activities on the device. This works beautifully with the timer solution except for the timer firing immediately instead of waiting for the period to complete.

I’ve also tried TimerOne with the same no-period problem.

The code is a multifile project or I’d dump it all here. I hope I’m missing something obvious. I’m no fan of interrupts on the Arduino, but I want to believe this is a pretty straightforward need.

Thanks in advance for any advice!

The code within your ISR is far too long.

http://gammon.com.au/interrupts

http://www.gammon.com.au/timers

Yes, I recognize that there is way too much code in the ISR and removing it doesn't change the timer behavior. Unfortunately, having refactored this code at least 20 times in and out of the isr, using flags to manage things and trying to use millis() as an alternative, this it is the minimum uninterruptable functionality. When it exists outside the isr, other actions (which take longer than the necessary timer period, ) blow the timing on the step sequence which is the most critical piece of functionality. I'd hoped moving onto the Mega would give me enough bandwidth to deal with this, but no luck so far (though speed does not seem to be a big issue here as long as stay away from the u8g2 lib functions.)

Also, as noted, the code in the step advance interrupt and the code int the timer isr is executing successfully in 8 microseconds. So there is lots of headroom in the timing for all the code to be excercised. If I could just get the timer code to defer until 320 milliseconds had occurred, I'd be sweet.

I spent years coding with interrupts on the PIC and 8051 platforms, they're not entirely unfamiliar. This is essentially just trying to do a one shot timer initiated by an external interrupt. But I'm probably cheating trying to use a nice canned timer lib. My hope was that, with everything working in the code except the length of the timer period, I might be missing something simple. I'm reading the links through multiple times.

Thanks!

Relays and valves don't move fast enough to require microsecond-accuracy timing from hardware timers. millis() should be enough.

It is difficult to say much about the small snippet you posted. I have so many questions about what else is running. There might be something you are doing which disrupts the timer.

The MIDI thing is doubly concerning. I would be surprised if that is safe to use inside an interrupt and even if it is, it could be modifying the timer.

Are you using analogWrite() anywhere? Maybe a servo on timer3's pins?

Thanks for thinking about this!!

You’re dead on about the reduced need for timing accuracy based on the electromechanical components with the exception of initiating the ON|OFF with a BPM sync. The length of the operation could vary by 10-15% with no problems as long as I don’t vary in my response to that sync pulse.

So far, the hardware/timer isr combo does great at this problem. I just get faced with either the 8 u-sec pulse (which isn’t long enough to even engage the relay,) or not being able to shut off the valve before my next pulse arrives. When I ditch the timer ISR and try handling things in the main loop by counting millis, the other operations (reading 8x8 patterns from the eeprom and previewing and/or loading them onto the OLED screen,) take longer than the length of a BPM step (which, for a 140 BPM sync means I’m trying to handle 2.33 Hz or a period of 425-430 milliseconds.) This should be long enough to do a lot of stuff, but the u8g2 operations are slower than I like, so it results in glitched step syncing.

I was suspicious of the MIDI call as well, but I’ve pulled out the MIDI SysEx call in the ISR altogether and it doesn’t change the behavior. I can pull almost all the code out of the ISR and nothing changes with the timer not using the period.

I’m not doing any analog reads or writes, no PWM or servos anywhere. Other than exercising the hardware UART for MIDI (not software UART), I’m not doing anything that should be using interrupts or timers. (the OLED is using I2C, so that could be a problem, but none of that’s occurring during the ISR.)

I’ve attached a zip of the entire multi-file project, but I know it’s a lot to look through so I’m not really expecting anyone to dig in.

SEQ_u8g2.zip (8.49 KB)

The problem is that Timer3 fires immediately rather than waiting for its period to end

There may be a bug in the Timer1 and Timer3 libraries. See the note in the source code.

void start() __attribute__((always_inline)) {
	TCCR3B = 0;
	TCNT3 = 0;		// TODO: does this cause an undesired interrupt?
	resume();
    }

If there is an issue it may be that the PWM mode used triggers the overflow interrupt at BOTTOM, not TOP.

You can try two modifications to the library code to check this out.
EDIT: Originally posted with TIFR1 and TOV1 in error.

void start() __attribute__((always_inline)) {
	TCCR3B = 0;
	TCNT3 = 0;		// TODO: does this cause an undesired interrupt?
	TIFR3 |=(1<<TOV3);//clear overflow interrupt flag by writing one to register.
        resume();
    }
void start() __attribute__((always_inline)) {
	TCCR3B = 0;
	TCNT3 = 1;		//see if setting the starting value >0  changes things
	resume();
    }

Wow! Thanks!!

cattledog nailed it!

void start() __attribute__((always_inline)) {
 TCCR3B = 0;
 TCNT3 = 1; //see if setting the starting value >0  changes things
 resume();
    }

fixed the problem for me!!!

Thanks!!! --Tim