Hi, es gibt ja die befehle subi und sbci Dafür kann man aber ja immer nur ein Register nehmen, wie subtrahiert man also was, das in 4 Registern steht?
In 4 registern ? Eine 32 bit Zahl ? Also, es gibt SUB, SBC, SUBI, etwa. Damit kann man alles machen. Was soll denn genau gemacht werden ? Unsigned32 - konstante32 unsigned32 - konstante16 Unsigned32 - unsigned32 unsigned32 - signed32 unsigned32 - unsigned16 unsigned32 - signed16 unsigned32 - unsigned8 unsigned32 - signed8
> Dafür kann man aber ja immer nur ein Register nehmen, wie subtrahiert > man also was, das in 4 Registern steht? Genauso, wie du das in der Schule mit Papier und Bleistift gelernt hast: Mit dem niederwertisten Byte beginnen (SUB oder SUBI), dann der Reihe nach die höherwertigen Bytes abarbeiten und dabei jeweils den Übertrag berücksichtigen (SBC bzw. SBCI). Du brauchst für n Bytes also 1 SUB[I]- und n-1 SBC[I]-Befehle.
hm, also wenn sie in temp1..4 steht, und man zB 1 abziehen möchte, quasi so? : subi temp1, (1) subi temp2, (1) sbci temp2, (1) subi temp3, (1) sbci temp3, (1) subi temp4, (1) sbci temp4, (1)
Nein, so: subi temp1, (1) sbci temp2, (0) sbci temp3, (0) sbci temp4, (0) bgezogen wird nur 1. Die sbci-Befehle haben nur die Aufgabe, evtl. entstandene Borrows auf die nächste Stelle zu übertragen.
Nicht vergessen. auf tiefer Adresse steht auch auch das tieferwertige byte.
Bist du dir da ganz sicher? Ich würd mir mal bei Wikipedia oder so anschauen, was der Unterschied zwischen Little und Big Endian ist. MfG Marius
Hallo, für den AVR gilt in diesem Fall die Reihenfolge, in der sie der Programmierer in die Register rein geschrieben hat. Anhaltspunkt wäre nur Ablage von 16Bit Werten in den Doppelregistern und damit die Nutzung der zugehörigen Befehle und die Ablage von Adressen im Flash wegen der Wordorganisation. Verbindlich ist es aber genaugenommen auch nicht oder habe ich im AVR-Datenblatt was übersehen? Gruß aus Berlin Michael
oha wrote: > Nicht vergessen. auf tiefer Adresse steht auch auch das tieferwertige > byte. Als ASM-Programmierer kann man das halten, wie man will. So lange keine Kompatibilität zu einer Progrmmiersprache erforderlich ist und man auf die Bequemlichkeit verzichten will, eine long-Variable im Debugger als solche zu sehen, kann man die vier Bytes sogar wahllos im Speicher verstreuen, oder gar Teile davon, oder alles in Registern halten.
Der AVR arbeitet intern schon mit little endian. Daher macht man sich Zusatzarbeit wenn man umgekehrt arbeiten will.
oha wrote: > Der AVR arbeitet intern schon mit little endian. Daher macht man sich > Zusatzarbeit wenn man umgekehrt arbeiten will. Will man nur rechnen, ergibt sich keine Zusatzarbeit. Erst, wenn das Ergebnis ein Adreßpointer (X,Y,Z) sein soll, ergibt sich eventuell etwas MOV-Arbeit. Man hat also als Assemblerprogrammierer die freie Wahl. Ich benutze in den Arithmetikroutinen (+,-,/,*) daher gar keine festen Register, sondern Defines (a0..a3,b0..3). Wie man die dann Registern zuordnet, ist wurscht. Peter
Peter Dannegger wrote: > Will man nur rechnen, ergibt sich keine Zusatzarbeit. Abgesehen von den AT90 und ein paar antiken Tinys haben AVRs auch ein bischen 16-Bit Arithmetik, wie ADIW. Damit wird aus dem Beispiel oben sbiw temp1, (1) sbci temp3, (0) sbci temp4, (0) und dann ist nicht ganz unwichtig wie temp1/2 zueinander liegen.
ich hab so ein programm, dass mir die spannung in V anzeigt. wenn ich nun möchte, dass es mir immer 1V weniger anzeigt, muss ich dann das ganze platzieren, bevor es in ASCII oder nachdem es in ASCII umgewandelt wurde?
4register wrote: > ich hab so ein programm, dass mir die spannung in V anzeigt. wenn ich > nun möchte, dass es mir immer 1V weniger anzeigt, muss ich dann das > ganze platzieren, bevor es in ASCII oder nachdem es in ASCII umgewandelt > wurde? Sinnigerweise wird man das vor der ASCII Umwandlung machen. Was interessiert alles was nach der ASCII Umwandlung kommt, was dein String zu bedeuten hat? Eben, gar nichts. Für die Ausgaberoutinen ist das einfach nur ein Text, nicht mehr.
Nochmals zurueck zu 16bit operationen... ADIW und SUBIW sind wesentliche Vereinfachungen, die man nicht sausen lassen sollte.
oha wrote: > Nochmals zurueck zu 16bit operationen... ADIW und SUBIW sind wesentliche > Vereinfachungen, die man nicht sausen lassen sollte. Sind sie das? Der Schreibaufwand im Assembler ist geringer, aber sonst gibts kaum einen Unterschied zu den Einzeloperationen.
>es gibt ja die befehle subi und sbci >Dafür kann man aber ja immer nur ein Register nehmen, wie subtrahiert >man also was, das in 4 Registern steht? Und wenn du dann damit fertig bist, probierst du den Spass mal als Addition. Dann wirst du feststellen, dass die AVR-Architektur der letzte Schrott ist und bei Atmel, im Gegensatz zu ihrer Werbung, niemals Kenner der Materie gearbeitet haben. Atmel ist der letzte Schrott den es gibt!
oha wrote: > Nochmals zurueck zu 16bit operationen... ADIW und SUBIW sind wesentliche > Vereinfachungen, die man nicht sausen lassen sollte. Sie sind speziell nur dazu gedacht, ein kleines konstantes Offset auf nen Pointer zu berechnen. In einer Math-Lib spielen sie keine Rolle, da möchte man 2 x-beliebige Werte addieren können. Peter
zumal subi sbci genauso 2 Takte braucht wie sbiw daher nur 1 Wort weniger Flash-Bedarf aber nicht schneller und eingeschränkt.
Lymangood wrote: > Und wenn du dann damit fertig bist, probierst du den Spass mal als > Addition. Begründung? Wenn du ADDI,ADCI vermisst, dann denk dran, dass sich Addition und Subtraktion hier letztlich nur in den Flags unterscheiden. Für den Assembler-Programmierer ist das etwas hässlich aber dem Compiler völlig schnurz. Andererseits erpart das so erheblich Coderaum für wichtigere Befehle (SUBI+SBCI benötigen zusammen 12,5% des Coderaums).
>hier letztlich nur in den Flags unterscheiden. Naja, das ist ja das Problem, wenn du mit Zahlen grösser 8 Bit arbeitest. >(SUBI+SBCI benötigen zusammen 12,5% des Coderaums). Das ist Quatsch bei annähernd einhundert Befehlen. Oder, auf was beziehst du dich mit den 12,5 Prozent?
Lymangood wrote: >>(SUBI+SBCI benötigen zusammen 12,5% des Coderaums). > Das ist Quatsch bei annähernd einhundert Befehlen. Oder, auf was > beziehst du dich mit den 12,5 Prozent? Ja, alles Quatsch und Atmel ist scheiße! Zusammen mit den Argumenten belegen beide Befehle (in allen möglichen Parametervarianten) nun mal 12,5 Prozent der möglichen 16 Bit.
Lymangood wrote:
> Atmel ist der letzte Schrott den es gibt!
Na ja, ich würde a den Mund nicht so voll nehmen. Instruktionssätze
werden üblicherweise nicht mit irgend jemandem ausgehandelt, sondern an
Hand von Statistiken über realen Code konstruiert. Als Randbedingung
gehen Restriktionen durch Codewortbreite, vorhandene Hardwaremodule etc.
pp. ein.
Aber das sieht man als Feld-, Wald- und Wiesengroßmaul natürlich
nicht...
Hm, habs in allen Variationen probiert, aber nie zieht der Controller was ab! Dachte mir, dass dürfte nicht weiter schwierig werden, aber irgendwie funktioniert das nicht. Er zeigt halt nun statt 2,5 mal 2,3 V oder so an, aber nie das gewünschte :-(
Es ist der Code, der hier auf der Seite vorgestellt wird und läuft soweit einwandfrei. Bei den ***** habe ich die Subtraktion mit hingeschrieben, da die Zahl nach der Multiplikation ja in temp1-4 abgelegt wird. .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 ;************** subtraktions-Test subi temp1, (1) sbci temp2, (0) sbci temp3, (0) sbci temp4, (0) ;************** ; 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
@Simon K. >Zusammen mit den Argumenten belegen beide Befehle (in allen möglichen >Parametervarianten) nun mal 12,5 Prozent der möglichen 16 Bit. Haha, tut mir leid aber ich kann dir echt nicht folgen. (roflmao) (Wieviel Parametervarianten sind es denn nu?) @Uhu Uhuhu >Na ja, ich würde a den Mund nicht so voll nehmen. Instruktionssätze >werden üblicherweise nicht mit irgend jemandem ausgehandelt, sondern an >Hand von Statistiken über realen Code konstruiert. Als Randbedingung >gehen Restriktionen durch Codewortbreite, vorhandene Hardwaremodule etc. >pp. ein. Ja, so sollte es eigentlich sein. Hat aber nur indirekt mit dem Fallbeispiel zu tun. >Aber das sieht man als Feld-, Wald- und Wiesengroßmaul natürlich >nicht... Blanke Polemik. Auf das Beispiel selbst bist du nicht näher eingegangen, wozu auch.
Hi >Bei den ***** habe ich die Subtraktion mit hingeschrieben, da die Zahl >nach der Multiplikation ja in temp1-4 abgelegt wird. Und was soll da jetzt subtrahiert werden? @anderen: Tauscht eure EMailadressen, und lasst die Sekundanten die Wahl der Waffen. Kindergarten. MfG Spess
Ähm, naja 1. So dass bei 2V zum Beispiel 1V erscheint. Hab ich das falsch verstanden?
Hi Wenn ich das richtig gesehen habe, entspricht bei dir 1000 1V. Dann sollte es so gehen: subi temp1, Low(1000) sbci temp2, byte2(1000) sbci temp3, byte3(1000) sbci temp4, byte4(1000) Assembler2 benutzen! MfG Spess
ich kann da jetzt nicht mehr folgen ... 2-1=1 ja dann ? ?
So habe das mal getestet! Super, danke Spess! Es waren zwar nicht 1000 sondern 1.000.000 aber es scheint soweit zu klappen. Nur kann es sein, dass das auch sonst irgendwas am Programm ändert, ausgenommen die Subtraktion? Ich habe hier eine Spannung, die ich ändern kann. Und nun gibt es beim messen Sprünge auf 9,9V, wobei ich wegen der Referenz ja nur bis 5V messen kann. und gibt es irgendeinen Befehl für ein optionales "-" Zeichen? Da ich, um bei dem 2V Beispiel zu bleiben, wenn ich nun 3 abziehe (+)1V erhalte. Oder müsste das per if-Konstruktion gemacht werden? @pillepalle: also falls du damit meinst, dass du den Sinn meines Programmes nicht verstanden hast, es geht konkret darum den Offset eines elektr. Gerätes rauszuhauen und dafür bräuchte ich die Subtraktion
ja ,aber warum machst du das nicht schon vorher bevor du den ADC Wert in eine Spannung umrechnest also 5Volt ref/1023 = ca. 4,8mV also zieh doch deinen Offset schon mal vom gebildeten mittelwert ab und fertig ....
zu negativen Zahlen fällt mir jetzt nur Zweierkomplement zu ein also bei 8 Bit zahlen wäre dann ein gesetztes Bit 7 der hinweis auf eine negative zahl +125 01111101 7Dh 0 00000000 00h -55 11001001 C9h
>ja ,aber warum machst du das nicht schon vorher bevor du den ADC Wert in >eine Spannung umrechnest >also 5Volt ref/1023 = ca. 4,8mV also zieh doch deinen Offset schon mal >vom gebildeten mittelwert ab und fertig .... Hm, das muesste doch aufs selbe raus kommen, oder? Also ich hab das nun mal genauer getestet mit der Programmänderung und da geht dann später definitiv was schief... Also es zeigt nun zwar schön 0V an, aber sobald sich die Spannung ändert ist die Spannungsänderung nach der Programmänderung anders als sie ohne der Programmänderung ist.
Hi Einen Überlauf (->negative Zahl) erkennst du am gesetzten Carry-Flag nach der Subtraktion. Damit hast du dein 'Minus'. Um dein Ergebnis in einen positiven Wert (das was du als Wert nach dem 'Minus' ausgeben willst) umzurechnen, musst du Null-Ergebnis rechnen (entspricht 'neg' 2er-Komplement). MfG Spess
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.