so hier ist der komplette code,
.include "m8def.inc"
.def z0 = r1 ; Zahl für Integer -> ASCII Umwandlung
.def z1 = r2
.def z2 = r3
.def z3 = r4
.def temp1 = r16 ; allgemeines Register, zur kurzfristigen
Verwendung
.def temp2 = r17 ; Register für 24 Bit Addition,
niederwertigstes Byte (LSB)
.def temp3 = r18 ; Register für 24 Bit Addition, mittlerers
Byte
.def temp4 = r19 ; Register für 24 Bit Addition,
höchstwertigstes Byte (MSB)
.def adlow = r20 ; Ergebnis vom ADC-Mittelwert der 256
Messungen
.def adhigh = r21 ; Ergebnis vom ADC-Mittelwert der 256
Messungen
.def messungen = r22 ; Schleifenzähler für die Messungen
.def zeichen = r23 ; Zeichen zur Ausgabe auf den UART
.def temp5 = r24
.def temp6 = r25
; Faktor für Umrechung des ADC-Wertes in Spannung
; = (Referenzspannung / 1024 ) * 100000
; = 5V / 1024 * 1.000.000
.equ Faktor = 4883
.equ F_CPU = 4000000 ; Systemtakt in Hz
.equ BAUD = 9600 ; Baudrate
; Berechnungen
.equ UBRR_VAL = ((F_CPU+BAUD*8)/(BAUD*16)-1) ; clever runden
.equ BAUD_REAL = (F_CPU/(16*(UBRR_VAL+1))) ; Reale Baudrate
.equ BAUD_ERROR = ((BAUD_REAL*1000)/BAUD-1000) ; Fehler in Promille
.if ((BAUD_ERROR>10) || (BAUD_ERROR<-10)) ; max. +/-10 Promille
Fehler
.error "Systematischer Fehler der Baudrate grösser 1 Prozent und damit
zu hoch!"
.endif
; RAM
.dseg
.org 0x60
Puffer: .byte 10
; hier geht das Programm los
.cseg
.org 0
ldi temp1, LOW(RAMEND) ; Stackpointer
initialisieren
out SPL, temp1
ldi temp1, HIGH(RAMEND)
out SPH, temp1
sbi DDRB, PB0 ;Led an Port B0
;UART Initalisierung
ldi temp1, LOW(UBRR_VAL) ; Baudrate einstellen
out UBRRL, temp1
ldi temp1, HIGH(UBRR_VAL)
out UBRRH, temp1
sbi UCSRB, TXEN ; TX einschalten
; ADC initialisieren: Single Conversion, Vorteiler 128
; Kanal 0, interne Referenzspannung AVCC
ldi temp1, (1<<REFS0)
out ADMUX, temp1
ldi temp1, (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0)
out ADCSRA, temp1
Hauptschleife:
clr temp1
clr temp2
clr temp3
clr temp4
ldi messungen, 0 ; 256 Schleifendurchläufe
; neuen ADC-Wert lesen (Schleife - 256 mal)
adc_messung:
sbi ADCSRA, ADSC ; den ADC starten
adc_warten:
sbic ADCSRA, ADSC ; wenn der ADC fertig ist, wird dieses
Bit gelöscht
rjmp adc_warten
; ADC einlesen:
in adlow, ADCL ; immer zuerst LOW Byte lesen
in adhigh, ADCH ; danach das mittlerweile gesperrte High
Byte
; alle 256 ADC-Werte addieren
; dazu wird mit den Registern temp4, temp3 und temp2 ein
; 24-Bit breites Akkumulationsregister gebildet, in dem
; die 10 Bit Werte aus adhigh, adlow aufsummiert werden
add temp2, adlow ; addieren
adc temp3, adhigh ; addieren über Carry
adc temp4, temp1 ; addieren über Carry, temp1 enthält 0
dec messungen ; Schleifenzähler MINUS 1
brne adc_messung ; wenn noch keine 256 ADC Werte ->
nächsten Wert einlesen
; Aus den 256 Werten den Mittelwert berechnen
; Bei 256 Werten ist das ganz einfach: Das niederwertigste Byte
; (im Register temp2) fällt einfach weg
;
; allerdings wird der Wert noch gerundet
cpi temp2,128 ; "Kommastelle" kleiner als 128 ?
brlo nicht_runden ; ist kleiner ==> Sprung
; Aufrunden
subi temp3, low(-1) ; addieren von 1
sbci temp4, high(-1) ; addieren des Carry
nicht_runden:
; Ergebnis nach adlow und adhigh kopieren
; damit die temp Register frei werden
mov adlow, temp3
mov adhigh, temp4
ldi temp,high(4883) ;
cpi temp3,low(4883) ;Wert gleich 4883
cpc temp4,temp
breq ok ;ja,
cbi PORTB, PB0 ;Led aus
rjmp no_ok
ok:
sbi PORTB, PB0 ;Led an
no_ok:
; in Spannung umrechnen
ldi temp5,low(Faktor)
ldi temp6,high(Faktor)
rcall mul_16x16
; in ASCII umwandeln
ldi XL, low(Puffer)
ldi XH, high(Puffer)
rcall Int_to_ASCII
;an UART Senden
ldi ZL, low(Puffer+3)
ldi ZH, high(Puffer+3)
ldi temp1, 1
rcall sende_zeichen ; eine Vorkommastelle ausgeben
ldi zeichen, ',' ; Komma ausgeben
rcall sende_einzelzeichen
ldi temp1, 3 ; Drei Nachkommastellen ausgeben
rcall sende_zeichen
ldi zeichen, 'V' ; Volt Zeichen ausgeben
rcall sende_einzelzeichen
ldi zeichen, 10 ; New Line Steuerzeichen
rcall sende_einzelzeichen
ldi zeichen, 13 ; Carrige Return Steuerzeichen
rcall sende_einzelzeichen
rjmp Hauptschleife
; Ende des Hauptprogramms
; Unterprogramme
; ein Zeichen per UART senden
sende_einzelzeichen:
sbis UCSRA,UDRE ; Warten, bis UDR bereit ist ...
rjmp sende_einzelzeichen
out UDR, zeichen ; und Zeichen ausgeben
ret
; mehrere Zeichen ausgeben, welche durch Z adressiert werden
; Anzahl in temp1
sende_zeichen:
sbis UCSRA,UDRE ; Warten, bis UDR bereit ist ...
rjmp sende_zeichen
ld zeichen, Z+ ; Zeichen laden
out UDR, zeichen ; und Zeichen ausgeben
dec temp1
brne sende_zeichen
ret
; 32 Bit Zahl in ASCII umwandeln
; Zahl liegt in temp1..4
; Ergebnis ist ein 10stelliger ASCII String, welcher im SRAM abgelegt
wird
; Adressierung über X Pointer
; mehrfache Subtraktion wird als Ersatz für eine Division durchgeführt.
Int_to_ASCII:
push ZL ; Register sichern
push ZH
push temp5
push temp6
ldi ZL,low(Tabelle*2) ; Zeiger auf Tabelle
ldi ZH,high(Tabelle*2)
ldi temp5, 10 ; Schleifenzähler
Int_to_ASCII_schleife:
ldi temp6, -1+'0' ; Ziffernzähler zählt direkt im
ASCII Code
lpm z0,Z+ ; Nächste Zahl laden
lpm z1,Z+
lpm z2,Z+
lpm z3,Z+
Int_to_ASCII_ziffer:
inc temp6 ; Ziffer erhöhen
sub temp1, z0 ; Zahl subrahieren
sbc temp2, z1 ; 32 Bit
sbc temp3, z2
sbc temp4, z3
brge Int_to_ASCII_ziffer ; noch kein Unterlauf, nochmal
add temp1, z0 ; Unterlauf, eimal wieder addieren
adc temp2, z1 ; 32 Bit
adc temp3, z2
adc temp4, z3
st X+,temp6 ; Ziffer speichern
dec temp5
brne Int_to_ASCII_schleife ; noch eine Ziffer?
pop temp6
pop temp5
pop ZH
pop ZL ; Register wieder herstellen
ret
; Tabelle mit Zahlen für die Berechung der Ziffern
; 1 Milliarde bis 1
Tabelle:
.dd 1000000000, 100000000, 10000000, 1000000, 100000, 10000, 1000, 100,
10, 1
; 16 Bit Wert in Spannung umrechnen
;
; = 16Bitx16Bit=32 Bit Multiplikation
; = vier 8x8 Bit Multiplikationen
;
; adlow/adhigh * temp5/temp6
mul_16x16:
push zeichen
clr temp1 ; 32 Bit Akku löschen
clr temp2
clr temp3
clr temp4
clr zeichen ; Null, für Carry-Addition
mul adlow, temp5 ; erste Multiplikation
add temp1, r0 ; und akkumulieren
adc temp2, r1
mul adhigh, temp5 ; zweite Multiplikation
add temp2, r0 ; und gewichtet akkumlieren
adc temp3, r1
mul adlow, temp6 ; dritte Multiplikation
add temp2, r0 ; und gewichtet akkumlieren
adc temp3, r1
adc temp4, zeichen ; carry addieren
mul adhigh, temp6 ; vierte Multiplikation
add temp3, r0 ; und gewichtet akkumlieren
adc temp4, r1
pop zeichen
ret