Thursday, February 28, 2008

Idle and Power Save ADC Example ( Mega )

The second ADC control example is rather more ambitious and uses
both the Idle and Power Save sleep modes. The sample period is set via
an interrupt from Timer0.



/******************************************************************************

Sample program to demonstrate using the Mega103 analog to digital converter
(ADC) in conjunction with the Idle sleep mode during sampling and Power Save
sleep mode between samples for lowest possible power when the cpu must restart
itself. Note that if the cpu can be interrupted by an external event, you could
use the Power Down mode for even lower power.

For demonstration purposes, the high 8 bits of channel 3 (10-2) are written
to PORTB, the set of LEDs on an STK300.

Ron Kreymborg
May 2001

******************************************************************************/

#include <stdlib.h>
#include <interrupt.h>
#include <signal.h>

#define BYTE unsigned char
#define SLEEP asm("sleep"::)
#define CLK0_DIVIDER 0x05 // PCK0 / 128
#define CLK0_COUNT 256-128 // 500 mSec tick
#define ADC_CONTROL (1<<ADEN | 1<<ADIE | 1<<ADPS2 | 1<<ADPS0)

typedef enum
{
UNDEFINED_MODE,
POWER_SAVE_MODE,
IDLE_MODE
} MODE;

int main(void);
void AtoDconverter(void);
void SetPowerSaveMode(MODE type);
void InitAtoD(void);
void InitTicker(void);
void StartupDelay(void);

static volatile int Sample[8]; // a/d converter samples
static volatile BYTE Index; // current sample

int main(void)
{
// PORTB
outp(0xff, PORTB); // all low
outp(0xff, DDRB); // all output
sbi(ACSR, ACD); // disable comparator

InitTicker(); // configure Timer0
InitAtoD(); // init the A/D converter

StartupDelay(); // allow clocks to settle
sei(); // hello world

AtoDconverter(); // never comes back

return 0;
}

//---------------------------------------------------------------------
//
void AtoDconverter(void)
{
BYTE value;

while (1)
{
SLEEP; // power save sleep

// Ensure sleep mode during conversions is Idle mode.
//
SetPowerSaveMode(IDLE_MODE);

// Take the current sample readings
//
for (Index=0; Index < 8; Index++)
{
cbi(ADCSR, ADEN); // turn off ADC
outp(Index, ADMUX); // select the channel
outp(ADC_CONTROL | 1<<ADSC, ADCSR); // turn ADC on and start conversion
SLEEP; // idle sleep
}

// Restore sleep mode to Power Save for maximum power savings.
//
SetPowerSaveMode(POWER_SAVE_MODE);

value = ~(Sample[2]>>2); // display channel 3
outp(value, PORTB);
}
}

//-----------------------------------------------------------------------------
//
void InitAtoD(void)
{
// Enable ADC with a 125,000 KHz clock
//
outp(ADC_SAMPLE, ADCSR);

// Setup to start in Power Save sleep mode
//
SetPowerSaveMode(POWER_SAVE_MODE);
}

//---------------------------------------------------------------------
// Setup the MCUCR register as requested.
//
void SetPowerSaveMode(MODE type)
{
switch (type)
{
case POWER_SAVE_MODE:
sbi(MCUCR, SM0);
sbi(MCUCR, SM1);
sbi(MCUCR, SE);
break;

case IDLE_MODE:
cbi(MCUCR, SM0);
cbi(MCUCR, SM1);
sbi(MCUCR, SE);
break;
}
}

//---------------------------------------------------------------------
// Initialise the Timer0 to provide a sample period
//
void InitTicker(void)
{
sbi(ASSR, AS0); // clock from external crystal
outp(CLK0_DIVIDER, TCCR0);
outp(CLK0_COUNT, TCNT0);
sbi(TIMSK, TOIE0); // enable Timer0 interrupts
}

//---------------------------------------------------------------------
// Startup delay
//
void StartupDelay(void)
{
int i, j;

for (i=0; i<200; i++)
j = i;
}


//-----------------------------------------------------------------------------
// INTERRUPT HANDLERS
//-----------------------------------------------------------------------------
//
//---------------------------------------------------------------------
// The event that triggers an A/D sample.
//
SIGNAL(SIG_OVERFLOW0)
{
outp(CLK0_COUNT, TCNT0); // reset counter
}

//-----------------------------------------------------------------------------
// The current AtoD conversion has completed. Copy the value (in ADCH and ADCL)
// to the current array location.
//
SIGNAL(SIG_ADC)
{
// Read the ADC into the current entry
//
Sample[Index] = inp(ADCL) | inp(ADCH)<<8;
}

No comments: