Hallo Florian,
ich versuch mal das meiste zu erklären ...
Code:
.include "C:\VMLAB\include\m32def.inc"
.def temp = r16
.org $00 ; 1 $000 Reset Go to Reset handler
rjmp start
.org $02
rjmp dummy
.org $04
rjmp dummy
.org $06
rjmp dummy
.org $08
rjmp dummy
.org $10
rjmp dummy
.org $12
rjmp dummy
.org $14
rjmp dummy
.org $16
rjmp dummy
.org $18
rjmp dummy
.org $20
rjmp ad_vector ; ADC complete
.org $22
rjmp dummy
.org $24
rjmp dummy
.org $26
rjmp dummy
.org $28
rjmp dummy
Die Interrupt Vektor Table. Je nach ausgeführtem Interrupt springt er hier zu den entsprechenden Unterfunktionen.
Code:
dummy:
nop
nop
nop
nop
reti
Wenn ein nicht gewünschter Interrupt ausgeführt wird (also einer, der nichts mit dem ADC zu tun hat und nicht der Reset ist) springt er in diese Funktion. Die verplemtern nur Rechenzeit und sollte überflüssig sein.
Code:
ad_vector:
in temp, ADCH
sbi ADCSR,ADSC ; StartConversion
sei
ret
Die Funktion wird über die Interrupt Table aufgerufen, sobal ein Wert gewandelt wurde.
in temp, ADCH liest has High Byte der ADC Wertes ein. Eigentlich müsste man hier noch eine zweite Variable einlesen, in der der Wert ADCL (das Low Byte) eingelesen wird. Fehlt hier völlig (warum, siehe weiter unten).
Anschließend werden hier die Interrupts allgemein angeschaltet (da sie niergends abgeschaltet werden, sollte das eigentlich überflüssig sein).
Code:
ldi temp,high(RAMEND)
out sph,temp
ldi temp,low(RAMEND)
out spl,temp
Initialisierung des Stack
Das löschen der Register sollte überflüssig sein.
Code:
sbi ADCSR,ADEN ; ADC-Enable
sbi ADCSR,ADIE ; ADC-EnableEnable Interrupt
sbi ADCSR,ADPS2 ; SET Prescale to //
cbi ADCSR,ADPS1 ; Divisions Factor //
cbi ADCSR,ADPS0 ; by 16
Hier wird der ADC angeschaltet, der Interrupt (sobald eine Wandlung fertig ist) angeschaltet, und der Prescaler auf 16 gesetzt.
Beim ADMUX Register sind folgende Werte wichtig:
Die beiden MSB (REFS0/1) sind auf 0 zu setzen um mit ARef als Referenz zu arbeiten.
ADLAR (wird im Bsp. gesetzt) dient dazu, das Ergebnis nicht rechtsbündig (also 8 Bit im unteren Byte und 2 Bit im High byte), sondern linksbündig (also 8 bit im high byte und 2 bis im low byte) auszugeben.
D.h. da oben nur das High Byte ausgelesen wurde, und linksbündig aktiviert ist, werden hier im Beispiel nur 8 bit genutzt (anstatt 10 möglichen).
Die restlichen 5 bit im ADMUX Registen (MUX 4- 0) dienen dazu, einzustellen, welcher der ADC pins eingelesen werden soll. Da alle 5 Bits auf 0 sind wird der ADC0 eingelesen.
Code:
sbi ADMUX, ADLAR ; Get 8 most significant bits in ADCL
linksbündig wird aktiviert.
Code:
sbi ACSR,ACIE ; ACIE: Analog Comparator Interrupt Enable
sbi ACSR,ACIS1 ; ACIS1, ACIS0: Analog Comparator //
sbi ACSR,ACIS0 ; Interrupt Mode Select //
Hier wird wohl etwas für den Analog Comparator eingestellt. Diese Passage verstehe ich auch nicht und würde sie einfach löschen.
Dann kommen ein paar nop's - benötigt er wohl zur Stabilisierung (würde ich je nach verwendeter Taktfrequenz evtl. noch ein wenig länger machen).
Code:
sbi ADCSR,ADSC ; StartConversion
Hier wird das erste Mal eine Wandlung gestartet. Zukünftige Wandlungen werden automatisch nach einer vollendeten Wandlung gestartet (im Interrupt).
Mit sei werden die Interrupts alle aktiviert.
Danach folgt nur noch eine Endlosschleife, die nach jeder vollendeten Wandlung per Interrupt unterbrochen wird.
Das macht das Programm:
Das Programm wandelt die am ADC0 liegende Spannung bezogen auf AREF in einen digitalen 8 bit Wert um, der in der Schleife immer aktuell in der Variable Temp steht. Dieser muss nur ausgelesen werden und beispielsweise über das UART oder ein LCD angezeigt werden.
Falls du noch Fragen hast melde dich!
Ich hoffe dir geholfen zu haben (allerdings gebe ich keine Garantie über die geschribenen Dinge, da ich nebenher Fußball schaue und ich noch nie etwas mit dem ATMega32 gemacht habe).
Viele Grüße
Flite
Lesezeichen