© Norman Dunbar 2020
N. DunbarArduino Software Internalshttps://doi.org/10.1007/978-1-4842-5790-6_7

7. ATmega328P Configuration and Management

Norman Dunbar1 
(1)
Rawdon, West Yorkshire, UK
 

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.

../images/494109_1_En_7_Chapter/494109_1_En_7_Figa_HTML.gif 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!

../images/494109_1_En_7_Chapter/494109_1_En_7_Figb_HTML.gif 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.

The SUT and CKSEL fuse bits are discussed following Table 7-1 which describes the low fuse bits.
Table 7-1

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

The CKSEL fuse bits, shown in Table 7-2, select the desired internal or external oscillator to be used as the system clock. On the Arduino boards, this is always an external crystal oscillator running at 16 MHz, so the other options are not of much use there, but can be used if you build your own boards and do away with the crystal.
Table 7-2

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

When using the low-power crystal oscillator, the CKSEL3:1 bits determine the frequency of the oscillator in use, as shown in Table 7-3. Our Arduinos with their 16 MHz oscillators will be using the 111binary option.
Table 7-3

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

From the factory, the ATmega328P is shipped with this fuse set to 62hex which is 0110 0010binary and is configured as follows:
  • 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.

This disables everything covered by this fuse and means that, according to the data sheet
  • 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

Table 7-4 lists the high fuse bits and their purpose.
Table 7-4

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

High fuse bits of interest to Arduino users are the BOOTSZ bits shown in Table 7-5 and the BOOTRST fuse bit shown in Table 7-6. It’s probably not wise to play with the others!
Table 7-5

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

../images/494109_1_En_7_Chapter/494109_1_En_7_Figc_HTML.gif 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.

The BOOTRST fuse has two possible values. The Arduino always sets this fuse to 0, meaning programmed. The possible values for this fuse are shown in Table 7-6. The ATmega328P’s RESET vector can be configured to point either at the bootloader or at the application address space in Flash RAM.
Table 7-6

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

From the factory, this fuse defaults to D9hex or 1101 1001binary and means that
  • 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.

The remaining fuses are not programmed. Therefore
  • 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

This fuse is set to DEhex, 1101 1110binary, on the Uno, but to DAhex, 1101 1010binary, on the Duemilanove, the Diecimila, and the Nano. This programs the following fuses:
  • 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

The extended fuse byte controls the BOD or brown-out detector in the microcontroller. Not all of this fuse byte is used, and the relevant bits are described in Table 7-7.
Table 7-7

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

All other bits of the extended fuse are unused and should always be programmed as 1binary to avoid possible problems. Table 7-8 shows how the BOD fuse bits can be configured.
Table 7-8

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).

../images/494109_1_En_7_Chapter/494109_1_En_7_Figd_HTML.gif 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.

../images/494109_1_En_7_Chapter/494109_1_En_7_Fige_HTML.gif 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!

The available BOD settings on an ATmega328P are shown in Table 7-9.
Table 7-9

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 hysteresis on the detection level should be interpreted as
Vbot+ = Vbot + Vhyst/2
and
Vbot- = Vbot - Vhyst/2

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

The WDT has three separate 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.

    ../images/494109_1_En_7_Chapter/494109_1_En_7_Figf_HTML.gif 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.

The code shown in Listing 7-1 from the Uno bootloader shows the test being made and the Watchdog Timer disabled, if configured accordingly.
#ifdef WATCHDOG_MODS                                   ①
    ch = MCUSR;                                        ②
    MCUSR = 0;                                         ③
    WDTCSR |= _BV(WDCE) | _BV(WDE);                    ④
    WDTCSR = 0;                                        ⑤
    // Check if the WDT was used to reset, in which    ⑥
    // case we don't bootload and skip straight to
    // the code. woot.
    if (! (ch &  _BV(EXTRF)))                          ⑦
        app_start();  // skip bootloader
#else
    asm volatile("nop ");
#endif
Listing 7-1

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

