0
\$\begingroup\$

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.

\$\endgroup\$
  • \$\begingroup\$ Try implementing a loop and using a conditional block to see if the timer1 counter TCNT1 goes more than 0xFF. Put a compare value higher than that of course, like 0xFFFF (top value). \$\endgroup\$ – Diego C Nascimento Jan 27 '14 at 18:50
0
\$\begingroup\$

There is a Hardware and there is a Software aspect of this.

1st - the hardware - As you are aware the PWM is averaged by your DC meter to indicate a value between 0 and 5V, hopefully related to your duty cycle. Where as this will greatly depend upon your load. e.g. No load will just build up the charge at 5V and stay there. Too much and it will dissipate before it can by measured. Where you load can be estimated with f=1/2PiRC

2nd - the software - check out Secrets of Arduino PWM it has several examples of various modes that you can exercise through, in which you should get the stated results and from there you can tweak them to your needs.

3rd there are online PWM calculators. online calculator and another

\$\endgroup\$
  • \$\begingroup\$ Thanks for the links to the PWM calculators! Never knew they were out there, sort of a pain doing it by hand. \$\endgroup\$ – user36193 Jan 28 '14 at 4:17
0
\$\begingroup\$

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

\$\endgroup\$

Your Answer

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