Reducing Clock Frequency of Microcontroller
Another way to reduce power consumption of the device and extend the battery life is to reduce the frequency of the microcontroller. It is not always necessary for the microcontroller to operate at full frequency. Thus, it's a good approach to reduce the number of executable instructions per second while having the enhanced battery life.
The ATmega 328P microcontroller uses an external oscillator at a frequency of 16 MHz to generate clock cycles. The relationship between the current operating frequency of the microcontroller and the amount of consumed energy is depicted in the Figure XXX. As may be seen, the reduction of the frequency can triple the life of the battery.
Once the clock frequency is reduced, it is necessary not to forget that this change doesn’t affect only the frequencies of the microcontroller itself, but also all the synchronous peripherals. This means that the frequency will also affect delay()
or millis()
functions calls. As well, it affects the speed of I/O communications (e.g. serial line), speed of A/D converter and the like.
When changing the clock frequency, the interrupts handling must be disabled. This will ensure that the write operation or changing the clock frequency operation will not be interrupted. Suitable place to change of the clock frequency is thus the setup()
function, where the clock frequency may be set before the program itself starts.
In case of ATMEL ATmega 328P microcontroller, the frequency may be changed by altering the register Clock Prescale Register value, abbreviated CLKPR
. Such change allows to lower the value of the clock frequency and thus reduce the energy consumption. Its structure is shown in Figure XXX.
The highest bit of CLKPR
registry is marked as CLKPCE
- Clock Prescaler Change Enable. If the frequency of the microcontroller is to be changed, it must be set to 1 and all other bits to 0. It will be automatically reset either after 4 cycles or after recording the CLKPS
entries.
The combination of bits from CLKPS0
toCLKPS3
(Clock Prescaler Select Bits) allows to set the Clock Division Factor. It determines how much will the frequency of the microcontroller decrease. This change may be done at any time while the program is running. Although 4 bits are available, which allows for 16 different combinations, only 8 is allowed. Possible combinations are shown in Table XXX.
CLKPS3 | CLKPS2 | CLKPS1 | CLKPS0 | Clock Division Factor | Frequency |
---|---|---|---|---|---|
0 |
0 |
0 |
0 |
1 |
16 MHz |
0 |
0 |
0 |
1 |
2 |
8 MHz |
0 |
0 |
1 |
0 |
4 |
4 MHz |
0 |
0 |
1 |
1 |
8 |
2 MHz |
0 |
1 |
0 |
0 |
16 |
1 MHz |
0 |
1 |
0 |
1 |
32 |
500 kHz |
0 |
1 |
1 |
0 |
64 |
250 kHz |
0 |
1 |
1 |
1 |
128 |
125 kHz |
1 |
0 |
0 |
0 |
256 |
62.5 kHz |
In order to change the frequency of the ATmega 328P microcontroller, the following steps are required:
- Globally turn off the interrupts by calling the
noInterrupts()
macro. - Set the bit value of
CLKPCE
to 1 and all other bits of theCLKPR
registry to 0. - Next, it is necessary to write the values of
CLKPS
bits, which corresponds to the particular prescaler of Clock Division Factor. - Globally turn on the interrupts by calling the
interrupts()
macro.
The Arduino Uno with the standard Blink example is used to show a change in the frequency of the microcontroller. The built-in LED diode will blink in regular 1 second intervals. The source code of the solution is shown in the Listing XXX.
#include <Arduino.h>
void setup(){
pinMode(LED_BUILTIN, OUTPUT);
}
void loop(){
digitalWrite(LED_BUILTIN, HIGH);
delay(1000);
digitalWrite(LED_BUILTIN, LOW);
delay(1000);
}
Once compiled and started, the LED 1 diode will be turned on for 1 second and then will be turned off for another 1 second.
Next, the setup()
function is adjusted to reduce the frequency of the microcontroller to 2 MHz – value of Clock Division Factor is set to 8. For this purpose, it is necessary to set the values of CLKPS0
and CLKPS1
bits to 1, both of these together with CLKPCE
are available as macros.
void setup(){
noInterrupts();
CLKPR = _BV(CLKPCE); // 0x80
CLKPR = _BV(CLKPS0 | CLKPS1); // 0x03
interrupts();
pinMode(LED_BUILTIN, OUTPUT);
}
This adjustment reduces the frequency of microcontroller to 2 MHz. This time, the entire turn on and off cycle will not take 2 seconds, but 4, this is caused due to decreased frequency which affected the behavior of the delay()
and milis()
functions.
- TODO: vypocet
Should we add a line, to show the elapsed time (in milliseconds) since the program started, to the end of the loop()
function, we would always see the same values, whether the change of the frequency is adjusted or not. This means that this value is dependent on the set frequency. This problem can be avoided by setting the F_CPU
macro to the correct frequency in the time of the compilation.
[!NOTE]
The
_BV()
macro is utilized in the code, it will convert the number of bit, which is received as a parameter to the corresponding numeric value (byte). Thus, the recordsCLKPR = _BV(CLKPCE);
and
CLKPR = 0x80;
are equivalent.