Given that we currently do not know if the bootloader was compiled with WATCHDOG_MODS defined, perhaps the setup() code in Listing 7-2 could be added at the top of our sketches to ensure that potentially rogue Watchdog Timer reset loops can be avoided.
#include <avr/wdt.h>
void ensureWDTisOff() {
    // wdt_disable() will disable interrupts and
    // call wdt_reset first, then disable the WDT.
    wdt_disable();                                     ①
    // Reset the MCU Status Register.
    MCUSR=0;                                           ②
}
void setup() {
    // Ensure the WDT has not gone rogue!
    ensureWDTisOff();
    // Do the sketch's own initialisation here.
    pinMode(...);
    ...
}
Listing 7-2

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

To keep your code running, without the Watchdog Timer resetting the AVR microcontroller, your code must, periodically, clear the Watchdog Timer counter by executing the wdr assembly language instruction prior to the timeout period expiring. This instruction has been defined in the AVRLib code as follows:
#define wdt_reset() __asm__ __volatile__ ("wdr")

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.

../images/494109_1_En_7_Chapter/494109_1_En_7_Figg_HTML.gif 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

The Watchdog Timer Control Register, WDTCSR, is 8 bits wide; and the individual bits have their usages defined in Table 7-10.
Table 7-10

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

The bits are used as follows:
  • 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 – then
    • If 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.

Given the preceding discussion, we can pick and choose the Watchdog Timer modes that we wish to configure in our code as follows:
  • 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!

../images/494109_1_En_7_Chapter/494109_1_En_7_Figh_HTML.gif 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

In order that rogue programs don’t cause problems by accidentally setting the WDT, and to try and ensure that any changes are valid ones, there is a certain timed sequence of events that must be followed in order to configure WDTCSR when either WDE or the prescaler bits WDP3:0 are being changed:
  • 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

The Watchdog Timer can be configured, as mentioned earlier, by setting bits WDP3:0 according to Table 7-11, to time out after an approximate set time.
Table 7-11

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.

Listing 7-3 shows an example Arduino sketch to enable the Watchdog Timer, in Watchdog Timer Reset (WDR) mode.
#include "avr/wdt.h"
void setup() {
    wdt_reset();
    // Fire WDT every 8 seconds.
    wdt_enable(WDTO_8S);
}
void loop() {
    // Make sure we reset the WDT.
    wdt_reset();
    // Do our loopy stuff here. It must
    // complete in less time than the
    // WDT timeout period.
    ...
}
Listing 7-3

Arduino code to enable the Watchdog Timer

Unfortunately, the wdt_enable(WDTO_8S) instruction in Listing 7-3 only sets the WDE and prescaler bits; it does not set the interrupt enable bit, WDIE, for you. At present, the AVRLib doesn’t have the ability to set the Watchdog Timer interrupt, so, if Watchdog Timer interrupts are required, you need to configure everything manually. Listing 7-4 is a function which will enable the Watchdog Timer Interrupt mode, without enabling the WDR mode. Your project can then use the Watchdog Timer interrupt handler to carry out some work periodically and not have to worry about calling wdt_reset() within the timeout period.
#include <avr/wdt.h>
void wdt_interrupts(uint8_t value) {
    // Save existing interrupt state.
    uint8_t oldSREG = SREG;                            ①
    // Set the WDP3-WDP0 bits for the prescaler.
    uint8_t wdt_setting;
    value = (value > 9) ? 9 : value;                   ②
    wdt_setting = (value > 7) ? (1 << WDP3) : 0;
    wdt_setting |= (value & 7);
    // Disable interrupts and reset WDT.               ③
    noInterrupts();
    wdt_reset();
    // Clear WDT restarted flag.
    MCUSR &= ~(1 << WDRF);                             ④
    // Do the timed sequence next.                     ⑤
    #if defined WDTCSR
        // ATmega168/328/2560 etc
        WDTCSR |= ((1 << WDCE) | (1 << WDE));
        WDTCSR = (wdt_setting | (1 << WDIE));
    #elif defined WDTCR
        // ATtiny25/45/85 etc
        WDTCR |= ((1 << WDCE) | (1 << WDE));
        WDTCR = (wdt_setting | (1 << WDIE));
    #else
        #error "Unknown WDT Control Register on your AVR."
    #endif
    // Put interrupts back as they were previously.
    SREG = oldSREG;                          ⑥
}
Listing 7-4

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.

That takes care of enabling the Watchdog Timer interrupt. To disable it, use the function in Listing 7-5.
#include "avr/wdt.h"
void wdt_noInterrupts() {
    // Disable WDT interrupts leaving
    // everything else untouched.
    #if defined WDTCSR
        // ATmega328 etc
        WDTCSR &= ~(1 << WDIE);             ①
    #elif defined WDTCR
        // ATtiny85 etc
        WDTCR &= ~(1 << WDIE);              ①
    #else
        #error "Unknown WDT Control Register on your AVR."
    #endif
}
Listing 7-5

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.

The preceding code could now be used to program yet another replacement Blink sketch. Listing 7-6 shows how a regular blink could be applied to an LED using nothing but the Watchdog Timer interrupt.
void setup() {
  wdt_interrupts(WDTO_1S);
  pinMode(LED_BUILTIN, OUTPUT);
}
void loop() {
  ; //Do nothing.
}
ISR(WDT_vect) {
  PINB |= (1 << PINB5);
}
Listing 7-6

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:

../images/494109_1_En_7_Chapter/494109_1_En_7_Figj_HTML.gif If the WDTON fuse has been programmed (i.e., has value 0binary), then you will be unable to disable the Watchdog Timer. In this case, the Watchdog Timer is always running in Watchdog Reset (WDR) mode.
  • 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().

Listing 7-7 is an example Arduino sketch to completely disable the Watchdog Timer.
#include "avr/wdt.h"
void setup() {
    wdt_reset();
    MCUSR &= ~(1 << WDRF);    ①
    wdt_disable();            ②
}
Listing 7-7

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.

../images/494109_1_En_7_Chapter/494109_1_En_7_Figk_HTML.gif 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.

The following sleep modes are available:
  • Idle

  • ADC Noise Reduction

  • Power Down

  • Power Save

  • Standby (only when there is an external crystal or ceramic resonator)

  • Extended Standby

To put the AVR microcontroller to sleep, the code must
  • 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 values defined for the various modes are as follows and can be found in the header file $ARDINC/avr/iom328p.h:
#define SLEEP_MODE_IDLE (0x00<<1)
#define SLEEP_MODE_ADC (0x01<<1)
#define SLEEP_MODE_PWR_DOWN (0x02<<1)
#define SLEEP_MODE_PWR_SAVE (0x03<<1)
#define SLEEP_MODE_STANDBY (0x06<<1)
#define SLEEP_MODE_EXT_STANDBY (0x07<<1)

../images/494109_1_En_7_Chapter/494109_1_En_7_Figl_HTML.gif 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.

Listing 7-8 is a simple function to flash the built-in LED a few times. It’s called from setup() to show that we are indeed alive and from the loop() when it’s doing “real” work.
#include <avr/sleep.h>
#include <avr/interrupt.h>
void flashLED(byte flashes) {
    for (byte x = 0; x < flashes; x++) {
        digitalWrite(LED_BUILTIN, HIGH);
        delay(250);
        digitalWrite(LED_BUILTIN, LOW);
        delay(250);
    }
}
Listing 7-8

Non-functioning sleep sketch, flashLED()

Listing 7-9 is the setup() function which sets the requirements for Idle sleep mode and then flashes the LED a couple of times to show we are alive and well, so far. Setting the sleep mode doesn’t put the Arduino to sleep at that point – that comes later.
void setup() {
    set_sleep_mode(SLEEP_MODE_IDLE);
    pinMode(LED_BUILTIN, OUTPUT);
    // Show we are alive
    delay(1500);
    flashLED(2);
}
Listing 7-9

Non-functioning sleep sketch, setup()

