dbuezas / lgt8fx

Board Package for Logic Green LGT8F328P LGT8F328D and LGT8F88D
359 stars 90 forks source link

Power management library (pmu.h) doesn't work after the first analogRead is made #155

Closed dbuezas closed 11 months ago

dbuezas commented 3 years ago

This works fine:

#include <PMU.h>
void setup(){
    pinMode(LED_BUILTIN, OUTPUT);
    digitalWrite(LED_BUILTIN, 1);
    PMU.sleep(PM_POFFS0, SLEEP_1S);
    digitalWrite(LED_BUILTIN, 0);
}
void loop(){
}

This doesn't work. It doesn't go to sleep:

#include <PMU.h>
void setup(){
    pinMode(LED_BUILTIN, OUTPUT);
    analogRead(A1); // this breaks it!
    digitalWrite(LED_BUILTIN, 1);
    PMU.sleep(PM_POFFS0, SLEEP_1S);
    digitalWrite(LED_BUILTIN, 0);
}
void loop(){
}

After some debugging, I pinned point the line that causes this behaviour. It is cores/lgt8f/wiring_analog.c:97

sbi(ADCSRA, ADSC);

which just starts one measurement.

Curiously, setting ADCSRA = 0; before sleeping doesn't do anything, but wrapping the sleep fn like this:

bitClear(ADCSRA, ADEN);
PMU.sleep(PM_POFFS0, SLEEP_1S);
bitSet(ADCSRA, ADEN);

(i.e disabling the ADC temporarily) makes it work again.

I see that in the lib there is a #if defined(__LGT8FX8E__) before clearing and restoring the ports, dir registers and the ADCSRA, but replicating that behaviour doesn't work in the 328p.

My assumption is that the PMU library in this core is not actually made for the lgt328p, but the 328d instead. There's probably a lot more current that can be saved by making a new version for the 328p.

Thoughts?

LaZsolt commented 3 years ago

I read almost the same issue in connection with the Arduino_Vcc library. https://github.com/Yveaux/Arduino_Vcc/issues/7 As I can understand it, if the internal reference selected with the MUX, it will remain on during the sleep mode.

dbuezas commented 3 years ago

I see. But not only that, the board doesn't even go to sleep at all. I assume it gets woken back up. Maybe the analog comparator wakes it up?

LaZsolt commented 3 years ago

I think clocks of the internal modules must be shut down before sleep mode by setting PRR and PRR1 registers.

dbuezas commented 3 years ago

I played a bit and the lib is definitely not made for the 328p.

The PMU library uses the sleep command instead of the RRR register. It also switches the clock to the secondary 32khz one. (the sleep command should also turn off almost all peripherals)

I tried turning everything off via RRR registers, stopping all timers and the main clock and switching to the internal 32khz one, it also saves a similar amount of power, but the sleep command combined with the power save mode selection (SMCR) and doing the internal 32khz clock trick (as the lib does) seems to be best.

In any case, I find no explanation for having to disable the ADC via bitClear(ADCSRA, ADEN);. Disabling it via RRR doesn't do anything either.

Unfortunately I don't have a unit w/o LDO so I think 90% of the remaining consumption is that and the power LED. I'll have to find an excuse to butcher a mini-style just to see how low this chip can go in deep sleep.

It even has a full off mode with an pin change interrupt or some "LPRC" timer (low power oscillator, up to 1s) which seems to turn off even the RAM (unit will reset after sleep). I really wonder how much that consumes!

#include <PMU.h>

void megaSleep() {
  // WARNING: it looks like to upload again you need to press the reset button manually
  DPS2R = 0b1100; // enable off sleep for 1s
  PMU.sleep(0b111); // trigger dps2 mode
}
dbuezas commented 3 years ago

~Also, the PMU lib resets the clock to 32Mhz after wake up.~ update: my mistake

LaZsolt commented 3 years ago

I found an example, how to use sleep modes on page 7 in this doc: https://github.com/dbuezas/lgt8fx/blob/master/docs/Porting_from_LGT8FX8D_to_LGT8FX8P_v1.0.0.pdf

LaZsolt commented 1 year ago

Uhh. It appeared to me after several days of studying the sleep modes this sentence:

In sleep modes, LGT8FX8P will not automatically disable function of analog module, such as ADC, DAC, AC (analog comparator), LVD (low voltage detector) and so son.

The third sentence of the Power management section. And what could be the "and so on" thing?

dbuezas commented 1 year ago

what could be the "and so on" thing?

Maybe the other peripherals? (serial, spi, timers, ...)

LaZsolt commented 1 year ago

