embeddedavravr-gccavrdudeattiny

How to get an ATtiny24A MCU into sleep mode?


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: enter image description here

enter image description here

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.


Solution

  • 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