Question

I've been experimenting with the PWM Waveform Generation Modes on the ATMega328P. I've been getting some strange results and I can't figure out if its a problem with how I'm writing the firmware or how I'm interpreting the datasheet.

Here's the first piece of code that I wrote to emulate the analogWrite() function:

// Waveform Generation Mode 0
// Table 15-4 of the datasheet

void setup()
{
  DDRB = (1<<PB1); // set pin 9 as output

  TCCR1A |= (1<<COM1A1);
  OCR1A = 125;
}

void loop()
{
}

The above code produces an average voltage output of around 2.5V (49% duty cycle) out of pin 9. The strange thing (for me) is that according to the datasheet, TIMER1 is a 16bit timer, so it should overflow at 65536 ticks. From what I understand setting OCR1A between 0 and 65535 will change the duty cycle of the pulse. So, having set the OCR1A at 125, shouldn't I be getting an output of around 0.01 V instead of 2.5V? The results seem to imply that the clock is overflowing at 255.

For my second foray into PWM land, I wanted to try and create a 2.5V signal using the ATMega's fast PWM mode. Here's what I got:

// Waveform Generation Mode 14
// Table 15-4 of the datasheet

void setup()
{
  DDRB = (1<<PB1);

  TCCR1A |= (1<<COM1A1) | (1<<WGM11);
  TCCR1B |= (1<<WGM13) | (1<<WGM12) | (1<<CS10);

  ICR1 = 19999;
  OCR1A = 10000;
}

void loop()
{
}

I set ICR1 (the overflow value) arbitrarily to 20000 ticks then set OCR1A (the compare value) to about half that. I set Channel A to non-inverting mode, but (I think) it wouldn't have made a difference if I set it to inverting mode. When I flashed this onto the Arduino I was getting a steady voltage average of 5V (100% duty cycle) out of pin 9, and I can't for the life of me figure out why.

I would appreciate any insight you can offer.

Was it helpful?

Solution

Answered by joeymorin on AVRfreaks:

Note that on the Uno, the Arduino init code that runs before your setup() configures a lot of stuff, including all three timers on the 328P.

From wiring.c:

   sbi(TCCR0A, WGM01); 
   sbi(TCCR0A, WGM00); 
   sbi(TCCR0B, CS01); 
   sbi(TCCR0B, CS00); 
   sbi(TIMSK0, TOIE0); 

   sbi(TCCR1B, CS11); 
   sbi(TCCR1B, CS10); 
   sbi(TCCR1A, WGM10); 

   sbi(TCCR2B, CS22); 
   sbi(TCCR2A, WGM20);

This starts all three timers with a prescaler of 64.

TIMER0 is placed into mode 3 (fast PWM) with the overflow interrupt enabled to support the timing functions (millis(), micros(), and delay()).

TIMER1 is placed into mode 1 (fixed 8-bit phase-correct PWM).

TIMER2 is placed into mode 1 (phase-correct PWM).

void setup() 
{ 
  DDRB = (1<<PB1); // set pin 9 as output 

  TCCR1A |= (1<<COM1A1); 
  OCR1A = 125; 
} 

void loop() 
{ 
} 

Since WGM10 in TCCR1A is already set, setting COM1A1 will enable the PWM output in non-inverting mode, just as analogWrite() would.

TIMER1 is a 16bit timer, so it should overflow at 65536 ticks. From what I understand setting OCR1A between 0 and 65535 will change the duty cycle of the pulse. So, having set the OCR1A at 125, shouldn't I be getting an output of around 0.01 V instead of 2.5V? The results seem to imply that the clock is overflowing at 255.

In mode 1 it behaves like an 8-bit timer.

void setup() 
{ 
  DDRB = (1<<PB1); 

  TCCR1A |= (1<<COM1A1) | (1<<WGM11); 
  TCCR1B |= (1<<WGM13) | (1<<WGM12) | (1<<CS10); 

  ICR1 = 19999; 
  OCR1A = 10000; 
} 

void loop() 
{ 
}

Since WGM10 in TCCR1A is already set, setting WGM11, WGM13, and WGM12 will select mode 15, not mode 14. Mode 15 is fast PWM with TOP = OCR1A (not ICR1). Since you are also using setting OC1A output for PWM with COM1A1, this will result in an OC1A remaining high.

As mentioned already, if you want to configure timers in the Arduino environment, you should do it from scratch with = instead of |=.

theusch wrote:

And, for all I know, more might be run after setup()

From Arduino's main.cpp: Code:

#include <Arduino.h> 

int main(void) 
{ 
   init(); 

#if defined(USBCON) 
   USB.attach(); 
#endif 

   setup(); 

   for (;;) { 
      loop(); 
      if (serialEventRun) serialEventRun(); 
   } 

   return 0; 
}

JJ

OTHER TIPS

OCR1A control the frequency (overflow restart the timer), while OCR1B control the duty cicle (overflow will change the output pin state)

in the second example i can't uderstand why you are using ICR1and how is possible you are getting 8V as output as the arduino runs at 5V... or you are fryng it or your reading are inaccurate.

please take a look here, it will explain a lot of thing about PWM, fast PWM, and more.

http://arduino.cc/en/Tutorial/SecretsOfArduinoPWM

edit: i've missed you are using ICR1 as TOP and OCR1A as counter. Then the problem is: by default, register are initalized at some value by the arduino libraries. TCCR1A = 1 -> (WMG10 ON, this is bad) TCCR1B = 11 -> (prescaler = 64)

you are NOT overriding this value but just putting to 1 some bit; that way, instead of having activatid pin WGM11, WMG12, and WMG13, you will also have bit WMG10 on, causing to fall on mode 15.

Also the final prescaler will still be 16 instead of 8.

Solution is to change |= with =, so you will override default value.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top