CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

Software adc stabilization

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
Guest








Software adc stabilization
PostPosted: Thu Aug 21, 2008 10:43 pm     Reply with quote

I've been trying to get a stable reading out of the adc, and came up with an algorithm that has moderate success:

average the last 32 readings

finds the highest value out of the last 32 averages. My input signal is such that this value varies by about +/30

the high average has hysteresis applied to it:
the stored value is only changed if the new value is 30 greater or less than the old value

the new stored value is then new value - 30


Code:

#define HYST 30

int32 highest(int32 * hi_array, int32 last_hi_val)
{
  int32 hi_val,ret_val,x;
 
  for(x=0;x<32;x++)
  {
    if(hi_array[x] > hi_val)
    {
      hi_val = hi_array[x];
    }
  }
  if(last_hi_val > (hi_val + HYST))
  {
    ret_val = hi_val+HYST;
  }
  else if(last_hival < (hi_val - HYST))
  {
    ret_val = hi_val-HYST;
  }
  else
  {
    ret_val = last_hi_val;
  }
  return hi_val;
 
}



I then print out this number, as well as the corresponding voltage (0-3.3V)

The output stays extremely stable, but there is some small error when compared to the voltmeter. Also when I vary the input, the output approaches the measured, but doesn't quite get there. For instance, going from .5V to 1V, the output will go from 155 to 305, instead of 310. But going from 1V to .5V, the output will go from 305 to 160. But the error is always less than 20, or about 66mV.

Is there some additional correction I can make to reduce that error, or is it simply a product of the algorithm?
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Thu Aug 21, 2008 11:35 pm     Reply with quote

Threads on filters:
http://www.ccsinfo.com/forum/viewtopic.php?t=17876
http://www.ccsinfo.com/forum/viewtopic.php?t=3462
http://www.ccsinfo.com/forum/viewtopic.php?t=19509
SherpaDoug



Joined: 07 Sep 2003
Posts: 1640
Location: Cape Cod Mass USA

View user's profile Send private message

PostPosted: Fri Aug 22, 2008 7:52 am     Reply with quote

I often use the "Olympic scoring" filter, which is VERY easy to implement.

Loop 10 times.
In each loop read the A/D and keep the sum, the maximum reading and the minimum reading.
When the loop is done subtract the max and min from the sum and divide by 8 (shift 3 bits).

This only needs 3 RAM locations plus the loop counter and a single easy division. It removes high and low outliers and averages the rest. If the A/D is 12 bits the sum will still fit in a 16bit variable.
_________________
The search for better is endless. Instead simply find very good and get the job done.
wangine



Joined: 07 Jul 2009
Posts: 98
Location: Curtea de Arges, Romania

View user's profile Send private message Send e-mail Yahoo Messenger

PostPosted: Wed May 05, 2010 3:52 pm     Reply with quote

I used this code to read from the ADC conversion TC1047A.
It displays more noise and was the only software solution.
I hope it's useful.
Code:

long int temp, total;
long int mem[15];

//   int count=0;


//**************************************************************
void init_adc()
{
   

//   setup_adc_ports(sAN0|VSS_VDD);

   setup_adc_ports(sAN1|VREF_VREF);

    SETUP_ADC(ADC_CLOCK_DIV_8);   

    SET_ADC_CHANNEL(1);


    READ_ADC(ADC_START_ONLY);
}
//**************************************************************
void medie ()
{
      total = (mem[0]+mem[1]+mem[2]+mem[3]+mem[4]+mem[5]+mem[6]+mem[7]
      +mem[8]+mem[9]+mem[10]+mem[11]+mem[12]+mem[13]+mem[14])/15;

}
//********************************************

#INT_RTCC   // This function will be called on a Timer0 interrupt
void timer0_interrupt_service()
{
   static int count = 0;

//disable_interrupts(GLOBAL);
//**************************
//         temp = read_adc()/3.1; // conversia direct in V val la 3.3 V ref ** 10 bit

//         temp = ((read_adc()/19.84)-500);  // conversia direct in V la 3.3V ref ** 16 bit

      mem[count] = read_adc(ADC_READ_ONLY)-24;
      count++;
   
   if (count >= 15)
      {
         count = 0;
      }

//      printf( temp);
//  enable_interrupts(INT_RTCC);
//  enable_interrupts(GLOBAL);
}
vinniewryan



Joined: 29 Jul 2009
Posts: 154
Location: at work

View user's profile Send private message MSN Messenger

PostPosted: Wed May 05, 2010 4:23 pm     Reply with quote

Can you add a capacitor to the input pin to help narrow the amount your adc input bounces around? Or do you need it to bounce around for some reason?
_________________
Vinnie Ryan
wangine



Joined: 07 Jul 2009
Posts: 98
Location: Curtea de Arges, Romania

View user's profile Send private message Send e-mail Yahoo Messenger

PostPosted: Thu May 06, 2010 1:25 pm     Reply with quote

If you put capacitor, input increases and variations hysteresis first reading still does not decrease, is much more difficult.
Put the above code solved the problem.
The full code includes display driver.
Primary code:
Code:

/////////////////////////////////////////////////////////////////////////
////                                                                 ////
////                                                      ////
////     LED seg f    Pin D0                                         ////
////     LED seg a    Pin D1                                         ////
////     LED seg e    Pin D2                                         ////
////     LED seg c    Pin D3                                         ////
////     LED seg dp   Pin D4                                         ////
////     LED seg d    Pin D5                                         ////
////     LED seg b    Pin D6                                         ////
////     LED seg g    Pin D7                                         ////
////     LED Anode 1  Pin E0                                         ////
////     LED Anode 2  Pin E1                                         ////
////     LED Anode 3  Pin E2                                         ////
////                                                                 ////
/////////////////////////////////////////////////////////////////////////


