Baremetal Arduino: PWM Example

0 - Introduction

In the world of microcontrollers, PWM is used to communicate with some motors, or even other microcontrollers, one example is, the servos and ESC in a RC car, it is also a great way to create a DAC (Digital to Analog Converter). In today’s article, you will learn how to use PWM without any of the Arduino libraries.

If you do not own an Arduino, don’t worry, you can buy one here or, follow the article anyways because near the end, a link to the emulator Wokwi is given.

You will also need this datasheet and this schematic, as there will be references made to them explaining what things do.

1 - Code

Before starting, add the following includes and define to the top of your ‘main.c’ file:

#include <avr/io.h>
#include <util/delay.h>

#ifndef F_CPU
#define F_CPU 16000000UL
#endif

Now create a function named ‘init_pwm’, set the ‘OC0A’ pin as output, in the Arduino Nano, that is D6, then set ‘OCR0A’ to the desired duty cycle, in Timers 0 and 2 that is 0 to 255, in Timer 1, it is 0 to 65535. On register TCCR0A set the mode to fast PWM by setting bits ‘WGM01’ and ‘WGM00’, and set ‘COM0A1’ to make the pin be set and cleared by the timer, finally, set the prescaler to 8 by setting ‘CS01’ in register ‘TCCR0B’:

void init_pwm()
{
    // Set OC0A (PD6, D6 on Arduino nano) as output
    DDRD |= (1 << DDD6);
    // duty cycle (0 - 255)
    OCR0A = 0;
    // Clear OC0A (PD6, D6 on Arduino nano) on compare match, set on bottom
    // ↓ Bottom  ↓ OCR0A
    // ──────────┐         ┌──────────┐
    //           └─────────┘          └─────────
    // ↑ 0                 ↑ 255
    TCCR0A |= (1 << COM0A1);
    // Set mode to Fast PWM
    TCCR0A |= (1 << WGM01) | (1 << WGM00);
    // Set prescaler to 8
    TCCR0B |= (1 << CS01);
}

Finally, on main, run the ‘init_pwm’ function and add an infinite loop:

int main()
{
    init_pwm();

    // loop forever
    while (1);
    return 0;
}

You can now test the code on Wokwi or, if you have an Arduino to test this code in, you can use this makefile we made in a previous article that will be getting updated as needed.

And that’s the basics of PWM on the ATMega328p. Note that for each timer you can have two PWM signals by using both ‘OCRnA’ and ‘OCRnB’, check the datasheet to learn which pins does each timer use.

As always, thanks for reading and stay tuned for more tech insights and tutorials. Until next time and keep exploring the world of tech!