Läsa värde med ADC på AVR? (asm)

PIC, AVR, Arduino, Raspberry Pi, Basic Stamp, PLC mm.
Glattnos
Inlägg: 3106
Blev medlem: 29 oktober 2009, 20:01:18

Läsa värde med ADC på AVR? (asm)

Inlägg av Glattnos »

Hej

Jag försöker läsa av ett värde via ADC på min Atmega168, men jag vet inte riktigt hur det ska fungera.

Jag skriver såhär:

Kod: Markera allt

.ORG ADCCaddr					;Avbrottsvecktor för ADC
	rjmp Ultra
-------------------------------------------------------------------------------------------------------------

	ldi		temp, 0b00100101	;ADC - AREF(Internal Vref turned off), Left adjusted, ADC5
	sts		ADMUX, temp

	ldi		temp, 0b11001110	;ADC - ADENable, ADStartConversation, ADInterruptEnable... 
	sts		ADCSRA, temp		;...ADPrescalerSelectBits 110 = System clock/64 to ADC
	sei
-------------------------------------------------------------------------------------------------------------

Ultra:
	ldi		adc_data, ADCH
	reti
Jag har naturligtvis skrivit mer men jag tog ut det som har med ADC att göra. Det jag inte förstår är: När ska avbrottet för ADC inträffa?
Enligt databladet och en bok som jag har så ska man skriva så och då ska det bli såhär: "This starts the process so the ADC interrupt will occur at the end of the first conversion."

Compilatorn ger inga varningar och det går att stega sig genom programmet, men Interruptet inträffar aldrig.
Säkert något uppenbart fel som jag har gjort. Kan någon vänlig själ hjälpa mig?
Användarvisningsbild
Swech
EF Sponsor
Inlägg: 4750
Blev medlem: 6 november 2006, 21:43:35
Ort: Munkedal, Sverige (Sweden)
Kontakt:

Re: Läsa värde med ADC på AVR? (asm)

Inlägg av Swech »

Kod: Markera allt

Ultra:
   ldi      adc_data, ADCH
   reti
Bör väl vara

Kod: Markera allt

Ultra:
   lds      r16,ADCH
   sts     adc_data,r16
   reti
Sen måste du spara status i din interrupt rutin också

Swech
Glattnos
Inlägg: 3106
Blev medlem: 29 oktober 2009, 20:01:18

Re: Läsa värde med ADC på AVR? (asm)

Inlägg av Glattnos »

Swech: Hmm...Kanske det, men varför ger inte kompilatorn några error på det då tro, den brukar vara snabb med det det annars.
Men nu är det så att adc_data är r17 så mellan r17 och ADCH kanske det går att använda ldi

Men, det du beskriver är ju inne i avbrottet. Men när ska avbrottet ske? Jag får det ju inte ens att inträffa.
Jag kan få Timer-avbrott att fungera, då får jag ju interrupt vid Owerflow.

Hmm...Kan du ge en kort beskrivning på: "spara status i din interrupt rutin"
Användarvisningsbild
Swech
EF Sponsor
Inlägg: 4750
Blev medlem: 6 november 2006, 21:43:35
Ort: Munkedal, Sverige (Sweden)
Kontakt:

Re: Läsa värde med ADC på AVR? (asm)

Inlägg av Swech »

1.
Vart får du det inte att funka? I AVR Studio eller på en "riktig" processor?

2.
LDI och LDS är ju inte riktigt samma sak :)
LDI betyder att ladda med ett konstantvärde
I ditt fall så laddar du R17 med adressen till ADCH men du vill väl ha värdet som finns där?

3.
Status
Vid interrupt så blir ju dina statusflaggor sabbade eftersom du med mycket stor sannolikhet kör
några instruktioner som påverkar flaggorna.

Kod: Markera allt

;----------------------------
;	
;----------------------------
INT_ROUT:
        IN        R2,SREG          ;SAVE STATUS
        PUSH    R16		 ;SAVE R16    
.
.      SPARA ALLA REG HÄR SOM DU PILLAR MED, I DETTA EXEMPEL R16
.
.       DIN KOD HÄR
.
        POP      R16                ;RESTORE R16
        OUT     SREG,R2          ;SAVE STATUS
        RETI    
Du sparar alltså SREG i ett register det första du gör. OBS detta register får du INTE använda någon
annanstans i ditt huvudprogram. Ta något av R2-R15 registrena.

Swech
Användarvisningsbild
jesse
Inlägg: 9240
Blev medlem: 10 september 2007, 12:03:55
Ort: Alingsås

Re: Läsa värde med ADC på AVR? (asm)

Inlägg av jesse »

