Does sleep_mode() even work?

Go To Last Post
14 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

On this page it says that you can call set_sleep_mode() and then call sleep_mode() to go into sleep. So my code is:

 

void GotoSleep( void )
{
  set_sleep_mode(SLEEP_MODE_IDLE);             // use while debugging with HID Listen
  //set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  sleep_mode();
}

and my main loop looks like this:

 

int main( void )
{
  cli();        		    // turn off global interrupts

  usb_init();			      // init usb port to send debugging information
  _delay_ms( 1000 );   // debounce delay

  HardwareInit();       // initialize all pheripherals, ports etc.
  ModemInit();          // initialize APC220 serial wireless modem

  sei();

  while (1)   	        // main loop, runs forever
  {
    GotoSleep();
  }

  return 0;             // never reached
}

When I add a print statement in the GotoSleep() method (which prints to USB) and I run the program I see an endless output of print statements, whilst I would expect to see only one since the CPU would sleep after it reaches GotoSleep().

 

Does sleep_mode() even work?

 

BTW: I'm using a Teensy 2.0 (ATMega32U4)

This topic has a solution.

Building my dreams!

Last Edited: Wed. Sep 5, 2018 - 12:03 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I have limited experience of using USB on AVRs, but I would assume that there are interrupts involved, which will make the AVR exit sleep.

Four legs good, two legs bad, three legs stable.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

That's what I thought.

 

So I modified the code so it turns the built-in LED on and off:

 

void GotoSleep( void )
{
  LED_OFF;
  set_sleep_mode(SLEEP_MODE_IDLE);             // use while debugging with HID Listen
  //set_sleep_mode(SLEEP_MODE_PWR_DOWN);   
  sleep_mode();
}

and in the interrupt service routine for PD0 / INT0 I added:

 

ISR( INT0_vect )
{
  if ( bit_is_clear( PIND, PIND0 ) )   // if key is pressed
  {
    _delay_ms( 55 );   // debounce delay
    
    if ( bit_is_clear( PIND, PIND0 ) )     // if key is still pressed
    {
       LED_ON;      
    }
  }
}  

The LED is now continiously turned on. I would expect it to be off.

Building my dreams!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You are missing some vital details like which model of AVR this is. As you mention USB I'm going to take a wild guess at ATmega32U4. That has a table like this in the datasheet:

 

 

As you are selecting "IDLE" then the number of potential wakeup sources is quite wide. We can't possibly know what is hidden inside your calls to:

  usb_init();			      // init usb port to send debugging information
  
  HardwareInit();       // initialize all pheripherals, ports etc.
  ModemInit();          // initialize APC220 serial wireless modem

The comment on the middle one is especially vague!

 

So what interrupts may be enabled inside any of those?

 

Like John I am going with USB interrupts causing the system to be woken.

 

As you commented code shows, if you were to aim for POWER_DOWN the table suggests it is far more difficult to wake the AVR though even there USB Async interrupts will do it.

 

I do kind of wonder if a USB connected AVR is the right candidate for sleep modes anyway? If it's connected to a USB host isn't there going to be a virtually unlimited source of energy anyway?

 

If, OTOH, it's a device that operates standalone most of the time and only plugs into a host rarely to upload to the mothership then try developing your sleeping stuff in absence of active USB.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Also, although it may not be relevant to your question, I would suggest a 55mS delay in an ISR is a bad idea.

Four legs good, two legs bad, three legs stable.

This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

I've changed the code a bit:

 

int main( void )
{
  cli();        		    // turn off global interrupts

  //usb_init();			      // init usb port to send debugging information
  //_delay_ms( 1000 );   // debounce delay

  HardwareInit();       // initialize all pheripherals, ports etc.
  ModemInit();          // initialize APC220 serial wireless modem

  sei();

  while (1)   	        // main loop, runs forever
  {
    GotoSleep();
    _delay_ms(500);
  }

  return 0;             // never reached
}

I turned off the USB which is indeed the source of the wake-up.

 

