EmbeddedRelated.com
Forums

Lowest Current Consumption with ADC Running

Started by "emil.marinov" July 22, 2008
Hi~

I am doing some testing with the MSP430FG461x processor right now in
an attempt to get the current consumption under 100 microamperes.
The program uses the ADC to obtain temperature, then display it onto
the LCD. Whenever I measure the current on the board, it shows up as
lower 500 to upper 600 microamps. This is sounding an alarm in my
head because 1) while not interrupting, the current should be MUCH
lower than that (I measured it at 9 microamps before adding the ADC
part) 2)I was told by someone else who's worked with this
microprocessor before that for a program like this, the current
consumption should stay under 100 microamps. I've pasted the code
below.

#include
#include
#include "msp430xG46x.h"
#include
#include "string.h"
#include
volatile unsigned int i,k;
volatile unsigned int ADCresult;
volatile unsigned long int DegC, DegF;
int stringLength;
char displayBuffer[10];

void ReverseArray(char*orig,unsigned short int b);
//--------------------------------
---------
void main(void)
{
WDTCTL = WDTPW + WDTHOLD; // Stop WDT

P1DIR = (0xff & ~(BIT0+BIT1)); // P1.0, P1.1 input, others output
P1OUT = 0;
P2DIR = (0xff & ~BIT5); // P2.5 must be input
P2OUT = 0;
P3DIR = (0xff & ~BIT5); // P3.5 must be high
P3OUT = (BIT1 + BIT2); // '2013 could possibly tied these high
P4DIR = 0xff;
P4OUT = 0;
P5DIR = 0xff;
P5OUT = 0;
P6DIR = 0xff;
P6OUT = 0;
P7DIR = 0xff;
P7OUT = 0;
P8DIR = 0xff;
P8OUT = 0;
P9DIR = 0xff;
P9OUT = 0;
P10DIR = 0xff;
P10OUT = 0;

FLL_CTL0 |= XCAP14PF; // Configure load caps
TACTL = TASSEL_1 + TACLR; // Clock = ACLK (32768),
clear
TACCTL0 = CCIE; // CCR0 interrupt enabled
TACCR0 = 32768-1; // #counts for 1s
TACTL |= MC_1; // Setting mode bits
starts timer
while(1)
{_BIS_SR(LPM3_bits + GIE);} // Enter LPM3
}
//--------------------------------
---------
// Timer A0 interrupt service routine
#pragma vector=TIMERA0_VECTOR
__interrupt void Timer_A (void)
{
initLCD_A();
dispChar(15,0);

ADC12CTL0 = ADC12ON + REFON + REF2_5V + SHT0_7;
// Turn ADC,ref on. Ref = 2.5V,
sampling time
ADC12CTL1 = SHP; // Use sampling timer
ADC12MCTL0 = INCH_10 + SREF_1; // Select channel A10,
Vref+
ADC12IE = 0x01; // Enable ADC12IFG.0
for (i = 0; i < 0x3600; i++); // Delay for reference
start-up
ADC12CTL0 |= ENC; // Enable conversions
__enable_interrupt(); // Enable interrupts
ADC12CTL0 |= ADC12SC; // Start conversion
__bis_SR_register(LPM0_bits); // Enter LPM0

// DegC = (Vsensor - 986mV)/3.55mV
// Vsensor = (Vref)(ADCresult)/4095)
// DegC -> ((ADCresult - 1615)*704)/4095
__no_operation(); // SET BREAKPOINT

sprintf(displayBuffer,"%ld",DegF);
stringLength = strlen(displayBuffer);
ReverseArray(displayBuffer,stringLength);
for(i=0;i {dispChar(displayBuffer[i]-0x30, i+1);}

}
//--------------------------------
---------
#pragma vectorC12_VECTOR
__interrupt void ADC12ISR(void)
{
ADCresult = ADC12MEM0; // Move results, IFG is
cleared
DegC = ((((long)ADCresult-1615)*704)/4095);
DegF = ((DegC * 9/5)+32); // Calculate DegF
__no_operation(); // SET BREAKPOINT

__bic_SR_register_on_exit(LPM0_bits); // Exit LPM0
}
//--------------------------------
---------
void ReverseArray(char*orig,unsigned short int b)
{
unsigned short int a;
int swap;

for(a=0;a<--b;a++) //increment a and decrement b until they meet
eachother
{
swap=orig[a]; //put what's in a into swap space
orig[a]=orig[b]; //put what's in b into a
orig[b]=swap; //put what's in the swap (a) into b
}

}

Beginning Microcontrollers with the MSP430

I think your TimerA0 isr is faulty. You should have moved most of that
to the while(1) loop in main after "_BIS_SR(LPM3_bits + GIE);".

"for (i = 0; i < 0x3600; i++);" wasts energy.

"__bis_SR_register(LPM0_bits);" will not make your TimerA0 isr enter LPM0.

Nesting the ADC12 interrupt inside TimerA0 interrupt is unnecessary
and incorrectly done.

Calling library routines inside isr may cause problems too.

--- In m..., "emil.marinov" wrote:
>
> Hi~
>
> I am doing some testing with the MSP430FG461x processor right now in
> an attempt to get the current consumption under 100 microamperes.
> The program uses the ADC to obtain temperature, then display it onto
> the LCD. Whenever I measure the current on the board, it shows up as
> lower 500 to upper 600 microamps. This is sounding an alarm in my
> head because 1) while not interrupting, the current should be MUCH
> lower than that (I measured it at 9 microamps before adding the ADC
> part) 2)I was told by someone else who's worked with this
> microprocessor before that for a program like this, the current
> consumption should stay under 100 microamps. I've pasted the code
> below.
> #include
> #include
> #include "msp430xG46x.h"
> #include
> #include "string.h"
> #include
> volatile unsigned int i,k;
> volatile unsigned int ADCresult;
> volatile unsigned long int DegC, DegF;
> int stringLength;
> char displayBuffer[10];
>
> void ReverseArray(char*orig,unsigned short int b);
> //--------------------------------
> ---------
> void main(void)
> {
> WDTCTL = WDTPW + WDTHOLD; // Stop WDT
>
>
> P1DIR = (0xff & ~(BIT0+BIT1)); // P1.0, P1.1 input, others output
> P1OUT = 0;
> P2DIR = (0xff & ~BIT5); // P2.5 must be input
> P2OUT = 0;
> P3DIR = (0xff & ~BIT5); // P3.5 must be high
> P3OUT = (BIT1 + BIT2); // '2013 could possibly tied these high
> P4DIR = 0xff;
> P4OUT = 0;
> P5DIR = 0xff;
> P5OUT = 0;
> P6DIR = 0xff;
> P6OUT = 0;
> P7DIR = 0xff;
> P7OUT = 0;
> P8DIR = 0xff;
> P8OUT = 0;
> P9DIR = 0xff;
> P9OUT = 0;
> P10DIR = 0xff;
> P10OUT = 0;
>
>
> FLL_CTL0 |= XCAP14PF; // Configure load caps
> TACTL = TASSEL_1 + TACLR; // Clock = ACLK (32768),
> clear
> TACCTL0 = CCIE; // CCR0 interrupt enabled
> TACCR0 = 32768-1; // #counts for 1s
> TACTL |= MC_1; // Setting mode bits
> starts timer
> while(1)
> {_BIS_SR(LPM3_bits + GIE);} // Enter LPM3
> }
> //--------------------------------
> ---------
> // Timer A0 interrupt service routine
> #pragma vector=TIMERA0_VECTOR
> __interrupt void Timer_A (void)
> {
> initLCD_A();
> dispChar(15,0);
>
> ADC12CTL0 = ADC12ON + REFON + REF2_5V + SHT0_7;
> // Turn ADC,ref on. Ref = 2.5V,
> sampling time
> ADC12CTL1 = SHP; // Use sampling timer
> ADC12MCTL0 = INCH_10 + SREF_1; // Select channel A10,
> Vref+
> ADC12IE = 0x01; // Enable ADC12IFG.0
> for (i = 0; i < 0x3600; i++); // Delay for reference
> start-up
> ADC12CTL0 |= ENC; // Enable conversions
> __enable_interrupt(); // Enable interrupts
> ADC12CTL0 |= ADC12SC; // Start conversion
> __bis_SR_register(LPM0_bits); // Enter LPM0
>
> // DegC = (Vsensor - 986mV)/3.55mV
> // Vsensor = (Vref)(ADCresult)/4095)
> // DegC -> ((ADCresult - 1615)*704)/4095
> __no_operation(); // SET BREAKPOINT
>
> sprintf(displayBuffer,"%ld",DegF);
> stringLength = strlen(displayBuffer);
> ReverseArray(displayBuffer,stringLength);
> for(i=0;i > {dispChar(displayBuffer[i]-0x30, i+1);}
>
>
> }
> //--------------------------------
> ---------
> #pragma vectorC12_VECTOR
> __interrupt void ADC12ISR(void)
> {
> ADCresult = ADC12MEM0; // Move results, IFG is
> cleared
> DegC = ((((long)ADCresult-1615)*704)/4095);
> DegF = ((DegC * 9/5)+32); // Calculate DegF
> __no_operation(); // SET BREAKPOINT
>
>
> __bic_SR_register_on_exit(LPM0_bits); // Exit LPM0
> }
> //--------------------------------
> ---------
> void ReverseArray(char*orig,unsigned short int b)
> {
> unsigned short int a;
> int swap;
>
> for(a=0;a<--b;a++) //increment a and decrement b until they meet
> eachother
> {
> swap=orig[a]; //put what's in a into swap space
> orig[a]=orig[b]; //put what's in b into a
> orig[b]=swap; //put what's in the swap (a) into b
> }
>
> }
>

Also, you are doing some monster math with all those multiplies and
divides:
>> DegC = ((((long)ADCresult-1615)*704)/4095);
>> DegF = ((DegC * 9/5)+32);
I don't know if your selected MSP430 device has a hardware multiplier
or not, but if not, that is a lot of code execution to complete those
temperature conversions (more runtime = less low power mode).

If you have lots of free flash space, you may want to consider using
a look-up table for the most common temperature ranges and saving the
hard math for temperatures outside of your expected range. Or just
build everything into a table, but that would be 4K for a 12-bit ADC
value range.

<...short pause for some quick math...>

Okay, it looks like for every incremental increase in the ADC12
reading (for example, reading 2000d versus 2001d), the temperature
resolution would be 0.1719 degrees C (66.1880C vs. 66.3599C). Since
the decimal values get lopped off anyway, you could easily shift your
12-bit readings down to, say, 10 bit by throwing away the lowest two
bits and still maintain a Celcius resolution of 0.687 degrees C.
That makes your entire Celius lookup table only 1KBytes in size.
Create a Fahrenheit table of like size (1KBytes) and you have a
decreased your execution time by several factors. You just need to
use a spreadsheet or simple C/Basic/Perl/your-favorite-language
program to pre-generate the values from your equations and load them
as a header file into your source code at compile time.

Just my $0.02 worth; Hope it helps.

--- In m..., "old_cow_yellow"
wrote:
>
> I think your TimerA0 isr is faulty. You should have moved most of
that
> to the while(1) loop in main after "_BIS_SR(LPM3_bits + GIE);".
>
> "for (i = 0; i < 0x3600; i++);" wasts energy.
>
> "__bis_SR_register(LPM0_bits);" will not make your TimerA0 isr
enter LPM0.
>
> Nesting the ADC12 interrupt inside TimerA0 interrupt is unnecessary
> and incorrectly done.
>
> Calling library routines inside isr may cause problems too.
>
> --- In m..., "emil.marinov" wrote:
> >
> > Hi~
> >
> > I am doing some testing with the MSP430FG461x processor right now
in
> > an attempt to get the current consumption under 100
microamperes.
> > The program uses the ADC to obtain temperature, then display it
onto
> > the LCD. Whenever I measure the current on the board, it shows
up as
> > lower 500 to upper 600 microamps. This is sounding an alarm in
my
> > head because 1) while not interrupting, the current should be
MUCH
> > lower than that (I measured it at 9 microamps before adding the
ADC
> > part) 2)I was told by someone else who's worked with this
> > microprocessor before that for a program like this, the current
> > consumption should stay under 100 microamps. I've pasted the
code
> > below.
> >
> >
> >
> >
> >
> >
> > #include
> > #include
> > #include "msp430xG46x.h"
> > #include
> > #include "string.h"
> > #include
> >
> >
> > volatile unsigned int i,k;
> > volatile unsigned int ADCresult;
> > volatile unsigned long int DegC, DegF;
> > int stringLength;
> > char displayBuffer[10];
> >
> > void ReverseArray(char*orig,unsigned short int b);
> > //----------------------------
----
> > ---------
> > void main(void)
> > {
> > WDTCTL = WDTPW + WDTHOLD; // Stop WDT
> >
> >
> > P1DIR = (0xff & ~(BIT0+BIT1)); // P1.0, P1.1 input, others
output
> > P1OUT = 0;
> > P2DIR = (0xff & ~BIT5); // P2.5 must be input
> > P2OUT = 0;
> > P3DIR = (0xff & ~BIT5); // P3.5 must be high
> > P3OUT = (BIT1 + BIT2); // '2013 could possibly tied these high
> > P4DIR = 0xff;
> > P4OUT = 0;
> > P5DIR = 0xff;
> > P5OUT = 0;
> > P6DIR = 0xff;
> > P6OUT = 0;
> > P7DIR = 0xff;
> > P7OUT = 0;
> > P8DIR = 0xff;
> > P8OUT = 0;
> > P9DIR = 0xff;
> > P9OUT = 0;
> > P10DIR = 0xff;
> > P10OUT = 0;
> >
> >
> > FLL_CTL0 |= XCAP14PF; // Configure load caps
> > TACTL = TASSEL_1 + TACLR; // Clock = ACLK
(32768),
> > clear
> > TACCTL0 = CCIE; // CCR0 interrupt
enabled
> > TACCR0 = 32768-1; // #counts for 1s
> > TACTL |= MC_1; // Setting mode bits
> > starts timer
> > while(1)
> > {_BIS_SR(LPM3_bits + GIE);} // Enter LPM3
> > }
> > //----------------------------
----
> > ---------
> > // Timer A0 interrupt service routine
> > #pragma vector=TIMERA0_VECTOR
> > __interrupt void Timer_A (void)
> > {
> > initLCD_A();
> > dispChar(15,0);
> >
> > ADC12CTL0 = ADC12ON + REFON + REF2_5V + SHT0_7;
> > // Turn ADC,ref on. Ref = 2.5V,
> > sampling time
> > ADC12CTL1 = SHP; // Use sampling timer
> > ADC12MCTL0 = INCH_10 + SREF_1; // Select channel
A10,
> > Vref+
> > ADC12IE = 0x01; // Enable ADC12IFG.0
> > for (i = 0; i < 0x3600; i++); // Delay for
reference
> > start-up
> > ADC12CTL0 |= ENC; // Enable conversions
> > __enable_interrupt(); // Enable interrupts
> >
> >
> > ADC12CTL0 |= ADC12SC; // Start conversion
> > __bis_SR_register(LPM0_bits); // Enter LPM0
> >
> > // DegC = (Vsensor - 986mV)/3.55mV
> > // Vsensor = (Vref)(ADCresult)/4095)
> > // DegC -> ((ADCresult - 1615)*704)/4095
> > __no_operation(); // SET BREAKPOINT
> >
> > sprintf(displayBuffer,"%ld",DegF);
> > stringLength = strlen(displayBuffer);
> > ReverseArray(displayBuffer,stringLength);
> > for(i=0;i > > {dispChar(displayBuffer[i]-0x30, i+1);}
> >
> >
> > }
> > //----------------------------
----
> > ---------
> > #pragma vectorC12_VECTOR
> > __interrupt void ADC12ISR(void)
> > {
> > ADCresult = ADC12MEM0; // Move results, IFG
is
> > cleared
> > DegC = ((((long)ADCresult-1615)*704)/4095);
> > DegF = ((DegC * 9/5)+32); // Calculate DegF
> > __no_operation(); // SET BREAKPOINT
> >
> >
> > __bic_SR_register_on_exit(LPM0_bits); // Exit LPM0
> > }
> > //----------------------------
----
> > ---------
> > void ReverseArray(char*orig,unsigned short int b)
> > {
> > unsigned short int a;
> > int swap;
> >
> > for(a=0;a<--b;a++) //increment a and decrement b until they
meet
> > eachother
> > {
> > swap=orig[a]; //put what's in a into swap space
> > orig[a]=orig[b]; //put what's in b into a
> > orig[b]=swap; //put what's in the swap (a) into b
> > }
> >
> > }
>