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\$
  • \$\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.

ICR1 = 0xffff;

OCR1A = <initial duty cycle>;

TCCR1A =
    // Compare output mode:
    (1 << COM1A1) | (0 << COM1A0) | 
    // lower 2 WGM bits:
    (0 << WGM11) | (0 << WGM10);

TCCR1B =
    // upper 2 WGM bits:
    (1 << WGM13) | (0 << WGM12) |

    // Prescaler/start timer:
    (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\$
  • \$\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

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.