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?
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.
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.
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).