void GotoSleep( void )
{
  set_bit(PIND, PIND6);
  set_sleep_mode(SLEEP_MODE_IDLE);             // use while debugging with HID Listen
  //set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  sleep_mode();
}

What happens now is that when I press the button (PD0 / INT0) the LED turns on for half a second and then turns off and stays off.

 

Note: the LED is located at PD6.

 

Since writing a one to PIND6 toggles the value of the port I believe I've demonstrated that the processor indeed falls aleep (otherwise you'd expect the LED to blink at 0.5 second intervals).

Building my dreams!

Last Edited: Wed. Sep 5, 2018 - 11:53 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

John_A_Brown wrote:
Also, although it may not be relevant to your question, I would suggest a 55mS delay in an ISR is a bad idea.

 

The 55ms is not a random value. I got it from the datasheet of the button manufacturer specifying the bounce time.

Building my dreams!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

That is completely the wrong way to debounce buttons! What you should do is forget INT0 - buttons never work well on external interrupts - only use INT0_vect if you have to use that as a wake up source. To actually read the button state run a timer interrupt. Perhaps at something like 5ms. Consider a button to have reached a stable state when checking it in 11 successive 5ms interrupts shows it to have been in the same state for all of them. Restart the count if the state has changed between one interrupt and the next.

 

If you do plan to use this micro with USB at any stage it could become seriously disturbed if its own interrupts cannot occur for 55ms+. USB works on things that happen every 1ms. so you may miss 55 frames of USB activity while you are trapped inside your interrupt-disabled delay.

Last Edited: Wed. Sep 5, 2018 - 12:29 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I use both INT0 and PCINT0 for my buttons and both work great. Why should I be wasting CPU time and power polling buttons (whether with a timer or a delay) when it's so much more efficient to use interrupts? When a button is only pressed sporadically it would be wasteful to poll. When your designing something that's powered by batteries powering down and waking on interrupts is a must.

 

I've heard the argument against interrupts for buttons before on this forum but I denounce it.

Building my dreams!

Last Edited: Wed. Sep 5, 2018 - 12:59 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

bigpilot wrote:
I use both INT0 and PCINT0 for my buttons and both work great. Why should I be wasting CPU time and power polling buttons (whether with a timer or a delay) when it's so much more efficient to use interrupts?
I am suggesting interrupts - just not INTn/PCINTn interrupts. You have already ably demonstrated why trying to debounce in the ext in handler is a bad idea. It has no notion of temporal state so your only option was a _delay_ms(). While that is operating no other interrupts in the system can occur. So your USB will likely die and if you use other interrupts such as UART_RX then that has every chance of missing inbound characters too.
bigpilot wrote:
I've heard the argument against interrupts for buttons before on this forum but I denounce it.
Then you aren't thinking clearly.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

"I've heard the argument against interrupts for buttons before on this forum but I denounce it."

 

And I denounce your debounce method.

Four legs good, two legs bad, three legs stable.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

But, for what it's worth, I (and probably thousands of other people) do sometimes use an interrupt to wake a device from sleep when a button is pressed.

However, having woken up, you can kick off another timer to use for the debounce.

It's no more "wasteful" than your method, as both require that the device is running for the fabled 55mS.

Indeed, if you had a timer available that would run in a low power mode, it could be less "wasteful".

I have done similar things with chips that allow on-the-fly switching to a lower clock speed, to conserve power in products that run from coin cells.

Four legs good, two legs bad, three legs stable.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

On some AVRs you have to set a bit in a configuration register, and then set another bit in the same register within a few system clock cycles of the previous instruction in order to enter sleep mode.  The go to sleep function in the example code calls another function.  This will never have the two bits written in the correct time frame.  Your code is being too abstract; just set the two bits immediately one right after each other and you'll go into sleep mode. 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

What on earth are you talking about? He's using the asm macros from avr/sleep.h that are coded in Asm for the very reason that it guarantees timing constraints whatever the C optimisation setting is.