Nastavení ATtiny85 jako AD převodník + výstup PWM – Mikrokontroléry – Fórum – Programujte.com
 x   TIP: Přetáhni ikonu na hlavní panel pro připnutí webu

Nastavení ATtiny85 jako AD převodník + výstup PWM – Mikrokontroléry – Fórum – Programujte.comNastavení ATtiny85 jako AD převodník + výstup PWM – Mikrokontroléry – Fórum – Programujte.com

 

Kebule
~ Anonymní uživatel
7 příspěvků
12. 11. 2017   #1
-
0
-

Dobrý den,

prosím o radu ohledně AD převodníku v mikrokontroléru ATtiny85. Níže je můj kód a potřeboval bych ho zkontrolovat.

Můj plán byl generovat PWM signál na pinu PB4 podle hodnoty AD převodníku, který je aktivní na pinu PB3. PWM mi funguje, ale AD převodník ne. Je jedno zda připojím PB3 na Vcc nebo GND, výsledek je pořád stejný. Nastavení registrů pro ADC jsem prošel snad stokrát, ale nevidím chybu.

ADC mám nastavený následovně: Jako referenční napětí beru napájecí napětí, ADCH a ADCL zarovnávám vpravo, pin pro ADC je nastaven na PB3, vzorkuji frekvencí FCPU/128, je nastaven Free Running mode.

V hlavní smyčce poté vyčítám registry ADCH a ADCL. Ty ukládám do proměnné, tak že její maximální hodnota je 1023 při Vref=5V a měřenému napětí 5V. Tuto hodnotu poté přepočítám na rozsah pro PWM, tedy 0-255.

Ještě detail, jako měřené testovací napětí beru čistý PWM signál z druhého mikrokontroléru. Napájecí napětí je bráno pro oba mikrokontroléry ze stejného zdroje.

#ifndef F_CPU
#define F_CPU 1000000UL // 1 MHz clock speed
#endif

#include <avr/io.h>
#include <util/delay.h>

unsigned int adc_result_to_PWM_range(unsigned int AdcIn);

int main(void)
{
	// PORT B
	DDRB = 0b00010000; // SET PB4 as output, rest inputs
	
	// ADC
	ADMUX = 0 << REFS1 | 0 << REFS0 | 0 << ADLAR | 0 << REFS2 | 0 << MUX3 | 0 << MUX2 | 1 << MUX1 | 1 << MUX0; 
	ADCSRA = 1 << ADEN | 1 << ADATE | 1 << ADPS2 | 1 << ADPS1 | 1 << ADPS0;
	ADCSRB = 1 << BIN | 0 << ADTS2 | 0 << ADTS1 | 0 << ADTS0;	
	
	// PWM
	OCR1B=250;
	TCCR1 = 0b00000001;
	GTCCR = 0b01100000;
	
	unsigned int step = 0;
	
	while (1)
	{		
			ADCSRA |= 1 << ADSC;
			while(ADCSRA &(1 << ADSC));
			step = (8 << ADCH) | ADCL;	
						
			OCR1B = adc_result_to_PWM_range(step);
			_delay_ms(10);			
	}
}

unsigned int adc_result_to_PWM_range(unsigned int AdcIn){
	return (255 * (AdcIn / 1023));
}

Napadá vás, kde by mohla být chyba? Pokud k tomu potřebujete více informací, tak stačí napsat.

Díky za odpovědi, Kebule.

Nahlásit jako SPAM
IP: 213.226.248.–
KIIV
~ Moderátor
+43
God of flame
12. 11. 2017   #2
-
0
-

#1 Kebule
urcite nebude dobry napad posouvat hodnotu 8 o ADCH bitu. Kdyz uz, tak  ADCH << 8. Tady zase hrozi, ze se to bude pocitat jako 8b cislo, tudiz zase prijdes o vse, co tam bylo (posune se to mimo). Proste, pokud mas k dispozici primo 16 bitovou hodnotu ADC, tak necaruj s bitama.

Ostatne, pokud chces 8bitovou hodnotu, tak nastav ADLAR tak, abys mel hornich 8 bitu prave v ADCH a pouzivej jen ten bez ADCL.

