Sunday, 27 November 2011

Sleeping Arduino - Part 5 Wake Up Via The Watchdog Timer

Overview
Welcome to the fifth and final part of the "Sleeping Arduino" series, where we will cover how to wake the Arduino from sleep mode using the Watchdog Timer (WDT). When waking your Arduino from sleep, you could use one of the standard internal timers of an Arduino as I have detailed in Part 4, but if you are looking for the maximum sleep time and/or minimum sleep power consumption, you have the use the WDT;


As I have mentioned in this table, the WDT can give us a sleep time of 8 seconds, whereas the 'longest' 8/18bit timer will only give us a sleep time of ~4 seconds.


Watchdog Timer (WDT)
The Watchdog Timer on the Arduino's microprocessor only has one source to drive it: it's own separate internal 128kHz oscillator (as opposed to the 8/16bit internal timers, which can use either the 16Mhz system clock or an external clock). It is this separate oscillator that enables the WDT to function in the lowest power mode: SLEEP_MODE_PWR_DOWN (see here for more detailed info on the Arduino's power modes).

The WDT also has a prescaler, which is used to configure the timeout period. It supports timeout periods from 16ms to 8 seconds: 


Also it has three modes of operation:


  1. Interrupt - The WDT_vect interrupt sub-routing will be called when the WDT times out. Can be used to wake the micro from sleep modes, including the lowest power sleep mode (SLEEP_MODE_PWR_DOWN), where other timers are not available.
  2. System Reset - A watchdog time-out reset will occur, i.e. the micro-controller will be restarted. For use in handling code lockups.
  3. Interrupt and System Reset - First the WDT_vect interrupt sub-routing will be called, when completed a watchdog time-out reset will occur.






Please refer to my post here for the other operation modes of the WDT. The post is for the Atmega1281, but the functionality is pretty much the same.




Code


So, we will be using interrupt mode in our code to wake the Arduino from sleep every 8 seconds to toggle the state of the LED:


/*
 * Sketch for testing sleep mode with wake up on WDT.
 * Donal Morrissey - 2011.
 *
 */
#include <avr/sleep.h>
#include <avr/power.h>
#include <avr/wdt.h>

#define LED_PIN (13)

volatile int f_wdt=1;



/***************************************************
 *  Name:        ISR(WDT_vect)
 *
 *  Returns:     Nothing.
 *
 *  Parameters:  None.
 *
 *  Description: Watchdog Interrupt Service. This
 *               is executed when watchdog timed out.
 *
 ***************************************************/
ISR(WDT_vect)
{
  if(f_wdt == 0)
  {
    f_wdt=1;
  }
  else
  {
    Serial.println("WDT Overrun!!!");
  }
}


/***************************************************
 *  Name:        enterSleep
 *
 *  Returns:     Nothing.
 *
 *  Parameters:  None.
 *
 *  Description: Enters the arduino into sleep mode.
 *
 ***************************************************/
void enterSleep(void)
{
  set_sleep_mode(SLEEP_MODE_PWR_SAVE);   /* EDIT: could also use SLEEP_MODE_PWR_DOWN for lowest power consumption. */
  sleep_enable();
  
  /* Now enter sleep mode. */
  sleep_mode();
  
  /* The program will continue from here after the WDT timeout*/
  sleep_disable(); /* First thing to do is disable sleep. */
  
  /* Re-enable the peripherals. */
  power_all_enable();
}



/***************************************************
 *  Name:        setup
 *
 *  Returns:     Nothing.
 *
 *  Parameters:  None.
 *
 *  Description: Setup for the serial comms and the
 *                Watch dog timeout. 
 *
 ***************************************************/
void setup()
{
  Serial.begin(9600);
  Serial.println("Initialising...");
  delay(100); //Allow for serial print to complete.

  pinMode(LED_PIN,OUTPUT);

  /*** Setup the WDT ***/
  
  /* Clear the reset flag. */
  MCUSR &= ~(1<<WDRF);
  
  /* In order to change WDE or the prescaler, we need to
   * set WDCE (This will allow updates for 4 clock cycles).
   */
  WDTCSR |= (1<<WDCE) | (1<<WDE);

  /* set new watchdog timeout prescaler value */
  WDTCSR = 1<<WDP0 | 1<<WDP3; /* 8.0 seconds */
  
  /* Enable the WD interrupt (note no reset). */
  WDTCSR |= _BV(WDIE);
  
  Serial.println("Initialisation complete.");
  delay(100); //Allow for serial print to complete.
}



/***************************************************
 *  Name:        enterSleep
 *
 *  Returns:     Nothing.
 *
 *  Parameters:  None.
 *
 *  Description: Main application loop.
 *
 ***************************************************/
void loop()
{
  if(f_wdt == 1)
  {
    /* Toggle the LED */
    digitalWrite(LED_PIN, !digitalRead(LED_PIN));
    
    /* Don't forget to clear the flag. */
    f_wdt = 0;
    
    /* Re-enter sleep mode. */
    enterSleep();
  }
  else
  {
    /* Do nothing. */
  }
}



All parts of this series:



References

  • Martin's excellent tutorial here.
  • Darius's  excellent blog here.

Saturday, 26 November 2011

Sleeping Arduino - Part 4 Wake Up Via Internal Timer

Overview
Welcome to part 4 of the Sleeping Arduino Series. In this entry we will cover how to get the Arduino to wake from sleep using one of the micro-controller's internal timers. This does not require any external hardware, all is implemented in code.

Arduino Timers
So,we need to go into a little detail on the micro controller's internal timers. The Atmega168 in the Arduino Diecimila has three internal timers:
  • Timer/Counter 0 - 8 bit (Max timer duration: 16.4ms)
  • Timer/Counter 1 - 16 bit (Max timer duration: 4.1s)
  • Timer/Counter 2 - 8 Bit (Max timer duration: 16.4ms)
Whether the timer is 8 bit or 16 bit defines how much the timer can count up to: an 8 bit timer counting to 256 and 16 bit to 65536. What drives the counter to count can be configured to be either the internal 16Mhz clock or an external clock source on T1 pin. We will be using the internal timer in this tutorial.
Once a timer's counter has reached its maximum value and increments once more, it will overflow and the counter will reset to zero. This overflow event can be configured to trigger an overflow interrupt, which we will use to wake the Arduino from sleep mode. Note you can also modify the value within a timer's counter from your code to tune the overflow period.

Figure 1. Timer source
One thing I haven't mentioned yet is the prescaler, this is another counter that is used in conjunction with the the timer counter to extend the time between timer overflows. The prescaler can be set the the following values: 1:1, 1:8, 1:64, 1:256, 1:1024, these are the ratios of prescaler overflow to clock cycles.
The following formula shows the calculation of the timeout period.


Figure 2. Timeout period formula

Take the following examples:

16 bit Timer1 no prescaler (1:1)
Overflow period =  1/16Mhz x 1 x 2^16 = 4.09 milliseconds
16 bit Timer1 with prescaler of 1:1024
Overflow period = 1/16Mhz x 1024 x 2^16 = 4.09 seconds  
So we can see that using the maximum prescaler value of 1:1024, we can get maximum Timer1 overflow period of 4.09 seconds, this is the maximum time we can put the Arduino to sleep for using Timer1. If you want a longer sleep period than this, you can use the Watch Dog Timer, which can provide a sleep time of about 8 seconds (it can also be used in a lower power/sleep mode than Timer1, see Arduino/Atmega168 Timers For Sleeping). Using the WDT for waking from sleep is described in the 5th part of this series.

Timers and Power Modes
As I have mentioned in part 1 of this series, not all hardware peripherals of the micro-controller are available in all power modes. Looking at our table here, we can see that timer1's lowest running power mode is IDLE, so when we put the micro-controller to sleep, we need to make sure we don't enter a sleep mode below this, or the timer will be disabled.

Control Registers
The following are the timer related control registers we will use to configure Timer1
  • TCCR1B - CS10, CS11 & CS12 - Timer control register B and the pre-scaler selection bits.
  • TCNT1 - 16bit counter register. This is the actual register that will count up each timer tick. When the value in this register rolls over from 65536 to 0, the overflow interrupt will fire.
  • TIMSK1 - TOIE1 - Interrupt mask register and  overflow interrupt enable bit. This register contains the control bits to enable the various interrupts available to timer1, including TOIE1 which we will set to enable the overflow interrupt.


For more information on these registers, see section 16.11 of the datasheet here.


Operation
Our code will operate as follows:
  1. Configure the serial port and LED pin.
  2. Configure Timer1's prescaler so the timer will expire every 4.09 seconds (TCCR1B).
  3. Clear Timer1's counter TCNT1
  4. Enable Timer1 overflow interrupt (TIMSK1).
  5. The main loop will then:
    1. Enter IDLE sleep mode.
    2. When the Timer1 overflow occurs, the interrupt will wake the processor
    3. The LED state will be toggled.
    4. Re-enter IDLE sleep mode.
Code
In the following code the Arduino board will go to sleep (SLEEP_MODE_IDLE) for 4.09 seconds, wake up and toggle the LED and go back to sleep again.
/*
 * Sketch for testing sleep mode with wake up on timer.
 * Donal Morrissey - 2011.
 *
 */
#include <avr/sleep.h>
#include <avr/power.h>

#define LED_PIN (13)

volatile int f_timer=0;



/***************************************************
 *  Name:        ISR(TIMER1_OVF_vect)
 *
 *  Returns:     Nothing.
 *
 *  Parameters:  None.
 *
 *  Description: Timer1 Overflow interrupt.
 *
 ***************************************************/
ISR(TIMER1_OVF_vect)
{
  /* set the flag. */
   if(f_timer == 0)
   {
     f_timer = 1;
   }
}


/***************************************************
 *  Name:        enterSleep
 *
 *  Returns:     Nothing.
 *
 *  Parameters:  None.
 *
 *  Description: Enters the arduino into sleep mode.
 *
 ***************************************************/
void enterSleep(void)
{
  set_sleep_mode(SLEEP_MODE_IDLE);
  
  sleep_enable();


  /* Disable all of the unused peripherals. This will reduce power
   * consumption further and, more importantly, some of these
   * peripherals may generate interrupts that will wake our Arduino from
   * sleep!
   */
  power_adc_disable();
  power_spi_disable();
  power_timer0_disable();
  power_timer2_disable();
  power_twi_disable();  

  /* Now enter sleep mode. */
  sleep_mode();
  
  /* The program will continue from here after the timer timeout*/
  sleep_disable(); /* First thing to do is disable sleep. */
  
  /* Re-enable the peripherals. */
  power_all_enable();
}



/***************************************************
 *  Name:        setup
 *
 *  Returns:     Nothing.
 *
 *  Parameters:  None.
 *
 *  Description: Setup for the serial comms and the
 *                timer. 
 *
 ***************************************************/
void setup()
{
  Serial.begin(9600);
  
  /* Don't forget to configure the pin! */
  pinMode(LED_PIN, OUTPUT);

  /*** Configure the timer.***/
  
  /* Normal timer operation.*/
  TCCR1A = 0x00; 
  
  /* Clear the timer counter register.
   * You can pre-load this register with a value in order to 
   * reduce the timeout period, say if you wanted to wake up
   * ever 4.0 seconds exactly.
   */
  TCNT1=0x0000; 
  
  /* Configure the prescaler for 1:1024, giving us a 
   * timeout of 4.09 seconds.
   */
  TCCR1B = 0x05;
  
  /* Enable the timer overlow interrupt. */
  TIMSK1=0x01;
}



/***************************************************
 *  Name:        enterSleep
 *
 *  Returns:     Nothing.
 *
 *  Parameters:  None.
 *
 *  Description: Main application loop.
 *
 ***************************************************/
void loop()
{
  if(f_timer==1)
  {
    f_timer = 0;
    /* Toggle the LED */
    digitalWrite(LED_PIN, !digitalRead(LED_PIN));
    
    /* Re-enter sleep mode. */
    enterSleep();
  }
}

Wednesday, 23 November 2011

Arduino/Atmega168 Timers For Sleeping

The following is a table outlining the available timers of the Arduino, their max timeout period (how long you can put your Arduino to sleep for) and the lowest power saving mode that the timer can be used in. I hope it will help you in your choice of sleep timer.


Timer Size Max Timeout Period Min Power Mode
Timer0
8 bit
16.4ms
IDLE
Timer1
16 bit
4.1s
IDLE
Timer2
8 bit
16.4ms
POWER_SAVE
Watch Dog Timer
N/A
8s
PWR_DOWN


Note PWR_DOWN is the power mode with the least power consumption. Please refer to Part 1 Overview Of Arduino Sleep Modes for a description of the available power modes.


Details for the use of timers and watch dog for sleep control can be found in parts 4 and 5 of the Sleeping Arduino Series.


If you need a longer sleep period than 8 seconds, and don't want to manage a software timer that is ticked every 8 seconds, then you could consider an external real-time clock such as:
http://proto-pic.co.uk/deadon-rtc-ds3234-breakout/

Friday, 18 November 2011

Beagleboard Ubuntu 11.10 Single User Mode

Oops
I recently had a problem with root access on my Beagleboard running Ubuntu 11.10 from an SD card. Basically I had accidentally removed my user account from the admin group. As it was the only user account I had set-up when installing Ubuntu: I was stuck!


Solution
The only option that I was aware of that would allow me to recover root access, was to boot Ubuntu in single user mode and add my user account back into the admin group.
I tried this by editing the UBoot bootargs parameter so I could enter Single user mode, setting it to:


OMAP3 beagleboard.org #  setenv bootargs 'console=ttyS0,115200n8 
root=1f01 rootfstype=jffs2 reboot=cold,hard S init=/bin/sh' 
OMAP3 beagleboard.org # boot
(NOTE This won't work!)


Note the addition of 'S' to the kernel parameters, which should instruct Ubuntu to enter into single user mode. AFAIK 'emergency' can also be used, but that brings you into a more basic environment.


This should have worked! But Ubuntu wasn't booting into single user mode. I didn't understand it until I looked at the complete bootcmd UBoot was using, which was the following:


setenv bootcmd 'mmc init;
fatload mmc 0 0x82000000 boot.scr;
source 0x82000000';
setenv autostart yes;
saveenv;
boot


Ahh, so the boot.scr script was being loaded from the boot partition on the SD card. I had a look at that file by mounting the SD card on my MAc and found that that script was overriding the bootargs parameter that I had manually set in UBoot.


Simple solution was to run the following commands in UBoot that bypassed the script on the boot partition of the SD card:
mmc init; mmc rescan 0; fatload mmc 0 0x82000000 
fatload mmc 0:1 0x80000000 uImage 
fatload mmc 0:1 0x81600000 uInitrd 
setenv bootargs ro vram=12M omapfb.mode=dvi:1280x720MR-16@60 
mpurate=auto root=UUID=76bfdd66-898d-4d50-be7a-4c8fd2691266 fixrtc 
console=ttyO2,115200n8 S
bootm 0x80000000 0x81600000


Once booted into the command prompt I added my user to the admin group and restarted the board. Problem solved.


Hindsight:
  • Could I have mounted the SD card on my Linux PC and edited the appropriate file to re-add my user account to the admin group?
  • Always a good idea to create a second user account that has admin access.