Hello everyone,
I wanted to create two 50% duty cycle non-overlapping complementary PWM signals with variable frecuency (20-200kHz) determined by an auxiliary function (of around 10 Hz), using the ATmega328P.
I started using the solution provided by cattledog in this post but I have encountered some issues when changing the frecuency while the timer is running (a lot of noise is produced), described in the datasheet:
When changing the TOP value the program must ensure that the new TOP value is higher or equal to the value of all of the compare registers. If the TOP value is lower than any of the compare registers, a compare match will never occur between the TCNT1 and the OCR1x. Note that when using fixed TOP values, the unused bits are masked to zero when any of the OCR1x registers are written. As the third period shown in Figure 15-8 on page 103 illustrates, changing the TOP actively while the Timer/Counter is running in the phase correct mode can result in an unsymmetrical output. The reason for this can be found in the time of update of the OCR1x register. Since the OCR1x update occurs at TOP, the PWM period starts and ends at TOP. This implies that the length of the falling slope is determined by the previous TOP value, while the length of the rising slope is determined by the new TOP value. When these two values differ the two slopes of the period will differ in length. The difference in length gives the unsymmetrical result on the output.
I then changed to Timer1 in Mode 8 (PWM, phase and frequency correct) setting the TOP value with ICR1.
The datasheet recommends using OCR1A to define the variable TOP value, however I am using it to generate one of the PWM signals:
Using the ICR1 register for defining TOP works well when using fixed TOP values. By using ICR1, the OCR1A register is free to be used for generating a PWM output on OC1A. However, if the base PWM frequency is actively changed by changing the TOP value, using the OCR1A as TOP is clearly a better choice due to its double buffer feature.
So I dont know if anyone knows of a solution to the problem using Timer1 or if it is necessary to do some hardware changes.
A snipppet of my code:
int overflow_flag = 0;
void setup() {
pinMode(9, OUTPUT); //output A
pinMode(10, OUTPUT); //output B Complementary
Serial.begin(9600); // setup serial
TCCR1A = 0; //clear timer registers
TCCR1B = 0;
TCNT1 = 0;
//ICR1 and Prescaler sets frequency
//no prescaler .0625 us per count @ 16Mh
//80 counts x .0625 = 5 us = 200Khz
TCCR1B |= _BV(CS10); //no prescaler
ICR1 = 40;//PWM mode counts up and back down for 80 counts
//output A set rising/clear falling
//Rise at TCNT 21 upslope, High 38 counts, Fall at TCNT 21 downslope
//47,5% Duty Cycle Pulse centered on TCNT 40. High 38 Low 42
TCCR1A |= _BV(COM1A1) | _BV(COM1A0); //output A set rising/clear falling
//output B clear rising/set falling
//Fall at TCNT 19 upslope, Low 42, Rise at TCNT 19 downslope
//47.5% Duty Cycle Pulse centered on TCNT 0. High 38 Low 42
TCCR1A |= _BV(COM1B1); //output B clear rising/set falling
TCCR1B |= _BV(WGM13); //PWM mode with ICR1 Mode 10
TCCR1A |= _BV(WGM11); //WGM13:WGM10 set 1010
//Set timer1 interrupt on overflow
TIMSK1 |= _BV(TOIE1);
sei();
}
ISR (TIMER1_OVF_vect) {
if(overflow_flag==1) {
OCR1A = semi_output; //Pin 9 match
OCR1B = semi_output; //Pin 10 match
ICR1 = output;
overflow_flag=0;
}
}
void loop() {
//Some code to calculate the variable output value
output= //variable value (Sin function of 10Hz)
semi_output = round(output/2);
overflow_flag = 1;
delay(5);
}
Thanks in advance!