1
\$\begingroup\$

I'm having a hard time to set a 16 bit PWM on atmega328 that works with frequency of 50Hz and have a duty cycle that can be varied. I also have my doubts that if I can have an ADC working together with this 16 bit PWM since both use timer1. There are the auto trigger source selection ADTSX on the register ADCSRB that I supposed should be able to select the clock source for my analog conversion but I've setted and nothing changed, I couldn't have timer1 ISR and analog conversion at same time and I didn't tested for PWM and ADC together because I don't know how to set the PWM.

This is the code just for setting PWM that I've right now:

#include <avr/io.h>

int flag = 0;

ISR(TIMER1_COMPA_vect) {
  if (flag == 0)
    OCR1A = 11;
  else
    OCR1A = 160;    
}

int main(void) {
   DDRB |= (1 << DDB1);
   // PB1 as output
  OCR1A = 160;
  // set non-inverting mode
  TCCR1A |= (1 << COM1A1);
  // set 10bit phase corrected PWM Mode
  TCCR1B |= (1 << WGM12);
  // set no prescaler 
  TCCR1B |= (1 << CS10);
   while (1)
   {
   }
}

PS: I'll be varying the duty cycle between 1ms to 2ms activating an ESC controlling a brushless motor.

\$\endgroup\$
7
  • \$\begingroup\$ What is the system clock rate? \$\endgroup\$ – Ignacio Vazquez-Abrams Jul 6 '16 at 8:15
  • \$\begingroup\$ Hi Ignacio, thank you for your quick reply. The system clock rate is 16Mhz. \$\endgroup\$ – Rinaldi Segecin Jul 6 '16 at 8:22
  • \$\begingroup\$ Have you considered using a servo library instead of hand-rolling your own code? \$\endgroup\$ – Ignacio Vazquez-Abrams Jul 6 '16 at 8:55
  • \$\begingroup\$ No I have not, because I may want to change the frequency later, or use a different duty cycle for another project, and the servo library would not suffice. \$\endgroup\$ – Rinaldi Segecin Jul 6 '16 at 9:08
  • \$\begingroup\$ "I couldn't have timer1 ISR and analog conversion at same time"? Why not? Shouldn't be a problem at all. \$\endgroup\$ – JimmyB Jul 6 '16 at 12:38
2
\$\begingroup\$

For full 16 bit resolution, you'll want Waveform Generation Mode 8, i.e. WGM13:0 = 1000b.

In this mode, you define the resolution (=max counter value) through the value of the Input Capture Register, so you need ICR1 = 0xffff.

Depending on the polarity you want you set the Compare Output Mode COM1A1:0 to 10b or 11b.

TCCR1B = 0; // (If timer may already be running:) Make sure the timer is stopped during (re-)configuration

TCNT1 = 0x0000; // (If timer may have already been used since last reset:) Set counter value to begin of PWM cycle to prevent a potential glitch in the first cycle

ICR1 = 0xffff; // "TOP" = 0xffff -> full range of 16 bit counter

OCR1A = <initial duty cycle>; // 0x0000...0xffff

TCCR1A =
    // Compare output mode (10b):
    (1 << COM1A1) | (0 << COM1A0) | 

    // lower 2 WGM bits (00b):
    (0 << WGM11) | (0 << WGM10);

TCCR1B =
    // upper 2 WGM bits (10b):
    (1 << WGM13) | (0 << WGM12) |
    
    // Set prescaler/start timer (001b):
    (0 << CS12) | (0 << CS11 ) | (1 << CS10);

(Haven't tried this.)

Now the timer is running up and down toggling the OC1A pin every time it passes OCR1A. All you have to do is set a new OCR1A value when you want to change the duty cycle; the rest is done by the hardware.

\$\endgroup\$
5
  • \$\begingroup\$ Thank you for your response Jimmy. I was able to set the frequency right because I can see 50Hz calculated by the eq: f_{pfc} = f_{clk} / (2 * prescaler * ICR) and setting 8 for prescaler 0x4E20 for ICR. I don't know I'm can't vary the duty cycle altering OCR1A with double slope operation I don't know if I need to change ICR to another value to compensate the time when the pwm is on the duty cycle (high level). \$\endgroup\$ – Rinaldi Segecin May 11 '17 at 22:56
  • \$\begingroup\$ OCR1A needs to be setted on every loop iteration. Eg. to set duty cycle of 2ms OCR1A must be equal to 0x07D0. With the configuration mentioned previously we'll be able to vary 20000 times the duty cycle on the 20ms period. Thank you very much everyone. \$\endgroup\$ – Rinaldi Segecin May 11 '17 at 22:57
  • \$\begingroup\$ Notice that ICR1 should only be set after TCCR1A and TCCR1B has been configured to use ICR1 as TOP. And OCR1A and OCR1B use double buffering for PWM and should also only be set after TCCR1A and TCCR1B has been configured to use PWM. See this answer for more details. \$\endgroup\$ – hlovdal Nov 16 '20 at 20:13
  • \$\begingroup\$ @hlovdal All setup should be done before the timer is actually started (via TCCR1B) or you risk a race condition by reconfiguring a timer that's already running. \$\endgroup\$ – JimmyB Dec 22 '20 at 9:41
  • \$\begingroup\$ Yes, I have split up configuring TCCR1B and starting TCCR1B in two operations (with modifying ICR1, OCR1A and OCR1B in between) in my answer. \$\endgroup\$ – hlovdal Dec 23 '20 at 18:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.