Serial, spi, timers are not analog modules so they are not "and so on", if the quoted sentence above can be taken seriously.

Anyway, after I finish studying the sleep modes I plan to fix this package's power management examples.

dbuezas commented 1 year ago

Right I see. Then the differential amplifier could be the missing one

LaZsolt commented 1 year ago

I found 2 more analog modules. These 2 modules can be shut down by writing ADCSRD register to 0x0. The internal reference voltage source and the Voltage division circuit which a resistor divider with a multiplexed input.

So there are 8 analog devices with their own shutdown control bits. ADC, DAC0, AC0, AC1, LVD, DAP, IVREF and the internal voltage division circuit.

LaZsolt commented 1 year ago

I have read this issue many times, but I just now understood, what was your idea about the issue.

My assumption is that the PMU library in this core is not actually made for the lgt328p, but the 328d instead. There's probably a lot more current that can be saved by making a new version for the 328p.

Thoughts?

Low-Power library for Arduino is a useful, simple and widely used library with better power settings in sleep. Examples: LowPower.standby(); LowPower.idle(SLEEP_8S, ADC_OFF, TIMER2_OFF, TIMER1_OFF, TIMER0_OFF, SPI_OFF, USART0_OFF, TWI_OFF);

    pinMode(wakeUpPin, INPUT);   
    attachInterrupt(0, wakeUp, LOW);
    LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF); 
    detachInterrupt(0); 

This library could be copied into this repository and could be expanded with LGT8F328x's capabilities. With this library the PMU library will be obsolete.

Edit: Document how to use LowPower library: https://www.engineersgarage.com/reducing-arduino-power-consumption-sleep-modes/

sullivanzheng commented 1 year ago

Unfortunately I don't have a unit w/o LDO so I think 90% of the remaining consumption is that and the power LED. I'll have to find an excuse to butcher a mini-style just to see how low this chip can go in deep sleep.

I am pushing hard to get that 1uA. I decimated LDO, power LED, and another LED onboard (I used some version of mini pro board with LGT8F328p without USB-TTL chip such as CH341 etc.) @dbuezas .

@LaZsolt I tried PMU lib and also found that is only for 328D and maybe I should use some of 328E's code which is embraced by #if defined(LGT8FX8E). It looks to me that these code section

    //In function void PowerControl::sleep(pmu_t mode, period_t period)
    #if defined(__LGT8FX8E__)
    adcsra_reg = ADCSRA;
    ddrd_reg = DDRD;
    portd_reg = PORTD;
    ddrb_reg = DDRB;
    portb_reg = PORTB;
    didr0_reg = DIDR0;

    DIDR0 = 0xff;
    ADCSRA = 0;

    DDRD &= 0xdf;
    DDRB &= 0xf9;
    PORTD |= 0x20;
    PORTB |= 0x06;
    #endif

carefully set up the peripherals that may have substantial power consumption.

Now I am getting ~41uA for DPS2 mode, just by setting DDRx to 0 and PORTx to 0xff to enable pull-up to avoid floating input. Clearly that is not the end of the road.

==========UPDATE=============== By adding ADCSRA = 0; I got 6.8uA for DPS2 mode and sleep forever... And by PMU.sleep(PM_POFFS1, SLEEP_16S); I got 15.6uA. Looks like the WDT costs ~9uA. See if I can go further.

dbuezas commented 1 year ago

Did you try turning the low voltage detector (LVD) off? AFAIK it is like the atmega's BOD but has more features.

VDTCR |= 1<<WCE; // this should allow changing other bits during the next 6 cycles. Not sure if it is needed for VDTEN
VDTCR &= ~(1<<VDTEN);
dbuezas commented 1 year ago

Maybe also VDREN: VDTCR &= ~(1<<VDTEN | 1<<VDREN);

LaZsolt commented 1 year ago

@sullivanzheng

By adding ADCSRA = 0; I got 6.8uA for DPS2 mode and sleep forever...

It is interesting why save power ADCSRA = 0; because ADC wasn't turned on before. My experiment: https://github.com/dbuezas/lgt8fx/issues/202#issuecomment-1419681782

LaZsolt commented 1 year ago

Turning off the LVD:

  uint8_t old_sreg = SREG;
   cli();                                                // noInterrupts();
   uint8_t tmp = VDTCR & ~( (1<<VDTEN) | (1<<VDREN) ); 
   VDTCR = (1<<WCE);                                     // 0x80
   VDTCR = tmp;
// sleep_cpu(); and others
   SREG = old_sreg;                                      // interrupts(); sei();

Update: cli(); sei(); noInterrupts(); interrupts(); recommended

LaZsolt commented 1 year ago

