Forum Menu




 


Log in Problems?
New User? Sign Up!
AVR Freaks Forum Index

Post new topic   Reply to topic
View previous topic Printable version Log in to check your private messages View next topic
Author Message
Chipit
PostPosted: May 20, 2012 - 01:44 PM
Newbie


Joined: May 20, 2012
Posts: 4
Location: Peak District, UK

Hello my names andrew and i've been teaching myself about C programming and avr's on and off for the past few months. this is my first proper project apart from flashing leds etc.

Project;
A tiny13 charlieplexed battery gauge for my friends R/C car. Using 6 leds of appropriate colours to show power left in batteries.

Operation;
A potential divider reduces 11.1v nominal battery to ~1v to feed adc Pinb4 which is sampled 10/second. A array stores the LED status. A timer interupt scans the array choosing which led to light as only one can be lit at a time, which are on Pinb0-Pinb2.

The problem;
Works perfectly when the adc ref is set to vcc and using a potentiometer, but when changed in code to internal 1.1v ref LEDS flicker wildly hardly ever show a steady state.
I've tried feeding the adc pin from a 1.5v battery and potentiometer to isolate any possible supply ripple, also 5v supply to avr is decoupled with a big capacitor.
Can anyone suggest things to try to stop the irratic readout when ref is internal 1.1v?

Also i stole a lot of the code from Eloque's great tut on here, and anajonsr's awesome vidoe's on youtube! Very Happy

P.S. I dont get to a computer very often...

Here is the code....


Code:
/*..........Charlieplexing Battery Guage...........*/

#define LED1on 864   
#define LED2on 884
#define LED3on 891       //adc count at which leds switch on
#define LED4on 903      //0>1023 = 0>ref volts
#define LED5on 924
#define LED6on 942

#define ADCfire 627    /*9.6mhz(clock) /255(timer overflow) /6(No of leds) /627 = 1/10
                  second, counts up to 65535  possible (unsigned int) */


#include<avr/io.h>
#include<util/delay.h>            //Header files
#include<stdlib.h>
#include<avr/interrupt.h>

void checkLED  (void);
void setresult (int ADCcount);
void setled    (char nLED);        //Define prototype for setled function
void setpowerguage (char npower);  //Define prototype for setpowerguage function
char aLEDarray [6] = {0};         //Array to hold led status

uint8_t ncurrentLED;               //defining variable ncurrentLED
unsigned int ADCtick;               //counts up to 65535  possible

int main(void)                  //Main program

   DDRB  = 0b000000;               //All ports data direction to input
   PORTB = 0b000000;            //All ports to 0

   sei();

   ADCSRA |= 1<<ADPS1;       //PRESCALER 64. 150K = @ 9.6MHZ
   ADCSRA |= 1<<ADPS2;       //PRESCALER 64. 150K = @ 9.6MHZ
   ADMUX  |= 1<<MUX1 ;      //ADC INPUT PB4 IC PIN3
   ADMUX  |= 1<<REFS0 ;      //ADC VOLTAGE REFERENCE INTERNAL(COMMENT OUT FOR EXTERNAL)
   ADCSRA |= 1<<ADEN ;       //ENABLE ADC
   TIMSK0 |= 1<<TOIE0;     //enable CTC interupt
   ADCSRA |= 1<<ADIE ;       //INTERUPT ENABLE
   ADCSRA |= 1<<ADSC ;       //START CONVERSION
   
   TCCR0A &= ~1<<WGM00;    //wave generation mode 0 (0 by default)
   TCCR0B |= 1<< CS00;     //timer prescaler to scan leds,fires interupt 37647 times a second

      while (1)               //Endless loop
      {
      }

   }

ISR(TIM0_OVF_vect)
   {   
   DDRB  = 0b000000;      //set all ports to 0 ready for new reading
   PORTB = 0b000000;   
   checkLED();            //call checkled function
   }


ISR(ADC_vect)                   //adc interupt vector
   {
   uint8_t thelow = ADCL;                  //reading lowest 8 bits of adc
   uint16_t tenbitvalue =ADCH << 8 | thelow;   //convert ADC to 10 bits
   setresult  (tenbitvalue);            //call setresult with tenbitvalue
   }


