Using an Arduino 2560 to generate two PWMs, Timer 5 runs correctly on its own, however, once a similar PWM definition using Timer 1 is added to the code, Timer 5 PWM stops. This was tested with Timers 3 and 4 as well. The Timer 1 routine runs on its own, so that code works. My understanding is that all timers are independent with their own counters, registers and interrupt vectors, but apparently there is some unfortunate interaction. The sketch can be run on any Arduino with at least 4 timers (Timers 0 and 2 are used elsewhere, but not included in this simplified test code). To test the sketch on a 2560, comment out line 20 (TIMSK1 = ) and put a scope on pins 2 and 3 to see each with a PWM 30 Hz signal with different duty cycles (12% and 50%). What might be the reason for this interaction causing Timer 5 to fail with line 20 (Timer/Counter 1 Interrupt Mask Register) included? Any enlightenment would be very much appreciated!
/* ATmega2560 Multiple-Timer Test using PWM phase-correct mode
Timer 5 only runs properly when TIMSK1 is commented out, or TIMSK1 = 0; (line 20)
Timer 1 runs at 60Hz and Timer 5 at 30Hz and outputs PWM on 2560 pins 2 & 3
Logic for Timer 1 eliminated for simplicity. Timer 1 definition is enough to cause the problem with Timer 5
*/
uint16_t TOP_T5 = 4200; // Timer 5 counts up to 4200 and counts back down to zero
volatile int phase_correct_state_T5 = 0; // triangular wave at bottom = 0 and top = 1
float TOP = 16650.0; // 60 Hz for TIMER 1
void setup() {
Serial.begin(115200);
uint8_t sreg = SREG;
cli();
// Parameters to generate 60 Hz triangular wave with TIMER1 in phase-correct mode - count up/count down
TCCR1A = 0; // set entire TCCR1A register to 0
TCCR1B = 0; // same for TCCR1B
TIMSK1 = 0;
TCNT1 = 0; // initialize counter value to 0
TCCR1A |= (1 << WGM11); // set phase-correct mode
TCCR1B |= (1 << WGM11) | (1 << WGM13) | (1 << CS11); // prescaler = 8
TIMSK1 |= (1 << OCIE1A) | (1 << OCIE1B) | (1 << TOIE1) | (1 << ICIE1); // enable Output compare interrupt
TIFR1 |= (1 << OCF1B) | (1 << OCF1A);
ICR1 = 16650;
TOP = 16650;
OCR1B = 250; // start duty cycle close to zero
OCR1A = TOP - 250;
DDRD |= B11111000; // PD3-PD7 for outputs 1 to 5
DDRB |= B00000001; // PB0 out for output 6
PORTC |= B00000000;
PORTD |= B00000000;
// Timer 5 in phase-correct PWM mode at 30 Hz
TCCR5A = 0; // set entire TCCR3A register to 0
TCCR5B = 0; // same for TCCR3B
TCNT5 = 0; // initialize counter value to 0
TCCR5A |= (1 << WGM51); // set phase-correct mode
TCCR5B |= (1 << WGM51) | (1 << WGM53) | (1 << CS50) | (0 << CS51) | (0 << CS52); // prescaler = 8
TIMSK5 |= (1 << OCIE5A) | (1 << OCIE5B) | (1 << TOIE5) | (1 << ICIE5); //enable Output compare interrupt
TIFR5 |= (1 << OCF5B) | (1 << OCF5A);
ICR5 = 4200; // Set TOP value
TOP_T5 = 4200; // make the same as ICR1
OCR5A = TOP_T5 - 100; // minimum duty cycle
OCR5B = 100; // minimum duty cycle
PORTE |= B00000000; // use positions 5 & 6 for this case
DDRE |= B00001100; // PE4 & PE5 on 2560 (pins 2 & 3)
SREG = sreg;
sei(); // enable global interrupts
}
void loop() {
OCR5A = 2100;
OCR5B = 500;
}
ISR(TIMER5_CAPT_vect) // used to determine if counter has topped out
{
cli(); // stop interrupts
phase_correct_state_T5 = 1; // now counting down - falling
sei(); // enable global interrupts
}
ISR(TIMER5_OVF_vect) // used to determine if counter has bottomed out
{
cli(); // stop interrupts
phase_correct_state_T5 = 0; // now counting UP - rising
sei(); // enable global interrupts
}
ISR(TIMER5_COMPA_vect) // OCR1A used to start and stop PE4 (pin 3)
{
cli(); // stop interrupts
if (phase_correct_state_T5 == 0) // wave is rising, so turn ON PE4
{
PORTE |= 1 << 4; // turn ON PE4
} else // wave is falling, so turn OFF PE4
{
PORTE &= ~(1 << 4); // turn OFF PE4
}
sei();
}
ISR(TIMER5_COMPB_vect) // OCR1B used to start and stop PE5 (pin 2)
{
cli(); // stop interrupts
if (phase_correct_state_T5 == 1) // wave is falling, so turn ON PE5
{
PORTE |= 1 << 5; // turn ON PE5
} else // wave is rising, so turn OFF PE5
{
PORTE &= ~(1 << 5); // turn OFF PE5
}
sei();
}
Just solved the problem based on someone else's answer to a similar issue. I turned on the OCR1A and OCR5A interrupts without defining an ISR for Timer 5. They thought that may be resetting the processor. Anyway, the problem is resolved by defining the interrupt vectors / routines for Timer 1. PWM using TImer - #2 by pylon
Judging by the code, you do not understand the meaning of all these registers and simply copied everything that came to hand into the code:
There is no WGM11 in TCCR1B register
Capture interrupt can't be used in PWM mode
But the main problem is - In order to create a PWM signal, interrupts are not needed at all. The point of generating PWM using a timer is that the timer does it itself. After enabling PWM mode you don't need firing an interrupts and switching the pins manually, the timer do it automatically.
Your approach for generating Phase Correct PWM signals on OC1A-pin (DPin-11 for TC1: Ch-A) and OC5A (DPin-46 for TC5: Ch-A) should be logical as follows. Start the timer (TC1/TC5) after initializing all the required parameters.
The sketch given below (tested) generates 5 Hz signal as a test case. To generate round 50 Hz for TC1 and 25 Hz for TC5, change ICR1/ICR5 as per formula given.
To change duty cycle connect pot with A0 and A1; read the pot value and store them into OCR1B and OCR5B registers and then slowly rotate the pots.
#define OC1A 11 //see attached file for the schematic for the DPins
#define OC5A 46
void setup()
{
pinMode(OC1A, OUTPUT); //PWM signal pin
TCCR1A = 0; //reset the registers to override init's value
TCCR1B = 0;
//-- phase correct PWM; Mode-10; f = 16 MHz/(2xNxTOP = ICRn)-------
//-- with N = 256, for round f= 5 Hz (test), ICR1 = 6250 for OC1A (11)---------
TCCR1A |= (1 << WGM11); //Mode-10 phase correct PWM
TCCR1A |= (1 << COM1A1); //non-inverting
TCCR1B |= (1 << WGM13);
ICR1 = 6250; //TOP
OCR1A = 3125; //50% duty cycle
//----------------------------------------------
pinMode(OC5A, OUTPUT);
TCCR5A = 0; //reset timers to override intit's values
TCCR5B = 0;
//-- phase correct PWM; Mode-10; f = 16 MHz/(2xNxTOP = ICRn)-------
//-- with N = 256, for round f= 5 Hz (test), ICR5 = 6250 for OC5A (46)-------
TCCR5A |= (1 << WGM51); //Mode-10 phase correct PWM
TCCR5A |= (1 << COM5A1); //non-inverting
TCCR5B |= (1 << WGM53);
ICR5 = 6250; //TOP
OCR5A = 3125; //50% duty cycle
//----------------------------------
TCCR1B |= (1 << CS12); //start TC1 TC1 with prescaler 1/256 for 5 Hz (test)
TCCR5B |= (1 << CS52); //start TC5 with prescaler 1/256 for 5 Hz (test)
}
void loop()
{
OCR1B = analogRead(A0); //changes duty cycle of OC1A wave
OCR5B = ananlogRead(A1); //changes duty cycle of OC5A wave
delay(1000); //test interval
}
TC1/TCNT1 and TC5/TCNT5 are dedicated timers. Once these are initialized and started, they begin generating PWM signal (or any other timing function) without any intervention from the MCU.
Thanks for your responses. It is interesting that specifying WGM51 in the TCCR5B register, produces the required 30Hz phase-correct PWM. Without it, it's a poorly shaped 2KHz signal. So, it is doing something I can't explain, but it makes all the difference! See the two scope traces.
The reason for using interrupts is to schedule additional pin switching using Timers 0 and 2. PWM output on the OC5A/B pins would not support the required logic. The code submitted is less than 5% of the complete sketch, and all the Timer0/1/2 code functions properly (the way I need it to operate). If you can explain the reason why including WGM51 in the TCCR5B register correctly produces 30Hz PWM, that would be greatly appreciated, since it may be hardware specific, in which case it may not work when the code is transferred to an ATmega324PB. Thanks!!!
You can not exactly produce 30 Hz signal. There will be fractional.
The data sheet says that Mode-10 (WGM53-WGM50 = 1010) with TOP value in ICR5 will produce phase correct PWM signal on OC5A/46-pin. So, WGM51 bit has to be set to HIGH for the corresponding hardware to be active.
I have included the interrupt routines for Timers 0, 1, 2, 3 this time so you can see why I used interrupts rather than the pure PWM from OC3A & OC3B. I changed back to Timer3 since the final destination is an ATmega324PB that doesn't have Timer5. (Timer4 and Timer5 were only debugging tests.)
Running the sketch as shown produces the approx. 30Hz PWM on pins 2 and 3, however, if you remove (1<<WGM31) from TCCR3B settings (on line 36), the output is ~2KHz and "not pretty".
That shouldn't have any effect, but it does!
/* ATmega2560 Multiple-Timer Test using PWM phase-correct mode
Timer3 only works properly when TCCR3B |includes (1<<WGM31), which is not a TCCR3B parameter
Timer 1 runs at 60Hz and Timer 3 at 30Hz and outputs PWM on 2560 pins 2 and 3
*/
uint16_t TOP_T3 = 4200; // Timer 3 counts up to 4200 and counts back down to zero
volatile int phase_correct_state = 0; // triangular wave at bottom = 0 and top = 1
volatile int phase_correct_state_T3 = 0; // triangular wave at bottom = 0 and top = 1
float TOP = 16650.0; // 60 Hz for TIMER 1
void setup() {
Serial.begin(115200);
uint8_t sreg = SREG;
cli();
// Parameters to generate 60 Hz triangular wave with TIMER1 in phase-correct mode - count up/count down
TCCR1A = 0; // set entire TCCR1A register to 0
TCCR1B = 0; // same for TCCR1B
TIMSK1 = 0;
TCNT1 = 0; // initialize counter value to 0
TCCR1A |= (1 << WGM11) | (1 << WGM13); // set phase-correct mode
TCCR1B |= (1 << WGM11) | (1 << WGM13) | (1 << CS11); // prescaler = 8
TIMSK1 |= (1 << OCIE1A) | (1 << OCIE1B) | (1 << TOIE1) | (1 << ICIE1); // enable Output compare interrupt
TIFR1 |= (1 << OCF1B) | (1 << OCF1A);
ICR1 = 16650;
TOP = 16650;
OCR1B = 250; // start duty cycle close to zero
OCR1A = TOP - 250;
DDRD |= B11111000; // PD3-PD7 for outputs 1 to 5
DDRB |= B00000001; // PB0 out for output 6
PORTC |= B00000000;
PORTD |= B00000000;
// Timer 3 in phase-correct PWM mode at 30 Hz
TCCR3A = 0; // set entire TCCR3A register to 0
TCCR3B = 0; // same for TCCR3B
TCNT3 = 0; // initialize counter value to 0
TCCR3A |= (1 << WGM31); // set phase-correct mode
TCCR3B |= (1 << WGM31) | (1 << WGM33) | (1 << CS30) | (0 << CS31) | (0 << CS32); // prescaler = 8
TIMSK3 |= (1 << OCIE3A) | (1 << OCIE3B) | (1 << TOIE3) | (1 << ICIE3); //enable Output compare interrupt
TIFR3 |= (1 << OCF3B) | (1 << OCF3A);
ICR3 = 4200; // Set TOP value
TOP_T3 = 4200; // make the same as ICR3
OCR3A = TOP_T3 - 100; // minimum duty cycle
OCR3B = 100; // minimum duty cycle
PORTE |= B00000000; // use positions 5 & 6 for this case
DDRE |= B00001100; // PE4 & PE5 on 2560 (pins 2 & 3)
SREG = sreg;
sei(); // enable global interrupts
}
void loop() {
OCR3A = 2100;
OCR3B = 500;
}
ISR(TIMER1_CAPT_vect) // used to determine if counter has topped out
{
cli(); //stop interrupts
phase_correct_state = 1; // now counting down - falling
/* TIMSK0 &= ~(1 << OCIE0A) | ~(1 << TOIE0) | ~(1 << OCIE0B); // Disable the timer 0 interrupts
TIFR0 |= _BV(OCF0A);
OCR0A = OCR0A_1_CAPT; // set pulse start time in TIMER0 OCR0A and set pulse width in TIMER0 ISR
TCNT0 = 0; // Set the initial counter value to 0
TIMSK0 = (1 << OCIE0A); // Enable the compare match A interrupt
TI0_SW = 1; // this indicates if this is the first time TIMER0 interrupts
*/
sei(); // enable global interrupts
}
ISR(TIMER1_OVF_vect) // used to determine if counter has bottomed out
{
cli(); //stop interrupts
phase_correct_state = 0; // now counting UP - rising
/* TIMSK2 &= ~(1 << OCIE2A); // Disable the timer 0 interrupts
TIFR2 |= _BV(OCF2A);
OCR2A = OCR2A_1_OVF; // set pulse start time in TIMER2 OCR2A and set pulse width in TIMER2 ISR
TCNT2 = 0; // Set the initial counter value to 0
TIMSK2 = (1 << OCIE2A); // Enable the compare match A interrupt
TI2_SW = 1; // this indicates if this is the first time TIMER2 interrupts
*/
sei(); // enable global interrupts
}
ISR(TIMER1_COMPA_vect) // OCR1A used to start and stop bank 1
{
if (phase_correct_state == 0) // wave is rising, so turn ON bank 1 Position 1
{
PORTD |= 1 << 3;
} else // wave is falling, so turn OFF bank 1 Position 1
{
PORTD &= ~(1 << 3); // turn Off 1
}
}
ISR(TIMER1_COMPB_vect) // OCR1B used to start and stop bank 2 (top - duty cycle to offset bank 2 by 180 degrees
{
if (phase_correct_state == 1) // wave is falling, so turn ON bank 2
{
PORTD |= 1 << 5; // turm ON 3
} else // wave is rising - phase_correct_state = 0
{
PORTD &= ~(1 << 5); // turn OFF 3
}
}
ISR(TIMER3_CAPT_vect) // used to determine if counter has topped out
{
cli(); // stop interrupts
phase_correct_state_T3 = 1; // now counting down - falling
sei(); // enable global interrupts
}
ISR(TIMER3_OVF_vect) // used to determine if counter has bottomed out
{
cli(); // stop interrupts
phase_correct_state_T3 = 0; // now counting UP - rising
sei(); // enable global interrupts
}
ISR(TIMER3_COMPA_vect) // OCR1A used to start and stop PE4 (pin 3)
{
cli(); // stop interrupts
if (phase_correct_state_T3 == 0) // wave is rising, so turn ON PE4
{
PORTE |= 1 << 4; // turn ON PE4
} else // wave is falling, so turn OFF PE4
{
PORTE &= ~(1 << 4); // turn OFF PE4
}
sei();
}
ISR(TIMER3_COMPB_vect) // OCR1B used to start and stop PE5 (pin 2)
{
cli(); // stop interrupts
if (phase_correct_state_T3 == 1) // wave is falling, so turn ON PE5
{
PORTE |= 1 << 5; // turn ON PE5
} else // wave is rising, so turn OFF PE5
{
PORTE &= ~(1 << 5); // turn OFF PE5
}
sei();
}
Well, that's all very interesting ! ! ! That has fixed the problem. I removed the WGM31 from TCCR3B settings and chose another prescalar following your example, and it works as it should. I will change the Timer1 settings in TCCR1B as well. They worked, but not for the right reason, which could make the sketch fail on another controller.
Not being able to see what happens behind the scenes makes it difficult to know how it processes an incorrect setting; in this case messing with the prescalars.
There is nothing "behind the scenes" in that case. The macros like CS31 or WGM51 are nothing more than bitmasks which applicable to any registre. The CS31 and WGM31 has the same numerical value = 0x02, so if you applying the WGM31 to the TCCR3B - the result will the same as with CS31.
I would advise you to consult the datasheet for atmega2560 when writing code, and not to copy-paste program from different examples.
I find the spec sheets rather cryptic to say the least. There are websites that provide much clearer explanations, such as c++ - Proper implementation of Timer1 for PWM generation - Stack Overflow.
What is a macro but a bit of code that allows you to "call" it without having to write the entire function each time you use it. Firstly, it's wasn't obvious to me that CS31 was a macro, and secondly, an assignment to TCCR1B should not affect TCCR1A, or so I would have thought. So, how TCCR1B |= (1 << WGM11) messes with prescalers is still a mystery to me. None the less, you have shown me the way to solving my problem, so for that I am grateful. Thank you.
That very clearly explains how things happen. These named parameters, like WGM11, are simple constants that are not tied to any one specific register as I had assumed. So, "ORing" them with any register will turn ON that bit. Thank you for the very detailed explanation!
If you are concerned about hardware specific solutions for a 30Hz pwm synchronized with complex logic across multiple pins, maybe look into doing the PWM in software.
We started by doing the PWM in software, but the pulse width (duty cycle) was all over the map because it was dependent on CPU workload, so we decided to use interrupt processing and that keeps it very stable. Switching the outputs inside the interrupts (not OCR1) was necessary because we needed 4 PWM outputs that operate in pairs180 degrees apart, but with different variable duty cycles. Anyway, with B707's help, it's all good now.