Electronic – Setting a simple PWM on atmega328 at certain frequency

atmega328ppwm

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.

Best Answer

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.