This article was automatically translated from Japanese using AI. The Japanese version is the authoritative version.
What is PWM output?
PWM stands for pulse width modulation. It is a modulation method that works by changing the duty cycle of a waveform.
For details, see my previous post.
PWM output on Arduino
The Arduino UNO uses a Microchip microcontroller called the ATmega328 as its main chip.
Once you start digging deep into Arduino programming, you inevitably end up having to read the microcontroller’s datasheet, so it is worth taking a look at least once. (Which is exactly what I am doing now.)
The Arduino has three timers (Timer/Counter).
These timers govern all timing in an Arduino program.
Functions such as delay() and tone() are measured using them.
| Timer/Counter | Pin number | Bits | Role | PWM frequency |
| Timer0 | 5, 6 | 8 bit | Manages Arduino timing delay(), millis(), micros(), etc. | 977 Hz |
| Timer1 | 9, 10 | 16 bit | Servo library, etc. | 490 Hz |
| TImer2 | 3, 11 | 8 bit | tone(), etc. | 490 Hz |
This time we will change the PWM output by manipulating these timers.
Incidentally, since this alters the timers at their core, with some ingenuity you might also be able to tweak functions like delay() to your liking. (Although it seems more likely that they will simply be thrown off and behave erratically.)
Timer0 is generally tied to the system as a whole, so I recommend using Timer1.
Useful references
https://playground.arduino.cc/Main/TimerPWMCheatsheet/
https://www.arduino.cc/en/Tutorial/SecretsOfArduinoPWM
https://atooshi-note.com/arduino-1hz-pwm/
http://blog.kts.jp.net/arduino-pwm-change-freq/
http://garretlab.web.fc2.com/arduino/inside/hardware/arduino/avr/cores/arduino/wiring_analog.c/analogWrite.html
Program overview
The overall approach is to change the register settings of the Arduino’s timers so that the PWM output frequency can be set freely.
The goal is to be able to output low frequencies, so the program is written to output 10 Hz.
Here we connect an LED to pin 10 and write a program that lets us freely change the frequency and duty cycle of its light.
Since we are using pin 10, we will use Timer1.
Program code
//レジスタの設定を変えるためのもの
#include <avr/io.h>
int PWMPin = 10;
//関数の定義
//frq:周波数 (1Hz~指定できる)
//duty:指定したいduty比
void HzWrite(int frq, float duty) {
// モード指定
TCCR1A = 0b00100001;
TCCR1B = 0b00010100; //分周比256を用いる
// TOP値指定
OCR1A = (unsigned int)(31250 / frq);
// Duty比指定
OCR1B = (unsigned int)(31250 / frq * duty);
}
void setup() {
pinMode(PWMPin, OUTPUT);
}
void loop() {
HzWrite(10, 0.5);
delay(5000);
digitalWrite(PWMPin, LOW);
delay(5000);
}
Explanation of the program
First, include <avr/io.h> so that we can change the register settings.
#include <avr/io.h>
Next, to build a function that works together with delay() to repeat a 10 Hz output every five seconds, we define a function called HzWrite. Its arguments let us specify the frequency and the duty cycle.
void HzWrite(int frq, float duty) {
}
Next comes the mode setting.
The registers used here are TCCR1A/TCCR1B. (TCCR: Timer/Counter Control Register)
The “1” indicates Timer1; if you want to use Timer2, use TCCR2A/TCCR2B instead.
To set the PWM frequency to a specific value in Hz, you need to specify the TOP value yourself.
The larger the TOP value, the lower the output frequency.
Here we use 10 Hz as an example. Since this is very slow compared with the 16 MHz system clock, a large TOP value and a large prescaler are required. For this reason we use Timer1, which offers the largest range.
With the 8-bit Timer0 and Timer2, the maximum TOP value is 255 (2^8 – 1), whereas with the 16-bit Timer1 it is 65535 (2^16 – 1).
(The maximum is one less because the range is 0–255 or 0–65535: the number of values is 2^x, but the largest value is 2^x – 1.)
Internally, the counter increments (0, 1, 2, …) up to the TOP value, and when it matches OCRxA/OCRxB (x is the counter number; each counter has two output pins, A and B) the pin output changes (e.g., LOW→HIGH). Once the counter reaches the TOP value, it then decrements back down to 0 (65535, 65534, 65533, …), and just as during the increment phase, the pin output changes when the count matches OCRxA/OCRxB.
On the Arduino UNO you can change how fast this counter increments, to some extent, by changing the prescaler setting. (“To some extent” means you can choose from 1/8/64/256/1024.)
The prescaler is the ratio (n) used when dividing the frequency (multiplying it by 1/n).
In other words, dividing 1000 Hz by a prescaler of 10 gives 100 Hz.
Incidentally, with a prescaler of 1 the timer runs at 16 MHz, the system clock of the Arduino UNO (ATmega328).
In short, by changing the TOP value, the OCRxA/OCRxB values, and the prescaler, you can freely control the points at which the output switches.
Since we want 10 Hz here, we use a prescaler of 256 to leave plenty of margin.
On the Uno the clock is 16 MHz, so one count takes 1 / 16 MHz = 62.5 ns (prescaler 1).
With a prescaler of 256, counting all the way to TOP takes 62.5 ns x 256 x 65535 = 1.04856 s, so frequencies as low as 1 Hz can be specified.
This program can generate frequencies from 1 Hz to 31250 Hz.
However, as you approach 31250 Hz it becomes impossible to specify the duty cycle precisely.
If you want fine control over the duty cycle, you can only go up to about 300 Hz.
By changing the prescaler setting in this program, you can build a version that covers the frequency range suited to your own application.
In TCCR1A/TCCR1B you write what you want to configure.
The details here are rather involved, so let’s work through them roughly using the datasheet.
Here the values are given in binary, so they start with 0b. For TCCR1A you set COM1A1, COM1A0, COM1B1, COM1B0, unused, unused, WGM11, WGM10 to 1 or 0.
For TCCR1B you set unused (ICNC1), unused (ICES1), unused, WGM13, WGM12, CS12, CS11, CS10.


