Skip to content

Instantly share code, notes, and snippets.

@Wollw
Last active July 2, 2023 08:35
Show Gist options
  • Star 14 You must be signed in to star a gist
  • Fork 7 You must be signed in to fork a gist
  • Save Wollw/2425784 to your computer and use it in GitHub Desktop.
Save Wollw/2425784 to your computer and use it in GitHub Desktop.
ATmega328P PWM Example
/**
* A PWM example for the ATmega328P using the 8-Bit Fast PWM mode.
*/
#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdbool.h>
#include <util/delay.h>
int main (void) {
/**
* We will be using OCR1A as our PWM output register which is the
* same pin as PB1.
*/
DDRB |= _BV(PB1);
/**
* There are quite a number of PWM modes available but for the
* sake of simplicity we'll just use the 8-bit Fast PWM mode.
* This is done by setting the WGM10 and WGM12 bits. We
* Setting COM1A1 tells the microcontroller to set the
* output of the OCR1A pin low when the timer's counter reaches
* a compare value (which will be explained below). CS10 being
* set simply turns the timer on without a prescaler (so at full
* speed). The timer is used to determine when the PWM pin should be
* on and when it should be off.
*/
TCCR1A |= _BV(COM1A1) | _BV(WGM10);
TCCR1B |= _BV(CS10) | _BV(WGM12);
/**
* This loop is used to change the value in the OCR1A register.
* What that means is we're telling the timer waveform generator
* the point when it should change the state of the PWM pin.
* The way we configured it (with _BV(COM1A1) above) tells the
* generator to have the pin be on when the timer is at zero and then
* to turn it off once it reaches the value in the OCR1A register.
*
* Given that we are using an 8-bit mode the timer will reset to zero
* after it reaches 0xff, so we have 255 ticks of the timer until it
* resets. The value stored in OCR1A is the point within those 255
* ticks of the timer when the output pin should be turned off
* (remember, it starts on).
*
* Effectively this means that the ratio of pwm / 255 is the percentage
* of time that the pin will be high. Given this it isn't too hard
* to see what when the pwm value is at 0x00 the LED will be off
* and when it is 0xff the LED will be at its brightest.
*/
uint8_t pwm = 0x00;
bool up = true;
for(;;) {
OCR1A = pwm;
pwm += up ? 1 : -1;
if (pwm == 0xff)
up = false;
else if (pwm == 0x00)
up = true;
_delay_ms(10);
}
}
/**
* A PWM example for the ATmega328P using the 8-Bit Fast PWM mode.
*/
#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdbool.h>
#include <util/delay.h>
int main (void) {
/**
* We will be using OCR0A as our PWM output register which is the
* same pin as PD6.
*/
DDRD |= _BV(PD6);
/**
* There are quite a number of PWM modes available but for the
* sake of simplicity we'll just use the 8-bit Fast PWM mode.
* This is done by setting the WGM00 and WGM01 bits. The
* Setting COM0A1 tells the microcontroller to set the
* output of the OCR0A pin low when the timer's counter reaches
* a compare value (which will be explained below). CS00 being
* set simply turns the timer on without a prescaler (so at full
* speed). The timer is used to determine when the PWM pin should be
* on and when it should be off.
*/
TCCR0A |= _BV(COM0A1) | _BV(WGM00) | _BV(WGM01);
TCCR0B |= _BV(CS00);
/**
* This loop is used to change the value in the OCR0A register.
* What that means is we're telling the timer waveform generator
* the point when it should change the state of the PWM pin.
* The way we configured it (with _BV(COM0A1) above) tells the
* generator to have the pin be on when the timer is at zero and then
* to turn it off once it reaches the value in the OCR0A register.
*
* Given that we are using an 8-bit mode the timer will reset to zero
* after it reaches 0xff, so we have 255 ticks of the timer until it
* resets. The value stored in OCR0A is the point within those 255
* ticks of the timer when the output pin should be turned off
* (remember, it starts on).
*
* Effectively this means that the ratio of pwm / 255 is the percentage
* of time that the pin will be high. Given this it isn't too hard
* to see what when the pwm value is at 0x00 the LED will be off
* and when it is 0xff the LED will be at its brightest.
*/
uint8_t pwm = 0x00;
bool up = true;
for(;;) {
OCR0A = pwm;
pwm += up ? 1 : -1;
if (pwm == 0xff)
up = false;
else if (pwm == 0x00)
up = true;
_delay_ms(10);
}
}
@Meserlion
Copy link

you mean OCRA01 in second example?

@Meserlion
Copy link

"We will be using OCR1A as our PWM output which is the * same pin as PD6."

@pfabreu
Copy link

pfabreu commented Nov 28, 2015

No, I think it's OCR0A.

@controlMec
Copy link

please can you post code for dimmer controlled by atmega 328p

@gitcnd
Copy link

gitcnd commented Dec 1, 2021

Probably should mention HZ or the frequency in here someplace... what is it ?

@andrewlalis
Copy link

@gitcnd the frequency for the m328p chip is 16MHz

@gitcnd
Copy link

gitcnd commented Dec 4, 2021

@andrewlalis - I don't mean that (of course - not to mention - almost all 3.3v boards are 8mhz, and the m328p chip is actually 20mhz, not 16mhz, although most 5v people set it to 16mhz...)
I mean the output frequency of the default pwm circuit.

I did find a table of different boards and their defaults - there's about 10 different defaults, ranging from about 200hz to about 600hz - I think the atmega328p 3.3v board I'm using is 490hz

Sorry - can't find that table again - closed the browser tab, and can't see it in my history... grr...

@gitcnd
Copy link

gitcnd commented Dec 4, 2021

Found the HZ table:

https://www.arduino.cc/reference/en/language/functions/analog-io/analogwrite/

The above is the reason why this example is useless - it's called "m328p_fastpwm.c" but fails to explain what speed it runs at...

@Wollw
Copy link
Author

Wollw commented Dec 5, 2021

Sorry for any confusion, I completely forgot I'd ever posted this until this discussion got started. I haven't written any AVR code for some time, but I'm looking at the libavr documentation and ATMega328P datasheet at the moment and I'm trying to understand the problem. From what I remember and from looking over the Fast PWM section of the datasheet it seems like neither the main clock nor PWM frequency should really matter for the sake of the example. The value held by pwm is simply the point at which in each cycle the output pin is cleared, so I don't think it should matter how quickly those cycles happen as the amount of time the pin is high compared to the total time it takes for the timer to overflow is what would determine, for example, the brightness of an LED. From what I understand it should work the same at any clock speed.

Again though, it's been a long time since I've used any of this so I might be overlooking something.

Also, the second example's comment should refer to OCR0A since OC0A and PD6 are the same pin. Definitely a mistake in the comment I'll fix.

@ee-diary
Copy link

ee-diary commented Jun 9, 2022

is the header files #include <avr/interrupt.h> and #include <stdbool.h> required? I read several examples on ATmega328P Fast PWM mode Programming Examples but should i include these files?

@Wollw
Copy link
Author

Wollw commented Jun 9, 2022

stdbool.h is just a convenience so I can use the bool type, but not strictly needed if you change the type of the "up" variable. I don't think avr/interrupt.h is required either as I don't see anywhere I'm using interrupts in this example and it is probably just a remnant from something else I was doing. I'd test it out myself, but don't have the tool chain set up right now on this computer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment