ADC : Hodnota lítá mezi dvěma čísly

Dobrý den,

Řeším problém v tomto programu

#include <avr/io.h>
#include <avr/interrupt.h>

#define potenciometer PA0

char adc;

void init()
{
	//Nastavení vstupních a výstupních pinů
	DDRA = 0x00;
	
	ADMUX = (1 << REFS0);
	ADMUX |= (1 << ADLAR); //reference 5V + pridano ADLAR (pro rozliseni 8 bitu)
	ADCSRA |= (1 << ADEN) | (1 << ADIF) | (1 <<ADPS2) | (1 <<ADPS1)  | (1 <<ADPS0);	//povoleni AD prevodnika, Interrupt flag, preddelicka 128 pri frekvencii hodin 1MHz
	
	sei(); //povolení globálního přerušení
}

unsigned int Read_ADC(unsigned char channel){
	ADMUX &= 0xF0;														//vloz cislo kanalu, vymaskuj nepouzite bity
	ADMUX |= channel & 0x0F;
	
	ADCSRA |= (1 << ADSC);												//start prevodu A/D
	
	while(!(ADCSRA & (1<<ADIF)));										//cekej na priznak skonceni prevodu
	
	return ADCH;														//navratova hodnota - vysledek A/D prevodu 8 bitu
}

int main(void)
{		
	init(); //inicializuje procesor
	while(1)
	{
		adc = Read_ADC(0) / 2; // omezuje rozsah na 256 / 2
		
		if (adc > 99) //zajištění aby číslo nemělo více než 2 cifry
			adc = 99;
				
	vypis(adc); //výpis na displej
	}
return 0;		
}

Vždy, když dám potenciometr do mezi polohy, tak v promněnné adc se periodicky sřídají 2 hodnoty. Tato chyba se projevuje mezi 1 a 2, 11 a 12, 21 a 22 atd. až do 91 a 92.

Děkuji za rady.

to je normální jak nemáš blokovací kondiky a tlimivky na napajeni ADC

Blokovací kondíky a kondenzátory na vstupu ADC mám. Ale divné je, že se hodnota střídá periodicky. 1 2 1 2 a ne např. 1 2 2 1 atd.

Blizko puzdra uP zapoj medzi AD vstup a zem kondik 10n, inak pri mnene vstupu a startom prevodu by si mal chvilku pockat.

je vyhodne pouzivat preruseni, teda necekat na vysledek a potom ho v preruseni precist do nejake promenne. Dalsi dobra vec je zahazovat dva nejnizsi bity a pouzivat jen 256 hodnot.

Informaci nezahazovat, ale vhodně zpracovat - pokud to charakter aplikace dovolí tak vhodným číslicovým filtrováním. To musí vědět zadavatel co měří a proč. Připravit se o rozlišení je hovadina.

Kondík nepomohl, vstupy v programu neměním.

Co je číslicové filtrování?

Já potřebuji rozsah jen mezi 0 - 99.

Třeba obyčejné průměrování, klouzavý průměr, exponenciální klouzavý průměr… Najdeš na WiKi :wink:

Takže ty rozsah celého potenciometru potřebuješ rozprostřít na 0-99 ?
Jestli jo a máš to zapojený mezi 0V-5V a vyvedený střed, tak je lepší informaci zafiltrovat a pak udělat numericky scale [0-1023] na [0-99]

Mohl bych poprosit o ukázku nějakého elegantního řešení. Celkem by mě zajímalo jak na to. Díky

:arrow_right: administrator: příspěvek byl upraven
Předchozí příspěvky se necitují.

Souhlasím. Také by mě to zajímalo.

:arrow_right: administrator: příspěvek byl upraven
Předchozí příspěvky se necitují.

Tak třeba…
Soft generuje data zatížená šumem, ty filtruje a společně s originálem ukládá do csv souboru. Výstupy jsou v přiložených obrázcích.



DATA_FILTER.ZIP (103 KB)

Dobrý den,

Nedávno se mi do rukou dostal JTAG debuger a zjistil jsem, že hodnoty z ADC jsou správné. Asi tedy bude problém ve 595. Předem děkuji za rady.

/*
 * Minutka.c
 *
 * Created: 17. 4. 2015 15:08:06
 *  Author: Petr Liška
 */ 


#include <avr/io.h>
#include <avr/interrupt.h>
#define F_CPU 1000000UL
#include <util/delay.h>

//definice portů displeje na portu C
#define MR PC3
#define SHCP PC4
#define STCP PC5
#define OE PC6
#define DS_1 PC2
#define DS_2 PC7

#define tlacitko PD2

#define potenciometer PA0

#define piezo PD3

char vypisovane_cislo_desitky, vypisovane_cislo_jednotky;
volatile char citac = 0;
volatile char adc = 0;
volatile char sviti = 0;
volatile char odpocitavani = 0;
volatile char citac_2 = 0;

char cisla] = {
	//hgfedcba
	0b00111111, //0
	0b00000110, //1
	0b01011011, //2
	0b01001111, //3
	0b01100110, //4
	0b01101101, //5
	0b01111101, //6
	0b00000111, //7
	0b01111111, //8
	0b01101111  //9
};

ISR (TIMER0_OVF_vect){   //přerušní při přetečení časovače 0     
	// 2*262,144 = 524,288ms      
	if(citac_2 == 2){           
		citac_2=0;
		sviti ^= 1;     
		}        
		citac_2++;  
} 

