|
|
View previous topic :: View next topic |
Author |
Message |
Guest
|
Software adc stabilization |
Posted: Thu Aug 21, 2008 10:43 pm |
|
|
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
|
|
|
SherpaDoug
Joined: 07 Sep 2003 Posts: 1640 Location: Cape Cod Mass USA
|
|
Posted: Fri Aug 22, 2008 7:52 am |
|
|
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
|
|
Posted: Wed May 05, 2010 3:52 pm |
|
|
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
|
|
Posted: Wed May 05, 2010 4:23 pm |
|
|
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
|
|
Posted: Thu May 06, 2010 1:25 pm |
|
|
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);
} |
|
|
|
|
|
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
|