The microcontroller has several timers that can perform different functions, such as generating a PWM signal. In order for the timer to generate a PWM signal, it has to be pre-configured by editing the timer register. When we work in the Arduino IDE, the timers are configured without our knowledge in the Arduino.h library, and actually get the settings the developers wanted. And these settings are not very good: the default PWM frequency is low, and the timers are not used to their full potential. Let’s look at the standard PWM of the ATmega328 (Arduino UNO/Nano/Pro Mini):
D5 and D6
8 bits (0- 255)
D9 and D10
8 bits (0-255)
D3 and D11
8 bits (0-255)
In fact, all timers can easily give out 64 kHz PWM signal, and timer 1 – it is even 16 bits, and at the frequency that was given to him Arduino, could work with a resolution of 15 bits instead of 8, and that, by the way, 32768 gradations of filling instead of 256! So why this injustice? Timer 0 is in charge of timing and is set so that the milliseconds are ticking precisely. The other timers are combed to zero to prevent the Arduino-enthusiast from having unnecessary problems. This approach is generally understandable but would have made at least a couple of standard functions for a higher frequency, well, seriously! Okay, if they didn’t, we will.
The PWM generation is tuned through the timer registers. Then you will find some ready-made “pieces” of code, which you need to insert into setup(), and the PWM frequency will be reconfigured (the pre-delimiter and the timer mode will change). You can still work with the PWM signal with the analogWrite() function, controlling the filling of the PWM on the standard pins.
Changing the PWM Frequency on the ATmega328 (Arduino UNO/Nano/Pro Mini)
If you change the frequency on pins D5 and D6, you will lose the time functions (millis(), delay(), pulseIn(), setTimeout(), etc.), they will not work correctly. Also, the libraries that use them will stop working!
If you really want or need an overclocked PWM on the system (zero) timer without loss of time functions, you can correct them as follows:
Defines should be placed before plugging in the libraries so that they get into the code and substitute functions. The only thing is that you can not correct the delay in another library this way. You can use fixDelay() for yourself as written above.
The most important thing is CORRECT_CLOCK. This is an integer equal to the ratio of the default timer divider and the new one set (for PWM acceleration). For example, we set the PWM to 8 kHz. From the list above, we see that the default divider is 64, and 7.8 kHz will be 8, which is eight times smaller. CORRECT_CLOCK is set accordingly.
Related Video: Arduino PWM Tutorial #1 – How To Change PWM Frequency
Libraries for Working with PWM
In addition to fiddling with the registers manually, there are ready-made libraries that allow you to change the PWM frequency of the Arduino. Let’s take a look at some of them:
PWM library (GitHub) – a powerful library that allows you to change the PWM frequency on ATmega48 / 88 / 168 / 328 / 640 / 1280 / 1281 / 2560 / 2561 microcontrollers, of which 328 is on UNO/Nano/Mini and 2560 is an Arduino Mega.
Allows you to set any PWM frequency, pre-delay, TOP
Only one channel is available when working with 8-bit timers (for example, on the ATmega328, only D3, D5, D9, and D10)
Allows to work with 16-bit timers at a higher resolution (16 bits instead of the standard 8)
The library is very complicated, so it can’t be shredded into pieces.
See examples in the folder with the library!
GyverPWM library (GitHub) – the library, which we wrote together with my friend. The library allows very flexible work with PWM on microcontroller ATmega328 (we will add Mega later):
Allows you to set any PWM frequency in the range of 250 Hz – 200 kHz
Bit selection: 4-8 bits for 8-bit timers, 4-16 bits for 16-bit timers (at 4 bits, the PWM frequency is 1 MHz)
PWM mode selection: Fast PWM or Phase-correct PWM (favorable for motors)
Generation of meander frequencies from 2 Hz to 8 MHz on pin D9 with maximum accuracy
Only one channel is available when working with 8-bit timers (for example, on an ATmega328, only D3, D5, D9, and D10)
There are functions to reconfigure the standard PWM outputs without losing the PWM
The library is written in a very simple way, and you can take pieces of code from it