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
557 stars 145 forks source link

is the sleep library working correctly for megaTinyCore? #297

Closed microPaul closed 3 years ago

microPaul commented 3 years ago

I wonder if I'm working beyond my headlights. I see in the writeup of the megaTinyCore a statement that reads "This functionality will be made easier to use via the megaTinySleep library in a future version of the core."

I've done a sleep implementation with RTC wakeup on the ATtiny412 using megaTinyCore that seems to work well. But I'm having problems on a ATtiny3216 using a pin change interrupt to wake the processor, as in I can't get it to wake up. I'm using V2.2.3 of the megaTinyCore. Below is some minimal code to show the problem. I have an LED on A7 (illuminated when port output is high) and a momentary button switch to ground on B4 (closing the switch forces B4 low). When I comment out the sleep_cpu() call, pressing the button does cause an interrupt, but when the CPU is sleeping I don't seem to get an interrupt or the processor doesn't wake. I've stared at this test program for a long time but I still can't see what the problem is, so I'm now wondering if it might be a library problem. Any suggestions?

` /*

include <avr/sleep.h>

volatile uint8_t interruptSemaphore = 0;

void setup() { Serial.begin(57600, SERIAL_8N1); Serial.printf("\n\nSleepTest 210115-01\n\n"); delay(500);

// set up sleep mode //SLPCTRL.CTRLA = SLPCTRL_SMODE_IDLE_gc; // set sleep mode to "idle" //SLPCTRL.CTRLA = SLPCTRL_SMODE_STDBY_gc; // set sleep mode to "standby"
SLPCTRL.CTRLA = SLPCTRL_SMODE_PDOWN_gc; // set sleep mode to "power down" SLPCTRL.CTRLA |= SLPCTRL_SEN_bm; // enable sleep mode

// set up LED PORTA.OUTCLR = PIN7_bm; // LED off PORTA.DIRSET = PIN7_bm; // set DDR bit for LED output

// set up switch input PORTB.DIRCLR = PIN4_bm; // B4 is input PORTB.PIN4CTRL = PORT_ISC_FALLING_gc // interrupt on falling edge, input active | PORT_PULLUPEN_bm; // enable pullupinput w/pullup, no interrupts

sei(); // enable interrupts, if not already enabled

}

void loop() { uint32_t timeReference0 = 0; uint32_t timeReference1 = 0; while(1) { if (millis() - timeReference0 > 500) { // come here every 500 ms timeReference0 = millis(); // re-init the time reference ledToggle(); // toggle the LED } if (millis() - timeReference1 > 10*1000) { // come here every 10 seconds Serial.printf("\n\nTime to Sleep!\n"); // send message to console delay(500); // allow time for message to leave USART sleep_cpu(); // processor put to sleep // code execution starts here upon pin change interrupt to wake up processor timeReference1 = millis(); // re-init the time reference
} if ( (PORTB.IN & PIN4_bm) == 0) { // if the switch is closed, print the message below //Serial.printf("\n\nSwitch!\n"); } if (interruptSemaphore == 1) { // if the interrupt semaphore is set then execute code below interruptSemaphore = 0; // clear the semaphore Serial.printf("\n\nInterrupt!\n"); // send message to console } }

}

void ledOn(void) { PORTA.OUTSET = PIN7_bm; // LED on
}

void ledOff(void) { PORTA.OUTCLR = PIN7_bm; // LED off }

void ledToggle(void) { PORTA.OUTTGL = PIN7_bm; // LED toggle }

ISR(PORTB_PORT_vect) { //don't need to do anything. the interrupt itself will wake the processor PORTB.INTFLAGS = 0xff; // doesn't matter which pin caused int, clear them all interruptSemaphore = 1; }

`

SpenceKonde commented 3 years ago

