Hi, ich habe hier im Forum mal geschaut was ich so finde was das rechnen in Assembler anbelangt. Nur irgendwie passt nichts glaube ich... Kann die Verbindung zu meinem Problem nicht herstellen. Deshalb schildere ich mal was ich machen will... Der Bereich den ich habe ist 0-1024 und möchte das nun auf 0-450 bekommen. Also in Delphi, wo ich eigendlich zu Hause bin, würde ich es so machen. 1024 / 450 * x ok 1024 / 450 kann man als Konstante hinterlegen weil das ja "gleich" bleibt. In dem Fall währe sie 2,276 Und da wird es jetzt "spannend"... wie macht man das denn am einfachsten in Assembler ? Nimmt man die Werte alle erstmal mal 100 damit man das Komma wegbekommt? Oder geht es irgendwie leichter. Gruß AVRli...
Ist es denn unbedingt nötig, dass es von 0-450 ist? Warum nicht 0-512? ;)
Ja es gibt sogar noch ein weterer Bereich den ich berücksichtigen muss. 0-360... Ein "Poti" als Himmelsrichtungszeiger... also es soll die Gradanzahl angezeigt werden. Da ich auch lernen will wie man richtig rechnet in Assembler fragte ich. Gruß AVRli
warum nimmst du nicht einfach avr-gcc. Dann ist das problem schon gelöst. brulli
Was Du durchführen möchtest, nennt man Division. Sofern Du keinen Prozessor mit floating-point-Unterstützung verwendest, wirst Du mit Ganzzahlarithmetik auskommen müssen. Einige Prozessoren unterstützen Operationen wie Division und Multiplikation, andere tun das nicht, so daß man für diese Grundoperationen schon Funktionen schreiben muss. Du müsstest also schon erwähnen, um welchen Prozessor es Dir geht. (avr-gcc hat nur dann einen Sinn, wenn der betreffende Prozessor ein AVR ist und der betreffende Anwender C programmieren könnte. Da das Wort "Delphi" auftauchte, ist zumindest letzteres nicht sicher.)
Hallo AVRli, Wenn Du Informationen zum Rechnen (interessant sind ja wohl hauptsächlich Multiplikation und Division) suchst, dann kannst Du Dich ja mal bei www.atmel.com nach der Application-Note zu dem Thema umschauen - die ist ganz nett, ich habe sie auch schon verwendet. MfG, Khani
Schau dir mal diese Seite an http://www.avr-asm-tutorial.net/avr_de/rechnen/index.html Recht gute Erklärung.
Hi, ja sorry es ist der ATMEGA8 den ich verwende... Mit den Codebeispielen muss ich mich mal beschäftigen. Ich hoffe es geht irgendwie "leichter"... Auf der Assembler Ebene würde ich unbedingt bleiben wollen. In BASCOM oder C geht das bestimmt mit einer Zeile... Danke für die Infos...
>Nimmt man die Werte alle erstmal mal 100 damit man das Komma >wegbekommt? Du solltest sie mal 256 oder 65535 multiplizieren, das macht es einfacher.Das was du suchst nennt sich Fest-Komma-Zahlen. Bei * 256 wäre das Komma also bei Bit 8. Somit ist dein Zahlen bereich von 1024 * 256 bis 0, wenn du ohne Vorzeichen arbeiten willst. Du müsstest also eine 24 Bit Division durchführen, 16Bit für den Vorkommateil und 8 Bit für den gebrochenen Teil. Die Auflösung im Nachkommteil wäre dann 1/256 = 0.004. Also X = (1024 * 256) / 450 = 582 X div 256 = 2 X mod 256 = 70 somit V = 345 * (2 * 256 + 70) = 200790 200790 div 256 = 784 345 * (1024 / 450) = 785.0667 Nimmst du aber 16Bit Vorkomma +16Bit Nachkomma so wäre (1024 * 2^16) / 450 = 149131 345 * 149131 = 51450195 51450195 div 2^16 = 785.0676 Du arbeitest also am besten mit 32Bit Multiplikation und Divisione und speicherst den Wert 149131 als Fixpoint Konstante für 1024 / 450. Den Input von 345 einfach damit Multiplizieren und das Resultat durch 2^16 dividiren per virtuellem Rechtsshift (du wertest also nur du 2 MSB's des 32Bit Resultates aus). Gruß Hagen
Hi, zumindest, falls Du nicht weiterkommst, kannst Du auch folgendes versuchen: besorg Dir die Demo des Pascal AVRco Compilers, http://www.e-lab.de und schreibe Dir eîn minimales Programm mit Division. Nach dem Übersetzen hast Du auch ein sehr gut lesbares Assembler-Listing wo Du die eigentliche Division sehr leicht finden kannst. hth Gunter
Nochmal einfacher erklärt: 1.) du berechnest mit dem Taschenrechner deine Konstanten, also Round(1024 * 2^16 / 450) = 149131 2.) diese Konstanten speicherst du fest in dein Program als 32Bit Zahlen 3.) willst du nun X = Input * (1024 / 450) rechnen so brauchst du nur eine 32Bit Multiplikation im Code ausführen mit der gespeicherten Konstanten. Also X = Input * Konstante. In X stehen in den obersten 16Bits der Vorkommateil und in den unteren 16Bit der gebrochene Teil. 4.) falls der gebrochene Teil >= 2^(16 -1) ist so wird normalerweise der 16Bit Vorkommateil um +1 erhöht. Dies entspricht einer Rundung. Im Vorkommateil steht dein gesuchtes Resultat. Du benötigst also nur eine 32Bit Multiplikation und fertig. Gruß hagen
Um sauber zu runden musst du also nach der 32Bit Multiplikation einfach 2^15 auf das Resulat addieren. Die obersten 2 Bytes sind dann das korrekte Resultat. Gruß Hagen
Hallo, ich habe mir die Appnotes AVR202 16-Bit Arithmetics und AVR204 BCD Arithmetics geladen und nix verstanden... hahaha... Mir fehlen die Grundkenntnisse beim rechnen in Assembler so wie ich das sehe. Das wird noch. :-) @Hagen Genial der Ansatz... >Du solltest sie mal 256 oder 65535 multiplizieren, >das macht es einfacher. Ok mal 256 sollte dicke reichen. >Das was du suchst nennt sich Fest-Komma-Zahlen. GLEICH MERKEN !!! ;-) >Bei * 256 wäre das Komma also bei Bit 8. >Somit ist dein Zahlen bereich von 1024 * 256 bis 0, >wenn du ohne Vorzeichen arbeiten willst. Ja erstmal ohne Vorzeichen... Möchte dann doch erstmal "kleine Brötchen" backen... 262144-0 -> 1 Grad entspricht dann 728 (bei 360° Ausgang). >Du müsstest also eine 24 Bit Division durchführen, >16Bit für den Vorkommateil und 8 Bit für den gebrochenen Teil. >Die Auflösung im Nachkommteil wäre dann >1/256 = 0.004. Ich danke... das sollte mich weiter bringen... Hagen, dein LED Projekt unter Codesammlung ist ja wohl voll genial! Das bau ich mir mal um zu spielen. Aber erst ein Projekt fertig machen. Gruß AVRli
Ähm, danke. Falls du keine Multiplikation in Assembler hinbekommst, obwohl ja in den Application Notes schon fertige Sourcen für 32Bit drinnen sind, so könntest du ja per Schleife dividieren. Dein Input wird dann * 256 expandiert. Nun subtrhierst du davon solange 728 bis der Input <= 0 wird. Die Anzahl der Schleifendurchläufe ergibt dann deinen gesuchten Wert. Das LED Projekt ist schön und gut, hat aber einen Nachteil: du kannst keine Treiber zwischenschalten damit du den LED Strom erhöhen kannst. Und die 200mA maximal pro AVR begrenzt die ganze Geschichte auf 10 LEDs on maximal. Gruß Hagen
Hi Hagen, ok soweit. Meinen Input mal 256 nehmen. Macht man das in dem man auch in einer Schleife immer input:=input+input macht oder geht da was mit "schieben". Das mit dem Teilen würde ich hinbekommen. Fertige Routinen haben einen Nachteil... ;-( Da steigt man erst dahinter wenn man weiß was passieren soll. Gruß AVRli...
Die Multiplikation ist eigentlich einfach. Ich versuche dir das mal zu erklären. Als erstes müssen wir ausrechnen wie groß unsere Zahlenbereiche eigentlich wären, das bestimmt nämlich wieviele Bytes unsere Zahlen groß sein müssen und wie der Algo. auszusehen hat. Also auf den neueren AVRs wird die 8x8 Mul unterstützt, d.h. per Hardware kann man zwei Bytes miteinander multiplizieren und bekommt ein Word = 16 Bit Wert raus. Wollen wir größere Zahlen multiplizieren müssen wir also die gesammte Multiplikation aus diesen kleinen 8x8 Muls zusammenbauen. Nun zu den Zahlenbereichen. Du sagtest 8 Bit Nachkommagenauigkeit reicht dir. Also schon mal 1 Byte.Gehen wir davon aus das im Vorkomma ebenfalls nur 1 Byte benutzt werden soll so hiese dies das der Divisor der Konstanten (1024 / x) X eben >= 4 sein muß. Ich schätze mal das dürfte gegeben sein. Sprich deine Konstanten sind ja 360 und 450, also größer als 4. Somit benötigen unsere Fixpoint Konstanten nur 2 Bytes an Speicher, ergo wir Multiplizieren eine X Bit Zahl mit einer 16 Bit Festkommazahl. Nun müssen wir ermitteln wie groß denn X in Bits sein könnte. Da 1024 deine obere Grenze wäre sind die Inputs <= 1024, ergo 2 Bytes. D.h. wir müssen 2 16Bit Zahlen miteinander Multiplizieren und bekommen ein 32Bit Resultat. Die unterste Schranke für die Konstanten ist (1024 * 256) / 4 = 65536 -> [256, 0] in den 2 Bytes, da aber 256 im Vorkomma > 255 ist überschreitet dies unseren Wertebereich. Wir wählen also 5 als minimalsten Wert für die Konstante. Die oberste Schranke wäre dann (1024 * 256) / 1024 = 256 -> [1, 0] in den 2 Bytes. Logisch es muß 1.0 sein. Nun die Schranken vom Input: 1024 * (1024 * 256 / 4) = [4, 0, 0, 0] also 4 Bytes für das Resultat sind nötig ! Wir benötigen also tatsächlich eine 16Bit * 16Bit = 32Bit Multiplikation. Dazu wird nun die 16Bit Multiplikation mit jeweils den obersten 8Bit und den untersten 8 Bit des Multiplikaten multipliziert. Es entstehen also 2 mal eine 16Bit * 8Bit Multiplikation. Da aber der AVR nur 8x8 Muls besitzt müssen wir also diese beiden 16x8 Muls nochmals zerlegen. Insgesammt benötigen wir also 4 mal eine 8x8 Mul. Als Beispiel imDezimalen Zahlenbereich multiplizieren wir mal 12 * 34 auf einem Blatt Papier, die Schulmethode wäre dies. 12 * 34 2 * 4 + 1 * 4 + 2 * 3 + 1 * 3 ----------- 8 + 4 + 6 + 3 ---------- 408 Exakt so bauen wir unsere 16x16 Bit Mul zusammen, mit dem Unterschied das wir als Zahlenbasis nicht 10 sondern 256 = 1 Byte benutzen. I0 = r16 = Input Low I1 = r17 = Input High K0 = r18 = Konstante Low K1 = r19 = Konstante High T0 = r20 = Resultat LSB 0 T1 = r21 = Resultat LSB 1 T2 = r22 = Resullat MSB 0 T3 = r23 = Resultat MSB 1 Zero = r24 = 0 die Mul im AVR gibt ihr 16Bit Resultat in [r1:r0] zurück clr Zero mul I0, K0 mov T0, r0 mov T1, r1 mul I1, K1 mov T2, r0 mov T3, r1 mul I0, K1 add T1, r0 adc T2, r1 adc T3, Zero mul I1, K0 add T1, r0 adc T2, r1 adc T3, Zero so T = I * K, unser 32 Bit Resultat. Nun noch runden add T0, 0x80 adc T1, Zero adc T2, Zero adc T3, Zero In T steht jetzt eine 24Bit Vorkomma + 8Bit Nachkommazahl gerundet. Dich interessiert aber nur noch T1 bis T3. Gruß Hagen
Klasse!!! ;-D Ich drucke mir das gerade mal aus... Ich denke das werde ich begreifen! Ich melde mich wieder. Vielen Dank für die ausführliche Beschreibung! Vielleicht steigt man ja damit auch hinter die APPNOTES. Gruß AVRli...
Es gibt natürlich noch einige Optimierungen, sprich man kann Register einsparen und sogar Multiplikationen. clr T0 mul I0, K0 mov T0, r1 ldi T1, 0x80 add r0, T1 clr T1 adc T0, T0 adc T1, T1 mul I1, K0 add T0, r0 adc T1, r1 mul I0, K1 add T1, r0 In T1:T0 stände nun das gerundete 16 Bit Resultat. Dazu muß aber eben sichergestellt werden das Input <= 1024 und die Konstante = Dividend > 16 ist. d.h. 1024 / X * Input, wobei X > 16 und Input <= 1024. Denn 1024 / 16 = 64 und 64 * 1024 = 256 * 256, somit wäre die 2 Bytes Grenze für das Resultat überschritten. Mit obigem Code solltest du also hinkommen. Falls X > 16 und Input <= 1024. Gruß Hagen
Shit natürlich ein Fehler drinnen, sorry ;) mul I0, K0 mov T0, r1 ldi T1, 0x80 add r0, T1 clr T1 adc T0, T1 adc T1, T1 mul I1, K0 add T0, r0 adc T1, r1 mul I0, K1 add T0, r0 adc T1, r1 mul I1, K1 add T1, r0 Gruß Hagen
Hi Hagen, lass uns mal bei dem Beispiel bleiben ;-) .nolist .include "D:\Atmel\AVR Tools\AvrAssembler\Appnotes\m8def.inc" .list .def wrH = r25 .def I0 = r16 ; Input Low .def I1 = r17 ; Input High .def K0 = r18 ; Konstante Low .def K1 = r19 ; Konstante High .def T0 = r20 ; Resultat LSB 0 .def T1 = r21 ; Resultat LSB 1 .def T2 = r22 ; Resullat MSB 0 .def T3 = r23 ; Resultat MSB 1 .def Zero = r24 ; 0 ;* INTERRUPT EINSPRUNGADRESSEN FÜR DEN AVR AT90S8515 .cseg .org 0 rjmp Initial ;RESET ;* INITIAL ABSCHNITT Initial: ldi wrH,High(RamEnd) out sph,wrH ldi wrH,Low(RamEnd) out spl,wrH ; Stack initialisiert ; die Mul im AVR gibt ihr 16Bit Resultat in [r1:r0] zurück clr Zero ldi I0,low(512) ldi I1,high(512) ; ldi I0,low(725) ; ldi I1,high(725) ldi K0,low(255) ldi K1,high(255) mul I0, K0 mov T0,r0 mov T1,r1 mul I1,K1 mov T2,r0 mov T3,r1 mul I0,K1 add T1,r0 adc T2,r1 adc T3,Zero mul I1,K0 add T1,r0 adc T2,r1 adc T3,Zero ;so T = I * K, unser 32 Bit Resultat. ;Nun noch runden ldi wrH, $80 ; DEZ 128 add T0, wrH adc T1, Zero adc T2, Zero adc T3, Zero ; T3 T2 T1 T0 ; 00 01 FE 80 Hm irgendwas stimmt hier nicht... eine Zuweisung auf die T's...? :-I also richtig währe... 01FE00 80 was ist passiert? Die Werte ansich stimmen ja... ;-) Ich probiere mal noch nen bischen... Gruß AVRli...
ldi I0,low(512) ldi I1,high(512) Input also 1024 ldi K0,low(255) ldi K1,high(255) Die Konstante ist 1024/1028 ?? das ist doch nicht richtig oder ? Round(1024/1028 * 256) * 512 + $80 = $0001FE80 / 256 = $01FE = 510 = 1024/1028 * 512 == 510. 256 * 1024 / 255 = 1028. Stimmt also alles, nur deine Konstante ist falsch oder eben richtig, je nachdem wie man es betrachtet ;) Gruß Hagen
Nehmen wie mal 360 also Konstante und als Input 345. K = 1024/360 * 256 = 728 = $02D8 I = 345 = $0159 raus kommt $03D518 + $80 = $03D598 div 256 = $03D5 = 981. zur Überprüfung 1024/360 * 345 = 981.3333 Gruß Hagen
Ohh neee... peinlich'st... Ich nimm alles zurück und behaupte das Gegenteil... ;-D Klasse! Jaja einer zählt von 0 der andere von 1... Hagen handshakeDANKE ;-) Gruß AVRli...
Hi, @Hagen 1000 Dank, sehr schöne Anleitung, wäre toll für Codesamlung/Wiki Grüße Quark
Moment mal. Du hast einen 10 bit Wert (0 - 1024) mit dem ADC eingelesen und willst den nun umrechnen, so dass du einen Wert zwuschen 0 und 450 erhälst den du dann auf einem Display anzeigen kannst? Dann wäre das doch aber 450 / 1024 * x und nicht umgekehrt oder habe ich da jetzt was falsch verstanden. Sagen wir mal das Display hat 4 Stellen. 3 vor dem Komma und eine danach. Dann wäre die einachste Lösung wohl 1. multiplikation der 10bit Zahl mit 45. Das ergibt nen 16bit Wert 2. MSB des Ergebnis auf den ersten beiden Stellen des Displays anzeigen. 3. LSB mit 100 multiplizieren. 4. MSB des Ergebnis auf den letzten beiden Stellen anzeigen. 3 und 4 kann man um Rechenzeit in der Ausgaberoutine zu sparen auch noch folgendermassen abändern. 3. LSB mit 10 multiplizieren. 4 MSB des Ergebnis ist jetzt kleiner als 10 und kann daher mit deutlich weniger Rechenaufwand auf der 3. Stelle ausgegeben werden. 5. LSB des neuen Ergebnisses wieder mit 10 multiplizieren und MSB des Ergebnis auf Stelle 4 ausgeben.
oben schrieb er: " Der Bereich den ich habe ist 0-1024 und möchte das nun auf 0-450 bekommen. Also in Delphi, wo ich eigendlich zu Hause bin, würde ich es so machen. 1024 / 450 * x " womit Stefan wohl recht hat. Wenn du Zahlen im Bereich 0-1024 in den Bereich 0-450 oder 0-360 übersetzen willst so muß es tatsächlich 450 / 1024 * x heisen. Bei meinem Ansatz spielt das nur insofern eine Rolle das man nun versuchen sollte die Bitanzahl der Nachkommastellen gegenüber den Vorkommastellen zu erhöhen. Statt 16,8Bit also 8,16Bit um genügend Genauigkeit zu bekommen. Grundsätzlich kann man aber obige Multiplikation denoch benutzen, nur die Konstante ändert sich. @Stefan, so ganz konnte ich deinen Ausführungen nicht folgen. Primär wurde doch nur ein einfacher Weg gesucht um linear zwischen den Zahlenbereichen zu konvertieren, und nichts auf einem Display auszugeben. Gruß hagen
Hi, ich hab's soweit... ;-) grinsbisüberbeideohren @Stefan hast recht mit der Formel... war mein Fehler... ;-) In der Tat will ich das Ergebniss auf einen Display ausgeben. Vielleicht gibt es einen einfacheren Weg, kann schon sein. Ich habe mir Deinen Lösungsansatz auch gespeichert. Ich muss mit den Zahlen aber noch weiter rechnen. Ich denke es ist über die "Hagen" Methode einfacher. Ich kann mich aber auch irren. @Hagem Ich freue mich das ich gelernt habe mit Fest-Komma-Zahlen zu rechnen. Denn vor diesem "Problem" wird man wohl noch öffter stehen. Eine Frage zu den T's habe ich noch... Ich Dividiere nun wie folgt... SUB_AGAIN: subi T0,Low(728) sbci T1,Byte2(728) sbci T2,Byte3(728) sbci T3,Byte4(728) brlt SUB_FINISH ldi wrH,1 add ZL, wrH ldi wrH,0 adc ZH, wrH rjmp SUB_AGAIN SUB_FINISH: Das klappt soweit gut... Du schreibst... > In T steht jetzt eine 24Bit Vorkomma + 8Bit Nachkommazahl gerundet. > Dich interessiert aber nur noch T1 bis T3. Heist das das ich nun mit der Nachkommazahl rechne? Wenn ja wie läst man die zur "probieren" mal weg? Gruß AVRli...
siehe auch hier einige fertige Unterprogramme: http://mirror01.users.i.com.ua/~birua/math32.html Gruß
Ich hatte in meiner Beschreibung einen Fehler. Das Ergebnis der Multiplikation mit 45 müsste zuerst noch um 2 Stellen nach rechts geschoben werden ( mit lsr und ror ). Ich glaube die Methode erklärt man am besten an einem Beispiel. Ich habe dort aber statt mit 45 mit 4.5 multipliziert. Gehen wir mal davon aus der 10bit Wert befindet sich in r17:r16 und die 4 Stellen des Ergebnisses - davon ist die 4. die Nachkommastelle - werden in r16 - r19 gespeichert. Auf Rechenzeit optimiert könnte das so aussehen .include "m8def.inc" ldi r16, LOW(897) ldi r17, HIGH(897) ; r17: r16 enthält jetzt den Wert 897 ldi r22, 9 mul r17, r22 mov r21, r0 mul r16, r22 mov r20, r0 add r21, r1 ;r21:r20 = r17: r16 * 9 lsr r21 ror r20 ;r21:r20 ist nun gleich r17:r16 * 4.5 mov r16, r21 lsr r16 lsr r16 ; r16 enthält jetzt die erste Stelle ldi r22, 0b00000011 and r21, r22 ldi r22, 10 mul r21, r22 mov r21, r0 mul r20, r22 add r21, r1 mov r20, r0 mov r17, r21 lsr r17 lsr r17 ; r17 enthält nun die zweite Stelle lsr r21 ror r20 lsr r21 ror r20 mul r20, r22 mov r18, r1 mul r0, r22 mov r19, r1 ; r18 enthält nun die 3. und r19 die 4. Stelle ; Ergebnis = 897 / 1024 * 450 = 394.1 (Dieses Programm rundet immer ab) Wenn man die ersten beiden Zahlen Dieses Programms nicht mitzählt kommt man auf 36 CPU Takte. Wesentlich scneller ginge es wenn man sich mit 8bit Genauigkeit zufrieden gibt. Beispiel: Nehmen wir an der 8bit Wert, den wir umrechnen wollen, befindet sich in r16. Diesmal machen 4 Stellen beim Ergebnis keinen Sinn mehr also errechnen wir nur 3 Stellen die in r16 - r18 gespeichert werden. .include "m8def.inc" ldi r16, 224 ldi r22, 9 mul r16, r22 lsr r1 ror r0 ;r1:r0 ist nun gleich r16 * 4.5 mov r16, r1 ; erste Stelle ldi r22, 10 mul r0, r22 mov r17, r1 ;zweite Stelle mul r0, r22 mov r18, r1 ;dritte Stelle Das wären dann (ohne die erste Zeile) 13 Takte
> Heist das das ich nun mit der Nachkommazahl rechne? Ja, > Wenn ja wie läst man die zur "probieren" mal weg? das könntest du machen, nur verliert dann alles seinen Sinn. Unter deiner ersten,falschen Annahme von 1024 / 450, bekommt man ja 2.28 raus. Ohne Nachkommastellen also einfach 2. Aber bei 450 / 1024 = 0.44 also 0 ohne Nachkomma. Wenn du also den Algo. ohne Nachkommastellen benutzen würdest wirst du immer mit 0 multiplizieren. Ich meinte mit "interessiert dich nur noch T1 -T3" was anders. Du musst schon mit gebrochenen Zahlen rechnen, aber nach der Rundung bekommst du dein Resulat sofort als Ganzzahl. Und nur diese dürfte von Interesse sein, wenn ich dich richtig verstanden habe. Unter diesem Aspekt interessiert die T1 bis T3 nur weil dort eben der Ganzzahlteil drinnensteht. Sogesehen kannst du die eigentliche Berechnung mit gebrochenen Zahlen nicht weglassen, da sonst alles mathematisch falsch wäre. Dein jetziger Algo. ist die von mir vorgeschlagene Subtraktions-Schleife als Ersatz für eine Divison. Diesen Algo. würde ich nur nutzen falls der AVR keine HW-Muls unterstützt. Denn er ist im Durchschnitt wesentlich ineffizienter und im Codeverbrauch nur unwesentlich besser. Warum willst du die HW-Fähigkeiten der MCU nicht benutzen wenn sie schonmal vorhanden sind ? @Stefan: dein Vorschlag ist identisch mit meinem. Es gibt Unterschiede, richtig, aber mathematisch ist es das gleiche. Du "verlegst" nur die Kommastelle an eine andere Stelle. Man möchte X * 4.5 rechnen. Du biegst dir die 4.5 so zurecht das eine Ganzzahl entsteht -> 4.5 * 2 = 9. Somit (x * 2) * (4.5 * 2) = (r * 2) Das hat natürlich den Vorteil das für die Konstante 450 der zur Verfügung stehen Zahlenbereich am besten ausgenutzt wird. Hat aber den großen Nachteil das bei einer anderen Konstante wie zB. 360 der komplette Algo. anders programmiert werden muß. Die obige "universeller" Methode benötigt auch nur 19 MCU Takte. Gruß Hagen
Sie benötigt zwar nur 19 Takte, trennt das Ergebnis aber nicht in die einzelnen Stellen auf, was für eine Ausgabe aber nötig ist, ist daher also nicht gleich.
Sorry Stefan ich bezog mich auf die Ausgangsfrage, wie man mathematisch eine Division im AVR durchführen kann. Dazu gab es ja mehrere Lösungen, bis hin zum Reverse Engeeniering von Compilaten. Mathematisch sind denoch unserere Lösungen identisch. Natürlich geht deine Routine einen Schritt weiter und drösselt das Resultat gleich so auf das man eine BCD Konvertierung drinnen hat. Also sofort auf einem Display ausgeben kann. Mir ging es aber nur primär darum darsustellen wie man es allgemeingültig macht. Gruß hagen
Hi ihr beiden, es ist wirklich interessant wie das alles geht. Vielen Dank für die Geduld mit mir. @Stefan Danke für die Mühe mir das zu erläutern. Aber wie Hagen schon schrieb, ging es primär schon darum zu verstehen wie man mit dem AVR rechnet. Hagen bestätigte ja das das Ziel das selbe ist. Nix für ungut aber es war soviel "input" gestern das ich erstmal an Hagen's Ansatz festhalten möchte. Denn ich glaube es nun 100%tig verstanden zu haben. In diesem Projekt geht es nicht um Geschwindigkeit. @Hagen Ok mit den T's war nämlich so wie Du schreibst... immer 0. Ich hatte eine falsche Vorstellung von den Inhalten. Aber das haste ja nun "gerade" gerückt... Nun habe ich festgestellt das ich noch mit einem offset arbeiten darf. Denn 450 ist 1024 sondern nur 870-900... soll heißen meine Spannung die ich AD einlese ist von 0-4,7 oder so ähnlich. Das aber nur am Rande. Die Konstante 895 * 256 / 450 werde ich dann auf "Knopfdruck" einmal berechnen und in den EEprom schreiben. So ne Art "Setup". >Dein jetziger Algo. ist die von mir vorgeschlagene >Subtraktions-Schleife als Ersatz für eine Divison. >Diesen Algo. würde ich nur nutzen falls der AVR keine >HW-Muls unterstützt. Ja Hagen da bin ich wohl "betriebsblind" wenn man es so sagen darf. Meine AVR-Vorgänger waren aus der AT90S family... 2323, 2343, 2313 Das waren alle AVR mit denen ich bis jetzt gespielt habe. Die haben sowas glaube ich nicht... dann habe ich noch das Buch AVR-RISC von Trapert. MUL kenne ich seit gestern... :-) Da ich da mal subtrahiert hatte wuste ich noch wie es ging und das habe ich hier wieder verwendet. :-) Nun kommt bestimmt die "datasheet-Rüge". ...ich bin schon auf dem Weg ins PDF... ;-) >Warum willst du die HW-Fähigkeiten der MCU nicht >benutzen wenn sie schonmal vorhanden sind ? Moment muss erstmal lesen... hahaha Gruß und danke... AVRli
...gelesen und nicht gerad viel schlauer... Nicht lachen... :-I >Diesen Algo. würde ich nur nutzen falls der AVR keine HW-Muls >unterstützt. HW-Muls ? Ich dachte es gibt nun auch nen Befehl mit dem man 2 Register teilen kann. HW-Muls bezeichnet aber wohl eher eine bestimmte Technik. >Warum willst du die HW-Fähigkeiten der MCU nicht >benutzen wenn sie schonmal vorhanden sind ? Nun habe ich das ganze ATmega8 "Instruction Set Summary" durch. Ich glaub ich bin zu blöd dazu... Gruß AVRli...
mit HW-Mul meinte er sicher Hardware Multiplikation. Das ist der mul Befehl. Eine Hardware Division unterstützt dieser uC nicht.
Ja, HW-Muls = mul Mnemonic des AVRs. Division muß man selber bauen, und ein Weg dazu ist eben die Reziprokale Division. Wat'n dat nu wieder ? Die Reziprokale Divison ist eine Multiplikation mit dem Kehrwert. Also X / Y = X * (1 / Y), und wenn du genau hinschaust hast du mit obigen Postings gelernt wie man eine solche Division durchführt, denn obige Vorschläge sind nichts anderes als reziprokale Divisionen. Dieses Verfahren ist immer dann ideal wenn die Hardware die Divisionen nicht direkt unterstützt und wenn man durch eine Konstante dividiert. Es gibt aber auch Algorithmen die die reziprokale Divisionen mit variablen Werten durchführen können. Für gebrochene Zahlen gibt es zwei häufig verwendete Verfahren, eg. Datentypen. Das einfachste sind die Festkommazahlen, so wie oben. Die meisten RTL's der heutigen Compiler nutzen aber Fließkommazahlen. Deren Aufbau ist komplizierter, ermöglicht aber größere Wertebereiche und sind somit universeller. Sie sind aber niemals so effizient und auch leicht zuschneidbar wie die Festkomma-Arithmetik. Mit ein bischen Nachdenken und dem Wissen wie Festkommazahlen funktioieren, wird es ein leichtes mit gebrochenen Zahlen auf AVRs zu rechnen. Siehe vorherige Postings. Obwohl die ganze Thematik im Grunde sehr leicht ist, findet man im WEB denoch nur wenige gute Seiten die das alles exakt erklären. Das verführt dann auch zu den Aussagen "baue in BASIC deine Berechnungen, compiliere sie, und nehme den Assemblercode des Compilat als Ausgangsbasis". Nur, verstanden hat man es dann denoch nicht. Gruß Hagen
Hi Hagen, >Reziprokale Division. Wat'n dat nu wieder ? ohne Worte... ;-D >Die Reziprokale Divison ist eine Multiplikation mit dem Kehrwert. >Also X / Y = X * (1 / Y), ... Ja das kenne ich noch von der Berechnung von "Wiederständen". Da kam auch sowas mit 1/X vor... >Mit ein bischen Nachdenken und dem Wissen wie Festkommazahlen >funktioieren, wird es ein leichtes mit gebrochenen Zahlen auf >AVRs zu rechnen. Ein bischen Vertraut habe ich mich mit dem Verfahren schon gemacht. Ein Gleichung wie diese "C = (A/0.9) + (B/0.1)" habe ich mit dem Wissen schon realisieren können. >Obwohl die ganze Thematik im Grunde sehr leicht ist, findet man >im WEB denoch nur wenige gute Seiten die das alles exakt erklären. doch eine... :-) www.mikrocontroller.net >Das verführt dann auch zu den Aussagen >"baue in BASIC deine Berechnungen, compiliere sie, und > nehme den Assemblercode des Compilat als Ausgangsbasis". > Nur, verstanden hat man es dann denoch nicht. Das ist der Punkt! Ich finde auch die AppNotes von ATMEL und kann den Quellcode auch nutzen doch nutzen und verstehen sind zwei verschiedene Sachen. Ich danke und denke über den Kehrwert nach... ;-) Gruß AVRli...
> "C = (A/0.9) + (B/0.1)" Diese Gleichung würde ich umstellen nach C * 10 = A * 8 + A + B Damit fällt nur eine komplizierte Mul an. A * 8 +A +B sind 5 Additionen, und um C aus C * 10 zu bekommen eine Mul -> C = 0.1 * C'. Aber, bei AVR's mit Hardware Multiplikation kann sich das schnell relativieren, da 2 Taktzyklen für eine beliebige 8Bit Multiplikation im Grunde extrem gut sind. Intel CPUs zb. benötigen abhängig von den Werten die man multipliziert eine unterschiedliche Anzahl von Takten. Gruß hagen
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.