gcc avr library

... starting to look at different sleep/wake and on-board peripheral power options for the arduino and it appears that the IDE and most of the examples make heavy use of the power and sleep libraries here: avr-libc: Modules

Within the power libary (avr-libc: <avr/power.h>: Power Reduction Management) there is a lengthy listing of all the AVR chips that are supported - the 328p is noticably absent from this list. Since this is the chip that makes up the core of my Uno and Pro mini boards, should I be concerned about this?

I'll add some broken code to this post when I get home from work, but, basically, I haven't been able to get any meaningful Serial.println data to go to the SerialMonitor after waking from sleep. ... suspect USART isn't working, but can't get it to re-start.

here's the broken code (expecting clean output to the Serial Monitor on wake, getting jibberish on button press instead):

/* test sketch based on example code from Rocket Scream.

http://www.rocketscream.com/blog/2011/07/04/lightweight-low-power-arduino-library/

specifically:

https://github.com/rocketscream/Low-Power/tree/master/Examples/powerDownWakeExternalInterrupt

... modified July 2012 to:
    use internal pull up resistor, 
    change interrupt from LOW to FALLING, 
    power up the on-chip peripherals, and
    attempt output of test mssg to pc via serial connection.

compiled using Arduino IDE 1.0.1.  loaded onto 3.3 V/8 mHz arduino mini pro.

*/

// **** INCLUDES *****
#include <LowPower.h>
#include <avr/power.h>

// Use pin 2 as wake up pin
const int wakeUpPin = 2;

void wakeUp()
{
    // Just a handler for the pin interrupt.
}

void setup()
{
    // Configure wake up pin as input.
    // This will consumes few uA of current.
    pinMode(wakeUpPin, INPUT); 
    digitalWrite(2, HIGH);
    Serial.begin(9600);  
}

void loop() 
{
    // Allow wake up pin to trigger interrupt on falling.
    attachInterrupt(0, wakeUp, FALLING);
    
    // Enter power down state with ADC and BOD module disabled.
    // Wake up when wake up pin is low.
    LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF); 
 
    detachInterrupt(0); 

    // Do something here
    // Example: Read sensor, data logging, data transmission.
    
    power_all_enable();  // command to restore on-chip peripheral power
                         // as suggested by Premeaux, Emery; Evans, Brian (2011-12-07). 
                         // Arduino Projects to Save the World. Apress. 
    delay(100);

   // the following pinMode and digitalWrite commands work regardless of the
   // power_all_enable cmd.

    pinMode(13, OUTPUT); 
    digitalWrite(13, HIGH);
    delay(5000);  // keep light illuminated for 5 sec, so I can measure current.
    digitalWrite(13, LOW);
    delay(5000);  // go dark for another 5 sec, so I can also measure current.
    digitalWrite(13, HIGH);
    delay(5000);  // light back up for another 5 sec, so I can also measure current.
    digitalWrite(13, LOW);
     
    Serial.println("Do Something");   // doesn't work.  
                                      // jibberish:       ÔÒÕ««­-ëVHø 
                                      // sent to serial monitor each time after the second time the button pressed.
}

What's going on here?

Thoughts? Impressions? Feelings? ... share away.

Thanks.

-Matt

Look in sleep.h, the 328p is supported.

As for the serial data, with the chip asleep the serial lines might become floating. Also the USART might need to be re-established. I seem to recall similar problems a while ago.

Try ending serial before sleeping, eg.

Serial.end ();

Also (before that) enable the internal pull-up on the Tx pin so it doesn't float but goes high. Then upon waking do a Serial.begin() again.

... well, closer I guess. Thanks.

Setting pull up on Tx (digital pin1) and adding a few additional Serial.end() and Serial.begin() commands gets me to at least get a clean "Do Something" string sent to Serial Monitor, but the behavior is still a bit odd:

  • Serial Monitor always displays a two character "C?..." blurb immediately upon pressing the button.

  • There are absolutely no carriage returns in the output, despite using println statements.

hmmm.

reprint of revised code in its entirety:

/* test sketch based on example code from Rocket Scream.

http://www.rocketscream.com/blog/2011/07/04/lightweight-low-power-arduino-library/

specifically:

https://github.com/rocketscream/Low-Power/tree/master/Examples/powerDownWakeExternalInterrupt

... modified July 2012 to:
    use internal pull up resistor, 
    change interrupt from LOW to FALLING, 
    power up the on-chip peripherals, and
    attempt output of test mssg to pc via serial connection.

compiled using Arduino IDE 1.0.1.  loaded onto 3.3 V/8 mHz arduino mini pro.

*/

// **** INCLUDES *****
#include <LowPower.h>
#include <avr/power.h>

// Use pin 2 as wake up pin
const int wakeUpPin = 2;

void wakeUp()
{
    // Just a handler for the pin interrupt.
}

void setup()
{
    // Configure wake up pin as input.
    // This will consumes few uA of current.
    pinMode(wakeUpPin, INPUT); 
    digitalWrite(2, HIGH);  // enable pull up resistor on interrupt pin (digital pin 2)
    digitalWrite(1, HIGH);  // enable pull up resistor on the Tx pin (digital pin 1)
    Serial.begin(9600);  

    // new lines:
    Serial.println("Before sleep...");
    Serial.end();
}

