I am experiencing somewhat a peculiar phenomenon. I am trying to get an ATtiny24A into sleep mode. I had a working code before, but through revisions and testing of other parts of my program it seems that the new code no longer sends the MCU into sleep.
I designed a breakout board for programming the ATtiny24a. The programming board has an onboard LED connected to pin 8 of the MCU. It also, has another LED connected to pin 13 of the MCU. You can see the design:
To only test the sleep mode operation of the MCU, I created a new file dedicated only to that feature. I initialize the pin 8 (onboard LED) as an output, and pin 13 as an input. I then initialize the interrupt configuration to trigger on pin change, pin 13, using a tactile button pressed (from an external breadboard). Following this, I turn on the LED for about 3 seconds, then the MCU should go into sleep mode. It doesn’t. If it did, I wanted it to be able to wake up using the interrupt and continue into the while loop in the main function. Where I have the LED blink every second. Also in the while loop, I have an integer variable that counts up, and once a condition is met, the MCU should go back to sleep. None of this works. Here is the code:
#define F_CPU 1000000UL
#include <stdlib.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <util/delay.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);
When I upload this code into the MCU. The LED stays on indefinitely, it never enters the first sleep instruction before the while loop.
I have checked the sleep and io file library content via github. I have verified that the functions and attributes are valid; also I don't receive any compiler warnings or errors.
Rather then using the functions and attributes I tried using bitwise operations to configure the register directly:
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
to MCUCR |= 1 << SM1;
and
sleep_mode();
to MCUCR |= 1 << SE;
(this is more like sleep enabled).
With these replacements, the LED on the board stays on for 3 seconds then starts blinking indefinitely in the while loop. Ignoring the sleep instruction in the loop.
I have also tried using pointers and dereferencing to directly assigned the values to the register.
*((volatile unsigned char *) 0x55)) |= 1 << 4
4 representing the 4th bit (SM1) of the MCUCR register.
*((volatile unsigned char *) 0x55)) |= 1 << 5
5 representing the 5th bit (SE) of the MCUCR register.
It behaves the same as the above replacement.
I am going to continue to try to figure this out, but I do hope someone replies with a resolution and the reason why my code currently doesn't work. I ran other codes on the MCU, such as blinking the LEDs, Timer1 PWM output on pin 8, pin change interrupt trigger, and they have worked fine. I just can't get the MCU to enter sleep state anymore, even changing the MCU doesn't work.
First of all, you did not provide a compilable program. So I completed the necessary line myself
#include <util/delay.h>
You don't have a function defined for interrupt handling. You only have it declared. The result is then MCU is interrupted that the reset is invoked. You have to see it in the simulator in disassembly windows. There is an interrupt vector PCINT0_vect at address 0x0002. There is a jump to address 0x0017. At this address, there is a jump to address 0x0000, which is actually a reset.
Since you did not define any interrupt handling, you did set an interrupt vector, but in the absence of a function body, the translator replaced it with a jump to the reset address.
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
If I add the empty body to the ISR function, a problematic code will also appear, the result of which is the program's error.
ISR(PCINT0_vect, ISR_NAKED){
}
ISR_NAKED means that you do not want the compiler to add anything to this function. And strangely enough, he doesn't even return from the interrupt. Let's look again at the result of the compilation in the simulator. Now it jumps to the address after the main function (0x0170) But there is only the NOP instruction and then some linked code from the library follows, but it is not called correctly. This means that the program will go anywhere.
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
The correct solution is to use
ISR(PCINT0_vect){
}
The correct interrupt handler is now invoked, which is correctly terminated by the RETI instruction. Which means that the program can now normally continue at the point in the main function where the interruption occurred.
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