avr 睡眠模式和唤醒
avr sleep mode and wake up
我正在尝试让我的 AtTiny 13 进入睡眠状态并通过中断将其唤醒。它确实睡着了,但它永远不会醒来。完整代码:
#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdbool.h>
#include <avr/sleep.h>
#define RED (1<<PB4)
#define RED_HIGH PORTB |=RED
#define RED_LOW PORTB &= ~RED
#define RED_TOG PORTB ^= RED
#define BUTTON 1<<PB1
volatile static bool is_sleeping;
ISR(INT0_vect)
{
RED_TOG;
is_sleeping = true;
}
int main(void){
GIMSK |= 1<<INT0;
MCUCR |= 0<<ISC00 | 1<<ISC01;
sei();
DDRB |= RED;
DDRB &= ~BUTTON;
PORTB |= BUTTON;
RED_HIGH;
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
while(1){
if(is_sleeping){
sleep_enable();
sei();
sleep_cpu();
sleep_disable();
}
}
}
根据 sleep.h 数据,它应该有效。有什么想法吗?
更新:从IDLE模式唤醒没有问题;
假设没有硬件接线问题,您的代码按如下方式工作:启动后,您的 LED 亮起并且 while-loop 处于空闲状态,因为 is_sleeping
最初设置为零。第一个 INT0 中断切换 LED(将其关闭)并设置 is_sleeping
标志,以便 while 循环在下一轮进入保护代码。该代码使 MCU 在 sleep_mcu()
行休眠。一旦 INT0 中断等待 MCU,它就会从最后一个地方继续,即它会回到睡眠状态,因为 is_sleeping
仍然设置! (并且在您的代码中永远不会变回 false)。这意味着在 MCU 唤醒后它几乎立即进入睡眠状态并关闭直到下一个 INT0 中断。
所以回答你的问题它永远不会醒来我会说:它确实会醒来,但时间很短。如果您测量电流(例如使用示波器和分流电阻器),您会在它醒来并立即进入睡眠状态时观察到尖峰。
不管你主要问题是注意代码质量。嵌入式编程远非宽容,您可能会因为微不足道的错误而陷入困境数小时。例如,始终对宏定义进行防御。您将 BUTTON
定义为没有括号的 1<<PB1
。区别在于稍后您会受到运算符优先级的影响。例如使用 DDRB &= ~BUTTON
你没有你所期望的。您的右侧表达式展开为 11111100
(因为 ~1<<1
是 11111110 << 1
),而您想要 11111101
(因为 ~(1<<1)
是 ~ 00000010
)。如果您将 PB0 用于其他用途,您可能会遇到不希望的行为。
此外,在复制示例代码时,请确保您了解它的含义。 sleep.h
中的示例依赖于使用 sei
和 cli
互补。在您的代码中,您只坚持在循环中重新启用中断,这在这里毫无意义。
编辑: 由于您声称唤醒在 "idle" 模式下工作,那么您的下一个问题是您希望系统通过设置对 (ISC00,ISC01)
到 (0,1)
在 MCUCR
。请参阅数据表第 9.2 章说 "Note that recognition of falling or rising edge interrupts on INT0 requires the presence of an I/O clock" 而第 7.1 章中的 table 说 Clk_I/0 不存在 掉电模式。您唯一的选择是通过在 MCUCR
.
中设置对 (ISC00,ISC01)
到 (0,0)
来使 INT0 外部中断在低电平触发
我正在尝试让我的 AtTiny 13 进入睡眠状态并通过中断将其唤醒。它确实睡着了,但它永远不会醒来。完整代码:
#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdbool.h>
#include <avr/sleep.h>
#define RED (1<<PB4)
#define RED_HIGH PORTB |=RED
#define RED_LOW PORTB &= ~RED
#define RED_TOG PORTB ^= RED
#define BUTTON 1<<PB1
volatile static bool is_sleeping;
ISR(INT0_vect)
{
RED_TOG;
is_sleeping = true;
}
int main(void){
GIMSK |= 1<<INT0;
MCUCR |= 0<<ISC00 | 1<<ISC01;
sei();
DDRB |= RED;
DDRB &= ~BUTTON;
PORTB |= BUTTON;
RED_HIGH;
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
while(1){
if(is_sleeping){
sleep_enable();
sei();
sleep_cpu();
sleep_disable();
}
}
}
根据 sleep.h 数据,它应该有效。有什么想法吗?
更新:从IDLE模式唤醒没有问题;
假设没有硬件接线问题,您的代码按如下方式工作:启动后,您的 LED 亮起并且 while-loop 处于空闲状态,因为 is_sleeping
最初设置为零。第一个 INT0 中断切换 LED(将其关闭)并设置 is_sleeping
标志,以便 while 循环在下一轮进入保护代码。该代码使 MCU 在 sleep_mcu()
行休眠。一旦 INT0 中断等待 MCU,它就会从最后一个地方继续,即它会回到睡眠状态,因为 is_sleeping
仍然设置! (并且在您的代码中永远不会变回 false)。这意味着在 MCU 唤醒后它几乎立即进入睡眠状态并关闭直到下一个 INT0 中断。
所以回答你的问题它永远不会醒来我会说:它确实会醒来,但时间很短。如果您测量电流(例如使用示波器和分流电阻器),您会在它醒来并立即进入睡眠状态时观察到尖峰。
不管你主要问题是注意代码质量。嵌入式编程远非宽容,您可能会因为微不足道的错误而陷入困境数小时。例如,始终对宏定义进行防御。您将 BUTTON
定义为没有括号的 1<<PB1
。区别在于稍后您会受到运算符优先级的影响。例如使用 DDRB &= ~BUTTON
你没有你所期望的。您的右侧表达式展开为 11111100
(因为 ~1<<1
是 11111110 << 1
),而您想要 11111101
(因为 ~(1<<1)
是 ~ 00000010
)。如果您将 PB0 用于其他用途,您可能会遇到不希望的行为。
此外,在复制示例代码时,请确保您了解它的含义。 sleep.h
中的示例依赖于使用 sei
和 cli
互补。在您的代码中,您只坚持在循环中重新启用中断,这在这里毫无意义。
编辑: 由于您声称唤醒在 "idle" 模式下工作,那么您的下一个问题是您希望系统通过设置对 (ISC00,ISC01)
到 (0,1)
在 MCUCR
。请参阅数据表第 9.2 章说 "Note that recognition of falling or rising edge interrupts on INT0 requires the presence of an I/O clock" 而第 7.1 章中的 table 说 Clk_I/0 不存在 掉电模式。您唯一的选择是通过在 MCUCR
.
(ISC00,ISC01)
到 (0,0)
来使 INT0 外部中断在低电平触发