-
Notifications
You must be signed in to change notification settings - Fork 148
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add Sleep-with-millis and power-saving library, understand & document power-saving modes #158
Comments
Interesting observation in #264 -
Should be investigated here... |
Using 2.1.5, SLEEP_MODE_PWR_DOWN seems to be actually SLEEP_MODE_STANDBY from a power point of view. EDIT : |
Thank you for your great work! |
Do you have an issue with the PORT interrupt not working when you are using the PIT interrupt? Here is an example where I used both: https://github.com/freemovers/teddy_bear/blob/main/main.c
I can provide some Arduino examples later today as well if needed.
… On Feb 7, 2021, at 1:23 PM, ArnieO ***@***.***> wrote:
Thank you for your great work!
While we're all eagerly waiting for the megaTinySleep library, I am working on a board design using ATtiny1616, where I want the controller to periodically wake up from sleep, do some measurements etc - and go back to sleep.
But I also want to enable external wakeup if a relay input is triggered.
I have found out how to do those things separately, but have not gotten my head around how to get the ATtiny161 to do both. I think what is confusing me is the ISR that seems to need different input parameters for each case, and I have not seen a way to set up two ISRs in parallel.
Any hint on how this might be done?
I could of course add an external RTC chip, but with such a powerful microcontroller with a RTC sitting there ...
Any guidance would be highly appreciated!
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub, or unsubscribe.
|
@ArnieO - For now I think @freemovers is the guy you want to be asking about this; I have yet to have time to put one of these into sleep mode! I do think that this sounds like it just calls for a linear combination of your two pieces of code...? |
@freemovers : Thank you for your rapid response! My confusion: So it could well be that my issue is just lack of competence on the coding of this controller family:
@SpenceKonde : I thought so too until I started looking at code examples. @freemovers will surely be able to provide guidance! |
attachInterrupt() is an ABOMINATION Under the hood, all it does is store function pointers - then it's got a row of `ISR(PORTA_PORT_vect)) - well, they use a macro to generate the muiltiple near identical interrupts - but that's basically what it is- a bunch of ISR's that have a loop that runs 8 times, checking each bit of the int flags inturnm and if that is set and there is a function pointer corresponding to it, call that fnction. You use ONE attachInterrupt. On ONE port - bam. Every single pin interrupt is now claimed by WInterrupt.c and you'll get a multiple definition error if you try to define your own ISR the normal way (with ISR(PERIPHERAL_INTNAME_vect) ...). Each chip has limited number of interrupts. Somewhere I think in extras there's a .md file with a list of all the vector names used on 0/1-series I've actually been thinking about whether there is a way that I could make it only generate the ISR for ports that get an interrupt function attached to them.... that would at least take it from apocalyptically bad to only moderately bad. The pin would have to be known at compiletime, but you generally aren't deciding what pin is connected to the interrupt source at runtime... |
Oh, and no priority, generally speaking, first come first served - interrupts can't interrupt other interrrupts except that one interrupts can be marked as high priority, if it is, that can interrupt. In a situation where there are multiple interrupts simultaneously wanting to fire(generally, interrupts were disabled globally, or it was in an interrupt, it is done, interrupts reenabled and all these pending interrupts want to fire - this goes in a fixed numerical priorirty, but you can set a bit to make it do round-robin (IMO if you're considering that, either, you are paranoid and don't need it , or your code is ending up in a horrible position because the interrupts run too slowly and fire too often and like, maybe that's the thing to be fixing?). |
😆 Thank you for this great "ATtiny interrupts for dummies" writeup, @SpenceKonde - very helpful and very much appreciated! And I promise not to attempt using attachInterrupt() in my ATtiny project. 😉 |
Here is some basic code that uses the Periodic Interrupt Timer (PIT) and a PORT interrupt. The timer is triggered every 4 seconds to turn off the LED while the PORT interrupt is used to turn on the LED. PIT and PORT peripherals both work in Power Down sleep mode. #include <avr/sleep.h>
void RTC_init(void)
{
RTC.CLKSEL = RTC_CLKSEL_INT1K_gc; // 1kHz Internal Crystal Oscillator (INT1K)
while (RTC.STATUS > 0 || RTC.PITSTATUS); // Wait for all register to be synchronized
RTC.PITINTCTRL = RTC_PI_bm; // Periodic Interrupt: enabled
RTC.PITCTRLA = RTC_PERIOD_CYC4096_gc // 1024 / 4096 = 1/4Hz, 4 sec
| RTC_PITEN_bm; // Enable: enabled PIT
}
// Wake up routines
ISR(RTC_PIT_vect)
{
RTC.PITINTFLAGS = RTC_PI_bm; // Clear flag
digitalWrite(2, LOW);
}
ISR(PORTA_PORT_vect)
{
PORTA.INTFLAGS = PIN2_bm; // clear interrupt flag
digitalWrite(2, HIGH);
}
void setup() {
RTC_init();
pinMode(2, OUTPUT);
PORTA.PIN2CTRL = PORT_PULLUPEN_bm | PORT_ISC_FALLING_gc; // digital pin #5 on 412
set_sleep_mode(SLEEP_MODE_PWR_DOWN); // Set sleep mode to POWER DOWN mode
sleep_enable();
interrupts();
}
void loop() {
// put your main code here, to run repeatedly:
sleep_cpu();
} |
Thanks a lot @freemovers, this is very helpful! |
Your example here above use Do you see a way out; can timed sleep be done with an other counter? |
I have not used the RTC for millis() before (the option in the Arduino Menu), but I just use the interrupt above. You can still use the default timer for millis() and micros(). The RTC is enabled in the example above as follows (not from the Arduino menu): RTC.PITCTRLA = RTC_PERIOD_CYC4096_gc // 1024 / 4096 = 1/4Hz, 4 sec
| RTC_PITEN_bm; // Enable: enabled PIT Keep in mind that you cannot use the power down sleep mode since that will turn off the timer (TCA or TCD) that controls the millis() and micros(). These timers will only work in Idle Sleep mode: set_sleep_mode(SLEEP_MODE_IDLE); I'm not sure how often you change the neopixel, but you could update the neopixel while the MCU is running, and put it back in sleep mode after the neopixel is updated. |
They work in standby sleep mode too if you set runstandby. Standby gets
down t almost powerdown levels of powerconsumption and is the "good" sleep
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: spencekonde@gmail.com
On Mon, Feb 22, 2021, 12:02 Sander van de Bor ***@***.***> wrote:
I have not used the RTC for millis() before (the option in the Arduino
Menu), but I just use the interrupt above. You can still use the default
timer for millis() and micros(). The RTC is enabled in the example above as
follows (not from the Arduino menu):
RTC.PITCTRLA = RTC_PERIOD_CYC4096_gc // 1024 / 4096 = 1/4Hz, 4 sec
| RTC_PITEN_bm; // Enable: enabled PIT
Keep in mind that you cannot use the *power down* sleep mode since that
will turn off the timer (TCA or TCD) that controls the millis() and
micros(). These timers will only work in *Idle* Sleep mode:
set_sleep_mode(SLEEP_MODE_IDLE);
I'm not sure how often you change the neopixel, but you could update the
neopixel while the MCU is running, and put it back in sleep mode after the
neopixel is updated.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#158 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/ABTXEWYP22DFWTFYBBESUUDTAKE23ANCNFSM4L3C6ANQ>
.
|
Thank you both for pointing me in the right direction. And indeed - I got it working now! |
@SpenceKonde, most peripherals can run in standby mode, but looks like the TCA and TCD are limited to idle sleep: |
Ooo, I see what you meant. Right. |
An other point: It looks like millis() stops running while in EDIT EDIT 2 |
Oh, I see, to ensure that it doesn;t get called so frequently the pixels never latch.... |
I had even checked for it in the DISABLE_MILLIS case! But not RTC millis case. Might have predated RTC millis |
TinyNeoPixel dependency on micros() millis() not running during
Indeed, it seems like TCB should be used if needing to have a counter running during Standby. EDIT |
Yeah, non-micros-dependent version is checked in now - drop-in replacement.
If you want it. I'd recommend grabbing it from the repo, not planning to do
any releases for a while now because I gotta get some stuff sorted out on
ATTinyCore.
I'm not sure I follow your question? Why do you want an oscillator running,
unless it's being used by something? And if it is being requested by
something that runs in standby sleep, then it wouldn't turn off...
What do you mean by "it will no longer work if the code is recompiled with
a different clock source setting"? which setting are you referring to?
…On Tue, Feb 23, 2021 at 4:09 AM ArnieO ***@***.***> wrote:
*TinyNeoPixel dependency on micros()*
Great @SpenceKonde <https://github.com/SpenceKonde> , thank you for
looking into that!
Neopixel is a great solution for implementing a multi-colour signaling LED
using only one GPIO (the case in my current project). A library
implementation (with some minor limitations) that is not dependent on
micros() will be a nice improvement.
*millis() not running during SLEEP_MODE_STANDBY*
Only the TCB you should be able to run in Standby, but you have to enable
that option:
[image: image]
<https://user-images.githubusercontent.com/10295178/108818073-24b25a80-75b9-11eb-89dc-aaeeeb12ac6f.png>
OK, but this seems to be more generic than only TCB, running the clock
source during Standby can be enabled for all clock sources:
[image: image]
<https://user-images.githubusercontent.com/10295178/108818448-b0c48200-75b9-11eb-9cbc-ecbc63ac1eb3.png>
@SpenceKonde <https://github.com/SpenceKonde> : Do you see a generic way
to request the selected clock source to keep running during sleep, like a
function or macro that could be called right before sleep_cpu()?
If this is done in the code *"by writing a '1' to the Run Standby bit
(RUNSTDBY) in the respective oscillator's Control A register"*, it will
no longer work if the code is recompiled with a different clock source
setting.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#158 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/ABTXEW44XP7HYQ6MX6VLUG3TANWFRANCNFSM4L3C6ANQ>
.
--
____________
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
|
For the moment I am only trying to use it for verifying the sleep duration, while learning how to master these devices. So not a blocking point for my ongoing project.
If the port manipulation I attempted in my previous post had worked, it would no longer have worked if I rebuilt the code while in the Arduino IDE submenu selecting another clock source, for instance TCB0, because the port manipulation is done on TCB1 in the code. |
Okay, now I see the context. Yes, you most certainly do have to configure the same timer/counter. Keeping the oscillator running would be beside the point though - if you had the oscillator running, but you hadn't set the currently selected timer to run in standby, doing that wouldn't make the the timer (which wasn't set to run in standby) run in standby. Huh, I'm surprised that that didn't do something! What I would have expected is that it when you did that, it would sleep until the next millis overflow, and then the TCB overflow interrupt would fire and you would no longer be asleep.... You can't reasonably use the high speed timers we normally use for microsecond resolution timing while also sleeping - the overflows come often enough that you're waking up constantly. you can get like... 6.4 ms between interrupts with 20 MHz system clock while sleeping. And they're power hogs! I would be inclined to set up a second one to time it with - as in. have it drop a pin (with digitalWriteFast or VPORT write - those are both single cycle, vs what, 50 clocks for digitalWrite? It may even be more than that. digitalWrite is godawful slow for what it's doing - it's all to permit people to specify pins by Arduino pin numbers at runtime where the pins are mapped arbitrarily onto actual ports/bits). On the other AVR you could use pulseIn() if you're lazy and don't need extreme accuracy, or use a TCB for input capture if one of those isn't true. If you used an AVR DB-series part, or AVR DA-series or tinyAVR 2-series, you could put a pair of TCBs into CASCADE mode to do 32-bit input capture, and measure something 3 minutes long with a granularity of... 24ths of a microsecond? I'd probably clock the Dx at 16 MHz or 32 MHz (they work fine at 32 at room temp; I suspect they wouldn't at 85C) usingthe TCA prescaler prescaling /16 as it's clock source, that way I'd get 1 us granularity in a measurement of an event... oh... 71 minutes long (2^32 * (PRESCALE / F_CPU) / 1000 us/ms / 1000 ms/s / 60min/s = 71 minutes and change. Sometimes 16-bit input capture just doesn't feel like enough. But 32-bit always feels like way more than necessary. terminology: "register" manipulation, not "port" manipulation ("port manipulation" specifically refers to doing that to the PORT registers, which control the I/O pins, generally to get faster digitalRead/etc, occasionally to save flash (I did that once because the hundred-some-odd bytes of flash you get when you get rid of the last instances of each of the three digital I/O functions, and the last instance of any of them (for the tables they use), well, we had code that needed to go there...). Here, on DxCore |
Apparently I have misunderstood something, as I don't understand why Below is my (pseudo) code. I have three ISRs, each sets a flag that is acted upon in loop().
But timer value written to OLED display is 13 or 14 ms also with the TCB1 register manipulation at the end of setup(). I expected that line to cause TCB1 to run during the sleep period, so that 4014 ms was measured.
|
Deferred to 2.4.0. Sorry, blame microchip for releasing new parts. |
Sorry again, 2.5.0. Need to get work on other cores done and 2.4.0 has taken almost a month longer than expected :-( |
Thank you @SpenceKonde for all your great work and sharing of your know-how. To @freemovers, great sketch above which worked right away for me, too. Thanks for sharing. I am using Core 2.6.10 with a Nordic PPK2 at 3V0 powering an ATtiny402-SSN at 1MHz internal, no BOD, no WDT, with RTC on, with just 2x 100nF capacitors and an actice-low LED on PA2, trigger on PA6. Alas, to really come down to 600nA during sleep, I had to follow best practice advice and add input disables (edit: sorry, saw this link only after posting, maybe the image below can be seen as a visualization now...):
I find these measurements meet what's stated in the data sheet (table 33.4) and more than good enough for my intended app which polls and de-bounces some reed switch input and sends data only upon Serial request. And when Serial is on, which will be most of the time, there's plenty of power from the requesting line. A small battery will be backup only. |
This library will provide the following functionality:
This will also coincide with testing to better understand sleep modes on the tinyAVR 0-series and 1-series parts.
The text was updated successfully, but these errors were encountered: