SpenceKonde / megaTinyCore

Arduino core for the tinyAVR 0/1/2-series - Ones's digit 2,4,5,7 (pincount, 8,14,20,24), tens digit 0, 1, or 2 (featureset), preceded by flash in kb. Library maintainers: porting help available!
Other
564 stars 148 forks source link

Better power saving docs #695

Closed eloquentarduino closed 2 years ago

eloquentarduino commented 2 years ago

I was having troubles with deep sleep mode. Documentation says:

## Unused pins and sleep modes

IMPORTANT In order to minimize power consumption, you must eliminate or disable all floating pins... To minimize power consumption, one or more of the following must apply to every pin:

 - It is set as an OUTPUT
 - It is set INPUT_PULLUP or the internal pullups are otherwise enabled
 - It is connected to another device which is holding it HIGH or LOW
 - The input buffer is disabled in the PORTx.PINnCTRL register

I found that, at least for me, the only way to achieve low current (with my circuitery connected) is as below (copy-pasted from Atmal docs):

// call before sleep_cpu()
for (uint8_t pin = 0; pin < 8; pin++) {
    (&PORTA.PIN0CTRL)[pin] = PORT_ISC_INPUT_DISABLE_gc;
    (&PORTB.PIN0CTRL)[pin] = PORT_ISC_INPUT_DISABLE_gc;
    (&PORTC.PIN0CTRL)[pin] = PORT_ISC_INPUT_DISABLE_gc;
}

Setting as OUTPUT or INPUT_PULLUP doesn't work reliably.

SpenceKonde commented 2 years ago

This is a duplicate of the existing issue on expanded support for low power mode


Spence Konde Azzy’S Electronics

New products! Check them out at tindie.com/stores/DrAzzy GitHub: github.com/SpenceKonde ATTinyCore: Arduino support for almost every ATTiny microcontroller Contact: @.***

On Tue, Apr 26, 2022, 02:45 eloquentarduino @.***> wrote:

I was having troubles with deep sleep mode. Documentation says:

Unused pins and sleep modes

IMPORTANT In order to minimize power consumption, you must eliminate or disable all floating pins... To minimize power consumption, one or more of the following must apply to every pin:

  • It is set as an OUTPUT
  • It is set INPUT_PULLUP or the internal pullups are otherwise enabled
  • It is connected to another device which is holding it HIGH or LOW
  • The input buffer is disabled in the PORTx.PINnCTRL register

I found that, at least for me, the only way to achieve low current (with my circuitery connected) is as below (copy-pasted from Atmal docs):

// call before sleep_cpu() for (uint8_t pin = 0; pin < 8; pin++) { (&PORTA.PIN0CTRL)[pin] = PORT_ISC_INPUT_DISABLE_gc; (&PORTB.PIN0CTRL)[pin] = PORT_ISC_INPUT_DISABLE_gc; (&PORTC.PIN0CTRL)[pin] = PORT_ISC_INPUT_DISABLE_gc; }

— Reply to this email directly, view it on GitHub https://github.com/SpenceKonde/megaTinyCore/issues/695, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABTXEW7R4VHNVFMEBJWQTC3VG6GG7ANCNFSM5UKYVLLA . You are receiving this because you are subscribed to this thread.Message ID: @.***>

SpenceKonde commented 2 years ago

However, the fact that only disabling the input buffer works for you is extremely strange.

Please share the circuit connected to the chip. Can't really go any further without that.

SpenceKonde commented 2 years ago

Assigned to original issue creator as we need more information to have any hope here.

eloquentarduino commented 2 years ago

If I'm not wrong (will check on monday), I see this behavior even with anything connected. Need to double-check, though.

SpenceKonde commented 2 years ago

I would be most surprised if this happened with nothing connected to the pins as long as all I/O pins were either:

That all assumes the board wasn't filthy from hand soldering with flux paste. At least when I hand solder these parts, the immediate result attracts lint, dust and tumbleweeds of cat fur from miles around. (tip: clean the flux off sooner rather than later - the longer you leave it there, the harder ut is to get off - it seems to dry out, and particulate from the environment (for me, mostly cat hair) forms a composite with the flux that is that much more resistant to removal. (tip2: if the cat thought that flux covered board would be a comfy place to snooze and it's now covered in fur, and you have already soldered the pin header on you may think that you'll never get the fur off it. Don't worry: use a butane torch. The key is that the flame should be pointed in the general direction of the board for less than a second in total. Fur burns off instantly. Then clean the board normally with a toothbrush and 99% IPA so it doesn't pick up more fur. maybe do that in a different room, so you don't have a butane torch right next to the bottle of highly flammable alcohol. In any event, you're going to want to open the window - both to vent the fumes and to get rid of the smell of burnt hair from the previous step.) (tip3: After all SMT work, clean the board BEFORE you add the pin header. Rosin core solder is unique in that while it may leave crap behind, that crap isn't sticky, so you usuaklly don't have to clean after soldering pin header.... However for this specific test, you absolutely 100% do need to clean the board very thoroughly to rule out flux residue as a factor. Flux is an insulator, but air is a much better one) tip4: 99% IPA is what you want. not 91% Dirt cheap on amazon! That 70% drugstore IPA is for... cleaning scrapes and cuts or whatever it is they advertise it for. Because it's above the azeotropic composition, 99% IPA will remove traces of moisture and not leave the board damp. Whereas 91% may just move water around and 70% will leave a bunch of water behind.