Nahlásit jako SPAM
IP: 80.188.57.–
Program vždy dělá to co naprogramujete, ne to co chcete...
MilanL+1
Grafoman
12. 11. 2017   #3
-
0
-

#1 Kebule
nj máš to otočený
step = (8 << ADCH) | ADCL;      

- to udělá rotaci čísla 8 o počet bitů v ADCH, místo aby ti ADCH hodil do High byte Stepu,.

mělo by být
step = (ADCH << 8) | ADCL;

EDIT:

a ten výpočet pwm bych udělal bez těch vnitřních závorek aby se to nejdřív vynásobilo a pak dělilo, v tomhle pořadí bývají výpočty jednodušší a přesnější.

takhle jak to máš bych se bál, aby mi to kromě max hodnoty z ADC nevyhazovalu furt 0. konkrétně bych se bál toho zda při výpočtu INT mi tu část v závorce (to dělení) vezme jako desetinné číslo bo 99% hodnot by bylo 0,xxx

Nahlásit jako SPAM
IP: 185.112.167.–
Kebule
~ Anonymní uživatel
7 příspěvků
12. 11. 2017   #4
-
0
-

#2 KIIV
Aha, tak to jsem tedy zásadně přehlídl a namísto toho jsem trávil čas přepisováním registrů ADMUX a ADCSRA-B.
Cíl mojí práce je pouze naučit se s avr a oživit si C. To znamená, že bych rád dostal z AD převodníku všech 10 možných bitů a do budoucna již budu mít uložený funkční projekt.

Děkuji za radu, Kebule.

Nahlásit jako SPAM
IP: 213.226.248.–
Kebule
~ Anonymní uživatel
7 příspěvků
12. 11. 2017   #5
-
0
-

#3 MilanL
Přesně, k tomu jsem ještě došel sám, že to dělení celočíselných proměných není zrovna to pravé.

Jak již psal mistr KIIV a vy potvrdil, problém bude v tom "mém" bitovém posuvu.

Děkuji za radu, Kebule.

Nahlásit jako SPAM
IP: 213.226.248.–
KIIV
~ Moderátor
+43
God of flame
12. 11. 2017   #6
-
0
-

#4 Kebule
Kazdopadne: doporucuji pouzit rovnou 16b registr ADC misto blbnuti s ADCL/ADCH. Postara se to i o spravne poradi nacitani/zapisu. S ADCL/ADCH si muzes hrat, az to budes kutit v ASM.

Nahlásit jako SPAM
IP: 81.30.230.–
Program vždy dělá to co naprogramujete, ne to co chcete...
MilanL+1
Grafoman
12. 11. 2017   #7
-
0
-

#5 Kebule
no on to může být výsledek toho jak jsou v programu ty další posuny (1<<něco), ale to je něco trošku jinýho v těch proměných jsou obvykle nadefinovaný pozice řídících bitů a tyto instrukce nastavují dané jednotlivé bity.

Nahlásit jako SPAM
IP: 185.112.167.–
Kebule
~ Anonymní uživatel
7 příspěvků
13. 11. 2017   #8
-
0
-

Podle datasheetu, ale nemám možnost získat přímo 16 bitové číslo. Mám k dispozici pouze ADCH a ADCL registr. V případě, že zarovnám bity doleva, tak můžu číst pouze ADCH a získám 8 bitové rozlišení.

Právě to, teď zkouším. ALDARem zarovnám bity doleva a vyčítám pouze ADCH a to následně přiřazuji rovnou do registru pro PWM. Bohužel, na výstupu pořád nic.

while (1)
	{		
			ADCSRA = 1 << ADSC;
			while(ADCSRA &(1 << ADSC));
					
			OCR1B = ADCH;
			_delay_ms(10);		
	}

