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\$ Jul 6, 2016 at 8:15
  • \$\begingroup\$ Hi Ignacio, thank you for your quick reply. The system clock rate is 16Mhz. \$\endgroup\$ Jul 6, 2016 at 8:22
  • \$\begingroup\$ Have you considered using a servo library instead of hand-rolling your own code? \$\endgroup\$ Jul 6, 2016 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\$ Jul 6, 2016 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, 2016 at 12:38

1 Answer 1

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\$ May 11, 2017 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\$ May 11, 2017 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, 2020 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, 2020 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, 2020 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.