我正在经历一种奇怪的现象。我正在尝试让 ATtiny24A 进入睡眠模式。我之前有一个工作代码,但通过对程序其他部分的修改和测试,新代码似乎不再使 MCU 进入睡眠状态。
我设计了一个用于对 ATtiny24a 进行编程的分线板。编程板有一个板载 LED,连接到 MCU 的引脚 8。它还具有另一个连接到 MCU 引脚 13 的 LED。你可以看到设计:
为了仅测试 MCU 的睡眠模式操作,我创建了一个专用于该功能的新文件。我将引脚 8(板载 LED)初始化为输出,将引脚 13 初始化为输入。然后,我使用按下的触觉按钮(从外部面包板)初始化中断配置,以在引脚更改(引脚 13)时触发。接下来,我打开 LED 约 3 秒钟,然后 MCU 应进入睡眠模式。事实并非如此。如果确实如此,我希望它能够使用中断唤醒并继续进入主函数中的 while 循环。我让 LED 每秒闪烁一次。同样在 while 循环中,我有一个向上计数的整数变量,一旦满足条件,MCU 就应该返回睡眠状态。这些都不起作用。这是代码:
#define F_CPU 1000000UL
#include <stdlib.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#define LED PORTA5 //pin 8
#define BT PINA0 //pin 13
int main(void){
uint8_t clk = 0;
//SETUP IO PINS
DDRA |= 1 << LED; //setting up LED pin
DDRA &= ~(1 << BT); //setting up tactile button pin
//SETUP INTERRUPT
GIMSK |= 1 << PCIE0;
PCMSK0 |= 1 << PCINT0;
sei();
set_sleep_mode(SLEEP_MODE_PWR_DOWN); //setting sleep mode
PORTA |= 1 << LED; //initialize LED on
for(uint8_t i = 0; i < 3; i++ ){
_delay_ms(1000);
}
sleep_mode(); //enter sleep mode
while(1){
PORTA ^= 1 << LED;
_delay_ms(500);
if(clk > 5){
clk = 0;
sleep_mode();
}
clk += 1;
}
return 0;
}
ISR(PCINT0_vect, ISR_NAKED);
当我将此代码上传到 MCU 时。 LED 无限期地保持亮起,在 while 循环之前永远不会进入第一个睡眠指令。
我已经通过github检查了sleep和io文件库内容。我已验证功能和属性有效;我也没有收到任何编译器警告或错误。
然后使用我尝试使用按位运算直接配置寄存器的函数和属性:
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
至 MCUCR |= 1 << SM1;
和
sleep_mode();
到 MCUCR |= 1 << SE;
(这更像是启用睡眠)。
通过这些替换,板上的 LED 会保持亮起 3 秒,然后在 while 循环中开始无限期地闪烁。忽略循环中的睡眠指令。
我还尝试使用指针和取消引用来直接将值分配给寄存器。
*((volatile unsigned char *) 0x55)) |= 1 << 4
4 代表 MCUCR 寄存器的第 4 位(SM1)。
*((volatile unsigned char *) 0x55)) |= 1 << 5
5 代表 MCUCR 寄存器的第 5 位(SE)。
其行为与上述替换相同。
我将继续尝试解决这个问题,但我确实希望有人回复我的解决方案以及我的代码当前不起作用的原因。 我在 MCU 上运行了其他代码,例如 LED 闪烁、引脚 8 上的 Timer1 PWM 输出、引脚更改中断触发,它们工作得很好。我就是无法让MCU进入休眠状态,换MCU也没用。
首先,你没有提供可编译的程序。所以我自己完成了必要的线路
#include <util/delay.h>
您没有定义用于中断处理的函数。您只声明了它。结果是 MCU 中断并调用复位。您必须在反汇编窗口的模拟器中看到它。 地址0x0002处有一个中断向量PCINT0_vect。有一个跳转到地址0x0017。 在这个地址处,有一个跳转到地址0x0000,这实际上是一个复位。
由于您没有定义任何中断处理,因此您确实设置了中断向量,但在没有函数体的情况下,翻译器将其替换为跳转到复位地址。
00000000 10.c0 RJMP PC+0x0011 Relative jump
00000001 15.c0 RJMP PC+0x0016 Relative jump
00000002 14.c0 RJMP PC+0x0015 Relative jump to 0x0017
00000003 13.c0 RJMP PC+0x0014 Relative jump
..
..
00000017 e8.cf RJMP PC-0x0017 Relative jump to 0x0000
如果我将空主体添加到ISR函数中,也会出现有问题的代码,其结果是程序错误。
ISR(PCINT0_vect, ISR_NAKED){
}
ISR_NAKED 意味着您不希望编译器向此函数添加任何内容。奇怪的是,他甚至没有从中断中回来。 我们再看一下模拟器中编译的结果。现在它跳转到主函数之后的地址(0x0170),但是只有NOP指令,然后是一些来自库的链接代码,但它没有被正确调用。这意味着该程序将去往任何地方。
00000000 10.c0 RJMP PC+0x0011 Relative jump
00000001 15.c0 RJMP PC+0x0016 Relative jump
00000002 6d.c1 RJMP PC+0x016E Relative jump to 0x0170
00000003 13.c0 RJMP PC+0x0014 Relative jump
..
..
43: }
0000016F 6f.cf RJMP PC-0x0090 Relative jump
49: }
00000170 00.00 NOP No operation
--- No source file -------------------------------------------------------------
00000171 2f.d0 RCALL PC+0x0030 Relative call subroutine
00000172 08.f4 BRCC PC+0x02 Branch if carry cleared
00000173 81.e0 LDI R24,0x01 Load immediate
00000174 08.95 RET Subroutine return
00000175 57.d0 RCALL PC+0x0058 Relative call subroutine
00000176 88.f0 BRCS PC+0x12 Branch if carry set
00000177 9f.57 SUBI R25,0x7F Subtract immediate
00000178 90.f0 BRCS PC+0x13 Branch if carry set
00000179 b9.2f MOV R27,R25 Copy register
0000017A 99.27 CLR R25 Clear Register
0000017B b7.51 SUBI R27,0x17 Subtract immediate
0000017C a0.f0 BRCS PC+0x15 Branch if carry set
0000017D d1.f0 BREQ PC+0x1B Branch if equal
0000017E 66.0f LSL R22 Logical Shift Left
0000017F 77.1f ROL R23 Rotate Left Through Carry
00000180 88.1f ROL R24 Rotate Left Through Carry
正确的解决方案是使用
ISR(PCINT0_vect){
}
现在调用正确的中断处理程序,并由 RETI 指令正确终止。这意味着程序现在可以在主函数中发生中断的地方正常继续运行。
00000000 10.c0 RJMP PC+0x0011 Relative jump
00000001 15.c0 RJMP PC+0x0016 Relative jump
00000002 6d.c1 RJMP PC+0x016E Relative jump to 0x0170
00000003 13.c0 RJMP PC+0x0014 Relative jump
..
..
47: ISR(PCINT0_vect){
00000170 1f.92 PUSH R1 Push register on stack
00000171 0f.92 PUSH R0 Push register on stack
00000172 00.90.5f.00 LDS R0,0x005F Load direct from data space
00000174 0f.92 PUSH R0 Push register on stack
00000175 11.24 CLR R1 Clear Register
00000176 cf.93 PUSH R28 Push register on stack
00000177 df.93 PUSH R29 Push register on stack
00000178 cd.b7 IN R28,0x3D In from I/O location
00000179 dd.27 CLR R29 Clear Register
49: }
0000017A 00.00 NOP No operation
0000017B df.91 POP R29 Pop register from stack
0000017C cf.91 POP R28 Pop register from stack
0000017D 0f.90 POP R0 Pop register from stack
0000017E 00.92.5f.00 STS 0x005F,R0 Store direct to data space
00000180 0f.90 POP R0 Pop register from stack
00000181 1f.90 POP R1 Pop register from stack
--- C:\Atmel Studio\7.0\GccApplication1\GccApplication1\Debug/.././main.c
00000182 18.95 RETI Interrupt return