void setresult (int ADCcount)      //funtion setresult and its varible ADCcount
{
   if      (ADCcount >=LED6on)
       setpowerguage(6);
      
    else if(ADCcount >=LED5on)      //check which setpowerguage number to call
       setpowerguage(5);

    else if(ADCcount >=LED4on)
       setpowerguage(4);            

    else if(ADCcount >=LED3on)
       setpowerguage(3);

    else if(ADCcount >=LED2on)
       setpowerguage(2);
   
    else if(ADCcount >=LED1on)
       setpowerguage(1);      
      
    else
       setpowerguage(0);
}



void setpowerguage (char npower)   //Function setpowerguage and its varible npower
   {

      ncurrentLED = 0;               
      
      for (uint8_t i = 0; i < 6; ++i)   //Reset all of array to 0 using a loop
      {                           
         aLEDarray[i] = 0;         
      }
      
      for (uint8_t i = 0; i < npower; ++i)  //filling array with required leds to be lit
      {
         aLEDarray [i] = 1;
      }
   }



void checkLED (void)   //function checkLED called from timer interupt vector
{


   if (aLEDarray[ncurrentLED]==1) // checking against ncurrentLED which led needs to be lit
   {
      setled(ncurrentLED);       //send led number to setled
      ncurrentLED ++;            // advance to next led
   }
   
   else
   {
      ncurrentLED++;
   }
   
   if(ncurrentLED == 6)      //if current led count = 6 reset ncurrentLED.
   {
      ncurrentLED = 0;
   
      if(ADCtick == ADCfire)//9.6mhz(clock) / 255(timer overflow) / 6(No of leds) / 627 = 1/10 second
      {
         ADCSRA |= 1<<ADSC; //scan led's Xmany times then start new conversion
         ADCtick = 0;       //reset ADCcount
      }
   ADCtick ++;
   }


}

void setled (char nLED)         //Function setled and its varible nLED,
{
   switch (nLED)
      {
      case 0:
         DDRB  = 0b000011;   //switch case argument for each led according to nLED
         PORTB = 0b000001;
         break;
      case 1:
         DDRB  = 0b000110;
         PORTB = 0b000010; // lighting leds sequentially 1 3 2 4 5 6 (PCB design)
         break;
      case 2:
         DDRB  = 0b000011;
         PORTB = 0b000010;   
         break;
      case 3:
         DDRB  = 0b000110;
         PORTB = 0b000100;
         break;
      case 4:
         DDRB  = 0b000101;
         PORTB = 0b000100;
         break;
      case 5:
         DDRB  = 0b000101;
         PORTB = 0b000001;
         break;
      }
}            [/quote]



edited: 3/6/2012 reason: title change.


Last edited by Chipit on Jun 02, 2012 - 01:20 PM; edited 1 time in total
 
 View user's profile Send private message  
Reply with quote Back to top
Chipit
PostPosted: May 20, 2012 - 02:01 PM
Newbie


Joined: May 20, 2012
Posts: 4
Location: Peak District, UK

Just had a idea that maybe continually setting the adc pin to a input in setled, is not a good idea, but i would have thought when the adc samples the processor would be concerned with the adc and not be switching ports on and off would it not?
 
 View user's profile Send private message  
Reply with quote Back to top
gahelton
PostPosted: May 21, 2012 - 03:08 AM
Resident


Joined: Mar 19, 2003
Posts: 736


Here are a few suggestions.

First, the internal 1.1V "reference" is pretty poor as far as references go. You will be much happier using a good regulator/reference for VCC, and use VCC as your A/D reference. Getting references with initial tolerances of 3%,1%, and 0.5% is routine. Make sure that you get one with enough current capability to run your micro. FYI - this on-board reference issue is not unique to Atmel. The voltage reference accuracy of most micros with on-board references are fair to poor.

Second, generally speaking, measuring battery voltage can be done relatively slowly. So, to get the best A/D accuracy, run your A/D clock slowly (check datasheet for limits).

Third, take multiple battery voltage samples, then average. You can easily take 16,32, or 64 samples, sum, then average. Up to 64 sample can be added to a summation, and still stay within a 16 bit word (using the full 10 bit A/D result).
 
 View user's profile Send private message  
Reply with quote Back to top
Visovian
PostPosted: May 21, 2012 - 04:25 AM
Posting Freak


Joined: Aug 07, 2007
Posts: 1477
Location: Czech

Is there a capacitor 100nF on the AREF pin?
For test I would also try to filter the measured voltage with say 10 kOhm/1uF.

I do not think the internal reference is as miserable as gahelton wrote. Of course, it can differ 10% from 1.1V. You have to measure the actual value on the AREF pin and use it for computations.
I think it is sufficient for battery measuring.