Jag tror att simulatorn i AVRstudio4 inte simulerar ADC:n. Alltså får du aldrig något interrupt. Enda sättet att testa att du gjort rätt är alltså att köra det i hårdvara.
Glattnos
Inlägg: 3106
Blev medlem: 29 oktober 2009, 20:01:18

Re: Läsa värde med ADC på AVR? (asm)

Inlägg av Glattnos »

Swech:
1.Det är i AVR Studio som det inte fungerar, jag har testat det i hårdvaran också men det kan ju finnas fel i den fortsatta koden. Tex ldi - lds mm
2. Jasså, så då laddar jag alltså r17 med adressen till ADCH, ja det låter ju ganska logiskt eftersom ADCH väll bara är ett annat namn för den adressen? Det har jag inte tänkt på.
3. Okej, då förstår jag. Jag brukar lägga SREG på stacken när jag gör en Timer-rutin, det går väll lika bra, eller är det fördel att spara i ett register istället?

Jesse:
Aha, det kanske är så, finns det inget sätt att trigga ADC i AVR Studio?

Men min ursprungliga fråga kvarstår: När SKA avbrottet ske? "This starts the process so the ADC interrupt will occur at the end of the first conversion."

Ska det inträffa direkt efter "sei" eller när programmet har "rullat runt" en gång eller något liknande?
Användarvisningsbild
jesse
Inlägg: 9240
Blev medlem: 10 september 2007, 12:03:55
Ort: Alingsås

Re: Läsa värde med ADC på AVR? (asm)

Inlägg av jesse »

gör som vana att påverka så lite som möjligt i en interruptrutin. Annars måste du alltid hålla reda på vilka register som används av interruptet och därmed inte går att använda till något annat. När du sparar SREG på stacken måste du använda ett regsiter. För att inte förstöra innehållet i det registret måste du börja med att spara det. Jag brukar använda ett macro till det:

Kod: Markera allt

.macro interrupt
	push tmp
	in tmp,SREG
	push tmp
.endmacro

.macro interrupt_end
	pop tmp
	out SREG,tmp
	pop tmp
	reti
.endmacro
sedan lägger jag bara in dessa i varje interruptrutin:

Kod: Markera allt

; ----ADC INTERRUPT ----
ADCready: interrupt
... kod ...
interrupt_end
Ta också som vana att ge namn åt alla register du använder.
bättre att det heter "dec counter" än "dec r19" så förstår man direkt vad du gör.
(EDIT: såg att du ju har gjort det... kanske var det swech kodexempel jag tittade på )

På samma sätt så har ju alla bitar i ett externt register egna namn. Använd dom istället för ettor och nollor så blir koden ännu lättare att läsa, felsöka och ändra i:

Kod: Markera allt

ldi tmp,  (1<<ADEN)+(1<<ADIE)+0B011+(1<<ADSC)
out ADCSR,tmp 
(0b011 i det här fallet är en trebitarskod som man får gå in i databladet för att se vad den gör, men även det går att förtydliga, om inte annat i en kommentar)

Men om du inte får det att fungera i hårdvara även när du bytt ut ldi mot lds (eller in) så är ju frågan hur du tar hand om resultatet efter att du fått interruptet?
sodjan
EF Sponsor
Inlägg: 43251
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping

Re: Läsa värde med ADC på AVR? (asm)

Inlägg av sodjan »

> När SKA avbrottet ske?

Sen citerar du dokumentationen där det ju står :

> "This starts the process so the ADC interrupt will occur at the end of the first conversion."

Svaret är alltså "at the end of the first conversion" d.v.s när ADC'n är "klar",
vilket ju är ganska rimligt igentligen...
hatten
Inlägg: 94
Blev medlem: 9 maj 2008, 22:16:23
Ort: Uppsala

Re: Läsa värde med ADC på AVR? (asm)

Inlägg av hatten »

Glattnos skrev: Men min ursprungliga fråga kvarstår: När SKA avbrottet ske? "This starts the process so the ADC interrupt will occur at the end of the first conversion."
Ska det inträffa direkt efter "sei" eller när programmet har "rullat runt" en gång eller något liknande?
AD-omvandlingen påbörjas då du sätter ADSC (givet att ADEN är satt) i ADCSRA. Det tar sedan ett antal klockcykler innan omvandlingen är färdig och interrupten triggas. Saxat från ATmega164s datablad; "A normal conversion takes 13 ADC clock cycles. The first conversion after the ADC is switched on (ADEN in ADCSRA is set) takes 25 ADC clock cycles in order to initialize the analog circuitry."

Hur många "CPU-cykler" detta motsvarar beror naturligtvis på hur du har prescalat ADC-klockan.
Glattnos
Inlägg: 3106
Blev medlem: 29 oktober 2009, 20:01:18

Re: Läsa värde med ADC på AVR? (asm)

Inlägg av Glattnos »

