Hi, ich wollte mit dem AVR in Assembler eine Zahl subtrahieren und schreib einfach mal das Beispiel hin subi XL, low(1,2) sbci XH, high(1,2) Jetzt kommt aber beim assemblieren die Fehlermeldung " syntax error, unexpected ',' " Wie kann man denn auch ','-Zahlen subtrahieren? Gruß
Dir ein Format überlegen, wie du deine nicht-Integerzahlen auf 8bit-Register abbilden kannst. Dann überlegen, welche Operation du dazu auf diese Register anwenden musst und das ganze in Assembler assemblieren. Ein 8bit-µC kennt nativ keine Kommazahlen.
Hi,
ahso, ok. dann multiplizier ich das erst, zieh dann ab und dividiere am
Ende wieder. Danke soweit :-)
Jetzt wollte ich einfach mal, bei dem Programm, dass die Spannung misst
und anzeigt von der Seite hier, was abziehen.
Das sieht so aus:
.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
;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
; 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
Das ganze müsste ich doch zwischen ";in ASCII umwandeln" und ";an UART
senden" einfügen, oder? Und die Zahl liegt doch im Register XL und XH?
also, wenn man mal die ','-Zahlen weglässt:
subi XL, low(1)
sbci XH, high(1)
aber das Programm funktioniert dann nicht mehr richtig.
Wär super, wenn mal jemand drüber schauen könnte.
Gruß
Als ASM Programmierer sollte man von Floatingpointzahlen abstand nehmen. Ein gebraeuchlicher Weg ist zB mit hundertsteln zu arbeiten, dann wird aus 1.2 naemlich 1200 und dann des weiteren mit 16 oder 24 bit Ganzzahl zu arbeiten.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.