Hallo, ich möchte folgende Funktion in Assembler auf einem PIC18F4685 realisieren: Input der Funktion: zwei 8-Bit Werte a und b sowie ein 4,5 oder 6 bit wert i i ist ein step counter, der mir die werte zwischen a und b in i schritte unterteilen soll. Beispiel: i ist 4-bit, a ist 10, b ist 156 wenn i 0 ist ist das ergebnis 10 wenn i 15 ist ist das ergebnis 156 wenn i 7 ist ist das ergebnis ca. 73 Diese Funktion möchte ich möglichst schnell in Assembler realisieren. Hat hier jemand eine idee? Danke, ALEXander.
Hi, habe ich dich richtig verstanden? Du möchtest einfach alle Werte zwischen a & b in einer bestimmten Schrittweite aufteilen. Dabei noch die Frage ob dir der einzel Wert reicht oder ob du alle Schritte haben möchtest? Bsp: a = 30 | b = 192 | i = 7 192-30 => 162/7 => ~23+30 => 53 Endwert | reicht oder i = 0 | 30 i = 1 | 192 i = 2 | 111 i = 3 | 84 i = 4 | 71 i = 5 | 62 i = 6 | . i = 7 | . i = 8 | . . . . Bzw. wie sieht genau deine Formel aus die du dem PIC bei bringen möchtest? ASM dauert natürlich etwas aber ist kein problem, ich hab schon nen Polynomfit 2. Ordnung gemacht und der dauert bei nem 8Bit (damals 16F876) ohne HardwareMultiplikation auch net lange. Gruß Yob
Hallo, genau, ich will alle Werte zwischen a und b in 16/32/64 schritte aufteilen und dann für den schritt i den entsprechenden wert haben: Hi, habe ich dich richtig verstanden? Du möchtest einfach alle Werte zwischen a & b in einer bestimmten Schrittweite aufteilen. Dabei noch die Frage ob dir der einzel Wert reicht oder ob du alle Schritte haben möchtest? Bsp: a = 30 | b = 192 | i ist 0-15 (die werte stimmen nur ungefähr, aber es ist glaube ich klar wie das gemeint ist). Berechnen kann ich das auch, aber effizient in Assembler auf dem PIC implementieren ist natürlich etwas anderes... i = 0 | 30 i = 1 | 40 i = 2 | 51 i = 3 | 62 i = 4 | 72 i = 5 | 83 i = 6 | . i = 7 | . i = 8 | . . . . i = 15 | 192 Danke und gruss, ALEXander. PS: Hier noch eine Emailantwort von einem Kollegen zum Thema This is a proportionality problem: - i is delimited by 0 and 2^j-1, j=4,5 or 6 : ex. j=4 then i is between 0 and 2^4-1=15 - x then can be calculated as follows: x = (b-a)*i / (2^j-1) + a example : j=4, b=233, a =10 1) i=0 ==> x=a=10 2) i=15 ==> x=(233-10) * 15 / 15 + 10 = 223+10=233 3) i=7 ==> x=223*7/15 + 10 = 114 Das ist schon mal gut, aber wie programmiert man das effizient in Assembler?
Hallo, hab hier jetzt keinen Nerv für Formeln, aber: Endwert (192) - Startwert (10) einfach, Subtraktion (Endwert-Startwert) (182) / Wertigkeit (16/32/64) -> Schrittweite, einfach, nur Rechts schieben Schrittweite * i (x) + Startwert -> Ergebnis. Schrittweite * i ist recht problemlos, Start-/Endwert für die Subtraktion und die Teilung / 16 passend erweitern je nach nötiger Genauigkeit und im Ergbebis dann wieder passend teilen. Sind dann auch alles nur Schiebeoperationen. Gruß aus Berlin Michael
Hmm, ich versteh zwei Sachen immer noch nicht: Wie mache ich aus einer Subtraktion eine Schiebeoperation? Und was heisst hier erweitern? Wäre sehr dankbar für einen Tipp, ALEXander.
Was ist für dich schnell, für mich brauchst du eine 16bit Multiplication, um das halbwegs vernünftig abzubilden.
Nehmen wir mal an, Du willst den Weg von a nach b in 16 Schritten zurücklegen. Dann ist die Schrittweite ja (b-a)/16. b-a kann man ganz einfach rechnen. Das "durch 16" macht man etwas trickreicher: Zunächst mal rechnet man nicht mit 8 bit Zahlen, sondern mit 16 bit. Die unteren 8 bit sind die Nachkommastellen. Also etwa so: aa = a * 256; bb = b * 256; Damit hast Du beide Zahlen um 8 bit nach links geschoben. Effizienter ist es in der Regel in C durch aa = (a << 8); Wenn Du dann die Differenz d = b-a berechnet hast, musst Du sie auch um 8 bit schieben: dd = (d << 8); Und weil Du in 16 Schritten arbeiten willst, musst Du diese Differenz dann durch 16 teilen. Das ist wieder 4 bit nach rechts schieben: dd = (dd >> 4); oder in Kurzform: dd = ((b-a) << 4); Jetzt addierst Du in einer Schleife 16mal dd auf aa rauf, dann bist Du bei bb angekommen. Der 8-bit-Wert, den Du haben willst, ist dann das obere Byte von aa, also (aa >> 8);
Ich schließe mich Wolfgang an, nur mit dem Zusatz das du statt 256 mit 100 multiplizieren musst. Ich würde es so machen: movf a,w subwf b,w mullw D'100' rlf PRODL, f bcf PRODL, 0 btfsc STATUS, C incf PRODH, f clrf STATUS, C //:2 rlf PRODL, f bcf PRODL, 0 btfsc STATUS, C incf PRODH, f clrf STATUS, C //:4 rlf PRODL, f bcf PRODL, 0 btfsc STATUS, C incf PRODH, f clrf STATUS, C //:8 rlf PRODL, f bcf PRODL, 0 btfsc STATUS, C incf PRODH, f clrf STATUS, C //:16 rlf PRODL, f bcf PRODL, 0 btfsc STATUS, C incf PRODH, f clrf STATUS, C //:32 rlf PRODL, f bcf PRODL, 0 btfsc STATUS, C incf PRODH, f clrf STATUS, C //:64 in PRODL steht dann der kleineste Schritt *100 Bsp: 156-10=146 -> 146/64= 2,28125 -> PIC-Prog => 146*100=14600 -> 146000/64=228 (binäres rechnen) -> in PRODL = 228 dann Wert sichern und zum aufaddieren würd ich die Multiplikation wählen wegen 1 Cycle + sicherung und dann ne Tabelle im PIC anlegen mit den Table befehlen (TBLWT). Grüße Yob
Das sieht doch schon mal super aus, vielen Dank. Eine Tabelle anlegen ist auch gut, allerdings werden die a und b Werte sich immer mal wieder ändern, so dass ich dann die werte neu berechnen muss. Vielen Dank, stürze mich gleich mal auf GPASM, ALEXander.
Nuja, a & b können sich ja ruhig ändern. Ist nur die Frage wielange die Interpolation dauern darf. Subtraktion Multiplikation Schieben Tabelle mit allen Zwischenwerten anlegen Denke das musst dann mal probieren & testen. Feinschliff kann man ja noch machen. Wenn du das kleine Programm soweit hast und alles passt, wärs klasse wenn du es mit in die Codesammlung posten würdest ^^. Bis dato helfen wir gern. Yob.
Klar, mache ich dann. Jetzt werd ich erst mal testen. Gruss, ALEXander.
Warum man allerdings die Werte mit 100 multiplizieren soll, verschließt sich meinem Intellekt. Vielleicht kann Yob das mal erklären? Das mit der notwendigen Tabelle kann er ja gleich mit erklären... Ich schreibe mal kurz den vollständigen Algorithmus in C hier hin: // Eingangsparameter uint8 startwert = 10; uint8 endwert = 150; uint8 logstep = 4; // fuer 2 ^ 4 == 16 steps // Rechenwerte uint8 steps = (0x01 << logstep); uint16 startwert16 = startwert << 8; uint16 increment16 = (endwert - startwert) << (8-logstep); // Ausgabeschleife for (; steps; steps--) { ausgabe(startwert16 >> 8); startwert16 += increment16; } // Ausgabe des Endwerts ausgabe(startwert16 >> 8); Wobei ich jetzt mal davon ausgehe, das Schieben schneller als Multiplizieren ist... das o.a. Assemblerprogramm läßt mich daran zweifeln. Also vielleicht doch lieber das "<<" durch eine Multiplikation ersetzen? Die Ausgabe der oberen 8 bits des aktuellen Schleifenwerts kann man aber sicherlich durch Direktzugriff auf das Byte lösen. Und die Schiebeoperation << 8 sicherlich auch durch direktes Laden des oberen Bytes + Nullen des unteren Bytes. Ich würde erst mal schauen, was der Compiler denn so aus dem Code macht.
Hast Du evtl vergessen, den Endwert auch zu schieben mit << 8 ? Oder am besten man schiebt erst die Differenz, oder? Und wieso schiebst Du dann mit (8-logstep) nach links? was macht das "8 -" dort? Vielen Dank für die Aufklärung, ALEXander.
Hallo Alexander, genau. Man schiebt die Differenz. Deswegen ist endwert-startwert auch in Klammern gesetzt. Den Endwert als solchen braucht man nur in der Differenzberechnung. Die "8" ist die selbe 8 wie beim Endwert und beim Startwert. Wenn ich den Increment um 8 schieben würde, dann würde ich mit einem Schleifendurchlauf auf den Endwert kommen. Wenn ich aber 16 Schleifendurchläufe haben will bis zum Endwert, dann darf ich bei jedem Schleifendurchlauf nur 1/16 der Differenz dazu addieren. Also ist der Increment = Differenz / 16. Durch 16 ist einfach ein Schieben nach rechts um 4 Stellen. Wenn ich also erst 8 Stellen nach links schieben soll und dann 4 nach rechts, komme ich also auf uint16 increment16 = (endwert - startwert) << (8-logstep); Alles klar jetzt?
Hm, leider seh ich in deinem C-Programm nicht durch, Wolfgang. Ich muss allerdings dazu sagen das ich auch kein C behersche. Ich habe deinen ersten Post wie folgt verstanden. Startwert = a = 10 (damit wir alle die selben Werte & Bezeichnungen haben) Endwert = b = 150 (Grundprinzip: (b-a)/i=kleinste Schrittweite [s]) Steps = i = 16 Du wolltest mit 16-Bit Zahlen rechnen um die Zahlen für die Nachkommastellen mit zuberechnen und somit eine Genauer lineare Interpolation zuerreichen. 1. von 8Bit auf 16-Bit -> *256 also x8 nach links schieben oder einfach ein zweites leeres 8Bit register vor definieren. a=10 -> 10*256=2.560|10 -> B'0000.1010' 2.560 -> 0000.1010|0000.0000=>aa b=150 -> 150*256=38.400|150 -> B'1001.0110' 38.400-> 1001.0110|0000.0000=>bb 2. Differenz bilden von b,a & auch 16 Bit-Wert durch x8 links schieben 150-10 = 140 -> 140*256=35.840 -> B'1000.1100|0000.0000' 3. Die Differenz durch die Anzahl der Teilschritte (Steps) durch x4 rechts schieben 35.8400:16=2240 -> B'0000.1000|1100.0000' -> kleinste Schrittweite so nun kann man die kleinste Schrittweite als Dezimalwert wie folgt auslegen. HighByte B'0000.1000'=8 LowByte B'1100.0000'=192 => 8,192 oder nur 8,000. Dieses Ergebnis ist aber "falsch" oder sehr ungenau! Denn: 150-10=140 -> 140:16=8,75 ! Ebenso wenn man nachrechnet! 16x8,192=131,072 ! Zudem wird der 16-Bit von a&b am Anfang garnicht benötigt! --- Bei meinem Asm-Programm bilde ich zuerst die Differenz mit den beiden 8-Bit Werten da dies ja so vorgeben sind muss man diese nicht größer machen. Die Differenz multipliziere ich mit 100 damit ich einen 16-Bit Wert erhalte. Multiplaktion weil diese Hardwaresitig geschiet und nur 1 Zyklus/Cycle brauch, schneller gehts nicht. Die 100 gibt auch gleich die Anzahl der Stellen nach dem Komma an. 150-10=140 -> 140*100=14.000 -> B'0011.0110|10110000' Dann teile ich die Differenz durch die Anzahl der Teilschritte (16) durch rechtsschieben (ganz großes sry, im o.a. Code ist das schieben falsch, poste ich noch mal, richtig). 14.000:16=875 -> B'0000.0011|0110.1011' so kann man die 875 auf 8,75 auflösen wenn wieder durch 100 teilt in 100er 10er 1er für eine Ausgabe auf einem LCD z.B. Im Nach hinein betracht geht deine Variante ebenso, Wolfgang wenns nicht genau sein muss. Tabelle: Bei einer linearen Interpolation möchte man ja die ganzen Messwerte zwischen 2 Punkten haben und kann man in einer Tabelle im PIC anlegen mit den Table-Befehlen laut Datenblatt was ich gesehen habe, mehr war damit nicht gemeint. Ich weis nicht was Alex noch mit dem Werten machen möchte. Rechtschieben richtig: ------------------------ rrf PRODH, f bcf PRODH, 7 btfsc STATUS, C goto n1 rrf PRODL, f bcf PRODL, 7 goto n2 n1 rrf PRODL, f bsf PRODL, 7 bcf STATUS, C n2 Yob.
Hallo Yob, im Grunde genommen hast Du meinen Ansatz schon gut verstanden, nur bei der Betrachtung der Differenz bist Du aufs falsche Gleis geraten: > 2. Differenz bilden von b,a & auch 16 Bit-Wert durch x8 links schieben > 150-10 = 140 -> 140*256=35.840 -> B'1000.1100|0000.0000' Soweit noch richtig. > 3. Die Differenz durch die Anzahl der Teilschritte (Steps) durch x4 > rechts schieben > 35.8400:16=2240 -> B'0000.1000|1100.0000' -> kleinste Schrittweite Auch noch richtig. > so nun kann man die kleinste Schrittweite als Dezimalwert wie folgt > auslegen. HighByte B'0000.1000'=8 LowByte B'1100.0000'=192 => 8,192 Nee, das ist jetzt falsch. Das kann man so nicht behaupten. Wenn Du diese Zahl (2240) wieder in Dezimal umrechnen willst, musst Du sie durch 256 teilen, also die vorherige Multiplikation wieder rückgängig machen. Das gibt 2240/256 = 8.75. Nach 16 Schritten gibt das 8.75 x 16 = 140. Solange Du beim Schieben nach rechts keine 1en herausschiebst, ist das Verfahren exakt. D.h. mit einer Erweiterung um n bits kann man eine Schleife über 2^n Steps ohne Genauigkeitsverlust fahren. Tabelle: was Alex mit den Werten machen will, weiss ich nicht. Da er es aber möglichst schnell haben will, geht es wahrscheinlich um sofortige Ausgabe auf einen Port. Gruß Wolfgang
Alex, wer hat Dich eigentlich getrieben, einen PIC zu nehmen? Die CPU ist ja fürchterlich! Und mit den Assemblerbefehlen wächst mit jeder Zeile der Frust! Hast Du schon mal den MSP430 von TI probiert? Da ist die CPU und der C-Compiler so gut, dass Du in der Regel auch komplexe Funktionen ohne Geschwindigkeitsverlust in C realisieren kannst. Für den ungeübten Anfänger ist der MSP430 um Längen besser geeignet.
Salü, das ganze Projekt baut auf auf der MidiBox Hardware Plattform (http://www.ucapps.de). Daher. Was will ich damit machen? Folgendes: Ich habe an den PIC RGB-LEDS angeschlossen, die zwischen zwei Farben in einer bestimmten Anzahl Schritten hin und her blinken sollen. Jede Farbe (RGB) hat eine Tiefe von 1 Byte. Das gibt mir zwei Farbwerte von jeweils 1 Byte (das Ganze dann drei Mal für RG und B), zwischen denen ich in 16, 32 oder 64 Schritten wechseln will, was dann ein Blinken ergibt. Für die 14, 30 oder 62 Zwischenwerte muss ich dann halt interpolieren, aber total genau braucht es nicht zu sein, lieber schnell. MSP430 schau ich gerne mal an, danke für den Tip. C ist mir auch viel lieber als Assembler, wenn ich ehrlich bin. Grüsse, ALEXander.
Hm, entweder seh ich es nicht oder das ganze is zusimple. Wenn du Schrittweite wieder durch 256 teilst ist das Ergebnis am Ende trotzdem 8 statt 8,75 & das ganze wäre doppelter Aufwand weil man dann einfach gleich 140:16 rechnet in dem 4 mal schiebt, was dann auch schneller ist. 2240 1120 :2 560 :4 280 :8 140 :16 70 :32 35 :64 17,5 -> 17 :128 8,5 -> 8 :256 Endergebnis = 8 (gibt ja kein Komma!) Ich will durch 16 -> 16=2^4 -> also 4 mal >> fertig Ob man es in C oder Asm macht sollte heute eigentlich egal sein, hoffe ich da die Compiler doch ausgereift sein sollten. In C hat man halt weniger Zeilen zuschreiben theoretisch. Grüße Yob
Yob, Du scheinst da einen Block zu haben. Mal sehen, ob ich ihn Dir transparent machen kann: Wir haben eine Differenz, die im Bereich 0-255 liegt. Also kleine ganze Zahlen. Um interpolieren zu können, müssen wir die Differenz teilen (in unserem Beispiel durch 16 für 16 Schritte). Dadurch bekommen wir Nachkommastellen. Das kann man in Integer nicht mehr rechnen. Also nehmen wir vor der Rechnung alles mit 256 mal und erzeugen uns damit 8 binäre Nachkommastellen. Dazu rechnen wir den Startwert und die Differenz mal 256. Jetzt können wir die Differenz durch 16 teilen, ohne dass wir Genauigkeit einbüßen. Denn die 8 bits, die wir uns nach dem Komma verschafft haben, sind mehr als die 4 bits, die wir durch die notwendige Division durch 16 (wegen der 16 Schritte) wieder verlieren. Jetzt machen wir die Iteration, und unser Ergebnis holen wir uns aus den oberen 8 bits unseres 16bit Additionsergebnisses. Ich weiss im Moment eigentlich nicht so richtig, wo Du das Verständnisproblem hast. Könnte es sein, dass Du übersehen hast, dass die Additionen während der Interpolationen 16 bit breit sind, und der Nachkommaanteil immer erhalten bleibt? Gruß Wolfgang
ich habe nicht alles genau durchgelesen, aber kommarechnen mache ich persönlich nur dann, wenn's wirklich nötig ist (und schlussendlich erwünscht). wie wär's wenn man zuerst multipliziert und dann dividiert? 1. bilde differenz (a-b) und speichere sie in einer 8bit-variable 2. multipliziere diese variable mit i -> 16bit-ergebnis 3. schiebe dieses 16bit-ergebnis 4. beachte nur die unteren 8bit dieses neun ergebnisses und addieren b dazu 5. diesen 8bit-wert zurück geben das dürfte nur ein paar cyklen brauchen. ...oder habe ich einen denkfehler gemacht?!?
Ah, jetzt hats geklingelt. Ja ich habe die Addition der beiden 16-Bit Werte von Startwert & Differenz übersehen. Ich hatte angenommen das die Different wieder auf 8-Bit verringert wird. Dadurch kommt die große Abweichung bei der Schrittweite dann. Deine Ergebnisse: Addi :256 Dezimal als Vergleich 2560 10 10,00 4800 19 18,75 7040 28 27,50 9280 37 36,25 11520 45 45,00 13760 54 53,75 16000 63 62,50 18240 72 71,25 20480 80 80,00 22720 89 88,75 24960 98 97,50 27200 107 106,25 29440 115 115,00 31680 124 123,75 33920 133 132,50 36160 142 141,25 38400 150 150,00 Joar so reichts eigentlich vollkommen aus. Jetzt is der Knoten raus. Gruß Yob.
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.