I have done no work in relation to power saving modes on the modern AVRs yet. Do note the text of that issue title "add megaTinySleep library" [it doesn't yet exist] "understand and document power saving modes" [that is to say, not only is there very little documentation on this in the context of the core, I am inexperienced and kinda clueless - I never got excited about power saving, everything I build remains plugged in, or the stuff it's controlling is eating so much power that the microcontroller current is a rounding error... so it's sort of the last thing I try to sort out, you know? Right now I've got a client complaining about the gerbers I exported, latest board manager release of megaTinyCore has the headline 2.2.x feature broken, same package build issue is also blocking DxCore 1.3.0 which is also nearly ready to go, after the big scary problem on that one got sorted out by this morning's azduino1 avr-gcc package... )

On Fri, Jan 15, 2021 at 9:56 AM microPaul notifications@github.com wrote:

I wonder if I'm working beyond my headlights. I see in the writeup of the megaTinyCore a statement that reads "This functionality will be made easier to use via the megaTinySleep library in a future version of the core."

I've done a sleep implementation with RTC wakeup on the ATtiny412 using megaTinyCore that seems to work well. But I'm having problems on a ATtiny3216 using a pin change interrupt to wake the processor, as in I can't get it to wake up. I'm using V2.2.3 of the megaTinyCore. Below is some minimal code to show the problem. I have an LED on A7 (illuminated when port output is high) and a momentary button switch to ground on B4 (closing the switch forces B4 low). When I comment out the sleep_cpu() call, pressing the button does cause an interrupt, but when the CPU is sleeping I don't seem to get an interrupt or the processor doesn't wake. I've stared at this test program for a long time but I still can't see what the problem is, so I'm now wondering if it might be a library problem. Any suggestions?

` /*

  • Program to test "sleep" with pin change interrupt to wake processor from "sleep" state.
  • When sleep_cpu() command on/about line 61 is commened out, the LED on A7
  • will blink once per second and closing the switch contact (from B4 to
  • ground) will cause an interrupt as shown by USART diagnostic message on/about
  • line 72. So interrupts are working.
  • When sleep_cpu() command on/about line 61 not commented out (allowed to be active)
  • the LED and Swtich all function as before, for the first 10 seconds of run time. At
  • 10 seconds of run time the sleep_cpu() function is called and the LED blinking will stop
  • (with LED active). Pressing the switch should cause an interrupt which would wake
  • the processor, but the interrupt does not seem to occur and the processor remains
  • in apparent sleep mode. */

include <avr/sleep.h>

volatile uint8_t interruptSemaphore = 0;

void setup() { Serial.begin(57600, SERIAL_8N1); Serial.printf("\n\nSleepTest 210115-01\n\n"); delay(500);

// set up sleep mode //SLPCTRL.CTRLA = SLPCTRL_SMODE_IDLE_gc; // set sleep mode to "idle" //SLPCTRL.CTRLA = SLPCTRL_SMODE_STDBY_gc; // set sleep mode to "standby" SLPCTRL.CTRLA = SLPCTRL_SMODE_PDOWN_gc; // set sleep mode to "power down" SLPCTRL.CTRLA |= SLPCTRL_SEN_bm; // enable sleep mode

// set up LED PORTA.OUTCLR = PIN7_bm; // LED off PORTA.DIRSET = PIN7_bm; // set DDR bit for LED output

// set up switch input PORTB.DIRCLR = PIN4_bm; // B4 is input PORTB.PIN4CTRL = PORT_ISC_FALLING_gc // interrupt on falling edge, input active | PORT_PULLUPEN_bm; // enable pullupinput w/pullup, no interrupts

sei(); // enable interrupts, if not already enabled

}

void loop() { uint32_t timeReference0 = 0; uint32_t timeReference1 = 0; while(1) { if (millis() - timeReference0 > 500) { // come here every 500 ms timeReference0 = millis(); // re-init the time reference ledToggle(); // toggle the LED } if (millis() - timeReference1 > 10*1000) { // come here every 10 seconds Serial.printf("\n\nTime to Sleep!\n"); // send message to console delay(500); // allow time for message to leave USART sleep_cpu(); // processor put to sleep // code execution starts here upon pin change interrupt to wake up processor timeReference1 = millis(); // re-init the time reference } if ( (PORTB.IN & PIN4_bm) == 0) { // if the switch is closed, print the message below //Serial.printf("\n\nSwitch!\n"); } if (interruptSemaphore == 1) { // if the interrupt semaphore is set then execute code below interruptSemaphore = 0; // clear the semaphore Serial.printf("\n\nInterrupt!\n"); // send message to console } }

}

void ledOn(void) { PORTA.OUTSET = PIN7_bm; // LED on }

void ledOff(void) { PORTA.OUTCLR = PIN7_bm; // LED off }

void ledToggle(void) { PORTA.OUTTGL = PIN7_bm; // LED toggle }

ISR(PORTB_PORT_vect) { //don't need to do anything. the interrupt itself will wake the processor PORTB.INTFLAGS = 0xff; // doesn't matter which pin caused int, clear them all interruptSemaphore = 1; }

`

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/SpenceKonde/megaTinyCore/issues/297, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABTXEW6O34FMVEHYH4Z22ULS2BJTFANCNFSM4WEFMSUA .

--


Spence Konde Azzy’S Electronics

New products! Check them out at tindie.com/stores/DrAzzy GitHub: github.com/SpenceKonde ATTinyCore https://github.com/SpenceKonde/ATTinyCore: Arduino support for all pre-2016 tinyAVR with >2k flash! megaTinyCore https://github.com/SpenceKonde/megaTinyCore: Arduino support for all post-2016 tinyAVR parts! DxCore https://github.com/SpenceKonde/DxCore: Arduino support for the AVR Dx-series parts, the latest and greatest from Microchip! Contact: spencekonde@gmail.com

SpenceKonde commented 3 years ago

But wait, waaaaait!

You're trying to use a FALLING interrupt to wake from a sleep mode (standby or powerdown) where the peripheral clock is stopped but you're not using a fully asynchronous pin! You need to either use a fully async pin (pins 2 or 6 in each port) which can detect FALLING and RISING without the clock, or use the CHANGE or LEVEL interrupt. https://github.com/SpenceKonde/megaTinyCore/blob/master/megaavr/extras/PinInterrupts.md

On Fri, Jan 15, 2021 at 9:56 AM microPaul notifications@github.com wrote:

I wonder if I'm working beyond my headlights. I see in the writeup of the megaTinyCore a statement that reads "This functionality will be made easier to use via the megaTinySleep library in a future version of the core."

I've done a sleep implementation with RTC wakeup on the ATtiny412 using megaTinyCore that seems to work well. But I'm having problems on a ATtiny3216 using a pin change interrupt to wake the processor, as in I can't get it to wake up. I'm using V2.2.3 of the megaTinyCore. Below is some minimal code to show the problem. I have an LED on A7 (illuminated when port output is high) and a momentary button switch to ground on B4 (closing the switch forces B4 low). When I comment out the sleep_cpu() call, pressing the button does cause an interrupt, but when the CPU is sleeping I don't seem to get an interrupt or the processor doesn't wake. I've stared at this test program for a long time but I still can't see what the problem is, so I'm now wondering if it might be a library problem. Any suggestions?

` /*

  • Program to test "sleep" with pin change interrupt to wake processor from "sleep" state.
  • When sleep_cpu() command on/about line 61 is commened out, the LED on A7
  • will blink once per second and closing the switch contact (from B4 to
  • ground) will cause an interrupt as shown by USART diagnostic message on/about
  • line 72. So interrupts are working.
  • When sleep_cpu() command on/about line 61 not commented out (allowed to be active)
  • the LED and Swtich all function as before, for the first 10 seconds of run time. At
  • 10 seconds of run time the sleep_cpu() function is called and the LED blinking will stop
  • (with LED active). Pressing the switch should cause an interrupt which would wake
  • the processor, but the interrupt does not seem to occur and the processor remains
  • in apparent sleep mode. */

include <avr/sleep.h>

volatile uint8_t interruptSemaphore = 0;

void setup() { Serial.begin(57600, SERIAL_8N1); Serial.printf("\n\nSleepTest 210115-01\n\n"); delay(500);

// set up sleep mode //SLPCTRL.CTRLA = SLPCTRL_SMODE_IDLE_gc; // set sleep mode to "idle" //SLPCTRL.CTRLA = SLPCTRL_SMODE_STDBY_gc; // set sleep mode to "standby" SLPCTRL.CTRLA = SLPCTRL_SMODE_PDOWN_gc; // set sleep mode to "power down" SLPCTRL.CTRLA |= SLPCTRL_SEN_bm; // enable sleep mode

// set up LED PORTA.OUTCLR = PIN7_bm; // LED off PORTA.DIRSET = PIN7_bm; // set DDR bit for LED output

// set up switch input PORTB.DIRCLR = PIN4_bm; // B4 is input PORTB.PIN4CTRL = PORT_ISC_FALLING_gc // interrupt on falling edge, input active | PORT_PULLUPEN_bm; // enable pullupinput w/pullup, no interrupts

sei(); // enable interrupts, if not already enabled

}

void loop() { uint32_t timeReference0 = 0; uint32_t timeReference1 = 0; while(1) { if (millis() - timeReference0 > 500) { // come here every 500 ms timeReference0 = millis(); // re-init the time reference ledToggle(); // toggle the LED } if (millis() - timeReference1 > 10*1000) { // come here every 10 seconds Serial.printf("\n\nTime to Sleep!\n"); // send message to console delay(500); // allow time for message to leave USART sleep_cpu(); // processor put to sleep // code execution starts here upon pin change interrupt to wake up processor timeReference1 = millis(); // re-init the time reference } if ( (PORTB.IN & PIN4_bm) == 0) { // if the switch is closed, print the message below //Serial.printf("\n\nSwitch!\n"); } if (interruptSemaphore == 1) { // if the interrupt semaphore is set then execute code below interruptSemaphore = 0; // clear the semaphore Serial.printf("\n\nInterrupt!\n"); // send message to console } }

}

void ledOn(void) { PORTA.OUTSET = PIN7_bm; // LED on }

void ledOff(void) { PORTA.OUTCLR = PIN7_bm; // LED off }

void ledToggle(void) { PORTA.OUTTGL = PIN7_bm; // LED toggle }

ISR(PORTB_PORT_vect) { //don't need to do anything. the interrupt itself will wake the processor PORTB.INTFLAGS = 0xff; // doesn't matter which pin caused int, clear them all interruptSemaphore = 1; }

`

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/SpenceKonde/megaTinyCore/issues/297, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABTXEW6O34FMVEHYH4Z22ULS2BJTFANCNFSM4WEFMSUA .

--


Spence Konde Azzy’S Electronics

New products! Check them out at tindie.com/stores/DrAzzy GitHub: github.com/SpenceKonde ATTinyCore https://github.com/SpenceKonde/ATTinyCore: Arduino support for all pre-2016 tinyAVR with >2k flash! megaTinyCore https://github.com/SpenceKonde/megaTinyCore: Arduino support for all post-2016 tinyAVR parts! DxCore https://github.com/SpenceKonde/DxCore: Arduino support for the AVR Dx-series parts, the latest and greatest from Microchip! Contact: spencekonde@gmail.com

microPaul commented 3 years ago

Ok, thank you! These are good things for me to check out., to get me going again.

And I understand about having different priorities and that sleep is not a big issue for most people. Sleep and low power on AVR is something I have totally ignored for about the last 4 years of my use of AVR. But I now have a project that needs to be no-power when idle. So I'm trying to learn about sleep mode. I did have good luck on the ATtiny412 with your core, using the PIT for wakeup.

Thanks for the advice re asynch inputs. I had totally overlooked that. I appreciate the info.

microPaul commented 3 years ago

It now works! Thank you Spence. I would have struggled with this for days, or longer, but your pointing out that I needed to go with a level interrupt to get it out of sleep (and not an edge interrupt) put me back on the right track.

I've changed my port pin interrupt to be level sensitive, rather than edge, and with that and other associated changes in my code, I can get it to enter sleep mode and then cause an interrupt that will bring it back to life. I haven't checked current consumption yet in sleep state for my current project, but for my ATtiny412 project using the PIT for wakeup, the processor was drawing only 0.6 microamps from a 3V source in the Sleep PowerDown mode, so no on-off switch needed.

Thank you again.

SpenceKonde commented 3 years ago

Don't underestimate the importance of whether your interrupt pin is fully async or not. I remember once getting into a situation where the fully async-ness caused an issue... I think it was that I turned on the pullup and enabled interrupt sense in a single write, and it worked with the partially async pins, because it had to be longer than 1 CLK_PER, but would trigger the fully async one?) it's a double-edged sword

microPaul commented 3 years ago

ok, good advice. thank you.