Edit:
Quote:
Works perfectly when the adc ref is set to vcc
Then why not use this (with a propper divider).
 
 View user's profile Send private message  
Reply with quote Back to top
Chipit
PostPosted: Jun 02, 2012 - 02:40 PM
Newbie


Joined: May 20, 2012
Posts: 4
Location: Peak District, UK

Almost sorted! My main problem turned out to be noise on the power lines upsetting the uC. the big electrolytic across the input wasn't able to filter out the noise from a dc-dc converter i was using to power the programmer, which had been powering the circuit, although the programmer has a LDO regulator it is actually bridged so it has no effect, you live and learn!

Thank you for all the suggestions,


Works perfectly when the adc ref is set to vcc
Then why not use this (with a propper divider).


Because i'm stubborn! But i will keep it in mind.

I've implemented the averaging, presume this is how you meant me to do it?

Code:
void callADC (void)
{   
   ADCresult = 0;
   
   DDRB  = 0b000000;      //set all ports to 0 ready for new reading
   PORTB = 0b000000;   
   
   for (uint8_t i=0; i<16; i++)   //take 16 conversions
      {   
         ADCSRA |= 1<<ADSC;       //start new conversion
            
         while (bit_is_clear(ADCSRA,ADIF)); wait until conversion completes & flag is 1
            
         uint8_t thelow = ADCL;                  //reading lowest 8 bits of adc
         uint16_t tenbitvalue = ADCH << 8 | thelow;   //convert ADC to 10 bits
         
         ADCresult = ADCresult + tenbitvalue;     //add result
      }
      
   setresult  (ADCresult >> 4); //right shift adcresult 4 places, divide by 16, call setresult.
}


Turning the LEDs off before taking a measurment improves things even more.

Finally i wuold like to learn how to use the ADC noise reduction mode, more out of curiosity and learning something new than absolute necesity. In the data sheet it seems to say, enable sleep mode, and then when sleep instruction is executed, adc will complete, and fire the adc interupt which wakes the processor back up.
But mine never wakes up. What could i be doing wrong?
Code:
for (uint8_t i=0; i<16; i++)   //take 16 conversions
      {   
         MCUCR  |= 1<<SE;  //ENABLE SLEEP MODE
         MCUCR  |= 1<<SM0; //SLEEP MODE ADC NOISE REDUCTION
            
         while (bit_is_clear(ADCSRA,ADIF));
            
         uint8_t thelow = ADCL;                  //reading lowest 8 bits of adc
         uint16_t tenbitvalue = ADCH << 8 | thelow;   //convert ADC to 10 bits
         
         ADCresult = ADCresult + tenbitvalue;     //add result
      }
 
 View user's profile Send private message  
Reply with quote Back to top
Visovian
PostPosted: Jun 02, 2012 - 07:55 PM
Posting Freak


Joined: Aug 07, 2007
Posts: 1477
Location: Czech

I have tested this
Code:
EMPTY_INTERRUPT (ADC_vect);

uint16_t adc_get(void)
{
    sleep_enable();
    ADCSRA |= (1 << ADSC); //start adc
    sleep_cpu();           //go sleep
    sleep_disable();       //after wakeup
   
    return ADC;
}


uint16_t adc_value; 
char tempstr[10];

int main()
{   
   set_sleep_mode (SLEEP_MODE_ADC);

   // ADC enable, prescaler = 128
   ADCSRA = (1<<ADEN)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);

   // reference voltage = AVCC
   // input = ADC5 (portc.5)
   ADMUX = (1<<REFS0) | 5;
   ADCSRA |=(1<<ADIE); //enable ADC interrupt
   uart_init();   
   sei();

   while(1)
   {
      adc_value = adc_get();
      itoa(adc_value,tempstr,10);
      uart_puts(tempstr);uart_puts("\r\n");
      _delay_ms(1000);
   }
}
 
 View user's profile Send private message  
Reply with quote Back to top
Chipit
PostPosted: Jun 02, 2012 - 08:31 PM
Newbie


Joined: May 20, 2012
Posts: 4
Location: Peak District, UK

Ah!
so a conversion has to be started before entering noise reduction mode, i will try that thank you.
 
 View user's profile Send private message  
Reply with quote Back to top
Display posts from previous:     
Jump to:  
All times are GMT + 1 Hour
Post new topic   Reply to topic
View previous topic Printable version Log in to check your private messages View next topic
Powered by PNphpBB2 © 2003-2006 The PNphpBB Group
Credits