LowPower 库:断电(睡眠)ATMega32u4 工作但不会发生唤醒
LowPower library: Powerdown (sleep) an ATMega32u4 works but wakeup doesn't happen
情况:
我 made/developing 这个控制器(有很多功能)使用 Pro Micro (ATMega32u4) 并希望在触摸它时使用唤醒功能扩展它(使用触摸传感器)。这一切都是为了在不使用时节省一些能源。所以当设备被拾取时up/touched,设备就被唤醒了。没有联系的时候,过一会就会休眠。触摸传感器的触及距离将通过外壳周围的一小条铜带延长。
设备图片:
我用触摸感应器的pin 9和IR-sensor的pin 16来唤醒,但是没有用。两种设备功能都正常,我可以读取触摸状态(低=未触摸或高=触摸)并且我可以接收红外命令,因为它已经完全实现。当接收到信号时,这些引脚会在一段时间内保持高电平。
软件问题:
我的代码有问题吗(参数错误?)?执行此代码时设备进入睡眠状态(但从未醒来):
#include <avr/sleep.h> // To enter sleep mode, save power
#include <LowPower.h> // To enter sleep mode, save power
......
#define TEP_IR_RECV_PIN 16
#define TEP_PIN_TOUCHSENSOR 9
......
......
void sleep()
{
attachInterrupt( TEP_PIN_TOUCHSENSOR, wakeup, HIGH );
attachInterrupt( TEP_IR_RECV_PIN, wakeup, HIGH );
// Enter power down state with ADC and BOD module disabled.
// Wake up when wake up pin is high.
LowPower.powerDown( SLEEP_FOREVER, ADC_OFF, BOD_OFF );
// Disable external pin interrupt on wake up pin.
detachInterrupt( TEP_PIN_TOUCHSENSOR );
detachInterrupt( TEP_IR_RECV_PIN );
}
我的EnjoyPad的休眠功能class(按下遥控器上的电源按钮或当没有触摸或做任何事情达到超时时会触发):
void TEnjoyPad::setSleepMode()
{
// Notify user device entering sleep mode, beep twice
setBeep( 200 );
setBeep( 200, 100, false );
// To be sure: Trigger end / unhandled down events and reset states
reset(init);
// Handle sleep event
eventSleep();
// Stop all peripherals
end();
// Finally go to sleep, code stops here
TEP_LIB_FUNCS::sleep();
// Device woke up
setBeep( 500, 100, false );
// Restart all peripherals
begin();
// Handle wake up event
eventAwake();
}
有什么想法是错误的吗?如果我需要更换触摸传感器的引脚,这是可能的,但我想确保它是正确的引脚,因为我需要焊接它(而不是在面包板上)。左侧引脚:10、14 和 A3
注意:我已经测试过,只是延迟替换而不是进入睡眠状态,睡眠后的功能工作正常。所以我可以听到哔哔声,设备再次启动。 sleep()后的代码没有问题,只是不想唤醒而已
好吧,我自己想出来了。似乎 pin 9 和 pin 16 不能与 attachInterrupt() 一起使用,所以它永远不会工作。还有用法不对,应该是这样的:
attachInterrupt( digitalPinToInterrupt( TEP_PIN_TOUCHSENSOR ), wakeup, HIGH );
想出了使用引脚更改中断来唤醒设备的想法,这似乎可行。但是,从休眠模式恢复无法在不发生故障的情况下恢复所有外围设备,因此需要硬件重置,必须从头开始。也由于睡眠时的变化,例如电缆与计算机断开连接(它也有电池)和 USB 连接丢失。并非我使用的所有 classes 都是我的,也没有针对此类断电问题进行优化。
无论如何,我把代码改成这样,只是为了向你展示这些变化,也许它也能帮助其他人:
#include <LowPower.h> // To enter sleep mode, save power
#include <avr/wdt.h> // For watchDog device reset
......
#define TEP_IR_RECV_PIN 16
#define TEP_PIN_TOUCHSENSOR 9
......
......
namespace TEP_LIB_FUNCS
{
void pciSetup(uint8_t iPin)
{
*digitalPinToPCMSK( iPin ) |= bit( digitalPinToPCMSKbit( iPin ) ); // enable pin
PCIFR|= bit( digitalPinToPCICRbit( iPin )); // clear any outstanding interrupt
PCICR|= bit( digitalPinToPCICRbit( iPin )); // enable interrupt for the group
}
#if defined( TEP_PIN_TOUCHSENSOR )
#if TEP_PIN_TOUCHSENSOR >= 8 && TEP_PIN_TOUCHSENSOR <= 13
ISR(PCINT0_vect)
{
#define TEP_SLEEP_WAKEUP_PIN_ENABLED 0
// Event handler for pins: D8 to D13
// Pin change wakeup event handler, does nothing but is required.
// Do not remove this.
}
#elif defined(A0) && defined(A5) && TEP_PIN_TOUCHSENSOR >= A0 && TEP_PIN_TOUCHSENSOR <= A5
ISR(PCINT1_vect)
{
#define TEP_SLEEP_WAKEUP_PIN_ENABLED 1
// Event handler for pins: A0 to A5
// Pin change wakeup event handler, does nothing but is required.
// Do not remove this.
}
#elif TEP_PIN_TOUCHSENSOR >= 0 && TEP_PIN_TOUCHSENSOR <= 7
ISR(PCINT2_vect)
{
#define TEP_SLEEP_WAKEUP_PIN_ENABLED 0
// Event handler for pins: A0 to A5
// Pin change wakeup event handler, does nothing but is required.
// Do not remove this.
}
#endif
#endif
void sleep()
{
// Possible pins Micro, Leonardo, other 32u4-based: 0, 1, 2, 3, 7.
// Because we use all pins already by other functions, we cannot
// use attachInterrupt(), it doesn't work.
// see also: https://www.arduino.cc/en/Reference/AttachInterrupt
// Instead of this we use a change event interrupt to wake up
// the device from sleep state.
//
// The device can be woke up by using:
// - The reset button
// - The touch sensor
// Okay, lets go
// We enable interrupts here to be sure it is going to work
interrupts();
#ifdef TEP_SLEEP_WAKEUP_PIN_ENABLED
// Set pin change interrupt enabled for sensor pin
// (See also https://playground.arduino.cc/Main/PinChangeInterrupt)
// old code: attachInterrupt( digitalPinToInterrupt( TEP_PIN_TOUCHSENSOR ), wakeup, HIGH );
// old code: attachInterrupt( digitalPinToInterrupt( TEP_IR_RECV_PIN ), wakeup, HIGH );
pciSetup( TEP_PIN_TOUCHSENSOR );
#endif
// Enter power down state with ADC and BOD module disabled.
// Wake up when wake up pin has changed.
LowPower.powerDown( SLEEP_FOREVER, ADC_OFF, BOD_OFF );
// Disable external pin interrupt on wake up pin.
// old code: detachInterrupt( TEP_PIN_TOUCHSENSOR );
// old code: detachInterrupt( TEP_IR_RECV_PIN );
}
void softReset()
{
wdt_enable(WDTO_15MS);
while(true) {}
}
// Function Implementation
void init(void)
{
MCUSR = 0;
wdt_disable();
}
} // end namespace TEP_LIB_FUNCS
还有我的EnjoyPad的休眠功能class(按下遥控器上的电源按钮或当没有触摸或做任何事情超时时会触发):
void TEnjoyPad::setSleepMode()
{
// Notify user device entering sleep mode, beep twice
setBeep( 200 );
setBeep( 200, 100, false );
// To be sure: Trigger end / unhandled down events and reset states
reset(init);
// Shut down any
broadcastSleepMode();
// Handle sleep event
eventSleep();
// Stop all peripherals
end();
// Turn off onboard led
digitalWrite( 13, LOW );
pinMode( 13, INPUT );
// Finally go to sleep, code stops here
TEP_LIB_FUNCS::sleep();
// Device woke up
setBeep( 500, 100, false );
// Handle wake up event
eventAwake();
// Restart whole device, start from scratch.
// This seems to be the best way to guarantee all
// peripherals will be initialized properly
// and without errors.
TEP_LIB_FUNCS::softReset();
}
注意:此类MCU重启速度快,唤醒继续或唤醒复位无明显差异。
暂时就这些,观看坦克;-)
------------
编辑:添加 pciSetup( TEP_IR_RECV_PIN );
时它似乎也能正常工作(因为已经为它定义了一个中断处理程序)。因此,设备也可以在收到 IR 命令时唤醒。没想到这是可能的,但尝试了一下,整洁的功能。
情况:
我 made/developing 这个控制器(有很多功能)使用 Pro Micro (ATMega32u4) 并希望在触摸它时使用唤醒功能扩展它(使用触摸传感器)。这一切都是为了在不使用时节省一些能源。所以当设备被拾取时up/touched,设备就被唤醒了。没有联系的时候,过一会就会休眠。触摸传感器的触及距离将通过外壳周围的一小条铜带延长。
设备图片:
我用触摸感应器的pin 9和IR-sensor的pin 16来唤醒,但是没有用。两种设备功能都正常,我可以读取触摸状态(低=未触摸或高=触摸)并且我可以接收红外命令,因为它已经完全实现。当接收到信号时,这些引脚会在一段时间内保持高电平。
软件问题:
我的代码有问题吗(参数错误?)?执行此代码时设备进入睡眠状态(但从未醒来):
#include <avr/sleep.h> // To enter sleep mode, save power
#include <LowPower.h> // To enter sleep mode, save power
......
#define TEP_IR_RECV_PIN 16
#define TEP_PIN_TOUCHSENSOR 9
......
......
void sleep()
{
attachInterrupt( TEP_PIN_TOUCHSENSOR, wakeup, HIGH );
attachInterrupt( TEP_IR_RECV_PIN, wakeup, HIGH );
// Enter power down state with ADC and BOD module disabled.
// Wake up when wake up pin is high.
LowPower.powerDown( SLEEP_FOREVER, ADC_OFF, BOD_OFF );
// Disable external pin interrupt on wake up pin.
detachInterrupt( TEP_PIN_TOUCHSENSOR );
detachInterrupt( TEP_IR_RECV_PIN );
}
我的EnjoyPad的休眠功能class(按下遥控器上的电源按钮或当没有触摸或做任何事情达到超时时会触发):
void TEnjoyPad::setSleepMode()
{
// Notify user device entering sleep mode, beep twice
setBeep( 200 );
setBeep( 200, 100, false );
// To be sure: Trigger end / unhandled down events and reset states
reset(init);
// Handle sleep event
eventSleep();
// Stop all peripherals
end();
// Finally go to sleep, code stops here
TEP_LIB_FUNCS::sleep();
// Device woke up
setBeep( 500, 100, false );
// Restart all peripherals
begin();
// Handle wake up event
eventAwake();
}
有什么想法是错误的吗?如果我需要更换触摸传感器的引脚,这是可能的,但我想确保它是正确的引脚,因为我需要焊接它(而不是在面包板上)。左侧引脚:10、14 和 A3
注意:我已经测试过,只是延迟替换而不是进入睡眠状态,睡眠后的功能工作正常。所以我可以听到哔哔声,设备再次启动。 sleep()后的代码没有问题,只是不想唤醒而已
好吧,我自己想出来了。似乎 pin 9 和 pin 16 不能与 attachInterrupt() 一起使用,所以它永远不会工作。还有用法不对,应该是这样的:
attachInterrupt( digitalPinToInterrupt( TEP_PIN_TOUCHSENSOR ), wakeup, HIGH );
想出了使用引脚更改中断来唤醒设备的想法,这似乎可行。但是,从休眠模式恢复无法在不发生故障的情况下恢复所有外围设备,因此需要硬件重置,必须从头开始。也由于睡眠时的变化,例如电缆与计算机断开连接(它也有电池)和 USB 连接丢失。并非我使用的所有 classes 都是我的,也没有针对此类断电问题进行优化。
无论如何,我把代码改成这样,只是为了向你展示这些变化,也许它也能帮助其他人:
#include <LowPower.h> // To enter sleep mode, save power
#include <avr/wdt.h> // For watchDog device reset
......
#define TEP_IR_RECV_PIN 16
#define TEP_PIN_TOUCHSENSOR 9
......
......
namespace TEP_LIB_FUNCS
{
void pciSetup(uint8_t iPin)
{
*digitalPinToPCMSK( iPin ) |= bit( digitalPinToPCMSKbit( iPin ) ); // enable pin
PCIFR|= bit( digitalPinToPCICRbit( iPin )); // clear any outstanding interrupt
PCICR|= bit( digitalPinToPCICRbit( iPin )); // enable interrupt for the group
}
#if defined( TEP_PIN_TOUCHSENSOR )
#if TEP_PIN_TOUCHSENSOR >= 8 && TEP_PIN_TOUCHSENSOR <= 13
ISR(PCINT0_vect)
{
#define TEP_SLEEP_WAKEUP_PIN_ENABLED 0
// Event handler for pins: D8 to D13
// Pin change wakeup event handler, does nothing but is required.
// Do not remove this.
}
#elif defined(A0) && defined(A5) && TEP_PIN_TOUCHSENSOR >= A0 && TEP_PIN_TOUCHSENSOR <= A5
ISR(PCINT1_vect)
{
#define TEP_SLEEP_WAKEUP_PIN_ENABLED 1
// Event handler for pins: A0 to A5
// Pin change wakeup event handler, does nothing but is required.
// Do not remove this.
}
#elif TEP_PIN_TOUCHSENSOR >= 0 && TEP_PIN_TOUCHSENSOR <= 7
ISR(PCINT2_vect)
{
#define TEP_SLEEP_WAKEUP_PIN_ENABLED 0
// Event handler for pins: A0 to A5
// Pin change wakeup event handler, does nothing but is required.
// Do not remove this.
}
#endif
#endif
void sleep()
{
// Possible pins Micro, Leonardo, other 32u4-based: 0, 1, 2, 3, 7.
// Because we use all pins already by other functions, we cannot
// use attachInterrupt(), it doesn't work.
// see also: https://www.arduino.cc/en/Reference/AttachInterrupt
// Instead of this we use a change event interrupt to wake up
// the device from sleep state.
//
// The device can be woke up by using:
// - The reset button
// - The touch sensor
// Okay, lets go
// We enable interrupts here to be sure it is going to work
interrupts();
#ifdef TEP_SLEEP_WAKEUP_PIN_ENABLED
// Set pin change interrupt enabled for sensor pin
// (See also https://playground.arduino.cc/Main/PinChangeInterrupt)
// old code: attachInterrupt( digitalPinToInterrupt( TEP_PIN_TOUCHSENSOR ), wakeup, HIGH );
// old code: attachInterrupt( digitalPinToInterrupt( TEP_IR_RECV_PIN ), wakeup, HIGH );
pciSetup( TEP_PIN_TOUCHSENSOR );
#endif
// Enter power down state with ADC and BOD module disabled.
// Wake up when wake up pin has changed.
LowPower.powerDown( SLEEP_FOREVER, ADC_OFF, BOD_OFF );
// Disable external pin interrupt on wake up pin.
// old code: detachInterrupt( TEP_PIN_TOUCHSENSOR );
// old code: detachInterrupt( TEP_IR_RECV_PIN );
}
void softReset()
{
wdt_enable(WDTO_15MS);
while(true) {}
}
// Function Implementation
void init(void)
{
MCUSR = 0;
wdt_disable();
}
} // end namespace TEP_LIB_FUNCS
还有我的EnjoyPad的休眠功能class(按下遥控器上的电源按钮或当没有触摸或做任何事情超时时会触发):
void TEnjoyPad::setSleepMode()
{
// Notify user device entering sleep mode, beep twice
setBeep( 200 );
setBeep( 200, 100, false );
// To be sure: Trigger end / unhandled down events and reset states
reset(init);
// Shut down any
broadcastSleepMode();
// Handle sleep event
eventSleep();
// Stop all peripherals
end();
// Turn off onboard led
digitalWrite( 13, LOW );
pinMode( 13, INPUT );
// Finally go to sleep, code stops here
TEP_LIB_FUNCS::sleep();
// Device woke up
setBeep( 500, 100, false );
// Handle wake up event
eventAwake();
// Restart whole device, start from scratch.
// This seems to be the best way to guarantee all
// peripherals will be initialized properly
// and without errors.
TEP_LIB_FUNCS::softReset();
}
注意:此类MCU重启速度快,唤醒继续或唤醒复位无明显差异。
暂时就这些,观看坦克;-)
------------
编辑:添加 pciSetup( TEP_IR_RECV_PIN );
时它似乎也能正常工作(因为已经为它定义了一个中断处理程序)。因此,设备也可以在收到 IR 命令时唤醒。没想到这是可能的,但尝试了一下,整洁的功能。