2
\$\begingroup\$

I'm trying to disable the brownout detection (BOD) of a ATmega328p at runtime, before putting it into power-down sleep like this:

    set_sleep_mode(SLEEP_MODE_PWR_DOWN);
    cli(); // disable interrupts
    sleep_enable();
    sleep_bod_disable();
    sei(); // enable interrupts
    sleep_cpu();
    // ...zZz...zZz...zZz...
    sleep_disable();

But this doesn't work. That means I measure no difference in power consumption during power-down sleep: 17 µA before and after (MCU runs at 3.3V)

For comparison, disabling BOD via the efuse (i.e. I set it to 0xff with avrdude) does work as expected, i.e. after that the power-consumption drops to 0.13 µA during power-down sleep. (which pretty much matches what is specified in the datasheet)

Is there some pitfall I'm missing here? Is the above power-down/BOD sequence sub-optimal?


Update 2022-05-30: To simplify things I've replaced the sleep_bod_disable() macro/inline assembler call with just:

MCUCR = _BV(BODS) | _BV(BODSE);
MCUCR = _BV(BODS);

This yields the following machine code/assembly:

 488:   c0 e6           ldi     r28, 0x60       ; 96
 48a:   d0 e4           ldi     r29, 0x40       ; 64

 4e8:   83 b7           in      r24, 0x33       ; 51
 4ea:   81 7f           andi    r24, 0xF1       ; 241
 4ec:   84 60           ori     r24, 0x04       ; 4
 4ee:   83 bf           out     0x33, r24       ; 51
 4f0:   f8 94           cli
 4f2:   83 b7           in      r24, 0x33       ; 51
 4f4:   81 60           ori     r24, 0x01       ; 1
 4f6:   83 bf           out     0x33, r24       ; 51
 4f8:   c5 bf           out     0x35, r28       ; 53
 4fa:   d5 bf           out     0x35, r29       ; 53
 4fc:   78 94           sei
 4fe:   88 95           sleep
 500:   83 b7           in      r24, 0x33       ; 51
 502:   8e 7f           andi    r24, 0xFE       ; 254
 504:   83 bf           out     0x33, r24       ; 51

However, this doesn't make a difference, i.e. power consumption is still at 17 µA in power down sleep.

With the original sleep_bod_disable() call the dis-assembly reads:

 4e4:   83 b7           in      r24, 0x33       ; 51
 4e6:   81 7f           andi    r24, 0xF1       ; 241
 4e8:   84 60           ori     r24, 0x04       ; 4
 4ea:   83 bf           out     0x33, r24       ; 51
 4ec:   f8 94           cli
 4ee:   83 b7           in      r24, 0x33       ; 51
 4f0:   81 60           ori     r24, 0x01       ; 1
 4f2:   83 bf           out     0x33, r24       ; 51
 4f4:   85 b7           in      r24, 0x35       ; 53
 4f6:   80 66           ori     r24, 0x60       ; 96
 4f8:   85 bf           out     0x35, r24       ; 53
 4fa:   8f 7d           andi    r24, 0xDF       ; 223
 4fc:   85 bf           out     0x35, r24       ; 53
 4fe:   78 94           sei
 500:   88 95           sleep
 502:   83 b7           in      r24, 0x33       ; 51
 504:   8e 7f           andi    r24, 0xFE       ; 254
 506:   83 bf           out     0x33, r24       ; 51

See also: compiler explorer

AFAICS, both versions satisfy the constraints specified in the ATmega328p manual:

Writing to the BODS bit is controlled by a timed sequence and an enable bit, BODSE in MCUCR. To disable BOD in relevant sleep modes, both BODS and BODSE must first be set to one. Then, to set the BODS bit, BODS must be set to one and BODSE must be set to zero within four clock cycles.

The BODS bit is active three clock cycles after it is set. A sleep instruction must be executed while BODS is active in order to turn off the BOD for the actual sleep mode. The BODS bit is automatically cleared after three clock cycles.

(Section 10.11.2 MCUCR - MCU Control Register, page 54, emphasis mine)

NB: The sei/sleep/out are all specified as taking one clock cycle and sei is specified to enable interrupts only after the following instruction executed.

\$\endgroup\$
10
  • \$\begingroup\$ I agree to your timing assumptions, the code looks perfect. Does it work if you remove the CLI/SEI instructions just for a test? \$\endgroup\$
    – Jens
    Commented May 30, 2022 at 1:12
  • 1
    \$\begingroup\$ @maxschlepzig: Is there a chance, that it wakes up immediately for unknown reason? What is your default clock source while running? \$\endgroup\$
    – Jens
    Commented Jun 1, 2022 at 0:17
  • 1
    \$\begingroup\$ @maxschlepzig: I tried your first assembly code on an atMega168PA (sorry, no 328P in house) with watchdog wake up each second and BOD fuses set to 1.8V. I measure 3.85µA consumption. This is expected for the WD, so this code works with this MCU. Without software BOD disable I measure 59.6µA (pretty much). \$\endgroup\$
    – Jens
    Commented Jun 1, 2022 at 22:44
  • 1
    \$\begingroup\$ @maxschlepzig: Update, I found a bare 328P and tested it with your code fragment. I read 24µA with BOD and 4.4µA with software BOD disable. What may be the difference? \$\endgroup\$
    – Jens
    Commented Jun 2, 2022 at 0:36
  • 1
    \$\begingroup\$ @Jens, thank you for your independent tests. I now managed to properly software disable BOD. See my comment to bigjosh's answer for the unexpected cause ... \$\endgroup\$ Commented Jun 2, 2022 at 23:01

1 Answer 1

1
\$\begingroup\$

Can you remove the cli right before the sleep and then take a current reading while the chip is halted? This eliminates any possibility that there is an ISR inadvertently running during the measurement.

Also would be nice to add an interlock at the top of the program so you know the chip is not silently resetting. Something like: pick an unused io pin and then add at the top of the program a 'while (iopin high)'. When you start your test, use a jumper to make the pin low then connect.thr pin to high. This way if the chip resets you will see mA of current indicating that the chip did reset and is stuck in the interlock.

\$\endgroup\$
1
  • 2
    \$\begingroup\$ Ok, I did that, including the interlock. Also, I started a fresh platformio project and stripped everything down for a minimal reproducer. Turns out I couldn't reproduce it anymore, i.e. the software BOD disable suddenly worked as expected (0.2 µA). Root cause: for my previous tests I symlinked some source files and I was affected by an SCons bug where the build system fails to detect changed files through symlinks. Yes, platformio uses SCons under the hood, and while I was using it on the command line, it's pretty non-verbose by default. \$\endgroup\$ Commented Jun 2, 2022 at 22:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.