Comment edited https://github.com/dbuezas/lgt8fx/issues/155#issuecomment-1419818081

dbuezas commented 1 year ago

does it make a difference on the consumption?

sullivanzheng commented 1 year ago

So there are 8 analog devices with their own shutdown control bits. ADC, DAC0, AC0, AC1, LVD, DAP, IVREF and the internal voltage division circuit. @LaZsolt

Should I turn them all off to get 1uA DPS2 sleep?

sullivanzheng commented 1 year ago

does it make a difference on the consumption?

@LaZsolt @dbuezas Something must be incorrect. Turned LVD off (the ATmega328p BOD equivalent) and there is no additional power saving. Still got ~6uA current. Trying to set ADSCRD = 0 to see if shutting down internal voltage reference and voltage divider could save more. -----------update----------------- The answer is no. Setting ADSCRD = 0; doesn't save power. Still ~6uA current.

LaZsolt commented 1 year ago

@sullivanzheng

Should I turn them all off to get 1uA DPS2 sleep?

No, you shouldn't. In DPS2 mode the LGT8F328P turning off all internal devices. Watch my experiment: https://github.com/dbuezas/lgt8fx/issues/202#issuecomment-1419681782

sullivanzheng commented 1 year ago

OMG @LaZsolt you are right. I used your code

https://github.com/dbuezas/lgt8fx/issues/202#issuecomment-1419681782

and got 0.67uA (670nA)! The best sleep current I ever got on any MCUs including ATtiny, ATmega, STM32, GD32, CH32, ESP8266, ESP32 and ESP32-C3. New record. (Well that is not exactly a fair comparison since I didn't disable WDT on ATtiny and ATmega, the sleep current is ~4uA. If WDT is disabled, the sleep current is sub 1uA level)

And if you go even further to lower the supply voltage to 1.8V, sleep current goes to 0.22uA (220nA). I am not sure if my meter is perfectly calibrated, but I measured current of 10M at 1.8V and it gives 0.19uA so I guess the accuracy is acceptable. @dbuezas @LaZsolt

dbuezas commented 1 year ago

That's astounding! do you wake up via reset or did you manage to get wake on pin at those currents?

sullivanzheng commented 1 year ago

That's astounding! do you wake up via reset or did you manage to get wake on pin at those currents?

Wake up via reset pin or wake on PortD pins doesn't change the power consumption.

Only if you turn on many PortD pins for pin change wake up from DPS2, the MCU is more susceptible to interference (in my test I use my hand to touch PortD pins randomly) and may drain 10s or 100s of uA additional current via the weak (20k-40k according to datasheet) pull-up resisters.

I am trying to use LPRC to enable periodic wake up (by setting DPS2R= 0b00001111) but without success so far. The MCU can only sleep for 50ms and then wake up instead of the claimed 1s on datasheet.

dbuezas commented 1 year ago

instead of the claimed 1s on datasheet

maybe you need to increase the prescaler on some clock to reach the full second

sullivanzheng commented 1 year ago

instead of the claimed 1s on datasheet

maybe you need to increase the prescaler on some clock to reach the full second

=====On LPRC clock and time to wake up===== @dbuezas I don't think so. the LPRC is an independent low power clock residing in AWSON block, neither associated with 32Khz RC internal oscillator, nor 16Mhz RC oscillator.

I used my joulescope to measure the wake-sleep cycle by setting DPS2R differently:

DPS2R = 0b1100; // sleep for 2.37s DPS2R = 0b1101; // sleep for 0.53s DPS2R = 0b1110; // sleep for 0.29s DPS2R = 0b1111; // sleep for 0.14s

Aha, I guess I found a datasheet error here. According to datasheet, the lowest 2 bits of DPS2R are TOS1 and TOS0., With TOS[1:0] = 00 being the shortest sleep time and 11 the longest.

Actually it is just the opposite. Someone may have written a wrong datasheet, or the MCU designer wrote the wrong verilog code.

====On accuracy and power consumption of DPS2 mode with LPRC periodical wakeup==== Bad news is the LPRC accuracy is very crappy. I suspect it is not even a clock but a RC charging/discharging-timing circuit... The longer it discharges, the timing gets increasingly inaccurate. But it is fine for me since I only need some kind of periodic wake up.

Another bad news is the current of LPRC is not really low power. It costs 5.78 uA to operate it.

So we have (if I am doing things correctly, please point out if I did something wrong. @LaZsolt )

When Vcc=3.3V

~It seems LGT is not doing a perfect job matching up ATmega328p on low power sleep. But it is still good for most application I guess.~ I changed my mind.

If you are really paranoidal about power saving, you can use Vcc = 1.8V at which lgt8f328p enjoys the privilege run at 32Mhz at active mode while ATmega328p demands at least 2.7V to operate only at 8Mhz. At Vcc=1.8V the LPRC only cost ~300nA to run. And the total power consumption is 1.1uA with LPRC periodical wake up. Interestingly time for wake up is now 2.78s instead of 2.37s.

=====The code===== The code I used for DPS2 sleep is as follows

#include "Arduino.h"
#include <avr/sleep.h>
#define SLEEP_MODE_DPS2 SLEEP_MODE_EXT_STANDBY

void setup() {
  DPS2R = 0;
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN,HIGH);
  delay(300);                     
  digitalWrite(LED_BUILTIN,LOW);
  delay(200);                    

  cli();
  IOCWK = 0;                       // port D level change wake up. 0 = No wake up when level changes
  set_sleep_mode(SLEEP_MODE_DPS2);
  sleep_enable(); 
  DPS2R = 0b1100; //1111- 0.14s 1110 - 0.29s, 1101 - 0.5s, 1100 - 2.3s, measured on joulescope. 
//DPS2R |= (1<<DPS2E);             // DPS2 enable (bit 3), LPRC timer disable (bit 2)

  sleep_cpu();
}