Pokud by jste měli chuť a čas, zkuste prosím projít registry pro nastavení AD převodníku (manuál zde: http://www.atmel.com/images/atmel-2586-avr-8-bit-microcontroller-attiny25-attiny45-attiny85_datasheet.pdf a je to strana 134).

Nahlásit jako SPAM
IP: 213.226.252.–
13. 11. 2017   #9
-
0
-

V ADCH máš platný jen 2 bity od LSB tedy zprava, ostatní jsou 0. Tvoje OCR1B = ADCH; tedy odpovídá zápisu OCR1B = ADC >> 8; čímž zahodíš 8 bitů  a ponecháš si jen 2 nejvyšší bity. Pokud tedy hodnota z ADC nepřekročí 255, tak OCR1B bude 0. Pro 256 až 511 bude OCR1B 1 .... atd. Máš tak sqělou informaci, ve které čtvrtině rozsahu ADC se vstupní napětí nachází   

Mělo by ti v C projít unsigned int value = ADC; Překladač tam pak dosadí sekvenci instrukcí, která oba Byty přečte a správně umístí do paměti. Ale to už tady někdo psal...

Převodník je tuším 10 bitů, takže pak 8-bitovou hodnotu lze získat bit. posunem vpravo o 2 bity. Tím vytratíš LSB bity. Pak z toho vzejde OCR1B = ADC >> 2

hu

Nahlásit jako SPAM
IP: 195.178.67.–
13. 11. 2017   #10
-
0
-

Co se týče získání hodnoty z ADC, ta je k dispozici až nějakou dobu od spuštění měření - po uplynutí vzorkovací periody. Jestli se nemýlím, ADC by měl vyvolat přerušení a při jeho obsluze jde hodnota přečíst. Jde tedy o to nastavit ADC na příslušný vstup, nastavit jeho vzorkovací periodu, nastavit a povolit přerušení a spustit ADC (možná může běžet kontinuálně a pak generovat přerušení s frekvencí 1/Tvzork).

hu

Nahlásit jako SPAM
IP: 195.178.67.–
KIIV
~ Moderátor
+43
God of flame
13. 11. 2017   #11
-
0
-

#9 hlucheucho
nacteni ADC musi projit. Mimochodem, pokud nastavi ADLAR = 1, tak hornich 8 bitu je v ADCH a dolni dva jsou v ADCL jako nejvice platne bity. Takze s tim problem neni.

Ale uz jsem nekde narazil i na nejaky timer, ktery mel jediny temp registr a vsechny dvojbajtove registry ho pouzivaly. Tam to pak prirozene nejde.

K tomu ziskani hodnoty z ADC - cekani na konec konverze tam ma:
while(ADCSRA &(1 << ADSC));
 

Nicmene tam zase ma kravinu (u prispevku #8):
ADCSRA = 1 << ADSC;
Kde smaze veskere ostatni flagy, co tak slozite nastavoval.

Nahlásit jako SPAM
IP: 185.163.41.–
Program vždy dělá to co naprogramujete, ne to co chcete...
KIIV
~ Moderátor
+43
God of flame
13. 11. 2017   #12
-
0
-

#8 Kebule
Ten operator  |=  je tam opravdu dulezity!

	while (1)
	{		
		ADCSRA |= 1 << ADSC;         // start konverze
		while(ADCSRA &(1 << ADSC));  // cekame dokud neskonci
				
		OCR1B = ADCH;
		_delay_ms(10);		
	}
Nahlásit jako SPAM
IP: 185.163.41.–
Program vždy dělá to co naprogramujete, ne to co chcete...
13. 11. 2017   #13
-
0
-

Začal bych tím, že bych výstup ADC zapisoval na par. port a voltmetrem si "očuchal", co leze. Použil bych ADCMUX v defaultním stavu (tj. nic nezapsat) - vstup ADC0 (je na PB5) s rozsahem 0 - VCC. Na vstup přivedu asi 1/5 VCC. Po sekvenci: 

ADCSRA = 1 << ADEN;
ADCSRA |= 1 << ADSC;

by mělo dojít ke spuštění ADC a naměření hodnoty. Pak musíš testovat flag ADIF: 

while ( !(ADCSRA & (1 << ADIF)) )
{
  //ceka na konec mereni
}
PORTA = ADC  //zapises na port obsah ADC, mel bys dostat na portu cca 205

a po přečtení ho musíš vynulovat tím, že do něj zapíšeš 1: 

ADCSRA |= 1 << ADIF;

S přerušením bys to měl snažší. Odpadlo by testování flagu a jeho vynulování. Přerušení povolíš nastavením ADIE a napíšeš příslušnou ruinu pro obsluhu přerušení, která přečte obsah z ADC. Nezapomeň, že je ještě globální povolení/zakázání přerušení.

hu

Nahlásit jako SPAM
IP: 195.178.67.–
13. 11. 2017   #14
-
0
-

Ještě jsem si všiml viz #1:

0 << REFS1 - výsledek je 0, ta je z hlediska bitového OR neutrální. Pokud má daný bit zůstat 0, je přehlednější ho v zápisu neuvést

hu

Nahlásit jako SPAM
IP: 195.178.67.–
13. 11. 2017   #15
-
0
-

#12 KIIV
while(ADCSRA &(1 << ADSC)); // cekame dokud neskonci

ADSC není flag konce měření, ve Free Running by asi odchytil jen první měření. Jak by se to chovalo při spouštění např. časovačem netuším. V datasheetu netvrdí, že je při vynulování ADSC aktualizován dataregister. U ADIF je to výslovně napsáno, proto se mi jeví vhodnější čekat na něj.

hu

Nahlásit jako SPAM
IP: 195.178.67.–
13. 11. 2017   #16
-
0
-

Ještě pozn.: Byl vynalezen přerušovací systém aby nebylo nutné neustále "očuchávat" okolní události a vynakládat na to strojový čas, kterého je na jednočipu většinou nedostatek a energii, které není nazbyt u bateriových zařízení. Z tohoto důvodu bych nastavil přerušení a procesor nechal dělat jinou práci nebo uvedl do IDLE ze kterého ho přerušení probudí. Zde může hardware udělat hromadu práce sám - časovač nastavený na vhodnou periodu spustí ADC, ten provede měření a teprve přerušení od ADC si vynutí "akci" - přečtení naměřené hodnoty a její další zpracovaní. PWM také nevyžaduje žádnou "spoluúčast" jádra a tak vyjma prvotního nastavení periferiíí a přebírání dat od ADC může jádro "spinkat". Díky minimu práce pak může mít jádro "lenivé" tempo na nějaké malé taktovací frekvenci.

Další výhodou přerušení je rychlá odezva na událost, která i s probuzením z IDLE představuje několik taktů oscilátoru.

hu

Nahlásit jako SPAM
IP: 195.178.67.–
KIIV
~ Moderátor
+43
God of flame
13. 11. 2017   #17
-
0
-

#15 hlucheucho
U free running modu se ADSC pouzit neda, to je pravda. A u free running modu spousteneho napriklad timerem taky netusim co se s nim deje. Asi by bylo nejjednodussi proste otestovat.

U blokujiciho pouziti (ve stylu Arduino analogRead), se nastavi pri startu konverze a je automaticky vynulovan pri jejim skonceni. Dalsi konverze se pak nastartuje dalsim zapsanim jednotky do ADSC.

U ADIF bez preruseni musis flag nulovat. Ale pokud se v ISR jen nastavuje nejaky flag pro zpracovani, tak je zbytecne nevyuzit rovnou tento. Chytak je nulovat ho pomoci ADCSRA |= 1<<ADIF (tj. nastavenim 1) a ne skutecnym nulovanim ADCSRA &= ~(1<<ADIF). Coz mas spravne, jen je dobre mit tam nejakou zminku, ze to tak ma opravdu byt. Dokud jsem to nevedel, tak jsem to bral jako chybu.

Pokud preruseni pouzijes, tak se nuluje sam.

Dalsi moznost je pouzit sleep mode Idle nebo ADC Noise Reduction. Pokud je ADC zapnute, tak se spusti prechodem do sleep rezimu automaticky. Coz je dobre vedet, pokud se clovek opravdu snazi setrit energii a divi se, proc ze se mu to furt budi.

Nahlásit jako SPAM
IP: 81.30.230.–
Program vždy dělá to co naprogramujete, ne to co chcete...
Kebule
~ Anonymní uživatel
7 příspěvků
13. 11. 2017   #18
-
0
-

Ano, teď tu kravinu vidím a uvědomuji si jí, děkuji KIIV (#12).

hlucheucho: Vyzkouším to co radíš, nastavit co nejméně registrů a rozhýbat základ. Vyčítat data z ADC při přerušení byl můj následující plán, potom co rozhýbu toto.
Výsledek ADC nemůžu poslat na PORT, jelikož mám pouze PORT B a k tomu jen sěst pinů. Výstup řeším PWMkem, LEDkou a voltmetrem.
Ty zápisy 0 do registrů tam mám, protože nastavení těchto registrů dokola ladím a vidím co a kam zapisuji.
U ADSC je napsáno: ADSC will read as one as long as a conversion is in progress. When the conversion is complete, it returns to zero. Writing zero to this bit has no effect.
Děkuji za rady.

Celý ADC chápu tak, že ho povolím a řeknu mu čím se bude spouštět (v mém případě Auto Triggering - Free Running Mode) a následně si setuji bit ADSC a čekám až ho ADC vyresetuje. V momentě kdy je ADSC nula, konverze je hotová a já mohu vyčíst data. Je to tak?

Pořád přemýšlím o tom, zda není problém v tom, že ten ADC krmím PWMkem z druhého mikrokontroleru bez nějakých filtrů apod.

Kebule
 

Nahlásit jako SPAM
IP: 213.226.252.–
KIIV
~ Moderátor
+43
God of flame
13. 11. 2017   #19
-
0
-

#18 Kebule
Pokud jedes free running, tak se ADSC pouzit neda, jelikoz bude neustale 1. Musis pouzit co poslal hlucheucho - ADIF + jeho vynulovani.

Voltmetr bude k nicemu, pokud tam nemas filtr nebo moznost merit primo stridu signalu (obvykle v %).

Pokud to uz pomoci PWM krmis, tak bez filtru to bude merit pomerne nahodile. Kapacita samplovaciho kondenzatoru je tusim asi 13pF a pokud vedes vystup na vstup, tak se bude hodne blizit 0 nebo 1023 (podle toho, jak rychle meris a jaka je frekvence PWM). Alespon RC filtr by se hodil.

Mozna by bylo lepsi rovnou merit stridu vstupniho signalu.

Nebo posilat pomoci serioveho portu. Nejakou malou HW podporu pro seriovou komunikaci v Tiny85 mas. Sice to neni zadny zazrak, ale lepsi nez to delat ciste softwarove.

Nahlásit jako SPAM
IP: 81.30.230.–
Program vždy dělá to co naprogramujete, ne to co chcete...
Kebule
~ Anonymní uživatel
7 příspěvků
13. 11. 2017   #20
-
0
-

Aha, tak to s tím ADSC jsem netušil. Program přepíšu a budu to řešit přes přerušení.

Výstup PWMka mně změřit voltmetrem jde. Na stejnosměrném rozsahu mně naměří efektivní hodnotu napětí. Naměřím postupně 0 až 5 voltů. Potom se musím dopočítat k hodnotě, ale tam je samozřejmě odchylka v měření atd., takže se dostanu pouze na přibližnou hodnotu registru ADC.

S kondenzátorem jsem již experimentoval, ale po ruce jsem měl pouze 10nF. Teď koukám, že v datasheetu je 14pF. Zkusím někde vyhrabat 14pF a případně připojit na vstup ADC lineární zdroj a uvidíme,

Budu Vás průběžně informovat o mém úspěchu, respektive neúspěchu.

Nahlásit jako SPAM
IP: 213.226.248.–
KIIV
~ Moderátor
+43
God of flame
13. 11. 2017   #21
-
0
-

#20 Kebule
14pF je interni kondenzator. Nabiji se ve fazi samplovani vstupu. Pak se prevede napeti z nej v AD prevodniku. Delka nabijeni je zavisla na nastaveni prescaleru ADC. Takze kapacita v radech nF nebude vubec na skodu. Pri pripojeni samplovaciho kondenzatoru si ukradne jen malou cast naboje a napeti se pak prilis nesnizi. Ale chce to na vstupu jeste nejaky rezistor, aby RC konstanta byla vhodne dlouha a napeti na kondenzatoru se nemenilo celou vecnost nez dosahne nove hodnoty pri zmene stridy.

Nahlásit jako SPAM
IP: 81.30.230.–
Program vždy dělá to co naprogramujete, ne to co chcete...
Kebule
~ Anonymní uživatel
7 příspěvků
13. 11. 2017   #22
-
0
-

Tak mně to nedalo, začal jsem testovat a hlavní problém byl použití měřeného napětí z výstupu PWM druhého mikrokontroléru. Je určitě potřeba filtr a odfiltrovat střídavou složku a dostat z PWM opravdu pouze střední hodnotu napětí (ne tu efektivní, jak jsem psal výše).

Teď když na ADC nastavím určité napětí, dostanu přesně to stejné na výstupu PWM. Je to otestováno na verzi níže a i na verzi, kdy se čte hodnota z ADC při přerušení.

#ifndef F_CPU
#define F_CPU 1000000UL // 1 MHz clock speed
#endif

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

unsigned long adc_result_to_PWM_range(unsigned long AdcIn);

int main(void)
{
	// PORT B
	DDRB |= 1 << DDB4; // SET PB4 as output, rest inputs
	
	// PWM
	OCR1B = 250;
	TCCR1 = 0b00000001;
	GTCCR = 0b01100000;
	
	// ADC
	ADMUX   = (1 << MUX1);
	ADMUX  |= (1 << MUX0);
	ADCSRA  = (1 << ADEN);
	ADCSRA |= (1 << ADATE);
	ADCSRA |= (1 << ADPS2);
	ADCSRA |= (1 << ADPS1);	
	ADCSRA |= (1 << ADSC);
	
	while (1)
	{		
			while ( !(ADCSRA & (1 << ADIF)));
			OCR1B = adc_result_to_PWM_range(ADC);
			_delay_ms(10);
			ADCSRA |= (1 << ADIF);		
	}
}

unsigned long adc_result_to_PWM_range(unsigned long AdcIn){
	return ((AdcIn * 1000 * 255) / 1023) / 1000;
}

Děkuji chlapi za rady a zároveň narovinu přiznávám, že se ještě ozvu.

Nahlásit jako SPAM
IP: 213.226.252.–
14. 11. 2017   #23
-
0
-

Jako filtr by měla být dolní propust - nejjednodušší je RC člen. Její horní mezní frekvence by měla být (výrazně) nižší než frekvence PWM.  Tak získáš stejnosměrnou složku s potlačením všech harmonických. U ADC by teoreticky měla stačit dvojnásobná vzorkovací frekvence než je nejvyšší frekvence na vstupu, volí se vyšší. Vstup se ošetřuje vstup dolní propustí tak aby její horní mezní frekvence byla menší než polovina vzorkovací frekvence právě kvůli potlačení nežádoucích vyšších frekvencí. Výstup PWM je obdélníkový signál, tedy neharmonický a bude tedy kromě základní harmonické obsahovat vyšší harmonické o určité amplitudě a fázi. To je důvod, proč ADC může výstup PWM bez filtru měřit chybně.

hu

Nahlásit jako SPAM
IP: 195.178.67.–
Zjistit počet nových příspěvků

Přidej příspěvek

Toto téma je starší jak čtvrt roku – přidej svůj příspěvek jen tehdy, máš-li k tématu opravdu co říct!

Ano, opravdu chci reagovat → zobrazí formulář pro přidání příspěvku

×Vložení zdrojáku

×Vložení obrázku

Vložit URL obrázku Vybrat obrázek na disku
Vlož URL adresu obrázku:
Klikni a vyber obrázek z počítače:

×Vložení videa

Aktuálně jsou podporována videa ze serverů YouTube, Vimeo a Dailymotion.
×
 
Podporujeme Gravatara.
Zadej URL adresu Avatara (40 x 40 px) nebo emailovou adresu pro použití Gravatara.
Email nikam neukládáme, po získání Gravatara je zahozen.
-
Pravidla pro psaní příspěvků, používej diakritiku. ENTER pro nový odstavec, SHIFT + ENTER pro nový řádek.
Sledovat nové příspěvky (pouze pro přihlášené)
Sleduj vlákno a v případě přidání nového příspěvku o tom budeš vědět mezi prvními.
Reaguješ na příspěvek:

Uživatelé prohlížející si toto vlákno

Uživatelé on-line: 0 registrovaných, 10 hostů

 

Hostujeme u Českého hostingu       ISSN 1801-1586       ⇡ Nahoru Webtea.cz logo © 20032024 Programujte.com
Zasadilo a pěstuje Webtea.cz, šéfredaktor Lukáš Churý