jesse: Tack för bra förklaring. Jag ska gå igenom koden ikväll och testa igen.

sodjan: Okej, tackar. Jag borde kanske ha skrivit att jag inte förstod meningen exakt pga att min engelska inte är så bra. Jag fick det till: "Detta startar prosessen så att ADC-avbrottet sker vid slutet av första konvereringen."
Du menar alltså att när ADC:n är färdig med att konvertera det analoga värdet till digitalt så är den "klar" och då sker avbrottet, eller?
I så fall, hur ofta sker det då avbrotten? Är det så att när programmet åtevänder från avbrottet så börjar ADC:n att kolla värde och konvertera igen för att på nytt skapa ett avbrott?
Blir det inte avbrott väldigt ofta då eller är ADC:n mycket långsammare än va CPU:n är(med mycket menar jag 100-10 000 gånger långsammare)?

hatten: Såg att du han svara också, så ADC-prescalern bestämmer alltså hur lång tid det tar mellan varje ADC-avbrott? Står i databladet att ADC:n ska jobba med 50kHz-200kHz, jag har CPU-klocka 8 MHz och har satt ADC-prescaler till 64. Vi säger att ADC-konverteringen tar 13 klockcykler.
Är detta rätt då:
ADC
8 000 000/64 = 125 000 hz ADC
(1 s/125 000) * 13 cykler = 0,000 008 s * 13 = 0,000 104 s
CPU
1 sek/8 000 000 = 0,000 000 125 s

Alltså: ADC-avbrott varje 0,000 104 s och CPU-instruktion vaje 0,000 000 125 s
0,000 104/0,000 000 125 = 832 gånger snabbare CPU jämfört med ADC
sodjan
EF Sponsor
Inlägg: 43251
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping

Re: Läsa värde med ADC på AVR? (asm)

Inlägg av sodjan »

> Du menar alltså att när ADC:n är färdig med att konvertera det analoga värdet till digitalt så är den "klar" och då sker avbrottet, eller?

Ja, as the docs says... :-)

> I så fall, hur ofta sker det då avbrotten?

En gång per omvandling.
Sen om du får starta om från koden eller om det går att ställa ADC'n
på kontinuerlig omvandlig, det vet jag inte.

> eller är ADC:n mycket långsammare än va CPU:n är

Jag är övertygad om att dokumenationen är kristallklar på den punkten.
Det beror ju även lite på hur snabbt du har valt att köra ADC'n.

> 832 gånger snabbare CPU jämfört med ADC

Du jämför äpplen med päron...
Du kan inte jämföra så där, de gör ju inte samma sak.
Skit i hur många gånger något är, kolla bara tiderna. Och
räkna i en vettig enhet, inte sekunder med 8 decimaler... :-)
hatten
Inlägg: 94
Blev medlem: 9 maj 2008, 22:16:23
Ort: Uppsala

Re: Läsa värde med ADC på AVR? (asm)

Inlägg av hatten »

Din uträkning är korrekt, 832 = 13*64 ([n_adc/sample * n_cpu/n_adc] ger [n_cpu/sample], dvs antalet CPU-klockcykler per sample).

ADC-prescalern bestämmer hur lång tid det tar att genomföra en AD-omvandling. Hur ofta avbrotten sker beror även på hur ofta du verkligen genomför omvandlingen!

I ditt kodexempel har du ADATE=0 och måste därför manuellt sätta ADSC varje gång du vill starta en omvandling. Man kan istället låta ADCn arbeta i ett annat läge, typ "Auto Triggered", vilket innebär att omvandlingar kommer att startas automagiskt med jämna intervall. Detta görs genom att sätta ADATE=1 och välja lämplig trigger-källa med bitarna ADTS2:0 i ADCSRB. Väljer du "Free running mode" (ADTS2:0 = 0) så kommer en ny omvandling att påbörjas just efter att en omvandling slutförts och du kommer att få ett färskt sampel var fjortonde(!) ADC-klockcykel. Då kommer alltså interrupten att triggas var 14*64 = 896e CPU-klockcykel.
Glattnos
Inlägg: 3106
Blev medlem: 29 oktober 2009, 20:01:18

Re: Läsa värde med ADC på AVR? (asm)

Inlägg av Glattnos »

Tack så mycket för de bra svaren!

Nu har jag pillat ihop en kod men vet inte om den fungerar i hårdvara. Men jag har några fler frågor, jag är ju nybörjare på det här:

1. Verkar koden rätt, eller har jag gjort något galet?

2. Hur kopplar man in hårdvaran på ADC? Jag har en ultraljudssensor LV-MaxSonar-EZ1 som ger 0-5 volt proportionellt mot avståndet, som jag förstår det.

Kod:

Kod: Markera allt