ISR (TIMER1_OVF_vect){   //přerušní při přetečení časovače 1
	TCNT1 = 61630; //nastavenie počáteční hodnoty počítadla
	citac++;
}

ISR (INT0_vect) {       //přerušení při stisku tlačítka (doběžná hrana)  
	odpocitavani ^= 1; 
} 

ISR (ADC_vect)
{
	adc = (char)ADCH / 2;
}

void init(void)
{
	OSCCAL = 0b10101100; // nastavení kalibrační konstanty
	
	//Nastavení vstupních a výstupních pinů
	DDRA = 0x00;
	DDRB = 0x00;
	DDRC = 0xFF;
	DDRD = 0xFF;
	DDRD &= ~(1 << tlacitko);
	
	//nastavení pull-up rezistoru
	PORTD |= (1 << tlacitko);
	
	//vypnutí pieza
	PORTD &= ~(1 << piezo);
	
	//595
	PORTC |= (1 << MR); //vypnutí master reset
	PORTC &= ~(1 << OE); //povolení výstupu na 595
	
	//počítání minut
	TIMSK |= (1 << TOIE1); //prerušení při přetečení TCNT1
	
	//ADC
	ADMUX |= (1 << REFS0) | (1 << ADLAR);
	ADCSRA |= (1 << ADEN) | (1 << ADIF) | (1<<ADATE) | (1<<ADIE) | (1 <<ADPS1) | (1 <<ADPS2) | (1 << ADSC);
	
	//blikání led
	TIMSK |= (1 << TOIE0); //prerušenie pri pretečení TCNT0
	
	//ext. přerušení
	GICR |= (1 << INT0); //povolení prerušení od INT0
	MCUCR |= (1 << ISC01);
	
	sei(); //povolení globálního přerušení
}

void pipani(void) //spouští piezo
{
	PORTD |= (1 << piezo);
	_delay_ms(200);
	PORTD &= ~(1 << piezo);
	_delay_ms(200);
}

void vysli_595(void)
{
	ADCSRA &= ~(1 << ADEN); // vypnutí AD převodníku
	
	PORTC &= ~(1 << STCP);
	for(int i = 0; i != 8; i++)
	{
		PORTC &= ~(1 << SHCP);
		
		if(vypisovane_cislo_desitky & 0b10000000) //zapis 1 bitu na místo desítek
		PORTC &= ~(1 << DS_1);
		else
		PORTC |= (1 << DS_1);
		
		if(vypisovane_cislo_jednotky & 0b10000000) //zápis 1 bitu na místo jednotek
		PORTC &= ~(1 << DS_2);
		else
		PORTC |= (1 << DS_2);
		
		PORTC |= (1 << SHCP); //nástupná hrana na SHCP
		vypisovane_cislo_jednotky <<= 1;
		vypisovane_cislo_desitky <<= 1;
	}
	PORTC |= (1 << STCP); //nástupná hrana na STCP
	
	ADCSRA |= (1 << ADEN) | (1 << ADSC); //zapnutí AD převodníku
}

void prevod_cisla(char cislo) //převádí na desítky a jednotky, zjištuje zda-li se má rozsvítit tečka
{
	unsigned char jednotky;
	unsigned char desitky;
	
	jednotky = cislo % 10; //zbytek po dělení 10
	desitky = cislo / 10; //celoočíselné dělení
	
	vypisovane_cislo_desitky = cisla[desitky];
	if(sviti) //pokud se má tečka rozsvítit
		vypisovane_cislo_desitky |= (1 << 7); //zmněň bit tečky
	else
		vypisovane_cislo_desitky &= ~(1 << 7); //zmněň bit tečky
		
	vypisovane_cislo_jednotky = cisla[jednotky];
	
	vysli_595(); //výpis na displej
	
	return;
}

void citani(void) //odpocitava dokud nedojde ke konecné hodnotě
{
	while (odpocitavani)
	{
		if(citac == 60) //pokud uběhla minuta
		{
			adc--;
			citac = 0;
		}
		
		prevod_cisla(adc); //převod čísla na desítky a jednotky
		
		if(adc == 0) //pokud čas vypršel
		{
			while (odpocitavani) //tlačítko není zmáčknuto
			{
				pipani(); //pípání pieza
			}
			break; //konec cyklu
		}
	}
}

void init_citani(void)
{
	citac = 0;
	ADCSRA &= ~(1 << ADEN); // vypnutí AD převodníku
	TCCR1B |= (1 << CS12); //předdelička 256 (32us) zapnutí odpočítávání
	TCCR0 |= (1 << CS02) | (1 << CS00); //preddelicka /1024 (1,024ms) zapnutí blikání
	
	citani(); //spouští odpočítávání
	
	TCCR0 &= ~(1 << CS02) | (1 << CS00); // vypnutí blikání
	TCCR1B &= ~(1 << CS12); // vypnutí odpočítávání
	sviti = 0;	//zajistí aby tečka nesvítila
	ADCSRA |= (1 << ADEN) | (1 << ADSC); //zapnutí AD převodníku
}

int main(void)
{		
	init(); //inicializuje procesor(porty,přerušení atd.)

	while(1)
	{		
		if (adc > 99) //zajištění aby číslo nemělo více než 2 cifry
			adc = 99;
				
		if(odpocitavani) //zjištění zda-li je tlačítko zmáčnuto
		{
			init_citani(); //inicializuje odpočítavací funkci, která poté volá funkci pro odpočítávání
		}
	
	prevod_cisla(adc); //převod čísla na desítky a jednotky a následný výpis na displej
	_delay_ms(10);
	}
return 0;		
}