In Listing 7-10, we can see the loop() function . This is where the code would normally be doing application work and then sleeping until some stimulus wakes it up for the next pass through the loop.
void loop() {
    noInterrupts();             ①
    // Enable sleep mode and disable Brown Out Detection.
    // BOD disable is permitted on ATmega328P.
    sleep_enable();             ②
    sleep_bod_disable();
    // Enable interrupts and execute the sleep_cpu() -
    // Which guarantees that the sleep_cpu() will execute
    // before any new interrupts will be fired.
    interrupts();               ③
    sleep_cpu();                ④
    // When we wake up, on an interrupt, disable
    // sleep mode while processing.
    sleep_disable();            ⑤
    // Do some application "stuff" here after waking up ...
    flashLED(4);                ⑥
    // On the next pass of the loop, we will go back to sleep.
}
Listing 7-10

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.

To put the AVR microcontroller to sleep, the steps are as follows:
  • 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 ATmega328P will be woken from sleep modes if an enabled interrupt occurs or if it is reset while asleep. The wake-up process is as follows, for an interrupt:
  • 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.

Table 7-12 shows a list of the various sleep modes available and whether they can be used on an Arduino.
Table 7-12

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.

To wake the device from Idle mode, one of the following stimuli will be required:
  • 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.

    ../images/494109_1_En_7_Chapter/494109_1_En_7_Figm_HTML.gif 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.

A summary of this sleep mode is given in Table 7-13.
Table 7-13

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.

To wake the device from this mode, one of the following stimuli will be required:
  • 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.

A summary of this sleep mode is given in Table 7-14.
Table 7-14

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).

A summary of this sleep mode is given in Table 7-15.
Table 7-15

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.

A summary of this sleep mode is given in Table 7-16.
Table 7-16

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.

A summary of this sleep mode is given in Table 7-17.
Table 7-17

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.

A summary of this sleep mode is given in Table 7-18.
Table 7-18

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.

Listing 7-11 is the setupComparator() function which has not been changed from Chapter 9, other than to #include the avr/sleep.h and avr/interrupt.h header files at the top of the function.
//=======================================================
// The purpose of the sketch is to test the various sleep
// modes and to see if the AC will wake the Arduino.
//=======================================================
#include <avr/sleep.h>
#include <avr/interrupt.h>
// This function sets up the comparator to fire an interrupt
// each time the ACO bit toggles. It uses D6 as the reference
// voltage and D7 as the voltage to be compared.
void setupComparator() {
    ACSR &= ~(1 << ACIE);
    ACSR &= ~(1 << ACD);
    DIDR1 |= ((1 << AIN0D) | (1 << AIN1D));
    ACSR &= ~(1 << ACBG);
    ADCSRB &= ~(1 << ACME);
    ACSR |= ((1 << ACIS1) | (1 << ACIS0));
    ACSR |= (1 << ACIE);
}
Listing 7-11

Analogue Comparator wake-up, setupComparator()

Listing 7-12 is pretty much the same setup() function to that in Chapter 9, with the minor addition of the call to function set_sleep_mode(). It is here that I tested each and every valid sleep mode to see which, if any, would be interrupted by the Analogue Comparator’s interrupt.
void setup() {
    pinMode(LED_BUILTIN, OUTPUT);
    setupComparator();
    // Here is where I set the various sleep modes.
    set_sleep_mode(SLEEP_MODE_IDLE);
}
Listing 7-12

Analogue Comparator wake-up, setup()

An interrupt is required to wake the ATmega328P, but we don’t need to do any work inside the ISR. Listing 7-13 shows the code required for just such a purpose; it is an empty ISR to handle the Analogue Comparator interrupt.
// Analogue Comparator Interrupt Handler. Simply used to
// wake up the device, so no code required.
EMPTY_INTERRUPT(ANALOG_COMP_vect);
Listing 7-13

Analogue Comparator wake-up, empty ISR