void loop() {}
LaZsolt commented 1 year ago

Bad news is the LPRC accuracy is very crappy.

Interestingly time for wake up is now 2.78s instead of 2.37s.

So lower VCC means longer timing. I suppose the timing of this RC timer is also temperature dependent.

LaZsolt commented 1 year ago

@sullivanzheng Your work is great. We got valuable information from these time and current measurements, I have no such a good metering devices like joulescope.

And by PMU.sleep(PM_POFFS1, SLEEP_16S); I got 15.6µA. Looks like the WDT costs ~9uA.

The value of 16.5 µA does not seem too much compared to DPS2 5.78 µA without SRAM and register retention. Chinese quality.

sullivanzheng commented 1 year ago

@sullivanzheng Your work is great. We got valuable information from these time and current measurements, I have no such a good metering devices like joulescope.

And by PMU.sleep(PM_POFFS1, SLEEP_16S); I got 15.6µA. Looks like the WDT costs ~9uA.

The value of 16.5 µA does not seem too much compared to DPS2 5.78 µA without SRAM and register retention. Chinese quality.

I don't think 16uA is the end game. That number was measured when I didn't know how to properly get sub 1uA power consumption by DPS2 sleep. (I wrote a bunch of random code to fiddle with all kinds of registers including ADSCRA=0 ADSCRD=0 etc.) may incur extra power consumption. Will see if I can get that number down a bit.

In addition, 16uA additional sleep current may be offset by shorter active time of lgt due to faster clock and single cycle instruction. Here is my calculation:

I got several project using ATmega328p with 1% duty cycle and sleep current of 5uA and active current of 4.7mA. Overall average current is 52uA.

If I switch to LGT, active time may be reduced by 70% (32Mhz vs 8Mhz), therefore duty cycle is now 0.3%, active current is 11mA. sleep current 16uA, total average is 49uA.

So it seems that the key is if I can fully leverage the faster core speed and slash the active time effectively.... STM32F103 (blue pill) and other faster MCU seems to treat the power saving using similar logic (faster core speed, shorter active time, to trade off higher sleep current ~ 15uA for full SRAM retention and WDT).

LaZsolt commented 1 year ago

@sullivanzheng

When Vcc=3.3V 5.78µA without SRAM and register retention for lgt8f328p vs 4.7µA for ATmega328p with WDT and full SRAM and register retention

4.7 µA for ATmega328p ? Not good. I just read the ATmega databook and it says:

Power Consumption at 1 MHz, 1.8V, 25°C
– Active mode: 0.2 mA
– Power-Down mode: 0.1 μA
– Power-Save mode: 0.75 μA (Including 32 kHz RTC)

and

If the On-chip Debug System is enabled by the fuse and the chip enters Sleep mode, the main clock
source is enabled and hence always consumes power. In the deeper Sleep modes, this will contribute
significantly to the total current consumption.
dbuezas commented 1 year ago

Heads up: it seems there are atmega328p clones which are out of spec in regards to power consumption: See here: https://youtu.be/PlGycKwnsSw The rest of the videos in the series are in the comments, very interesting material finishing with a specialist looking at the dye

I assume we all source these carelessly (ebay, ali,...), so no wonder.

dbuezas commented 1 year ago

Although last time I got 40uA on an aliexpress pro mini and I also had other chips powered (sleeping too). He reports >100uA on his likely-fake atmega328p