void loop() 
{
    // Allow wake up pin to trigger interrupt on falling.
    attachInterrupt(0, wakeUp, FALLING);
    
    // Enter power down state with ADC and BOD module disabled.
    // Wake up when wake up pin is low.
    LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF); 
 
    detachInterrupt(0); 

    // Do something here
    // Example: Read sensor, data logging, data transmission.
    
    power_all_enable();  // command to restore on-chip peripheral power
                         // as suggested by Premeaux, Emery; Evans, Brian (2011-12-07). 
                         // Arduino Projects to Save the World. Apress. 
    delay(100);

   // the following pinMode and digitalWrite commands work regardless of the
   // power_all_enable cmd.

    pinMode(13, OUTPUT); 
    digitalWrite(13, HIGH);
    delay(5000);  // keep light illuminated for 5 sec, so I can measure current.
    digitalWrite(13, LOW);
    delay(5000);  // go dark for another 5 sec, so I can also measure current.
    digitalWrite(13, HIGH);
    delay(5000);  // light back up for another 5 sec, so I can also measure current.
    digitalWrite(13, LOW);

    // new lines:     
    Serial.begin(9600);
    Serial.println("Do Something");  
    Serial.end();
}
    Serial.begin(9600);
    Serial.println("Do Something");  
    Serial.end();

It takes a finite time to output serial data, and you are shutting down the serial port before all the output has been sent. I suggest a short delay (although I don't usually like delays), eg.

    Serial.begin(9600);
    Serial.println("Do Something");  
    delay (15);  // should allow time for 14 characters at 9600 baud
    Serial.end();

To be more sophisticated you could check if the output buffer was empty, blah blah, but this will probably do in this case.

That would be one of the rare cases where Serial.flush() would be appropriate. It blocks until the output buffer is empty.

void HardwareSerial::flush()
{
  while (_tx_buffer->head != _tx_buffer->tail)
    ;
}

Thanks. A brief pause before sleeping to let the Serial commands run their course did the trick. I presume this will be a good concept to keep in mind when I move the serial stream over to an SD card instead of the USB serial stream.

... will tinker with the flush code suggested by jraskell also. I am unfamiliar with the "->" syntax used in the code example. In R, this is an assignment operation, but suspect something different here. ... Let's just say I can't make heads OR tails of the code example. nyuck, nyuck, nyuck.

Sorry, that wasn't a code sample. That shows the implementation of Serial.flush(). Just shows that the flush() call blocks until the output buffer is empty (meaning there's no more serial data to send). You just need to call Serial.flush(), and when it returns, you know it's ok to call end().

A code sample would be:

Serial.begin(9600);
Serial.println("Do Something");  
Serial.flush();
Serial.end();

jraskell:
That would be one of the rare cases where Serial.flush() would be appropriate. It blocks until the output buffer is empty.

The end method does that, also.

So it does. But it looks like it doesn't check that the transmit buffer (in the hardware) is empty, so you are likely to lose that last outgoing byte.

So, perhaps flush(), then wait for (say) 2 character times. The flush will get rid of most of the buffer and the delay will let the transmit buffer empty.

Well, I take that back. A quick test shows that if you do Serial.end() after sending data, it still all appears OK on the logic analyzer.

However if you go to sleep immediately afterwards, you lose the last two bytes. So I think a combination of Serial.end() followed by a delay of 2 character times (ie. 2 * 1/960) so say 3 mS.

Code to reproduce problem:

#include <avr/sleep.h>

void setup () {}
void loop ()
  {
  Serial.begin (9600);
  Serial.print ("test");
  Serial.end ();
  set_sleep_mode (SLEEP_MODE_PWR_DOWN);  
  sleep_mode ();  
  }

To make it not dependent on getting the delay right this (seems to) work:

#include <avr/sleep.h>

void setup () 
  {
  pinMode (13, OUTPUT);
  digitalWrite (13, LOW);
  }
void loop ()
  {
  Serial.begin (9600);
  Serial.print ("test");
  Serial.flush ();
  // wait for transmit buffer to empty
  while ((UCSR0A & _BV (TXC0)) == 0)
    {}
  Serial.end ();
  digitalWrite (13, HIGH);  // test indicator
  set_sleep_mode (SLEEP_MODE_PWR_DOWN);  
  sleep_mode ();  
  }

The delay() flushes the output, then the test for the transmit buffer empty lets those last two bytes escape. Then we shut down the serial port.

Seems to be a known bug:

http://code.google.com/p/arduino/issues/detail?id=871

Thanks to all. The little while loop snippet of code results in clean serial output to the pc. ... this really pulled back the curtain to reveal complexity. ... should I expect similar difficulties with SoftwareSerial instead of Serial? ... guess I'll find out.

MF

I doubt SoftwareSerial would have the same issue because it does bit banging.

As for the complexity, it is reasonably simple, considering what you are achieving. I mean, try outputting a serial string, waiting for it to be completely sent, and then putting the processor to sleep, in Windows, in half-a-dozen lines of code.