/////////////////////////////////////////////////////////////////////////
//      RA0 === constant 33k rezistor
//      RA1 === NTC termistor
//      RA2 === discharge resistor
//////////////////////////////////////////////////////////////////////////

#include "18F45k20.h"
#device adc=10

#FUSES NOWDT                    //No Watch Dog Timer
#FUSES INTRC_IO                 //Internal RC Osc, no CLKOUT
#FUSES NOPROTECT                //Code not protected from reading
#FUSES NOBROWNOUT               //No brownout reset
#FUSES NOPUT                    //No Power Up Timer
#FUSES NOCPD                    //No EE protection
#FUSES NOSTVREN                 //Stack full/underflow will not cause reset
#FUSES NODEBUG                  //No Debug mode for ICD
#FUSES NOLVP                    //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOWRT                    //Program memory not write protected
#FUSES NOFCMEN                  //Fail-safe clock monitor disabled
#FUSES NOWRTB                   //Boot block not write protected
#FUSES NOEBTR                   //Memory not protected from table reads
#FUSES NOEBTRB                  //Boot block not protected from table reads
#FUSES NOCPB                    //No Boot Block code protection
#FUSES NOMCLR                   //Master Clear pin used for I/O



#use delay(clock=8000000)//3276800)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7)

#include <display.c>
//#include <stdlib.h>
#include <math.h>

//#include<1wire_mod.c>
//#include<lcd.c>
//#include<ds1820_mod.c>


 long int temp, total;
long int mem[19];

//   int count=0;


//*******************************************************************************
void init_adc()
{
   

//   setup_adc_ports(sAN0|VSS_VDD);

   setup_adc_ports(sAN1|VREF_VREF);

    SETUP_ADC(ADC_CLOCK_DIV_8);   

    SET_ADC_CHANNEL(1);

//setup_vref (VREF_LOW | 100);
//setup_vref (VREF_HIGH | 22);


    READ_ADC(ADC_START_ONLY);
}


#INT_RTCC   // This function will be called on a Timer0 interrupt
void timer0_interrupt_service()
{//   int i;
   static int count = 0;
//disable_interrupts(GLOBAL);
//**************************
//         temp = read_adc()/3.1; // conversia direct in V val la 3.3 V ref ** 10 bit

//         temp = ((read_adc()/19.84)-500);  // conversia direct in V la 3.3V ref ** 16 bit

      mem[count] = read_adc(ADC_READ_ONLY)-24;

   if (count >= 19)
   {
   count = 0;
   }

   total = (mem[0]+mem[1]+mem[2]+mem[3]+mem[4]+mem[5]+mem[6]+mem[7]+mem[8]+mem[9]+mem[10]+mem[11]+mem[12]+mem[13]+mem[14]+mem[15]+mem[17]+mem[18])/19;
      count++;
          
//         printf( temp);
//  enable_interrupts(INT_RTCC);
//  enable_interrupts(GLOBAL);
}

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


//************************************************************************************
int medie (long int n)
{
//disable_interrupts(GLOBAL);
//**************************
//   mem[count] = temp;

//   count ++;

//   if (count >= 13)
//   {
//   count = 0;
//   }

   total = (mem[0]+mem[1]+mem[2]+mem[3]+mem[4]+mem[5]+mem[6]+mem[7]+mem[8]+mem[9]+mem[10]+mem[11]+mem[12])/13;
//   return total;
//   count ++;
//**************************
//  enable_interrupts(INT_RTCC);
//  enable_interrupts(GLOBAL);
}
//********************************************
int linear (long int x)
{

}
//============================================
// main program
//============================================
void main()
{
//   int i;
    temp = 0;
   total = 0;

   init_adc();
        // Timer 0 runs off internal clock with 1:256 prescaler
        // This means it should run over every ((256*256)/(4MHz/4)) = 65.5ms
        // It will be used to start an ADC conversion.
    SETUP_TIMER_0(RTCC_INTERNAL | RTCC_DIV_16);

    ENABLE_INTERRUPTS(INT_RTCC); 

    ENABLE_INTERRUPTS(GLOBAL);

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

   while(TRUE)
   {    
      //      medie(temp);

         display_number(total);
      


//*******************************************************
   
  }
}


Display.c
Code:
//                     0   1    2     3    4    5    6    7    8    9   
BYTE CONST LED_MAP[10] = {0x90,0xb7,0x19,0x15,0x36,0x54,0x50,0xb5,0x10,0x14};

//                       0      1    2     3    4    5    6    7    8    9   
BYTE CONST LED_MAP_P[10] = {0x80,0xa7,0x09,0x05,0x26,0x44,0x40,0xa5,0x00,0x04};

//===========================================
// this function use to display number 00=99
// input : n = number to display
//===========================================
void display_number(long int n)
{
 READ_ADC(ADC_START_ONLY);
// disable_interrupts(GLOBAL);
   output_d(LED_MAP[n/100]) ;//unit position
   output_high(PIN_E0);

   delay_ms(7);
   output_low(PIN_E0);
//*************************************
   output_d(LED_MAP_P[(n/10)%10]);//tens position;n=0 to 9
   output_high(PIN_E1);
 
   delay_ms(7); 
   output_low(PIN_E1);
//*************************************
   output_d(LED_MAP[n%10]) ;//unit position
   output_high(PIN_E2);

   delay_ms(7);
   output_low(PIN_E2);
//*************************************
//  enable_interrupts(INT_COMP);
// enable_interrupts(GLOBAL);
}    
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group