First, here we choose Mode 9, whose PWM mode is Phase and Frequency Correct.
In this case the TOP value is set in OCR1A.

Therefore WGM13 / WGM12 / WGM11 / WGM10 are 1, 0, 0, 1, respectively.

For COM1B1 / COM1B0: 0, 0 means no output; 0, 1 means toggle operation (the output is inverted on compare match);
1, 0 outputs LOW while the counter is between OCR1A/B and TOP and HIGH while it is between 0 and OCR1A/B;
1, 1 is the inverse of 1, 0.
Here we drive the output LED between LOW and HIGH at the desired frequency, so COM1B1 / COM1B0 are set to 1, 0.
We choose 1, 0 because it makes the sketch easier to follow.

Since we are using a prescaler of 256 here, CS12/CS11/CS10 are set to 1, 0, 0.
To summarize, we get the following.
TCCR1A = 0b00100001;
TCCR1B = 0b00010010;
Next we set OCR1A and OCR1B so that the output is generated with the specified frequency and duty cycle.
// TOP値指定
OCR1A = (unsigned int)(31250 / frq);
// Duty比指定
OCR1B = (unsigned int)(31250 / frq * duty);
Because Phase and Frequency Correct PWM counts up and then back down, the output frequency is given as follows.
Frequency frq = IC clock frequency / (prescaler * TOP value * 2)
Conversely, to determine the TOP value:
TOP value = IC clock frequency / (prescaler x frq x 2)
With a prescaler of 256 on the Arduino UNO, this gives
TOP value = OCR1A = 16,000,000 / (256 x frq x 2) = 31250 / frq
Since we want the LOW/HIGH switching point to be given by OCR1A/OCR1B = duty cycle,
OCR1B = 31250 / frq x duty
Unsigned int is used to prevent overflow.
Here is the main output routine.
void setup() {
pinMode(PWMPin, OUTPUT);
}
void loop() {
HzWrite(10, 0.5);
delay(5000);
digitalWrite(PWMPin, LOW);
delay(5000);
}
Set PWMPin, i.e., pin 10, as OUTPUT, and specify the frequency and duty cycle with HzWrite().
After waiting with delay(), turn the output off with digitalWrite(PWMPin, LOW) and call delay() again.
That covers the full program and how it works.
Afterword
When looking for blog posts on how to change the PWM output frequency, I found far more results by searching for AVR, ATmega328, or 328P than by searching for Arduino.
This post was only a rough overview, so if you want to dig deeper, I encourage you to look into it yourself.
