Sida 1 av 3
Läsa värde med ADC på AVR? (asm)
Postat: 13 december 2009, 22:49:04
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?
Re: Läsa värde med ADC på AVR? (asm)
Postat: 13 december 2009, 23:05:18
av Swech
Bör väl vara
Sen måste du spara status i din interrupt rutin också
Swech
Re: Läsa värde med ADC på AVR? (asm)
Postat: 13 december 2009, 23:24:44
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"
Re: Läsa värde med ADC på AVR? (asm)
Postat: 13 december 2009, 23:52:58
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
Re: Läsa värde med ADC på AVR? (asm)
Postat: 14 december 2009, 01:41:45
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.
Re: Läsa värde med ADC på AVR? (asm)
Postat: 14 december 2009, 08:13:01
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?
Re: Läsa värde med ADC på AVR? (asm)
Postat: 14 december 2009, 08:45:43
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?
Re: Läsa värde med ADC på AVR? (asm)
Postat: 14 december 2009, 10:13:30
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...
Re: Läsa värde med ADC på AVR? (asm)
Postat: 14 december 2009, 10:29:10
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.
Re: Läsa värde med ADC på AVR? (asm)
Postat: 14 december 2009, 10:46:44
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
Re: Läsa värde med ADC på AVR? (asm)
Postat: 14 december 2009, 11:22:27
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...

Re: Läsa värde med ADC på AVR? (asm)
Postat: 14 december 2009, 11:29:03
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.
Re: Läsa värde med ADC på AVR? (asm)
Postat: 15 december 2009, 19:55:53
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
Re: Läsa värde med ADC på AVR? (asm)
Postat: 15 december 2009, 22:09:37
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
Re: Läsa värde med ADC på AVR? (asm)
Postat: 15 december 2009, 23:00:21
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!