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.