.NOLIST
.INCLUDE	"m168def.inc"
.LIST
;===============================================================================================
;	Avståndsmätare - TEST av ADC
;
;	Programmet läser av en ultraljudssensor via ADC och visar värdet från ADCH på 
;	8 st lysdioder som sitter på PortD
;
;	CPU:		Atmel Atmega168 (Int. Oscilator 8 MHz)
;	Plattform:	AVR Dragon
;
;	Ver 1.0 [2009-12-14/glattnos@hotmail.com]
;===============================================================================================

.DEF	temp			= r16		;Temporärt register
.DEF	adc_data		= r17		;Data för digitalt ADC-värde

;-----------------------------------------------------------------------------------------------
;	Macron
;-----------------------------------------------------------------------------------------------
.MACRO INTERRUPT		;Macro i början av en avbrottsrutin 
 	push temp			;Lägg temp(1) på stacken
	in temp,SREG		;
	push temp			;Lägg SREG(2) på stacken
.ENDMACRO				;Slut macro

.MACRO INTERRUPT_END	;Macro i slutet av en avbrottsrutin
	pop temp			;Hämta SREG(2) från stacken
	out SREG,temp		;
	pop temp			;Hämta temp(1) från stacken
	reti				;Returnerar från avbrottet
.ENDMACRO				;Slut macro

;===============================================================================================
;	AVBROTTS-VEKTORER
;===============================================================================================
.CSEG							;Följande placeras i PM
.ORG 0000						;Avbrottsvektor för Reset
	rjmp Reset

.ORG ADCCaddr					;Avbrottsvecktor för ADC
	rjmp Ultra

;-----------------------------------------------------------------------------------------------
;	Reset	-	Initieringskod för att förbereda systemet
;-----------------------------------------------------------------------------------------------
.ORG 0x0100						;Startadress i PM för Reset
Reset:
	ldi		temp, LOW(RAMEND)	;Initiera stackpekare
	out		SPL, temp			;
	ldi		temp, HIGH(RAMEND)	;
	out		SPH, temp			;

	ldi		temp, 0xff			;Sätt PortD som utport
	out		DDRD, temp			;

;-----------------------------------------------------------------------------------------------
;	ADC		-	Initiering
;-----------------------------------------------------------------------------------------------

	;ADC - AREF(Internal Vref turned off), Left adjusted, ADC5
	ldi		temp, (0<<REFS1)+(0<<REFS0)+(0<<ADLAR)+0b101	
	sts		ADMUX, temp 

	;ADC - ADENable, ADStartConversation, ADInterruptEnable
	;ADPrescalerSelectBits 110 = System clock/64 to ADC
	ldi		temp, (1<<ADEN)+(1<<ADSC)+(1<<ADIE)+0b110	 
	sts		ADCSRA, temp

;========================================================================
;	Main	-	Huvudloop
;========================================================================
Main:
	mov		temp, adc_data			;Sätt värdet från ADC:n...
	out		PORTD, temp				;...till PortD
	sei

	rjmp	Main					;Börja om på Main

;===============================================================================================
;	AVBROTTSRUTIN	-	Avbrottsrutinen Ultra läser av UL-sensorn och sparar värdet
;===============================================================================================
Ultra: INTERRUPT					;Börja avbrottsrutinen med MACRO INTERRUPT 
	lds		adc_data, ADCL			;Lagra värdet från ADCL i adc_data
	lds		temp, ADCSRA			;Ladda temp med ADCSRA...
	sbr		temp, (1<<ADEN)			;..och sätt ADENable biten
	sts		ADCSRA, temp			;Lägg tillbaka värdet i ADCSRA
INTERRUPT_END						;Avsluta avbrottsrutinen med MACRO INTERRUPT_END


Användarvisningsbild
Swech
EF Sponsor
Inlägg: 4750
Blev medlem: 6 november 2006, 21:43:35
Ort: Munkedal, Sverige (Sweden)
Kontakt:

Re: Läsa värde med ADC på AVR? (asm)

Inlägg av Swech »

Börja med en vanlig potentiometer istället
Koppla första benet till 0V
Mittbenet till din AD ingång
och sista benet till +5V

Så vrider du och kollar att din AVR gör som du tänkt..... d.v.s tänder dioderna med 0-255 binärt

Swech
Användarvisningsbild
jesse
Inlägg: 9240
Blev medlem: 10 september 2007, 12:03:55
Ort: Alingsås

Re: Läsa värde med ADC på AVR? (asm)

Inlägg av jesse »

Det bästa är att testa om det fungerar i hårdvaran. Ett program kan alltid innehålla små fel som ingen upptäcker förrän det testas. Koden bör fungera, men jag har inte orkat lusläsa ADC-inställningarna du gjort och jämföra med databladet. Berätta hur det går!
Skriv svar