sullivanzheng commented 1 year ago

@sullivanzheng

When Vcc=3.3V 5.78µA without SRAM and register retention for lgt8f328p vs 4.7µA for ATmega328p with WDT and full SRAM and register retention

4.7 µA for ATmega328p ? Not good. I just read the ATmega databook and it says:

Power Consumption at 1 MHz, 1.8V, 25°C
– Active mode: 0.2 mA
– Power-Down mode: 0.1 μA
– Power-Save mode: 0.75 μA (Including 32 kHz RTC)

and

If the On-chip Debug System is enabled by the fuse and the chip enters Sleep mode, the main clock
source is enabled and hence always consumes power. In the deeper Sleep modes, this will contribute
significantly to the total current consumption.

On the datasheet for ATmega328p, chapter 28.4 speed grades, it says the safe operation area is from 2.7V to 5.5V? So I guess 1.8V @ 1Mhz is not officially recommended Vcc? They actually sell ATmega48/88/168V which officially support 1.8V.

BTW 1Mhz clock is quite slow. It will make active time substantially longer. Of course people worked with 8051 MCU will think that is not slow clock speed since AVR instructions are mostly 1T or 2T.

LaZsolt commented 1 year ago

kép

dbuezas commented 1 year ago

Out of curiosity, has anybody measured the consumption of the lgt while deep sleeping on different F_CPU clock speeds? I assume it should be the same since it isn't even running, but if isn't, there may be the possibility of dialling up the prescaler right before sleep. Again, I doubt this is a thing, but who knows

sullivanzheng commented 1 year ago

kép

Are we looking at the same part? I checked ATmega328p official product page but I got very different datasheet. In my datasheet, 1.8V is no more officially specified. Not only that, 20Mhz @ 5V is also removed as "external clock drive". For that reason, I bought several ATmega168V which officially support 1.8V to deal with a 1.8V circuit design. image

sullivanzheng commented 1 year ago

Out of curiosity, has anybody measured the consumption of the lgt while deep sleeping on different F_CPU clock speeds? I assume it should be the same since it isn't even running, but if isn't, there may be the possibility of dialling up the prescaler right before sleep. Again, I doubt this is a thing, but who knows

That is actually a very valuable question. In the PMU lib, there is a function:

// switch main clock to rc32k
void PowerControl::clock_switch(pmu_t mode)
{
    uint8_t btmp;

    if(mode > PM_IDLE) {
        // switch main clock to rc32k 
        btmp = (PMCR & 0x1F) | 0x50;
        PMCR = 0x80;
        PMCR = btmp;
        asm ("nop");
        asm ("nop");

        CLKPR = 0x80;
        CLKPR = 0x00; // 0x00 - no division
    }
}

And before PMU lib do_sleep(), it calls this clock_switch function.

However I tested with or without this function for DPS0 and DPS1 mode and found it makes no impact on current consumption. But for ADC noise reduction mode, changing main clock saves power. (Use 32Mhz clock 1.1mA, switch to 32Khz clock 0.5mA). But I guess this also has impact on ADC speed, since ADC_clk derives from main clock from CMU. image

LaZsolt commented 1 year ago

Check these new power management library files: lgt8fx_lowpower These files are modeled after the Arduino sources. Usage:

#include <avr\sleep.h>
#include "lgt8fx_sleep.h"
#include <avr\power.h>
#include "lgt8fx_power.h"

I have edited this comment:

This files are not ready to use. There will be a lot of work to do with it. I uploaded it for demonstration purposes only.

LaZsolt commented 1 year ago

You can check what is powered on-chip before sleep with this program.

LaZsolt commented 1 year ago

I found minor error. Corrected. https://github.com/dbuezas/lgt8fx/issues/155#issuecomment-1427133482

sullivanzheng commented 1 year ago

very handy library.

发自我的iPhone

在 2023年2月13日,上午11:55,LaZsolt @.***> 写道:

 I found minor error. Corrected. #155 (comment)

— Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you were mentioned.

LaZsolt commented 1 year ago

Power management library (pmu.h) doesn't work after the first analogRead is made.

Solved.

After the analogRead(); The ADIF bit of ADCSRA is set. ADIF = ADC Interrupt Flag So when any sleep mode started, the interrupt flag is immediately break it.

Interesting. It raises further questions, why hasn't this caused problems for others?

LaZsolt commented 1 year ago

Can this issue report be closed?

sullivanzheng commented 11 months ago

I think this issue is very well discussed. I have used DPS2 mode and LPRC as my "default deep-sleep and reset" routine for quite a few sensor and IoT projects. Very handy.

jayzakk commented 11 months ago

Forgot to close