Listing 7-14 is the main loop() function for the sketch. It puts the device to sleep in whatever mode I used in setup() and waits for a wake-up call. If one arrives, it will flash the built-in LED to show that it woke up. It will then go back to sleep.
void loop() {
    noInterrupts();
    sleep_enable();
    sleep_bod_disable();
    // Kill timer 0 and its overflow interrupt otherwise
    // it will wake the AVR from SLEEP_MODE_IDLE thus
    // negating the test. I need the AC to do the wake
    // up call!
    TCCR0B &= ~((1 << CS02) | (1 << CS01) | (1 << CS00));
    interrupts();
    // Go to sleep now.
    sleep_cpu();
    sleep_disable();
    // Reset Timer 0 to divide by 64 or delay() doesn't!
    TCCR0B |= ((1 << CS01) | (1 << CS00));
    // Flash the LED on wake up.
    for (short x = 0; x < 4; x++) {
        digitalWrite(LED_BUILTIN, HIGH);
        delay(250);
        digitalWrite(LED_BUILTIN, LOW);
        delay(250);
    }
}
Listing 7-14

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

The ATmega328P can be run at a number of supply voltages. On Arduino boards with a 16 MHz crystal or ceramic resonator, the supply voltage is limited to between 4.0 V and 5.5 V. The data sheet gives the figures shown in Table 7-19 for power consumption for each of those voltages, at 16 MHz, when the device is idle (in sleep mode Idle) and active or not sleeping.
Table 7-19

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

../images/494109_1_En_7_Chapter/494109_1_En_7_Fign_HTML.gif 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.

../images/494109_1_En_7_Chapter/494109_1_En_7_Figo_HTML.gif 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.

Table 7-20 shows the power consumption of the various ATmega328P internal peripherals and is taken from the data sheet. It should be noted that the typical figures listed are those for an AVR microcontroller running with a VCC of 5 V and a clock frequency of 8 MHz. This will not be accurate for an Arduino at 16 MHz, but read on, as all will become clear.
Table 7-20

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.

If we consider the AVR microcontroller running on a 5 V supply at 16 MHz, with all peripherals enabled and running, what is the idle power required?
Idle power at 5V = 2.4 mA
Peripheral percentages = 22.1% + ... + 16.6% = 99.3%
99.3% of 2.4 mA = 2.3832 mA
Added to 2.4 mA = 4.7832 mA
Resulting power = 5V * 4.5432 mA = 22.916 mW.
The same calculation in active mode results in
Active power at 5V = 9.6 mA
Peripheral percentages = 4.1% + ... + 3.0% = 18.3%
18.3% of 9.6 mA = 1.7568 mA
Added to 9.6 mA = 11.3568 mA
Resulting power = 5V * 11.3568 mA = 56.784 mW.

7.4.2 Power Reduction Register

Not all AVR microcontrollers have the same set of peripherals, and the relevant ones in the ATmega328P are as follows:
  • 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)

Those seven peripherals each have a single bit in the Power Reduction Register or PRR so that when set to a 1binary, that particular peripheral is powered off. The bits in question are
  • 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.

The on-off switches for each preceding peripheral are listed in Table 7-21 and, unless otherwise noted, should be written with a zerobinary to disable the appropriate peripheral.
Table 7-21

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.

../images/494109_1_En_7_Chapter/494109_1_En_7_Figp_HTML.gif 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.

The sketch in Listings 7-15, 7-16, and 7-17 could be used to enhance battery life for an Arduino device, running the blink sketch. Listing 7-15 is a function, disable(), which disables all the unwanted peripherals based on parameters passed to it, prior to powering them all off.
#include <avr/power.h>
void disable(bool ADCdisable, bool USARTdisable, bool SPIdisable,
             bool TIMER0disable, bool TIMER1disable, bool TIMER2disable,
             bool TWIdisable, bool ACdisable)
{
    // Disable ADC.
    if (ADCdisable)
        ADCSRA &= ~(1 << ADEN);
    // Disable USART0 RX and TX.
    if (USARTdisable)
        UCSR0B &= ~((1 << RXEN0) | (1 << TXEN0));
    // Disable SPI.
    if (SPIdisable)
        SPCR &= ~(1 << SPE);
    // Disable Timer/Counter 0
    if (TIMER0disable)
        TCCR0B &= ~((1 << CS02) | (1 << CS01) | (1 << CS00));
    // Disable Timer/Counter 1.
    if (TIMER1disable)
        TCCR1B &= ~((1 << CS12) | (1 << CS11) | (1 << CS10));
    // Disable Timer/Counter 2.
    if (TIMER2disable)
        TCCR2B &= ~((1 << CS22) | (1 << CS21) | (1 << CS20));
    // Disable TWI.
    if (TWIdisable)
        TWCR &= ~(1 << TWEN);
    // Disable Analogue comparator.
    if (ACdisable)
        ACSR &= ~(1 << ACD);
}
Listing 7-15