Oh right, covid, that's what IPA is used for these days, compulsively disinfecting your hands as if that will prevent you from catching an airborne disease (tip5: it wont. Neither does a mask when worn below your chin. The only truly effective countermeasure is a full face respirator, Like professionals use for spray paint This will muffle your voice; I know someone who only went out dressed like that until his second vaccine shot. He rigged up a mic in the mask and a speaker on his belt to talk to the cashiers. Dude also managed to score an oxygen concentrator just in case. That was before we knew essentially all transmission was airborne - all one needs is to make sure there's no air intake that any passersby can breath near, and never leave your home or let anyone in, and you can survive on mail-order. Eventually you'll go nuts like that - a slight problem. But if you turn on the TV (not recommended) you'll see that everyone else has gone stark raving mad too.) I've been told that for disinfecting things, ~70% works better than 91%. - though I haven't seen any peer reviewed papers on the matter. It's not completely implausible that the stronger stuff doesn't effectively contact some lucky pathogen particles as well as 70%. Just not covid - covid needs to reach our mouth or eyes or nose to infect us. (tip6 - if you're a nailbiter, you only need to disinfect your nails, not the whole hand)

mechatroniks-git commented 2 years ago

Good tips on board and nail cleaning Spence.

Measure the actual voltage on the pins, what do you see? Report back.

I've seen the sleep current increase if the voltage wasn't near 0V or VCC (whatever your rail is, 3.3V or 5V, etc.).

If you set it as an output and drive it high, you might have some leakage in your circuit. If you set as input_pullup, same concern, different resistance to VCC.

SpenceKonde commented 2 years ago

Yes skeep current skyrockets when right in the midle of Vcc and ground because the input buffer twiddles back and forth

SpenceKonde commented 2 years ago

I am closing as duplicate f #158

eloquentarduino commented 2 years ago

I soldered a fresh Attiny1616 to a simple breakout board, no flux, just the VCC, GND and UPDI pins to be sure I have the cleanest setup possible. Here is the code I'm using for sleep:

#include <avr/sleep.h>
#include <avr/power.h>

ISR(RTC_PIT_vect) {
    RTC.PITINTFLAGS = RTC_PI_bm;
}

namespace Sleep {
  void begin() {
    while (RTC.STATUS > 0) ;
    RTC.CLKSEL = RTC_CLKSEL_INT32K_gc;
    RTC.PITINTCTRL = RTC_PI_bm;
    RTC.PITCTRLA = RTC_PERIOD_CYC32768_gc | RTC_PITEN_bm;

    set_sleep_mode(SLEEP_MODE_PWR_DOWN);
    sleep_enable();
  }

  void enable() {
    // case 1: disable input buffers
    for (uint8_t pin=0; pin < 8; pin++) {
     (&PORTA.PIN0CTRL)[pin] = PORT_ISC_INPUT_DISABLE_gc; //Disable on PAx pin
     (&PORTB.PIN0CTRL)[pin] = PORT_ISC_INPUT_DISABLE_gc; //Disable on PBx pin
     (&PORTC.PIN0CTRL)[pin] = PORT_ISC_INPUT_DISABLE_gc; //Disable on PCx pin
    }

    // case 2: set pins as either OUTPUT or INPUT_PULLUP
    for (int i = 0; i < 16; i++)
        pinMode(i, OUTPUT);

    VPORTB.DIR &= ~PIN2_bm;

    set_sleep_mode(SLEEP_MODE_PWR_DOWN);
    power_all_disable();
    sleep_enable();

    for (int i = 0; i < 10; i++)
      sleep_cpu();
  }

  void disable() {
    for (int i = 0; i < 16; i++)
      pinMode(i, INPUT);

    VPORTB.DIR |= PIN2_bm;

    sleep_disable();
    power_all_enable();
  }
}

I tested both case 1 and case 2 separately. My measuring instrument is pretty cheap: an INA219 with 100 uA resolution. My results:

I hoped I had a better instrumentation. My conclusion: disabling input buffers is the most reliable way to lower the power consumption. Does it have any drawback?