In these last three chapters, I’ll dig deeper into the ATmega328P microcontroller itself and take a look at some very important features of the device, many of which are either ignored, unused, or just too confusing as you read through the data sheet.
This chapter looks at those features which allow the ATmega328P to be configured and which provide a certain amount of protection and power reduction for application code – this can help it run on battery power alone in some cases.
The following two chapters look deeper at the actual, usable hardware of the device.
The contents of Chapters 8 and 9 will, hopefully, assist you in developing projects using the AVR C++ rather than the Arduino Language – should that be your wish. Even if you have no wish to discard the Arduino Language, the source code for the numerous functions I discussed back in Chapters 2, 3, and 4 rely on the information you will find here.
7.1 ATmega328P Fuses
Fuses are special areas of the AVR microcontroller by which the device can be configured with a number of different settings. The ATmega328 has three fuses – low, high, and extended – each of which has different responsibilities.
When a brand-new device is shipped from the factory, the fuses are set to a default configuration, and this default may not be as required for any specific purpose.
There are many online fuse calculators where you can select various options and be shown the commands and/or values with which to program your fuses. www.engbedded.com/fusecalc is a good one.
It is easy to brick your device by setting incorrect fuses, so be exceedingly careful and don’t go playing around with things you don’t understand, yet! Ask me how I know!
You should also be very careful to remember that a fuse that is unprogrammed has a bit value of 1, while a programmed fuse has a bit value of 0. The online fuse calculators show programmed fuses with a ticked checkbox and an unprogrammed fuse with a clear checkbox. This is the opposite way round from what I consider normal, but that’s how the AVR microcontroller works.
The three fuses are discussed in the following.
7.1.1 Low Fuse Bits
The interesting bits in the low fuse, at least of interest to Arduino users, are the SUT and CKSEL fuses and possibly CKDIV8. The latter simply divides the chosen clock source (see CKSEL) by 8 to obtain the final system clock speed.
Atmega328P low fuse bits
Bit | Fuse | Purpose | Default |
---|---|---|---|
7 | CKDIV8 | Divides clock by 8 | Programmed |
6 | CKOUT | Clock output on pin PORTB0 | Unprogrammed |
5 | SUT1 | Selects startup time1 | Unprogrammed |
4 | SUT0 | Selects startup time0 | Programmed |
3 | CKSEL3 | Selects clock source3 | Programmed |
2 | CKSEL2 | Selects clock source2 | Programmed |
1 | CKSEL1 | Selects clock source1 | Unprogrammed |
0 | CKSEL0 | Selects clock source0 | Programmed |
7.1.1.1 SUT Fuse Bits
The SUT fuses comprise 2 bits in the high fuse byte. These determine how much of a delay there will be after the power is applied, or the system reset, in order to allow everything to settle down and become stable. Some power supplies, for example, need a bit more time than others to stabilize at the required voltage; and if the Arduino (or AVR microcontroller) was to start running too soon, it could “brown out” or otherwise not behave correctly. The startup and reset delays allow the oscillator to stabilize as well as the power supply.
As each different possible oscillator has different startup delays from power on or from a wake-up call during a Power Save sleep mode, you are advised to check the data sheet for your particular device as there are numerous variations in each of the settings for these two fuse bits for each particular oscillator.
7.1.1.2 CKSEL Fuse Bits
Atmega328P CKSEL oscillator choice fuse
CKSEL3–CKSEL0 Bits | Oscillator to Be Used |
---|---|
0000 | External clock |
0001 | Reserved, do not use |
0010 | Internal calibrated 8 MHz RC oscillator |
0011 | Internal 128 KHz RC oscillator |
0100–0101 | Low-frequency crystal oscillator |
0110–0111 | Full-swing crystal oscillator |
1000–1111 | Low-power crystal oscillator |
Atmega328P CKSEL oscillator frequency ranges
CKSEL3–CKSEL1 | Frequency |
---|---|
100 | 0.4–0.9 MHz |
101 | 0.9–3 MHz |
110 | 3–8 MHz |
111 | 8–16 MHz |
If a low-power crystal oscillator is being used, then the SUT1:0 fuse bits can be used to specify differing power-on/wake-up delays as defined in the data sheet.
7.1.2 Low Fuse Factory Default
CKDIV8 (bit 7) is programmed (zero) and causes the system clock to be divided by 8. See also CKSELn in the following.
SUT0 (bit 4) is programmed and sets the default startup time, after a reset, to 14 clock cycles plus 65 milliseconds over and above that of the power-up time. From initial power-up or wake from Power Save sleep mode, the startup delay time is six clock cycles. These delays allow the device and power supply to settle down before anything important starts running.
CKSEL3, CKSEL2, and CKSEL0 (bits 3, 2, and 0) are zero and cause the system clock to be defaulted to an internal 8 MHz oscillator. Given that the default also programs CKDIV8, then the device will only be running at 1 MHz. This does mean that while slower, it draws less current and can run from a lower supply voltage.
Fuses CKOUT (bit 6), SUT1 (bit 5), and CKSEL1 (bit 1) remain unprogrammed by default; and this means that the system clock pulse does not appear on PORTB, pin zero (CKOUT). SUT1 affects the startup time, and CKSEL1 affects the default system clock.
7.1.3 Arduino Low Fuse Settings
The Arduino boards running with the ATmega328P microcontroller set the low fuse to FFhex or 1111 1111binary – at least on the Nano with an ATmega328P, the Duemilanove with an ATmega328P, the Diecimila, and the Uno.
The chosen oscillator is not divided by 8 (CKDIV8 unprogrammed), and an external crystal oscillator in the range 8–16 MHz is in use (CKSEL3:1 unprogrammed) with a slow rising power supply (SUT1:0 unprogrammed alongside CKSEL0 unprogrammed). This allows for 16,384 clock cycles of startup delay at power on or from a Power Save sleep wake-up call with an additional 14 clocks plus 65 milliseconds from a reset.
SUT1:0, which define the startup time for the device, are set to 11binary; and this is a setting described as reserved in the ATmega328P data sheet. Hmmm, interesting.
7.1.4 High Fuse Bits
Atmega328P high fuse bits
Bit | Fuse | Purpose | Default |
---|---|---|---|
7 | RSTDISBL | Disables RESET pin and use for I/O | Unprogrammed |
6 | DWEN | Debug wire enable | Unprogrammed |
5 | SPIEN | Enables serial programming (SPI) and data downloading | Programmed |
4 | WDTON | Watchdog Timer always on | Unprogrammed |
3 | EESAVE | Preserves EEPROM data during chip erasure | Unprogrammed |
2 | BOOTSZ1 | Selects bootloader size bit 1 | Programmed |
1 | BOOTSZ0 | Selects bootloader size bit 0 | Programmed |
0 | BOOTRST | Selects RESET vector | Unprogrammed |
Atmega328P BOOTSZ bootloader fuse
BOOTSZ1–BOOTSZ0 | Boot Area Size | Application Address Range | Bootloader Address Range |
---|---|---|---|
11 | 128 Words | 0–F7Fhex | F80hex–FFFhex |
10 | 256 Words | 0–EFFhex | F00hex–FFFhex |
01 | 512 Words | 0–DFFhex | E00hex–FFFhex |
00 | 1024 Words | 0–BFFhex | C00hex–FFFhex |
One word in the AVR is equivalent to 2 bytes, or is it 4 bytes? It depends on which memory you are discussing.
Given that the Uno has a 512-byte – that’s an 8-bit byte – bootloader, its fuse settings define 128 words. If a word was 2 bytes, this would be only 256 bytes for the bootloader. It wouldn’t fit. So it appears that here, a word must surely be 4 bytes.
There is much confusion all over the Internet about what exactly a word is in the data sheet. However, be aware that in Static RAM, addresses point to bytes. A word there would be 2 bytes or 16 bits. In the Flash RAM, an address points to a 16-bit-wide “byte,” so a word of Flash RAM is two of those "bytes" given, in reality, 4 bytes.
Confused? The bootloader lives in Flash RAM, so each address is 16 bits wide, not 8, and still confusion reigns.
In Static RAM, address 0 points at the very first 8 bits in memory and address 1 the next 8 bits. In Flash RAM, address 0 points at the first 16 bits of flash and address 1 at the next 16 bits.
The Arduino Uno sets the BOOTSZ1:0 fuses to 11binary, while the Duemilanove, Diecimila, and Nano use 01binary resulting in a bootloader of 128 words and 512 (8-bit) bytes, for the Uno, and 512 words and 2048 (8-bit) bytes for the others.
Atmega328P BOOTRST reset vector fuse
BOOTRST | Purpose |
---|---|
0 | The RESET vector points to the bootloader address (see preceding table) |
1 | The RESET vector points at the application start address (address 0) |
The Arduino therefore sets this fuse so that the RESET vector will start executing at the bootloader rather than the application code, which is a good thing as it means that you will be able to reprogram the Arduino as the bootloader watches for programming instructions before jumping into the application code if none are forthcoming.
7.1.5 High Fuse Factory Default
SPIEN (bit 5) is programmed so serial programming (SPI) and data downloading is enabled.
BOOTSZ1:0 (bits 2 and 1) are programmed giving the device 2048 words of flash starting at address 3800hex for use as the bootloader area.
RSTDISBL (bit 7) being unprogrammed means that the RESET pin, pin 1, is not disabled and works as a RESET pin. If this fuse is programmed, you cannot program the AVR microcontroller unless you use a high-power programmer device. (This is the fuse setting that bricked one of my ATtiny85 devices when I managed to program it!)
DWEN (bit 6) being unprogrammed results in the Debug Wire interface being disabled. This is beyond the scope of this book. (In other words, it’s something I have not yet studied!)
WDTON (bit 4) not being programmed means that the Watchdog Timer is not always running and unable to be disabled. This means that the Arduino can program the Watchdog on or off as required.
EESAVE (bit 3) remaining unprogrammed results in the internal EEPROM being wiped clean each and every time the AVR microcontroller is programmed. If you need to save data in the EEPROM between program changes and uploads, then you should program this bit.
BOOTRST (bit 0) being unprogrammed means that the device will not jump to the bootloader address at startup. On reset, the device will start execution at the normal reset vector at address 0.
7.1.6 Arduino High Fuse Settings
SPIEN (bit 5) is the same as the factory default and enables SPI.
BOOTSZ1 (bit 2) is programmed (but unprogrammed on the Uno).
BOOTRST (bit 0) is programmed.
As detailed earlier, the Uno is programmed with 128 words of bootloader space, while other boards based on the ATmega328P use 512 words. A reset causes execution to start from the bootloader address.
7.1.7 Extended Fuse Bits
Atmega328P extended fuse bits
Bit | Fuse | Purpose | Default |
---|---|---|---|
2 | BODLEVEL2 | Brown-out detection trigger level2 | Unprogrammed |
1 | BODLEVEL1 | Brown-out detection trigger level1 | Unprogrammed |
0 | BODLEVEL0 | Brown-out detection trigger level0 | Unprogrammed |
Atmega328P BOD voltage ranges
Bits 2–0 | BOD Vmin | BOD Vtypical | BOD Vmax |
---|---|---|---|
111 | N/A | N/A | N/A |
110 | 1.7 V | 1.8 V | 2.0 V |
101 | 2.5 V | 2.7 V | 2.9 V |
100 | 4.1 V | 4.3 V | 4.5 V |
All other values for the BODLEVEL2:0 fuse bits are reserved and must not be used.
7.1.8 Extended Fuse Factory Default
From the factory, this fuse is set to FFhex or 1111 1111binary – so the brown-out detection is disabled. Only fuse bits 2–0 are used (BODLEVEL2:0); the rest should remain as 1binary.
7.1.9 Arduino Extended Fuse Settings
The Arduino sets this fuse to FDhex or 1111 1101binary which sets the brown-out detection threshold voltage to 2.7 V as only BODLEVEL1 is programmed. This is potentially a bad idea as the Arduino boards are running with an external 16 MHz crystal and the data sheet advises that this will only be reliable between 4.0 V and 5.5 V – so this fuse should really be set to FChex, 1111 1100binary, to give a 4.1 V threshold.
7.2 Brown-Out Detection
A brown-out is a feature of electrical supplies when the supply voltage is not stable and varies up and down from the typical voltage that the supply should be.
Some electrical devices either don’t run, don’t run properly, get confused, or reset themselves if the supply voltage goes too low.
Brown-out detection, or BOD, is a means by which the AVR microcontroller keeps a watchful eye on the power supply (VCC) and, when it drops below a configured level, initiates a system shutdown by pulling, and holding, the RESET pin low, until the power rises back above the BOD level again.
The BOD is enabled and configured using the BODLEVEL2:0 fuses as described in Section 7.1.7, “Extended Fuse Bits.” Remember a fuse is not programmed when it has a value of 1binary, but is programmed when it has the value 0binary.
The Arduino boards using the ATmega328P microcontroller are fused so that the BOD is 2.7 V typical, while a brand-new ATmega328P, supplied from the factory, will have the fuses unprogrammed and, thus, BOD will be disabled (111binary).
Given that the data sheet for the ATmega328P shows that the minimum voltage for a 16 MHz AVR microcontroller is 4.0 V, the default setting for an Arduino Uno, in this case, is a little out of range!
The default fuse settings can be found in the $ARDINST/boards.txt file. For a Nano with the ATmega328p, Duemilanove, and Uno, the extended fuse is set to FDhex. This is 1111 1101binary which means that BODLEVEL2:0 is 101, and that sets the BOD threshold at 2.7 V which is way below what the data sheet says is safe for the Arduino setup.
The ATmega328P is stable, when running with an external 16 MHz crystal, with VCC between 4.0 V and 5.5 V. Above this and the AVR microcontroller will probably let the magic blue smoke out and stop working. Below this and the ATmega328P will possibly not work correctly, and this may affect any sensors that are attached especially if accurate timings are required.
It is a well-known fact that all electronic devices seem to run on magic blue smoke. The reason for this belief is that when you somehow let the smoke out, most electronic devices stop working!
ATmega328 BOD voltage ranges
BODLEVEL2–BODLEVEL0 | BOD Minimum | BOD Typical | BOD Maximum |
---|---|---|---|
111 | BOD is disabled | ||
110 | 1.7 V | 1.8 V | 2.0 V |
101 | 2.5 V | 2.7 V | 2.9 V |
100 | 4.1 V | 4.3 V | 4.5 V |
0xx | Reserved. Don’t use |
The typical value is what the BOD settings define.
The ATmega328P, as mentioned , can run safely at 16 MHz if it has power supplied at at least 4.0 V. Anything below 4.0 V means the chip is likely to misbehave. This would be a problem if the device were being used with sensors to take measurements or relied on accurate timings.
To prevent this possible problem, the BOD trigger threshold voltage should be set to the 4.3 V typical setting shown earlier, by setting the BODLEVEL2:0 fuses to 100binary.
The trigger level has a built-in hysteresis to ensure that the BOD doesn’t keep triggering and putting the AVR microcontroller into a BOD reset loop as the supply voltage fluctuates. This hysteresis allows the voltage to briefly drop below the threshold level; and, if it quickly rises back above it, no BOD reset will take place.
The data sheet for the ATmega328P lists the hysteresis as 50 milliVolts, which implies the typical VBOT+ = 2.7V + 25mV ⇒ 2.725V and VBOT- = 2.7V – 25mV ⇒ 2.675V.
Some AVR microcontrollers allow the BOD to be disabled in software; others don’t. The ATmega328P is one of the devices that can disable the BOD – this is useful when entering various sleep modes as the BOD uses power that may be scarce, but disabling the BOD could leave your project open to apparently random resets if the voltage isn’t stable.
7.3 The Watchdog Timer
The Watchdog Timer (WDT) is an internal safety switch which can be used to prevent code hangups, runaways, etc. or which can be used to fire off an interrupt every so often. It runs off of a dedicated internal 128 KHz oscillator and can be programmed to fire at a number of preset intervals.
The Watchdog Timer interrupt can be used to wake the device from the bottom of a really deep sleep; this can be used to save power and preserve battery life. The AVR microcontroller can be put into a Power Down Sleep mode, and the interrupt will wake it periodically, do some essential processing, and then put it back to sleep again ready for its next wake-up call.
Sleep modes are discussed in Section 7.3.9, “Sleep Modes.”
7.3.1 Watchdog Timer Modes of Operation
Watchdog Reset (WDR) – If the Watchdog Timer has not been reset within the timeout period, the whole AVR microcontroller will be forced to a reset. On restarting after the reset, bit WDRF will be set to a 1binary in the MCU Status Register, MCUSR. The WDRF bit can be interrogated to determine if the AVR microcontroller was reset by the Watchdog, or otherwise.
On waking from a Watchdog-induced reset, the Watchdog Timer is still enabled; however, it is now firing with the shortest timeout period – not what you originally configured.
Watchdog Interrupt (WDI) – An interrupt will be fired at the end of every Watchdog Timer timeout period.
Interrupt and Reset – The interrupt will fire on the first Watchdog Timer timeout, and on the next timeout, the system will be reset, unless WDIE is set again to prevent it. As long as the WDIE bit is constantly being set, the system reset will not fire, only the interrupt.
The data sheet has the following warning about the use of the Watchdog Timer:
If the Watchdog is accidentally enabled, for example by a runaway pointer or brown- out condition, the device will be reset and the Watchdog Timer will stay enabled. If the code is not set up to handle the Watchdog, this might lead to an eternal loop of time-out resets. To avoid this situation, the application software should always clear the Watchdog System Reset Flag (WDRF) and the WDE control bit in the initialization routine, even if the Watchdog is not in use.
It should be noted that the Arduino code might be making this check, but that depends on how the bootloader was compiled. It is therefore still possible that your code could lead to a constantly resetting AVR microcontroller under the circumstances mentioned in the data sheet.
Arduino bootloader code to prevent Watchdog Timer reset loops
① This code will only execute if the bootloader was compiled with WATCHDOG_MODS defined, either on the command line that did the compile with ... -DWATCHDOG_MODS ... (see https://forum.arduino.cc/index.php?topic=27162.0), or in an appropriate make file. Otherwise, there is no check on the WDT restart status on boot, and Watchdog Timer reset loops are possible.
② This copies the MCU Status Register for later use.
③ This initializes the MCU Status Register.
④ Watchdog Timer timed sequence begins
⑤ This disables the Watchdog Timer completely.
⑥ Actually, this comment is wrong and misleading. It ignores the bootloader and jumps to the sketch code if the AVR microcontroller was reset by any means, other than bringing the RST pin low.
⑦ This checks that the reset was not caused by the RST pin being brought low by pressing the reset button, for example. Powering the board up, or a restart following a Watchdog Timer reset, will not set this bit.
7.3.2 Amended Sketch setup() Function
Amended setup() function to prevent Watchdog Timer reset loops
① Calling wdt_disable() will turn off global interrupts to prevent them interfering with the timed sequence of instructions to disable/enable the Watchdog Timer, clear the Watchdog Timer counter, and then completely disable the Watchdog Timer. After disabling, interrupts will be enabled again if they were enabled when wdt_disable() was called.
② This resets the MCU Status Register to a known state.
The Watchdog Timer can now be enabled to a known and desired state, if required, in setup() and a call to wdt_reset() executed each time through the loop() and any other long-running processes.
7.3.3 Watchdog Timer Reset
So, if you are using the Watchdog Timer, your code must include the file avr/wdt.h and call wdt_reset() at regular intervals. How regular? You must reset the Watchdog Timer within the configured timeout period.
The call to wdt_reset() is only necessary when the Watchdog Timer is configured to run in “WDT Reset” and “Reset and Interrupt” modes. It is not necessary to call wdt_reset() when running in “WDT Interrupt” (WDI) mode as that mode does not cause the microcontroller to be reset.
7.3.4 The Watchdog Timer Control Register
Watchdog Control Register
Bit | Name | Comments |
---|---|---|
7 | WDIF | Watchdog Interrupt Flag |
6 | WDIE | Watchdog Interrupt Enable |
5 | WDP3 | Watchdog Timer Prescaler 3 |
4 | WDCE | Watchdog Change Enable |
3 | WDE | Watchdog Reset Enable |
2 | WDP2 | Watchdog Timer Prescaler 2 |
1 | WDP1 | Watchdog Timer Prescaler 1 |
0 | WDP0 | Watchdog Timer Prescaler 0 |
WDIF is set when the Watchdog Timer times out and the Watchdog Timer interrupt is enabled. If global interrupts are also enabled, then the Watchdog Timer interrupt ISR fires, and this bit will be cleared automatically.
User code may, if desired, clear this bit by writing a 1binary to it when global interrupts are off. If it is not cleared, then the Watchdog Timer interrupt ISR will execute as soon as the global interrupts are subsequently re-enabled.
WDIE, if set, enables Watchdog Timer Interrupt mode (WDI).
Depending on the setting of the WDE bit – see in the following – thenIf WDE is also set, the WDT is now in Reset and Interrupt mode. The first timeout will execute the Watchdog Timer interrupt ISR, clear WDIF as in the preceding text, clear WDIE to disable WDI, and leave WDE set to ensure WDR mode. The second timeout will cause a system reset.
If WDE is clear, then the mode is Watchdog Timer Interrupt or WDI. Each time the timeout expires, the Watchdog Timer interrupt ISR will be executed. No system reset will occur.
- WDCE is used in the timing sequence that allows the Watchdog Timer to be configured. The configuration allows for
Setting or clearing the WDE bit
Setting or clearing the prescaler bits, WDP3:0
WDE enables the Watchdog Timer in Watchdog Reset (WDR) mode. If the Watchdog Timer is not reset before the timeout period expires, the AVR microcontroller will be reset, and on restarting, bit WDRF (Watchdog Timer Reset Flag) in MCUSR will be set to 1binary showing that the reset occurred due to the Watchdog Timer.
If WDIE see earlier – is also set, then the Reset and Interrupt mode is active. The first timeout will cause the interrupt to fire and execute the ISR, will clear WDIE to disable the Watchdog Timer interrupt, and will enable Watchdog Reset (WDR) mode. The second timeout will reset the system, unless WDIE was again set to enable the Watchdog Timer interrupt.
WDP3:0 set the Watchdog Timer prescaler to give the desired timeout period. If the Watchdog Timer counter is not cleared within this period, then the system will be reset, or the interrupt will be fired, depending on the settings on WDE and WDIE.
Watchdog Reset (WDR) mode – WDE is set and WDIE is clear. The system will reset if the Watchdog Timer counter is not itself cleared within the timeout period. No interrupts will fire.
Watchdog Interrupt (WDI) mode – WDE is clear and WDIE is set. The system will set WDIF on the timeout occurring and then fire the appropriate ISR if global interrupts are enabled.
Watchdog Reset and Watchdog Interrupt mode – WDE and WDIE are both set.
In this mode of operation, if the Watchdog Timer was initialized at time T with timeout period P, then the interrupt will fire at time T + P (assuming global interrupts are enabled of course).
If WDIE is again set to 1binary after the first P timeout, but before the second P timeout, then the system reset will not occur at time T + P + P. The Watchdog interrupt ISR will fire again instead.
If, on the other hand, WDIE is not set to 1binary prior to the end of the next timeout period, P, then the system will be reset. This will occur at T + P + P.
This sequence allows for such things as using the ISR to save any data that must be updated between restarts; shutting down any peripherals, motors, laser cutters, etc. to a safe state before the restart occurs; and so on. Until the system has restarted, and fully initialized, the state of various pins is potentially unknown; and a runaway laser cutter, for example, is not a good thing to have close by!
If the WDTON fuse has been programmed (i.e., has value 0binary) then you are unable to ever change bits WDE and WDIE in WDTCSR. The Arduino default is that this fuse bit is not programmed, so the Watchdog Timer can be enabled or disabled as you might wish.
With this fuse programmed, WDE is always 1binary, while WDIE is always 0binary, so the Watchdog Timer is always running in Watchdog Timer Reset (WDR) mode, and you cannot use the Watchdog Timer interrupt.
When the system is reset by the Watchdog Timer, on restarting, the Watchdog Timer is still enabled; however, it is now enabled at the smallest possible timeout setting, 16 milliseconds, and not perhaps as you configured it prior to the reset. This could cause Watchdog Timer reset loops. The Arduino bootloader should be checking and disabling the Watchdog Timer, but this depends on how the bootloader was compiled and cannot be relied upon. See Listing 7-2 for details on how to possibly prevent this from occuring.
7.3.5 Enabling the Watchdog Timer
Disable global interrupts. This will prevent any existing interrupt handlers from firing during the critical timed sequence, thus preventing a valid change to WDTCSR if the ISR in question takes longer than four system clock cycles to execute – which it will!
Reset the Watchdog Timer. It may already be running, and it should not be allowed to reset the AVR microcontroller while it is being reconfigured, especially when reducing the timeout period.
If the prescaler is being changed to reduce the timeout, and the new timeout period has already expired since the previous reset of the Watchdog Timer counter, then the Watchdog Timer will timeout as soon as the configuration completes.
To start changing WDTCSR, you must write a 1binary to both WDCE and WDE in the same instruction. If WDE is already a 1binary, you must still write a 1binary to it.
Within four system clock cycles, write the desired configuration bits for the prescaler (see below), interrupt enable and so on to WDTCSR and include a 0binary in bit WDCE. All bits must be set and/or cleared in the same instruction.
Enable global interrupts.
The AVRLib functions defined in avr/wdt.h take care of all this when you make calls to the function wdt_enable().
7.3.6 Setting the Watchdog Timer Timeout
Watchdog timeout settings
Timeout | WDP3-WDP0 | WDP3-WDP0 | Comments |
---|---|---|---|
16 mS | 0000bin | 0 | Only if VCC is 5 V. 3.3 V will be a longer timeout |
32 mS | 0001bin | 1 | Only if VCC is 5 V. 3.3 V will be a longer timeout |
64 mS | 0010bin | 2 | Only if VCC is 5 V. 3.3 V will be a longer timeout |
0.125 S | 0011bin | 3 | Only if VCC is 5 V. 3.3 V will be a longer timeout |
0.25 S | 0100bin | 4 | Only if VCC is 5 V. 3.3 V will be a longer timeout |
0.5 S | 0101bin | 5 | Only if VCC is 5 V. 3.3 V will be a longer timeout |
1.0 S | 0110bin | 6 | Only if VCC is 5 V. 3.3 V will be a longer timeout |
2.0 S | 0111bin | 7 | Only if VCC is 5 V. 3.3 V will be a longer timeout |
4.0 S | 1000bin | 8 | Only if VCC is 5 V. 3.3 V will be a longer timeout |
8.0 S | 1001bin | 9 | Only if VCC is 5 V. 3.3 V will be a longer timeout |
All other WDP3:0 values from 10 (1010binary) to 15 (1111binary) are reserved and should not be used.
Arduino code to enable the Watchdog Timer
Arduino code to enable Watchdog Timer interrupt
① Saving the status register preserves the state of global interrupts.
② There are 4 bits available here, so up to 16 values; however, we can only have 9 as the maximum value. If it is 8 or 9, then we need to set WDP3.
③ Interrupts and the Watchdog Timer must be disabled if we are going to change the Watchdog Timer settings. This will prevent rogue resets partway through changing the settings.
④ The data sheet says we must clear WDRF.
⑤ This is the timed sequence of instructions that we must complete in order that the changes to the Watchdog Timer will be considered valid.
⑥ Restore the global interrupt bit in the status register to how it was on entry to this function.
Arduino code to disable the Watchdog Timer interrupt
① This clears the WDIE bit and leaves the other bits unaffected. The WDE and WDP3:0 bits cannot be changed except under the terms and conditions of the previously mentioned timed sequence. The code here doesn’t violate those rules. The code attempts to determine between the ATmega328P and ATtiny85 microcontrollers – both of which I use.
Using the Watchdog Timer interrupt in the Blink sketch
The loop() function does nothing at all. All the blinking takes place in the ISR for the Watchdog Timer interrupt. In code like this, where an ISR is doing all the work, the main loop should really put the AVR microcontroller to sleep. And this is something we will deal with in Section 7.3.8, “Putting the AVR to Sleep.”
7.3.7 Disabling the Watchdog Timer
The Watchdog Timer can be disabled by following the sequence of events as follows:
Disable global interrupts. This will prevent any existing interrupt handlers from firing during the critical timed sequence, thus preventing a valid change to WDTCSR.
Reset the Watchdog Timer. It may already be running, and it should not reset the AVR microcontroller while it is being reconfigured.
Ensure that bit WDRF in MCUSR is cleared.
To start changing WDTCSR, you must write a 1binary to both WDCE and WDE in the same instruction. If WDE is already a 1binary, you must still write a 1binary to it. You are advised to preserve the existing state of bits WDP3:0 to prevent unintentional Watchdog Timer timeouts which will occur if the timeout period is being reduced, explicitly or implicitly, and the new timeout has already expired.
Within four system clock cycles, write a 0binary to bits WDCE and WDE. All bits must be set and/or cleared in the same instruction. The data sheet, surprisingly, advises clearing the entire register – which completely disagrees with its previous instruction to preserve the state of bits WDP3:0.
Enable global interrupts.
The AVRLib functions defined in avr/wdt.h take care of all this when you make calls to the function wdt_disable().
Arduino code to disable the Watchdog Timer
① This clears the flag in the MCU Status Register which indicates that the Watchdog Timer reset the AVR microcontroller.
② The function wdt_disable() is defined in the avr/wdt.h header file and carries out all the necessary instructions to disable the Watchdog Timer including disabling interrupts and enabling them afterward, if appropriate.
You could, obviously, extract the preceding code to a function of your own and call that whenever the Watchdog Timer needed to be disabled.
7.3.8 Putting the AVR to Sleep
The ATmega328P comes with six different sleep modes built in. These can be used to vastly reduce power requirements of the AVR microcontroller when it doesn’t need to be polling for button presses and so on. When sleeping, interrupts must normally be used to wake the device and let it run the necessary processing as required.
In other words, if the code is written in such a way as to not require the processor to be constantly polling sensors and so on in the main loop, then it can be put to sleep which will save power and make batteries last much longer.
Chapter 9 of the data sheet, “Sleep Modes”, has the following points of note:
When entering a sleep mode, all port pins should be configured to use minimum power. The most important is then to ensure that no pins drive resistive loads. In sleep modes where both the I/O clock (clkI/O) and the ADC clock (clkADC) are stopped, the input buffers of the device will be disabled. This ensures that no power is consumed by the input logic when not needed. In some cases, the input logic is needed for detecting wake-up conditions, and it will then be enabled.
If the input buffer is enabled and the input signal is left floating or have an analog signal level close to VCC/2, the input buffer will use excessive power.
For analog input pins, the digital input buffer should be disabled at all times. An analog signal level close to VCC/2 on an input pin can cause significant current even in active mode. Digital input buffers can be disabled by writing to the Digital Input Disable Registers (DIDR1 and DIDR0).
The "analogue input pins" referred to are those of the ADC and the Analogue Comparator. Chapter 9, Section 9.1.3, “Digital Input,” and Section 9.2.1.6, “Disable Digital Input,” describe how to disable the digital input buffers when using the Analogue Comparator or the ADC.
Idle
ADC Noise Reduction
Power Down
Power Save
Standby (only when there is an external crystal or ceramic resonator)
Extended Standby
Set bits SM2:0 in SMCR (Sleep Mode Control Register) to select the sleep mode required.
Set bit SE (Sleep Enable) in SMCR to enable the desired sleep mode.
Execute the sleep instruction.
The bits SM2:0 in the SMCR register are bits 3, 2, and 1, hence the use of the shift left instructions in the preceding definitions.
To put an AVR microcontroller to sleep in Idle mode, for example, you could execute code similar to that shown in Listings 7-8 to 7-10. You could, but I wouldn’t bother myself because it doesn’t work as expected with the code shown! Don’t worry as all will be made clear soon.
Non-functioning sleep sketch, flashLED()
Non-functioning sleep sketch, setup()
Non-functioning sleep sketch, loop()
① It is recommended to disable interrupts when changing sleep modes.
② In setup() we configured Idle sleep mode with set_sleep_mode(). This function call enables sleep modes, but still doesn’t put the device to sleep. Also here, we turn off the brown-out detector (BOD) while sleeping.
③ Sleep modes need an interrupt to wake the device. We need interrupts turning on, or we will simply sleep forever.
④ Finally, we put the device to sleep. The device will sleep in Idle mode until woken.
⑤ This is where the application does some work, after it wakes up from sleep. It should disable sleeping until ready to go back to sleep.
⑥ This is the “real” work that the application has to do!
If, while the device is asleep, an interrupt occurs, the device will wake up but will then halt itself for four clock cycles before executing the interrupt routine. Those four clock cycles are in addition to any startup time requirements.
Once the interrupt routine has been executed, control returns to the instruction after the sleep instruction that put the device to sleep previously. The sleep instruction is built in to the ATmega328P. Our C++ code can call it as it has been defined elsewhere. Normally, it is easier to use the various sleep functions in AVRLib.
“So what’s wrong with the preceding sketch?” I hear you think. “Why is SLEEP_MODE_IDLE not the best sleep mode for this demonstration?” You will possibly have realized that while an Arduino is in this sleep mode, the Timer/counter 0, used to count millis() and so on, is running with its Overflow interrupt enabled. And an interrupt wakes the AVR microcontroller when it fires, so in SLEEP_MODE_IDLE the main loop just runs and runs – it does sleep, but only until the timer/counter overflows and that happens every 1024 microseconds.
If you were to recompile the preceding code, but use SLEEP_MODE_PWR_DOWN instead, once the LED flashes twice in setup(), you won’t see it again until a proper interrupt occurs – but because I haven’t enabled any particular interrupts, the Arduino will simply go to sleep and never wake up. There’s a much better example in Section 7.3.10.
7.3.9 Sleep Modes
In the following discussions, regarding each different sleep mode, I give the sleep modes numbers; SLEEP_MODE_IDLE, for example, is sleep mode 0. I indicate the value in binary as well, in 3 bits. These are the 3 bits that need to be written to the SM2:0 bits in the Sleep Mode Control Register (SMCR). These 3 bits control which sleep mode the AVR microcontroller will enter when the Sleep Enable (SE) bit is set and a sleep instruction executed.
While there are 3 bits available, giving eight different sleep modes (000binary–111binary), sleep modes 4 (100binary) and 5 (101binary) are reserved and should not be used, leaving only six modes. Of these, one cannot be used on the Arduino as it requires Timer/counter 2 to be running in asynchronous mode, which is not possible on an Arduino, and another is dubious. We therefore have three good modes and two dubious ones left on our Arduinos.
Set the sleep mode required in bits SM2:0 of the SMCR. You can do this in a sketch by calling set_sleep_mode() or directly in your own code. The examples in this section use the easy method – calling set_sleep_mode() in AVRLib.
Set the Sleep Enable bit, SE, to 1binary in SMCR by calling sleep_enable() as part of the loop().
Execute the sleep (assembly language) instruction by calling sleep_cpu() in the loop().
On wake-up, the data sheet advises that you immediately clear the SE bit to 0binary. You can do this simply by calling sleep_disable() before starting the code that is to be carried out upon waking from sleep.
The interrupt fires and the AVR microcontroller wakes up from sleep.
The device is then halted for four clock cycles (over and above the device wake-up time).
The interrupt service routine (ISR) is executed.
Execution then continues from the instruction immediately following the sleep (or sleep_cpu()) instruction.
Sleep modes
Sleep Mode | Arduino | Comments |
---|---|---|
SLEEP_MODE_IDLE | Maybe | Only usable if Timer/counter 0 has the Overflow interrupt disabled |
SLEEP_MODE_ADC | Yes | Could be used before an analogRead() call |
SLEEP_MODE_PWR_DOWN | Yes | |
SLEEP_MODE_PWR_SAVE | Maybe | On Arduinos, this is effectively identical to SLEEP_MODE_PWR_DOWN which should be used instead |
SLEEP_MODE_STANDBY | Yes | This is just SLEEP_MODE_PWR_DOWN but with the main oscillator running |
SLEEP_MODE_EXT_STANDBY | No | This is just SLEEP_MODE_PWR_SAVE but with the main oscillator running |
7.3.9.1 Idle Sleep Mode
This is sleep mode 0 (000binary) and is the lightest sleep mode, it saves power but not really a lot, and it’s almost useless on an Arduino.
In Idle mode, CLKcpu and CLKflash are stopped while the remaining clocks continue to run. This means that the AVR microcontroller is unable to access the flash memory or actually run any instructions. Peripherals such as the USART, SPI, ADC, Two=Wire Interface (TWI), Analogue Comparator, the three timer/counters, and the Watchdog Timer will continue to run.
If the hardware is set up correctly, then the Asynchronous Timer/counter on Timer/counter 2 can also be used, but not on Arduinos.
External interrupts – INT0, INT1, and any Pin Change Interrupt
Internal interrupts such as a Timer/counter Overflow, USART Transmit Complete, the Analogue Comparator interrupt, or the Watchdog Timer interrupt.
As demonstrated earlier, this sleep mode is of very limited use on an Arduino as the Overflow interrupt for Timer/counter 0 is active when sketches are compiled. You would have to disable the Timer/counter 0 Overflow interrupt in your sketch to be able to use this sleep mode.
Please also note that the Analogue Comparator is definitely known to keep running in this sleep mode; this is an unknown quantity in the other sleep modes however.
Idle sleep mode summary
Sleep Mode 0 (Idle) | |||
---|---|---|---|
Clocks | CLKcpu (Core, RAM) | Stopped | |
CLKflash (Flash RAM, EEPROM) | Stopped | ||
CLKio (SPI, USART, TWI, GPIO pins, Timer/counters, external interrupts) | Running | ||
CLKadc (ADC) | Running | ||
CLKasy (Asynchronous Timer/counter 2) | Running (but not on an Arduino board) | ||
Oscillators | Main system oscillator | Running | |
Asynchronous Timer oscillator | Running (Not Arduino) | ||
Peripherals | ADC | Running | CLKadc |
Analogue Comparator | Running | ||
Core | Stopped | CLKcpu | |
RAM | Stopped | CLKcpu | |
EEPROM | Stopped | CLKflash | |
External interrupts | Running | CLKio | |
Flash RAM | Stopped | CLKflash | |
SPI | Running | CLKio | |
Timer/counters | Running | CLKio | |
TWI (address matching) | Running | CLKio | |
USART | Running | CLKio | |
Watchdog | Running | ||
Sleep Mode 0 (Idle) | |||
Wake on | External Reset | ||
INT0 interrupt | |||
INT1 interrupt | |||
Pin Change Interrupt | |||
TWI address match | |||
Timer/counter 2 interrupt | |||
SPM/EEPROM ready interrupt | |||
ADC Conversion Complete interrupt | |||
Watchdog Timer interrupt | |||
Other I/O interrupts | |||
Analogue Comparator interrupt |
If wake-up from the Analogue Comparator interrupt is not required, the Analogue Comparator should be powered down by setting the ACD bit in the Analogue Comparator Control and Status Register – ACSR.
If the ADC is enabled, an ADC conversion will be started automatically when the Idle sleep mode begins after the sleep instruction’s execution.
7.3.9.2 ADC Noise Reduction Sleep Mode
This is sleep mode 1 (001binary) and is used when there is a need to reduce any noise coming from the AVR microcontroller itself, so that the ADC can take better analogue readings. This sleep mode is usable on Arduinos.
In ADC Noise Reduction mode, CLKcpu, CLKio, and CLKflash are stopped while the remaining clocks continue to run. This means that while the AVR microcontroller is unable to access the flash memory, use the I/O pins (except for the analogue pins obviously), or actually run any instructions, peripherals such as the ADC, USART, SPI, ADC, TWI, Analogue Comparator, the three Timer/counters, and the Watchdog Timer will continue to run.
External (level) interrupts – INT0, INT1, and any Pin Change Interrupt
Internal interrupts such as the ADC Conversion Complete interrupt, a Timer/counter 2 interrupt (if running in asynchronous mode), USART Transmit Complete, or the Watchdog Timer interrupt.
ADC Noise Reduction sleep mode summary
Sleep Mode 1 (ADC Noise Reduction) | |||
---|---|---|---|
Clocks | CLKcpu (Core, RAM) | Stopped | |
CLKflash (Flash RAM, EEPROM) | Stopped | ||
CLKio (SPI, USART, TWI, GPIO pins, Timer/counters, external interrupts) | Stopped | ||
CLKadc (ADC) | Running | ||
CLKasy (Asynchronous Timer/counter 2) | Running (but not on an Arduino board) | ||
Oscillators | Main system oscillator | Running | |
Asynchronous Timer oscillator | Running (not Arduino) | ||
Peripherals | ADC | Running | CLKadc |
Analogue Comparator | Unknown! | ||
Core | Stopped | CLKcpu | |
RAM | Stopped | CLKcpu | |
EEPROM | Stopped | CLKflash | |
External interrupts | Stopped | CLKio | |
Flash RAM | Stopped | CLKflash | |
SPI | Stopped | CLKio | |
Timer/counters | Stopped | CLKio | |
TWI (address matching) | Stopped | CLKio | |
USART | Stopped | CLKio | |
Watchdog | Running | ||
Wake on | External Reset | ||
INT0 LOW (level) interrupt | |||
INT1 LOW (level) interrupt | |||
Pin Change Interrupt | |||
TWI address match | |||
Timer/counter2 interrupt – only if Timer/counter2 is running in asynchronous mode, which is not possible on an Arduino | |||
SPM/EEPROM ready interrupt | |||
ADC Conversion Complete interrupt | |||
Watchdog Timer interrupt |
When the SM2:0 bits in SMCR are written to 001binary, the sleep instruction makes the MCU enter ADC Noise Reduction mode, stopping the CPU but allowing the ADC, the external interrupts, the two-wire Serial interface address watch, Timer/counter 2 (only in asynchronous mode), and the Watchdog to continue operating (if enabled). This sleep mode basically halts CLKio, CLKcpu, and CLKflash while allowing the other clocks to run.
This improves the noise environment for the ADC, enabling higher-resolution measurements. If the ADC is enabled, a conversion starts automatically when this mode is entered.
Only an External Reset, a LOW level interrupt on the INT0 or INT1 pins, a Watchdog System Reset, a Watchdog Interrupt, a Brown Out Reset, a 2-wire Serial Interface address match, a Timer/counter 2 interrupt or an SPM/EEPROM ready interrupt will wake the AVR from this sleep mode.
7.3.9.3 Power Down Sleep Mode
This is sleep mode 2 (010binary). This is a deep sleep and saves the most power. Consequently, it has fewer wake-up stimuli. This sleep mode is usable on Arduinos.
In Power Down mode, all clocks are stopped. This means that the AVR microcontroller is effectively powered off; however, the Watchdog Timer continues to run, a LOW level interrupt on the INT0 and INT1 pins or any of the Pin Change Interrupts will wake the device as will the TWI address match interrupt and the Brown Out Detector (BOD).
Power Down sleep mode summary
Sleep Mode 2 (Power Down) | |||
---|---|---|---|
Clocks | CLKcpu (Core, RAM) | Stopped | |
CLKflash (Flash RAM, EEPROM) | Stopped | ||
CLKio (SPI, USART, TWI, GPIO pins, Timer/counters, external interrupts) | Stopped | ||
CLKadc (ADC) | Stopped | ||
CLKasy (Asynchronous Timer/counter 2) | Stopped | ||
Oscillators | Main system oscillator | Stopped | |
Asynchronous Timer oscillator | Stopped | ||
Peripherals | ADC | Stopped | CLKadc |
Analogue Comparator | Unknown! | ||
Core | Stopped | CLKcpu | |
RAM | Stopped | CLKcpu | |
EEPROM | Stopped | CLKflash | |
External interrupts | Running | CLKio | |
Flash RAM | Stopped | CLKflash | |
SPI | Stopped | CLKio | |
Timer/counters | Stopped | CLKio | |
TWI (address matching) | Running | CLKio | |
USART | Stopped | CLKio | |
Watchdog | Running | ||
Wake on | External Reset | ||
INT0 interrupt (LOW level only) | |||
INT1 interrupt (LOW level only) | |||
Pin Change Interrupt | |||
TWI address match | |||
Watchdog Timer interrupt (or Reset) | |||
Brown-Out Reset |
7.3.9.4 Power Save Sleep Mode
This is sleep mode 3 (011binary). This is also a deep sleep and saves much power. However, it is slightly less a deep sleep than the preceding mode, Power Down. This sleep mode is possibly usable on Arduinos but not advised as the asynchronous mode for Timer/counter 2 is not enabled.
In Power Save mode, all clocks are stopped except CLKasy. This is almost as deep a sleep as the preceding one, but on an Arduino board, the asynchronous mode of Timer/counter 2 cannot be used, so effectively, this too is a Power Save sleep. Once again, the AVR microcontroller is pretty much powered off; however, the Watchdog Timer continues to run, and the INT0 and INT1 level interrupts and any of the Pin Change Interrupts will wake the device as will the TWI address match interrupt and the BOD.
If this is not an Arduino board and Timer/counter 2 is configured in asynchronous mode, then Timer/counter 2 can also wake the device from this sleep mode with either an Overflow or a Compare Match interrupt. This isn’t possible on an Arduino though as the pins required for the external crystal for that particular timer mode are used for the main oscillator which has a 16 MHz crystal attached.
On an Arduino, Timer/counter 2 will not therefore be enabled during this sleep mode and according to the data sheet, we should be using power down mode, rather than Power Save mode if Timer/counter 2 is not running asynchronously.
Power Save sleep mode summary
Sleep Mode 3 (Power Save) | |||
---|---|---|---|
Clocks | CLKcpu (Core, RAM) | Stopped | |
CLKflash (Flash RAM, EEPROM) | Stopped | ||
CLKio (SPI, USART, TWI, GPIO pins, Timer/counters, external interrupts) | Stopped | ||
CLKadc (ADC) | Stopped | ||
CLKasy (Asynchronous Timer/counter 2) | Running (but not on an Arduino board) | ||
Oscillators | Main system oscillator | Stopped | |
Asynchronous Timer oscillator | Running (not Arduino) | ||
Peripherals | ADC | Stopped | CLKadc |
Analogue Comparator | Unknown! | ||
Core | Stopped | CLKcpu | |
RAM | Stopped | CLKcpu | |
EEPROM | Stopped | CLKflash | |
External interrupts | Running | CLKio | |
Flash RAM | Stopped | CLKflash | |
SPI | Stopped | CLKio | |
Timer/counters | Stopped | CLKio | |
TWI (address matching) | Running | CLKio | |
USART | Stopped | CLKio | |
Watchdog | Running | ||
Wake on | External Reset | ||
INT0 interrupt (LOW level only) | |||
INT1 interrupt (LOW level only) | |||
Pin Change Interrupt | |||
TWI address match | |||
Timer/counter2 interrupt | |||
Watchdog Timer interrupt (or Reset) | |||
Brown-Out Reset |
7.3.9.5 Standby Sleep Mode
This is sleep mode 6 (110binary). The data sheet advises not to use this sleep mode unless there is an external crystal running the main clock. This is appropriate for Arduino boards due to the 16 MHz crystal which is used to run the main system oscillator.
This mode is identical to the Power Down mode described earlier, apart from the main oscillator running. This sleep mode is usable on Arduinos.
In Standby mode, all clocks are again stopped. This means, yet again, that the AVR microcontroller is effectively powered off; however, the Watchdog Timer continues to run, and the INT0 INT1 Level interrupts and any of the Pin Change Interrupts will wake the device as will the TWI address match interrupt and the BOD.
Standby sleep mode summary
Sleep Mode 6 (Standby) | |||
---|---|---|---|
Clocks | CLKcpu (Core, RAM) | Stopped | |
CLKflash (Flash RAM, EEPROM) | Stopped | ||
CLKio (SPI, USART, TWI, GPIO pins, Timer/counters, external interrupts) | Stopped | ||
CLKadc (ADC) | Stopped | ||
CLKasy (Asynchronous Timer/counter 2) | Stopped | ||
Oscillators | Main system oscillator | Running | |
Asynchronous Timer oscillator | Stopped | ||
Peripherals | ADC | Stopped | CLKadc |
Analogue Comparator | Unknown! | ||
Core | Stopped | CLKcpu | |
RAM | Stopped | CLKcpu | |
EEPROM | Stopped | CLKflash | |
External interrupts | Running | CLKio | |
Flash RAM | Stopped | CLKflash | |
SPI | Stopped | CLKio | |
Timer/counters | Stopped | CLKio | |
TWI (address matching) | Running | CLKio | |
USART | Stopped | CLKio | |
Watchdog | Running | ||
Wake on | External Reset | ||
INT0 interrupt (LOW level only) | |||
INT1 interrupt (LOW level only) | |||
Pin Change Interrupt | |||
TWI address match | |||
Watchdog Timer interrupt (or Reset) | |||
Brown-Out Reset |
On a wake-up call, when sleeping in this mode, the device is back up and running in six clock cycles.
7.3.9.6 Extended Standby Sleep Mode
This is sleep mode 7 (111binary). The data sheet advises not to use this sleep mode unless there is an external crystal running the main clock.
This mode is identical to the Power Save mode described earlier, apart from the two main oscillators running. This sleep mode is best avoided on Arduinos as the asynchronous timer clock isn’t running. Use Standby sleep mode instead.
In Extended Standby mode, all clocks are again stopped; and once more, the AVR microcontroller is powered off for all intents and purposes. The Watchdog Timer does continue to run, and the INT0 and INT1 Level interrupts and any of the Pin Change Interrupts will wake the device as will the TWI address match interrupt and the BOD.
Extended Standby sleep mode summary
Sleep Mode 7 (Extended Standby) | |||
---|---|---|---|
Clocks | CLKcpu (Core, RAM) | Stopped | |
CLKflash (Flash RAM, EEPROM) | Stopped | ||
CLKio (SPI, USART, TWI, GPIO pins, Timer/counters, external interrupts) | Stopped | ||
CLKadc (ADC) | Stopped | ||
CLKasy (Asynchronous Timer/counter 2) | Running (but not on an Arduino board) | ||
Oscillators | Main system oscillator | Running | |
Asynchronous Timer oscillator | Running (not Arduino) | ||
Peripherals | ADC | Stopped | CLKadc |
Analogue Comparator | Unknown! | ||
Core | Stopped | CLKcpu | |
RAM | Stopped | CLKcpu | |
EEPROM | Stopped | CLKflash | |
External interrupts | Running | CLKio | |
Flash RAM | Stopped | CLKflash | |
SPI | Stopped | CLKio | |
Timer/counters | Stopped | CLKio | |
TWI (address matching) | Running | CLKio | |
USART | Stopped | CLKio | |
Watchdog | Running | ||
Wake on | External Reset | ||
INT0 interrupt (level only) | |||
INT1 interrupt (level only) | |||
Pin Change Interrupt | |||
TWI address match | |||
Timer/counter2 interrupt | |||
Watchdog Timer interrupt (or Reset) | |||
Brown-Out Reset |
On a wake-up call, when sleeping in this mode, the device is back up and running in six clock cycles.
7.3.10 Analogue Comparator
The data sheet is not very clear on whether or not the Analogue Comparator interrupt can be used to wake the device from some of the sleep modes. Believe me I searched in vain for the detail! To this end, I configured the various sleep modes and set up a circuit identical to the one described in Chapter 9 on the Analogue Comparator. There’s a full description of the circuit and how it works in Chapter 9, Section 9.1, “The Analogue Comparator.”
In the code in Listings 7-11 to 7-14, I set the Arduino into various sleep modes in setup() and then tested to see if the comparator would wake the AVR microcontroller from its slumbers.
Analogue Comparator wake-up, setupComparator()
Analogue Comparator wake-up, setup()
Analogue Comparator wake-up, empty ISR
Analogue Comparator wake-up, loop()
And the results? The Analogue Comparator does indeed wake up the Arduino when in SLEEP_MODE_IDLE but doesn’t wake it up in any other mode which is as I thought would be the case, but at least I now know.
7.4 Power Reduction
Many AVR microcontrollers come with numerous different, potentially power-hungry, peripherals. If these are not being used by a sketch, then they can be disabled by setting a bit in the PRR or Power Reduction Register. This will shut them down and reduce overall power consumption by the AVR microcontroller, thus increasing battery life on battery-powered devices.
Numerous parts of the AVR microcontroller can be disconnected or shut down to save power, and it is recommended that this be done if those peripherals are not in use. The Arduino system cannot determine which parts you are not using, so it is up to you, the maker, to decide and disable accordingly.
Power consumption and the various sleep modes discussed in Section 7.3.9, “Sleep Modes,” are a great way to reduce the power requirements of your project and can help – in some cases – to dramatically increase running time from battery power.
7.4.1 Power Consumption
ATmega328P power consumption
VCC | Idle Current | Idle Power | Active Current | Active Power |
---|---|---|---|---|
4.0 V | 1.75 milliAmps | 7.0 milliWatts | 7.0 milliAmps | 28 milliWatts |
4.5 V | 2.1 milliAmps | 9.45 milliWatts | 8.2 milliAmps | 36.9 milliWatts |
5.0 V | 2.4 milliAmps | 12.0 milliWatts | 9.6 milliAmps | 48.0 milliWatts |
5.5 V | 2.8 milliAmps | 15.4 milliWatts | 11.0 milliAmps | 60.5 milliWatts |
You should note that these figures are for a bare-bones AVR microcontroller and not for the whole Arduino board, complete with power-hungry devices like the voltage regulator, the always-on power LED, and so on.
You may be surprised to find out exactly how few components you actually need to run a device with an ATmega328P as the microcontroller and even fewer if your device can be run using an ATtiny85!
From Table 7-19, it can be seen that an active ATmega328P, running at 16 MHz with a VCC of 4 V, uses half the power of the same device running with a VCC of 5.5 V. Sadly, our Arduino boards are fixed at 5.0 V for VCC, so we are not able to do much in that area to reduce power consumption. However, for a bare-bones AVR microcontroller on a breadboard or circuit board of our own design, we do have the option.
While the Arduino boards themselves are excellent for prototyping, they are a tad expensive, and power hungry, to embed in the finished product. In the case where a device is deemed to be market-ready, the Arduino itself will normally be replaced by a minimal ATmega328P circuit, with far fewer resource requirements and a much lower cost.
ATmega328P peripherals’ power consumption
Peripheral | Typical Current | Active Extra | Idle Extra |
---|---|---|---|
ADC | 295.38 microAmps | 4.1% | 22.1% |
USART | 100.25 microAmps | 1.4% | 7.8% |
SPI | 186.5 microAmps | 2.9% | 15.7% |
Timer/counter 1 | 176.25 microAmps | 2.7% | 14.5% |
Timer/counter 0 | 61.13 microAmps | 0.9% | 4.8% |
Timer/counter 2 | 224.25 microAmps | 3.3% | 17.8% |
TWI | 199.25 microAmps | 3.0% | 16.6% |
7.4.1.1 Calculating Power Requirements
As the data sheet explains, it is possible to calculate the power requirements for each of the preceding seven peripherals, based on the voltage and frequency of the crystal or ceramic oscillator in use. The preceding typical figures are based on a 5 V AVR microcontroller running at 8 MHz. The Arduino runs at 16 MHz, so we need to get the calculator out.
It’s actually quite simple. Take the current drawn from Table 7-19 for the voltage your device is using, and then add the percentage for active or idle from Table 7-20 for the peripheral in question. If there are more than one peripherals, simply add each percentage.
7.4.2 Power Reduction Register
Analogue to Digital Converter (ADC)
Universal Synchronous/Asynchronous Receiver/Transmitter (USART)
Two-Wire Interface (TWI) aka I2C interface
Timer/counter 0
Timer/counter 1
Timer/counter 2
Serial Peripheral Interface (SPI)
PRADC which enables or disables power to the ADC
PRUSART0 which enables or disables power to the USART
PRSPI which enables or disables power to the SPI
PRTIM1 which enables or disables power to Timer/counter 1
PRTIM0 which enables or disables power to Timer/counter 0
PRTIM2 which enables or disables power to Timer/counter 2
PRTWI which enables or disables power to the TWI
What these bits do is to stop the clock to the peripheral. Once the clock has been stopped, that peripheral is effectively suspended, and there is no ability to write to, or read from, the device’s registers.
The data sheet warns that Resources used by the peripheral when stopping the clock will remain occupied, hence the peripheral should in most cases be disabled before stopping the clock.
What this means is that whatever peripheral you wish to power off should be disabled before powering it down. In the case of the ADC, for example, this would entail writing a 0binary to ADEN in the ADCSRA register to disable the ADC and then writing a 1binary to PRADC in the PRR to power it off.
Disabling ATmega328P peripherals
Peripheral | Bit | Register | Comments |
---|---|---|---|
ADC | ADEN | ADCSRA | Shuts down the ADC |
USART | RXENn | UCSRnB | Shuts down the USARTn Receiver |
USART | TXENn | UCSRnB | Shuts down the USARTn Transmitter |
SPI | SPE | SPCR | Shuts down the SPI |
Timer/counter 1 | CS12–CS10 | TCCR1B | Shuts down Timer/counter 1 when written as 000binary |
Timer/counter 0 | CS02–CS00 | TCCR0B | Shuts down Timer/counter 0 when written as 000binary |
Timer/counter 2 | CS22–CS20 | TCCR2B | Shuts down Timer/counter 2 when written as 000binary |
TWI | TWEN | TWCR | Shuts down the TWI/I2C Interface |
Analogue Comparator | ACD | ACSR | Shuts down the Analogue Comparator |
To power up a peripheral from its powered-down state, simply write a 0binary to the appropriate bit in the PRR. The peripheral will wake up again and will resume the state that it was in when powered down. You have to write a zero because that disables the power reduction for that peripheral.
The Analogue Comparator doesn’t have a bit in the PRR. It does, however, have the ACD bit in the Analogue Comparator Control and Status Register – ACSR. Write a 1binary to that bit to power off the Analogue Comparator.
7.4.3 Saving Arduino Power
Now you know what peripherals can be disabled and powered down, you are able to perhaps save a little of your device’s power by using the setup() function to power off all those bits of the ATmega328P that you don’t need for a sketch.
Taking the old favorite blink sketch, yet again, what does it actually need? Nothing more than the I/O pins and a timer/counter to work the delay() function. The delay() function and millis() and micros() all depend on Timer/counter 0. Listings 7-15 to 7-17 show an example blink sketch with unwanted peripherals turned off.
The AVRLib has some useful power maintenance functions, and these can be used to power off the peripherals we don’t need in our blink sketch. There is a trade-off of course: adding code to setup() to disable and power off these peripherals will increase the code size of the final sketch.
In the case of the blink sketch, it’s unlikely that this will be a problem, but for other sketches that might be pushing at the capacity of the AVR microcontroller, it could be a problem and you might have to revert to the direct manner of setting the registers in your code, rather than using the AVRLib functions.
Low-power blink, disable() function
Low-power blink, setup() function
Low-power blink, loop() function
The sketch uses 1006 bytes of Flash RAM now, which is obviously more than the standard blink sketch would use. In larger sketches, the overhead should be less.
7.4.4 The Power Functions
AVRLib power functions
Function Name | Description |
---|---|
power_adc_enable() | Enables power to the ADC |
power_adc_disable() | Disables power to the ADC |
power_spi_enable() | Enables power to the SPI |
power_spi_disable() | Disables power to the SPI |
power_timer0_enable() | Enables power to Timer/counter 0 |
power_timer0_disable() | Disables power to Timer/counter 0 |
power_timer1_enable() | Enables power to the Timer/counter 1 |
power_timer1_disable() | Disables power to the Timer/counter 1 |
power_timer2_enable() | Enables power to the Timer/counter 2 |
power_timer2_disable() | Disables power to the Timer/counter 2 |
power_twi_enable() | Enables power to the TWI |
power_twi_disable() | Disables power to the TWI |
power_usart0_enable() | Enables power to USART0 (the only one on the ATmega328P) |
power_usart0_disable() | Disables power to USART0 (the only one on the ATmega328P) |
__power_all_enable() | Enables power to all peripherals |
__power_all_disable() | Disables power to all peripherals |
Not all devices have these functions; it’s another one of those configuration things. The iom328p.h header file sets up the appropriate functions which are available for peripheral devices on the ATmega328P.
You are required to manually turn off the Analogue Comparator if you do not need it. There doesn’t appear to be a utility function to do so in the AVRLib code.
7.5 Bootloaders
The ATmega328P on an Arduino board is supplied already programmed with a bootloader. This is a small area of the Flash RAM set aside for a special program, and when the device is reset or powered on, a jump to the bootloader takes place.
The standard Uno bootloader delays startup of the ATmega320P for a brief period, to check that no programming commands are being received on the USART pins (D0 and D1). If there are no commands, the application code starts normally; and the blink sketch, or whatever you programmed last, starts executing.
If there are specific commands being read, then the bootloader starts running those commands and may, depending on what it is being commanded to do, overwrite the previously uploaded code with a new version or just upload a new sketch to the application area of the Flash RAM. The bootloader cannot update itself with a new version.
7.5.1 Flash Memory
The application section – This is where your sketch code is written to by the bootloader or the ICSP device.
The bootloader section, or BLS – This is where the bootloader lives.
There are fuses, BOOTSZ1:0 and BOOTRST, to determine the size and address of the bootloader sections; and, in the case of the BOOTRST fuse, it determines whether the device starts executing the bootloader or the application code on startup and/or reset.
While it is possible and, indeed, permitted for the bootloader to write to the application section, the converse is not true. The application cannot access the bootloader section.
You should also note that the whole of Flash RAM can be used as the application section if no bootloader is required. This seems to be easily done simply by using an ICSP device to do the programming.
The sections are considered completely separate by the device, and they can have different protection levels. This protection is determined by special lock bits. Boot Loader Lock Bits 0 protect the application section, while Boot Loader Lock Bits 1 protect the bootloader section. There are two other lock bits that protect the entire ATmega328P from either being reprogrammed or having its Flash RAM read out.
7.5.2 Lock Bits
The lock bits can be set in software, in serial or parallel programming mode. To clear the bootloader lock bits, a full chip erase command must be given. It may not be possible, but I have not checked this on my own devices – for obvious reasons, to ever unlock the device for programming if the device lock bits have been set.
7.5.2.1 Device Lock Bits
Device lock bits 0
MODE | LB02 | LB01 | Description |
---|---|---|---|
1 | 1 | 1 | The device is totally unprotected. It can be programmed or Flash and EEPROM contents read at will. |
2 | 1 | 0 | Programming the device – Flash or EEPROM – is disabled in serial or parallel mode. The fuse bits are also locked. Any code already programmed can still be read. |
3 | 0 | 0 | Programming and reading the device – Flash or EEPROM – is disabled in serial or parallel mode. The fuse bits are also locked. |
The default is LB Mode 1 which allows the device to be programmed and verified (read back) as required.
In case you are wondering, serial programming is when either an ICSP device or a high-voltage serial programmer is used and requires only a few pins; parallel, on the other hand, requires many more pins and is uncommon outside of large establishments which need to program many devices at once.
There are very good descriptions and circuit diagrams of both methods at Nick Gammon’s blog [www.gammon.com.au/forum/?id=12898] if you are interested.
As with fuses, lock bits are considered programmed when at 0binary and unprogrammed when 1binary.
7.5.2.2 Bootloader Lock Bits
The entire Flash RAM can be protected from a software update – BLB0 Mode 2 plus BLB1 Mode 2.
Only the bootloader section can be protected – BLB0 Mode 1 and BLB1 Mode 2 or 3.
Only the application section can be protected – BLB0 Mode 2 or 3 and BLB1 Mode 1.
The entire Flash RAM can be unprotected – BLB0 Mode 1 plus BLB1 Mode 1.
7.5.2.2.1 Bootloader Lock Bits 0
Bootloader lock bits 0
MODE | BLB02 | BLB01 | Description |
---|---|---|---|
1 | 1 | 1 | The application section is totally unprotected |
2 | 1 | 0 | The Store Program Memory (SPM) instruction is not allowed to write to the section. The application section is fully protected |
3 | 0 | 0 | The SPM instruction cannot write to the application section; and, at the same time, the Load Program Memory (LPM) instruction cannot read from it if executing from the bootloader. See Note in the following. |
4 | 0 | 1 | The LPM instruction, if executing from the bootloader, is not allowed to read from the application section. See Note in the following. |
If interrupt vectors are located in the bootloader section, interrupts will be disabled while code is executing from the application section.
7.5.2.2.2 Bootloader Lock Bits 1
Bootloader lock bits 0
MODE | BLB12 | BLB11 | Description |
---|---|---|---|
1 | 1 | 1 | The bootloader section is unprotected and can be written to, but not by code running in the application section |
2 | 1 | 0 | The Store Program Memory (SPM) instruction is not allowed to write to the section. The bootloader section is fully protected |
3 | 0 | 0 | The SPM instruction cannot write to the bootloader section; and, at the same time, the Load Program Memory (LPM) instruction cannot read from it if executing from the application section. See Note in the following. |
4 | 0 | 1 | The LPM instruction, if executing from the application section, is not allowed to read from the bootloader section. See Note in the following. |
If interrupt vectors are located in the application section, interrupts will be disabled while code is executing from the bootloader section.
7.5.3 Installing the Uno (Optiboot) Bootloader
The Uno bootloader is around 500 bytes in size, so takes up less of your precious Flash RAM when installed. It is found, should you wish to examine it in detail, in $ARDINST/bootloaders/optiboot/optiboot.c.
Although it’s commonly referred as the Uno bootloader, it is, in fact, quite easily installable into other devices. The comments in the source code mention that it is compatible with both the Duemilanove and the Diecimila and other ATmega168- or AtMega328P-based devices.
Close the IDE if it is open.
Open the file $ARDINST/boards.txt in your favorite text editor.
Find this line, for the ATmega328P variant:
diecimila.menu.cpu.atmega328.upload.maximum_size=30720Change it to the following:
diecimila.menu.cpu.atmega328.upload.maximum_size=32256Find this line – it’s a single line which has wrapped around on this page:
diecimila.menu.cpu.atmega328.bootloader.file= atmega/ATmegaBOOT_168_atmega328.hexChange it to the following single line, not wrapped as follows:
diecimila.menu.cpu.atmega328.bootloader.file= optiboot/optiboot_atmega328.hexSave the file.
Open the IDE again.
Make sure that the correct board is selected under Tools ➤ Boards.
Choose Tools ➤ Burn Bootloader.
You now have a smaller, faster bootloader and an additional 1.5 Kb of flash for your program – and all for free.
7.5.4 Optiboot Bootloader Operation
When the device is powered on, or reset, and if the BOOTRST fuse is set to enable the bootloader to be executed at startup, then the bootloader code for the device is executed.
One of the first tasks that the bootloader does is to check the MCUSR to determine if this was an External Reset or not. It does this by checking the EXTRF bit in the MCUSR. If that bit is set, then the device was reset by pulling pin 1, RST low, and not by a power-on, Watchdog, or BOD reset. This could have been done by the user pressing the reset button on the Arduino board or by the programming device using the DTR pin to pull the ATmega328P’s RST pin low. In any case, bit EXTRF will have been set.
In the case when this bit is not set, the bootloader assumes that the device is not about to be reprogrammed and jumps immediately to the application code, bypassing the rest of the bootloader itself. If the bit is set, then the bootloader continues executing.
The Watchdog Timer is set to fire after 1 second, the onboard LED is flashed once to indicate that it is waiting, and an infinite loop is entered to wait for characters coming in over the USART. If nothing is received after the 1 second timeout by the Watchdog, then the device will reset again but this time by the Watchdog. On restarting from a Watchdog-induced reset, the EXTRF bit in the MCUSR will no longer be set – the WDRF bit, on the other hand, will be set – so the application code is immediately executed.
The bootloader, if it continues executing, must have read at least 1 byte from the USART. These bytes are assumed to be commands from a subset of the STK500 communications protocol – the Optiboot bootloader currently only implements a subset of the STK500 instructions – and these commands are used to communicate with, usually, avrdude.
It is beyond the scope of this book to delve into the various bootloader commands as they really have little to do with application programming. Suffice it to say that the bootloader sits in a loop, reading characters from the USART and acting upon them, in addition to resetting the Watchdog Timer to prevent the device being reset by the Watchdog and messing up the programming.
Should you really wish to examine the Optiboot bootloader, there is a compilation listing for the ATmega328P, in the location $ARDINST/bootloaders/optiboot/optiboot_atmega328.lst – it does make interesting reading.