Hello,
I'm aware such basic topics aren't for many of us here, but maybe some beginners & hobbyists would found it useful.
Project description:Suppose you wan't to make some kind of device that request execution of code periodically and for some quite small amount of time. In my example here it is model of street lights (just for the sake of simplicity).
There are many ways to complete this task, but I've chose 3 LEDs and small micro (PIC12F509).
Hardware isn't important here (direct drive of LED with uP output pin w/series resistor, single cell LiPo battery without voltage stabilizer).
To achieve lowest possible uP current usage one way is to use SLEEP command. It puts uP in sleep mode making it power consumption as low as about 0.1-0.2uA (according to datasheet). It is about 6000-10000 times lower than normal operation @ 4MHz.
In this particular example it doesn't make much sense, considering LEDs consuming about 20mA, but designing low power battery powered system (e.g. wireless sensor mesh) makes each uA draw important when you don't want to replace/recharge batteries each day.
Using SLEEP command forces code to be somewhat different from casual execution scheme (with loops/interrupts/etc).
SLEEP in this particular example is used to enter sleep mode after configuring watchdog (WDT) to reset uP each 72ms. (nominally 12f509's WDT timer expires each 18ms, but using prescaler it is possible to extend that period up to 2.3s).
After watchdog expires, processor core is resetted and forces execution "from the beginning of code memory".
This implies main() function to be considered as interrupt-like handler. It should checks the source of reset (WDT? power-on-reset? pin-on-change-reset?) and behave accordingly.
Here you've got the code for analysis:
#if defined(__XC)
#include <xc.h> /* XC8 General Include File */
#elif defined(HI_TECH_C)
#include <htc.h> /* HiTech General Include File */
#endif
#include <stdint.h> /* For uint8_t definition */
#include <stdbool.h> /* For true/false definition */
// CONFIG
#pragma config OSC = IntRC // Oscillator Selection bits (internal RC oscillator)
#pragma config WDT = ON // Watchdog Timer Enable bit (WDT enabled)
#pragma config CP = OFF // Code Protection bit (Code protection off)
#pragma config MCLRE = ON // GP3/MCLR Pin Function Select bit (GP3/MCLR pin function is MCLR)
/******************************************************************************/
/* Main Program */
/******************************************************************************/
void main(void)
{
const char _iLits[4] = {0x01,0x03,0x04,0x02};
const char _iLitt[4] = {100,28,100,56};
char iLitIdx;
char bTimed;
char iBtnCnt;
char iTicks;
// setup OPTION as a first thing to do after each reset
OPTION = 0b11001010; /* GPWU off, GPPU off, WDT<-presc, presc 1:4 */
/* 18ms * 4 = 0.072s */
// setup TRIS
TRIS = 0x10;
/* power-up or another kind of reset occured (wdt?) */
if (STATUSbits.nPD)
{
/* after power-up init */
bTimed = 1;
iBtnCnt = 0;
iTicks = 0;
SLEEP();
}
/*
* STATUSbits.TO = 0 -> WDT timeout occured
* OPTIONbits.PSA = 1 -> prescaler to WDT
* OPTIONbits.PS = 1-7 -> WDT presc 1:1 to 1:128
*/
/* wake up*/
/* check the button periodically every WDT wakeup */
if (GPIObits.GP4)
{
/* button released */
if (iBtnCnt>15)
{
/* was long hold? */
/* time > threshold */
bTimed = !bTimed;
} else
/* short press */
if (iBtnCnt>2)
{
iLitIdx++;
}
iBtnCnt=0;
} else
{
/* button pressed */
iBtnCnt++;
if (iBtnCnt>15)
{
GPIO = 0x07;
while (!GPIObits.GP4)
{
CLRWDT(); /* prevent from resetting the CPU now */
}
}
}
/* increase Ticks ... */
iTicks++;
if (bTimed)
{
/* timed operation */
if (iTicks>_iLitt[iLitIdx])
{
/* light expired, switch to next */
iTicks=0x00;
iLitIdx++;
}
}
if (iLitIdx==4) iLitIdx = 0;
GPIO = GPIO & 0x38;
GPIO += _iLits[iLitIdx];
/* go to sleep for a wdt period or wait for key*/
SLEEP();
}