Low-power blink, disable() function

The setup() function in Listing 7-16 calls disable() with a list of peripherals to disable and then calls the AVRLib’s __power_all_disable() function which powers down all the peripherals. It then powers Timer/counter 0 back on to ensure that that is still working as it is needed by the sketch.
void setup() {
    // Disable the peripherals we don't want.
    disable(
    /* ADCdisable = */ true,
    /* USARTdisable = */ true,
    /* SPIdisable = */ true,
    /* TIMER0disable = */ false,
    /* TIMER1disable = */ true,
    /* TIMER2disable = */ true,
    /* TWIdisable = */ true,
    /* ACdisable = */ true);
    // Power down everything except Timer/Counter 0.
    // It's quicker this way, and less code bloat!
    __power_all_disable();
    power_timer0_enable();
    // Finally, do the sketch stuff.
    pinMode(LED_BUILTIN, OUTPUT);
}
Listing 7-16

Low-power blink, setup() function

The loop() in Listing 7-17 just blinks the LED as usual.
void loop() {
    digitalWrite(LED_BUILTIN, HIGH);
    delay(1000);
    digitalWrite(LED_BUILTIN, LOW);
    delay(1000);
}
Listing 7-17

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

As mentioned earlier, there are useful power handling functions in the AVRLib. These are, for the ATmega328P, as shown in Table 7-22.
Table 7-22

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.

../images/494109_1_En_7_Chapter/494109_1_En_7_Figq_HTML.gif 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 flash memory in the ATmega328P is divided into two sections:
  • 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.

../images/494109_1_En_7_Chapter/494109_1_En_7_Figr_HTML.gif 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

The device lock bits prevent anyone from reading or changing the contents of the AVR microcontroller. Those are lock bits LB1 and LB2 and have the following modes and settings.
Table 7-23

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.

../images/494109_1_En_7_Chapter/494109_1_En_7_Figs_HTML.gif 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.

../images/494109_1_En_7_Chapter/494109_1_En_7_Figt_HTML.gif As with fuses, lock bits are considered programmed when at 0binary and unprogrammed when 1binary.

7.5.2.2 Bootloader Lock Bits

With the two sets of lock bits, the following options can be selected. Details follow for the BLBn modes mentioned:
  • 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
These bits protect the application section of the device. There are two bits here, BLB01 and BLB02; and these are set according to a mode, known as BLB0 Mode. Table 7-24 summarizes the different modes:
Table 7-24

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.

../images/494109_1_En_7_Chapter/494109_1_En_7_Figu_HTML.gif 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
These bits protect the bootloader section of the device. There are two bits here, BLB11 and BLB12; and these are set according to a mode, known as BLB1 Mode. Table 7-25 summarizes the different modes:
Table 7-25

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.

../images/494109_1_En_7_Chapter/494109_1_En_7_Figv_HTML.gif 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.

If you wish to use a much smaller bootloader on your Duemilanove, for example, and save 1.5 Kb of Flash RAM for your own programs, it’s easy:
  • 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=30720
  • Change it to the following:

    diecimila.menu.cpu.atmega328.upload.maximum_size=32256
  • Find this line – it’s a single line which has wrapped around on this page:

    diecimila.menu.cpu.atmega328.bootloader.file= atmega/ATmegaBOOT_168_atmega328.hex
  • Change it to the following single line, not wrapped as follows:

    diecimila.menu.cpu.atmega328.bootloader.file= optiboot/optiboot_atmega328.hex
  • Save 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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.111.183