AVR sleep with external interrupts fixes (#813)
Fixing #812, #811, #809
This commit is contained in:
committed by
Olivier
parent
b132a8a81e
commit
3350d79fe6
+58
-54
@@ -37,24 +37,31 @@ volatile uint8_t _wakeUp1Interrupt =
|
|||||||
volatile uint8_t _wakeUp2Interrupt =
|
volatile uint8_t _wakeUp2Interrupt =
|
||||||
INVALID_INTERRUPT_NUM; // Interrupt number for wakeUp2-callback.
|
INVALID_INTERRUPT_NUM; // Interrupt number for wakeUp2-callback.
|
||||||
|
|
||||||
void wakeUp1() //place to send the interrupts
|
void wakeUp1()
|
||||||
{
|
{
|
||||||
|
// Disable sleep. When an interrupt occurs after attachInterrupt,
|
||||||
|
// but before sleeping the CPU would not wake up.
|
||||||
|
// Ref: http://playground.arduino.cc/Learning/ArduinoSleepCode
|
||||||
|
sleep_disable();
|
||||||
detachInterrupt(_wakeUp1Interrupt);
|
detachInterrupt(_wakeUp1Interrupt);
|
||||||
if (_wakeUp2Interrupt != INVALID_INTERRUPT_NUM) {
|
// First interrupt occurred will be reported only
|
||||||
detachInterrupt(_wakeUp2Interrupt);
|
if (INVALID_INTERRUPT_NUM == _wokeUpByInterrupt)
|
||||||
}
|
{
|
||||||
_wokeUpByInterrupt = _wakeUp1Interrupt;
|
_wokeUpByInterrupt = _wakeUp1Interrupt;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
void wakeUp2() //place to send the second interrupts
|
void wakeUp2()
|
||||||
{
|
{
|
||||||
|
sleep_disable();
|
||||||
detachInterrupt(_wakeUp2Interrupt);
|
detachInterrupt(_wakeUp2Interrupt);
|
||||||
if (_wakeUp1Interrupt != INVALID_INTERRUPT_NUM) {
|
// First interrupt occurred will be reported only
|
||||||
detachInterrupt(_wakeUp1Interrupt);
|
if (INVALID_INTERRUPT_NUM == _wokeUpByInterrupt)
|
||||||
}
|
{
|
||||||
_wokeUpByInterrupt = _wakeUp2Interrupt;
|
_wokeUpByInterrupt = _wakeUp2Interrupt;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool interruptWakeUp()
|
inline bool interruptWakeUp()
|
||||||
{
|
{
|
||||||
return _wokeUpByInterrupt != INVALID_INTERRUPT_NUM;
|
return _wokeUpByInterrupt != INVALID_INTERRUPT_NUM;
|
||||||
}
|
}
|
||||||
@@ -87,7 +94,8 @@ void hwPowerDown(period_t period)
|
|||||||
#endif
|
#endif
|
||||||
// Enable interrupts & sleep until WDT or ext. interrupt
|
// Enable interrupts & sleep until WDT or ext. interrupt
|
||||||
sei();
|
sei();
|
||||||
// Directly sleep CPU, to prevent race conditions! (see chapter 7.7 of ATMega328P datasheet)
|
// Directly sleep CPU, to prevent race conditions!
|
||||||
|
// Ref: chapter 7.7 of ATMega328P datasheet
|
||||||
sleep_cpu();
|
sleep_cpu();
|
||||||
sleep_disable();
|
sleep_disable();
|
||||||
// restore previous WDT settings
|
// restore previous WDT settings
|
||||||
@@ -108,46 +116,31 @@ void hwInternalSleep(unsigned long ms)
|
|||||||
#ifndef MY_DISABLED_SERIAL
|
#ifndef MY_DISABLED_SERIAL
|
||||||
MY_SERIALDEVICE.flush();
|
MY_SERIALDEVICE.flush();
|
||||||
#endif
|
#endif
|
||||||
while (!interruptWakeUp() && ms >= 8000) {
|
|
||||||
hwPowerDown(SLEEP_8S);
|
static const struct {
|
||||||
ms -= 8000;
|
uint16_t ms;
|
||||||
}
|
period_t period;
|
||||||
if (!interruptWakeUp() && ms >= 4000) {
|
} wdtTimes[] = {
|
||||||
hwPowerDown(SLEEP_4S);
|
{ 8000u, SLEEP_8S },
|
||||||
ms -= 4000;
|
{ 4000u, SLEEP_4S },
|
||||||
}
|
{ 2000u, SLEEP_2S },
|
||||||
if (!interruptWakeUp() && ms >= 2000) {
|
{ 1000u, SLEEP_1S },
|
||||||
hwPowerDown(SLEEP_2S);
|
{ 500u, SLEEP_500MS },
|
||||||
ms -= 2000;
|
{ 250u, SLEEP_250MS },
|
||||||
}
|
{ 120u, SLEEP_120MS },
|
||||||
if (!interruptWakeUp() && ms >= 1000) {
|
{ 60u, SLEEP_60MS },
|
||||||
hwPowerDown(SLEEP_1S);
|
{ 30u, SLEEP_30MS },
|
||||||
ms -= 1000;
|
{ 15u, SLEEP_15MS },
|
||||||
}
|
};
|
||||||
if (!interruptWakeUp() && ms >= 500) {
|
|
||||||
hwPowerDown(SLEEP_500MS);
|
for (size_t i = 0; i < sizeof(wdtTimes)/sizeof(wdtTimes[0]); ++i)
|
||||||
ms -= 500;
|
{
|
||||||
}
|
while (ms >= wdtTimes[i].ms && !interruptWakeUp() )
|
||||||
if (!interruptWakeUp() && ms >= 250) {
|
{
|
||||||
hwPowerDown(SLEEP_250MS);
|
hwPowerDown(wdtTimes[i].period);
|
||||||
ms -= 250;
|
ms -= wdtTimes[i].ms;
|
||||||
}
|
}
|
||||||
if (!interruptWakeUp() && ms >= 125) {
|
}
|
||||||
hwPowerDown(SLEEP_120MS);
|
|
||||||
ms -= 120;
|
|
||||||
}
|
|
||||||
if (!interruptWakeUp() && ms >= 64) {
|
|
||||||
hwPowerDown(SLEEP_60MS);
|
|
||||||
ms -= 60;
|
|
||||||
}
|
|
||||||
if (!interruptWakeUp() && ms >= 32) {
|
|
||||||
hwPowerDown(SLEEP_30MS);
|
|
||||||
ms -= 30;
|
|
||||||
}
|
|
||||||
if (!interruptWakeUp() && ms >= 16) {
|
|
||||||
hwPowerDown(SLEEP_15MS);
|
|
||||||
ms -= 15;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int8_t hwSleep(unsigned long ms)
|
int8_t hwSleep(unsigned long ms)
|
||||||
@@ -164,16 +157,26 @@ int8_t hwSleep(uint8_t interrupt, uint8_t mode, unsigned long ms)
|
|||||||
int8_t hwSleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode2,
|
int8_t hwSleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode2,
|
||||||
unsigned long ms)
|
unsigned long ms)
|
||||||
{
|
{
|
||||||
|
// ATMega328P supports following modes to wake from sleep: LOW, CHANGE, RISING, FALLING
|
||||||
|
// Datasheet states only LOW can be used with INT0/1 to wake from sleep, which is incorrect.
|
||||||
|
// Ref: http://gammon.com.au/interrupts
|
||||||
|
|
||||||
// Disable interrupts until going to sleep, otherwise interrupts occurring between attachInterrupt()
|
// Disable interrupts until going to sleep, otherwise interrupts occurring between attachInterrupt()
|
||||||
// and sleep might cause the ATMega to not wakeup from sleep as interrupt has already be handled!
|
// and sleep might cause the ATMega to not wakeup from sleep as interrupt has already be handled!
|
||||||
cli();
|
cli();
|
||||||
// attach interrupts
|
// attach interrupts
|
||||||
_wakeUp1Interrupt = interrupt1;
|
_wakeUp1Interrupt = interrupt1;
|
||||||
_wakeUp2Interrupt = interrupt2;
|
_wakeUp2Interrupt = interrupt2;
|
||||||
if (interrupt1 != INVALID_INTERRUPT_NUM) {
|
|
||||||
|
// Attach external interrupt handlers, and clear any pending interrupt flag
|
||||||
|
// to prevent waking immediately again.
|
||||||
|
// Ref: https://forum.arduino.cc/index.php?topic=59217.0
|
||||||
|
if (interrupt1 != INVALID_INTERRUPT_NUM) {
|
||||||
|
EIFR = _BV(INTF0);
|
||||||
attachInterrupt(interrupt1, wakeUp1, mode1);
|
attachInterrupt(interrupt1, wakeUp1, mode1);
|
||||||
}
|
}
|
||||||
if (interrupt2 != INVALID_INTERRUPT_NUM) {
|
if (interrupt2 != INVALID_INTERRUPT_NUM) {
|
||||||
|
EIFR = _BV(INTF1);
|
||||||
attachInterrupt(interrupt2, wakeUp2, mode2);
|
attachInterrupt(interrupt2, wakeUp2, mode2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,7 +197,8 @@ int8_t hwSleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mo
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Return what woke the mcu.
|
// Return what woke the mcu.
|
||||||
int8_t ret = MY_WAKE_UP_BY_TIMER; // default: no interrupt triggered, timer wake up
|
// Default: no interrupt triggered, timer wake up
|
||||||
|
int8_t ret = MY_WAKE_UP_BY_TIMER;
|
||||||
if (interruptWakeUp()) {
|
if (interruptWakeUp()) {
|
||||||
ret = static_cast<int8_t>(_wokeUpByInterrupt);
|
ret = static_cast<int8_t>(_wokeUpByInterrupt);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user