Inverter PWM only smooth when Serial.println() command is in timer interrupt

Hi, I would appreciate some assistance with understanding the result I am getting please.

My goal is to replicate a unipolar modulated PWM to provide gate signals to 4 MOSFETs of a full bridge single phase inverter (the inverter forms part of my micro solar energy system for my thesis).

I am using a MEGA2560 (R3).

I have coded:

  • Timer/Counter3 and Timer/Counter4 in fast PWM mode
  • One output compare inverted and another non-inverted on both T/C’s.
  • No pre-scaling, TOP set to 4550 to produce an approximate switching frequency of 3500Hz (to obtain a SPWM output of 50Hz)
  • Interrupt routine so the increase and decrease of on-time (compare) is performed at the correct time (after each time TOP is reached)

(If there is a better way of producing the PWM for the inverter than I would like to know this but this is not the focus of my thesis, the MPPT program is).

The program runs relatively well when I have the Serial.println() in the interrupt (came across this while performing other checks and was left in accidentally afterwards), that is - the outputs’ on-time increases and decreases quite smoothly.

When the Serial.println() is removed, instead of smoothly increasing and decreasing it acts more erroneously - on-time will be small than jump to a large value and back down and then have 2 to 3 iterations as it should then jump to a ‘random’ size/on-time (a few scope images attached).

Some things I have tried or identified:

  • It doesn’t matter where the Serial.println() command is, as long as it is in there
  • It doesn’t matter if I Serial.println(“text”) or Serial.println(micros())
  • I have tried artificial delays using delayMicros() from 250 to 1500us
  • I excluded Timer/Counter3 (because it is on the same port as Serial)
  • Attempted using Serial.flush(), if(Serial){}, Serial.available(), Serial.end() without success

Hope someone is able to shed some light.

Kind Regards,
Troy

void setup() {
  Serial.begin(9600);
  cli(); //disable all interrupts
  DDRE = (1<<DDE3) | (1<<DDE4); //make pins 5 and 2 digital outputs
  TCCR3A = (1<<COM3A1) | (1<<COM3B1) | (1<<COM3B0) | (1<<WGM31);  //non-inverting modefor OC3A, inverting modefor OC3B, fast PWM mode
  TCCR3B = (1<<WGM33) | (1<<WGM32) | (1<<CS30); //fast PWM mode, prescaler 1 (no prescaling)
  ICR3 = 4550; //set TOP to 4560
  OCR3A = OCR3B = 0; //Set initial values of compare for inverter PWM
  TIMSK3 = (1<<TOIE3); //Enable Timer/Counter3 Overflow Interrupt
  TCNT3 = 0; //set BOTTOM to 0
  DDRH = (1<<DDH4) | (1<<DDH3); //make pins 7 and 6 digital outputs
  TCCR4A = (1<<COM4A1) | (1<<COM4B1) | (1<<COM4B0) | (1<<WGM41);  //non-inverting mode)for OC3A, inverting mode for OC3B, fast PWM mode
  TCCR4B = (1<<WGM43) | (1<<WGM42) | (1<<CS40); //fast PWM mode, prescaler 1 (no prescaling)
  ICR4 = 4550; //set TOP to 4560
  OCR4A = OCR4B = 0; //Set initial values of compare for inverter PWM
  TCNT4 = 0; //set BOTTOM to 0
  sei(); //enable interrupts globally
}

void loop() {
  // put your main code here, to run repeatedly:

}
ISR(TIMER3_OVF_vect){

  int invComp = OCR3A; //Variable for current compare register value
  Serial.println(micros());
  static int invCompPrev; //Static variable to maintain the previous compare register value

  if (invComp > invCompPrev){ //Check to see if the on-time is currently increasing
    if (invComp >= 4550){ //Check if the compare value has reached TOP
      OCR3A = OCR3B = OCR4A = OCR4B -= 65; //Update Compare register value
    }
    else{
      OCR3A = OCR3B = OCR4A = OCR4B += 65;
    }
  }
  else{ //Else on-time is currently decreasing
    if(invComp <= 0){ //Check if the compare value has reached BOTTOM
      OCR3A = OCR3B = OCR4A = OCR4B += 65; //Update Compare register value
    }
    else{
      OCR3A = OCR3B = OCR4A = OCR4B -= 65;
    }
  }
  invCompPrev = invComp; //Update previous compare register value
}

It would take me too long to become sufficiently familiar with your program so these are just general comments.

Normally Serial.print() instructions should NOT be inside an ISR because print() needs interrupts and they are turned off during an ISR, My guess is that your Serial.print() is taking the place of a short delay() - or maybe not so short.

If this was my project I would take the Serial.print() out of the ISR and focus on finding what is wrong with the code.

ISRs can be very hard to debug. Have you an oscilloscope so you can watch what is happening?

...R

I agree with Robin2 that the Serial.print() statement is creating some sort of delay and that removing it and looking for root cause is the correct process.

I would take a look at the synchronization between timers 3 and 4 as they share the same prescaler and are started at slightly different times. I would place the TCNT3=0 and TCNT4=0 together at the end of setup.

I would also investigate keeping the timers not running until all of the setup is completed, TCNT values are set to 0, and then |= the bits into CS30 and CS40 at the end of setup to start the timers.

If synchronization is an issue, you may also want to investigate the ATmega2560 datasheet and read up on the GTCCR register. It can be used to synchronize Timers 0,1,3,4 & 5 but I'm not familiar of when that register is needed or used.

Thank you both very much for your responses.

Robin, I was suspect of a delay being created but was unable to replicate the result.

I did not realize that the print() needs interrupts and that they are turned off during an ISR (not obvious as I am still able to print within the ISR) but will look in to a possible interrupt within an interrupt causing an issue as well as re-visiting possible issues with the code - this makes sense to me, thank you.

Yes, I have hired an oscilloscope and think I will be taking tomorrow afternoon off to spend some time on this before the hire cost becomes to extravagant.

Cattledog, I tried placing both the TCNT# = 0 and assigning the CS bits at the end of the setup and synchronizing the timer without success;

  • Placing the TCNT at the end of set up did not change the output.
  • Placing the CS assignment at the end of setup did change the output put not in a desirable direction (alternated between an on state of about 14% and a spike (image attached).
  • The same occurs when both the TCNT command and CS assignments are placed at the end of the setup.

Based on the scope, the timers appear to operating in synchronicity anyway though (can also see my originals attachment of the scope).

Thank you again, I will continue my research and testing and post my findings and results.

Trox3:
but will look in to a possible interrupt within an interrupt

That can't happen because interrupts are switched off within an ISR

And, while you can override that DON'T

...R

I did not realize that the print() needs interrupts and that they are turned off during an ISR (not obvious as I am still able to print within the ISR)

The Arduino print function buffers output and then transfers data one byte at a time. The trigger to send the next byte is an interrupt generated when the previous byte's transmission has completed. One character may be sent from within the ISR, but I think what you are seeing is the buffered data being printed after the exit from the ISR.

I was suspect of a delay being created but was unable to replicate the result.

Have you tried experimenting with delay() in loop()? delayMicroseconds() will work within an ISR, but delay() with a millisecond argument will not as that function also relys on interrupts

Apologies for my slow reply and thank you for your suggestions.

ultimately I have removed the inverter and AC load from my project as it is not critical or really required for the goal of my thesis and agreed by my thesis supervisor but I will describe below, to the best of my memory, what I did try.

  • Tried delayMicroseconds() from 4 to 290us (as 1 cycle is 286us) in 4us increments
  • Re-visited the code (could not find anything to suggest why it wouldn’t work
  • Re-wrote the code anyway to try a different way
  • Moved the code in to the main loop and removed the ISR (using the overflow flag and clearing manually
  • Also noticed that SD card was no longer being written to
  • Re-Read up on timers in the data sheet to identify if I was using something incorrectly
  • Read up on interrupts in the data sheet - found they have a precedence based on their address - disabled all known non-critical interrupts with a higher precedence
  • Read up on the Serial object and its functions
  • Obtained the actual time for the code to execute by reading the microseconds in to a variable, printing the variables at the start of the loop then Serial.flush() the print immediately after so it waits for the print be completed (out of the buffer and actually printed) before entering the code to update the compare output register - 4us consistently
  • Exhausted here of ideas actually used the cursor measurements on the oscilloscope to find that even though to the naked eye with the print function in it looked good it actually wasn’t - the on-time would always increase or decrease in the correct direction but not by the correct amount, further the amount it increased or decreased was not consistent nor was there an identifiable pattern

I think that was everything but I could have tried something else.

The code below is one of the last ones I tried with.

void loop() {
  if((TIFR4 & (1<<TOV4))==0){ //Check if overflow flag is not set
    return;
  }
  else{
    TIFR4 = (1<<TOV4); //Clear overflow flag
    if (OCR3A > invCompPrev){ //Check if current direction of on-time is increasing
      switch(OCR3A){
      case 4550: //Check output compare (on-time) has reached TOP
      invCompPrev = OCR3A; //Update InverterComparePrevious value
      OCR3A = OCR3B = OCR4A = OCR4B -=65; //Decrease output compare (on-time)
      break;
      default:
      invCompPrev = OCR3A; //Update InverterComparePrevious value
      OCR3A = OCR3B = OCR4A = OCR4B +=65; //increase output compare (on-time)
      }
    }
    else{ //Else current direction of on-time is decreasing and update using similar mnethod as above
      switch(OCR3A){
      case 0:
      invCompPrev = OCR3A;
      OCR3A = OCR3B = OCR4A = OCR4B +=65;
      break;
      default:
      invCompPrev = OCR3A;
      OCR3A = OCR3B = OCR4A = OCR4B -=65;
      }
    }
  }
}

Could possibly try the code with just 1 timer to begin with, just to see if everything behaves nicely for 1 PWM channel (and its counterpart channel).

And also - could also try generating the PWM without interrupts, such as generating it using the main loop. And - while in the main loop - if feasible, avoid using functions like serial.print and other function that significantly takes up significant amounts of time (relative to the clock period).