Hallo zusammen,
ich suche nach einer Möglichkeit mit einem PIC in Assembler eine
Division durchzuführen. Konkret geht es darum, einen 8Bit Wert, der
allerdings nur Werte zwischen 5 und 90 liefert, durch einen ebenso 8Bit
Wert, genauer dez. 10 zu dividieren. Also in etwa so:
Ausgangswert Bsp1: 0000|0101 (dez. 5)
Weitere Werte 6...89
Ausgangswert Bsp2: 0101|1010 (dez. 90)
sollen jeweils durch den
Divisor: 0000|1010 (dez. 10)
geteilt werden. Für die weitere Verwendung werden sowohl das Ergebnis
als auch ein möglicher Rest benötigt.
Ich habe ein Beispiel gefunden, um 24Bit durch 16Bit zu dividieren, habe
jedoch Probleme, es auf 8Bit durch 8Bit umzuwandeln.
1
Div16
2
clrf REST_H
3
clrf REST_L
4
movlw D'24'
5
movwf DIV_COUNTER
6
div16_loop
7
rlf ZAEHLER_L, F
8
rlf ZAEHLER_M, F
9
rlf ZAEHLER_H, F
10
rlf REST_L, F
11
rlf REST_H, F
12
rlf DIV_COUNTER, F
13
movwf NENNER_L, W
14
subwf REST_L, F
15
movf NENNER_H, W
16
btfss STATUS, C
17
incfsz NENNER_H, W
18
subwf REST_H, W
19
btfsc STATUS, C
20
bsf DIV_COUNTER, 0
21
btfs DIV_COUNTER, 0
22
goto div16_jump
23
movf NENNER_L, W
24
addwf REST_L, F
25
movf REST_H, W
26
div16_jump
27
movwf REST_H
28
bcf STATUS, C
29
rrf DIV_COUNTER, F
30
decfsz DIV_COUNTER, F
31
goto div16_loop
32
rlf ZAEHLER_L, F
33
rlf ZAEHLER_M, F
34
rlf ZAEHLER_H, F
35
return
ZAEHLER_H, ..M, ..L und NENNER_H, ..L müssen vorher als Variable
eingetragen und mit Werten geladen werden. Mein bisheriger Versuch das
ganze auf 8/8Bit umzuwandeln, sieht wie folgt aus, klappt jedoch nicht
wirklich. Zudem bin ich mir unsicher, welche H und L Werte ich nun
ersetzen muss (ich habe ja kein H oder L, mir reicht ja "eins" wg.
8Bit).
1
Div8
2
clrf REST
3
movlw D'8'
4
movwf DIV_COUNTER
5
div8_loop
6
rlf ZAEHLER, F
7
rlf REST, F
8
rlf DIV_COUNTER, F ; bis hier sollte es noch stimmen?
9
movf NENNER, W ; ab hier bin ich mir unsicher
10
btfss STATUS, C
11
incfsz NENNER, W
12
subwf REST, W
13
btfsc STATUS, C
14
bsf DIV_COUNTER, 0
15
btfsc DIV_COUNTER, 0
16
goto div8_jump
17
movf NENNER_L, W
18
addwf REST_L, F
19
movf REST_H, W
20
div8_jump ; ab hier sollte es wieder passen?
21
movwf REST
22
bcf STATUS, C
23
rrf DIV_COUNTER, F
24
decfsz DIV_COUNTER, F
25
goto div8_loop
26
rlf ZAEHLER, F
27
return
Über eine nette und hilfreiche Antwort freue ich mich sehr :-)
Dieter L. schrieb:> nur Werte zwischen 5 und 90 liefert, durch einen ebenso 8Bit> Wert, genauer dez. 10 zu dividieren
Wenn Dein Divisor immer 10 ist, dann reicht eine Binär zu BCD Wandlung:
BCD_high ist das Ergebnis und BCD_low ist der Rest.
Gruß
John
Dieter L. schrieb:> 5 und 90 liefert, durch einen ebenso 8Bit> Wert, genauer dez. 10 zu dividieren.
Normalerweise sollte man für sowas ein Gleitkommapaket auf der
Hinterhand haben - ich hatte sowas für die PIC16Fxxx hier schon mal
gepostet - aber wenn's nur für diesen Spezialfall ist, dann geht das
auch durch schlichtes Abzählen:
akku:= inputwert;
counter:= 0;
goto hierhinein;
nochmal:
inc(counter);
hierhinein:
akku:= akku - 10;
if akku >= 0 then goto nochmal;
rest:= akku + 10
ergebnis:= counter;
Die Anzahl der Schleifen ist ja überschaubar und der Schleifeninhalt
kurz.
W.S.
Mit C habe ich mich noch gar nicht befasst, aber vllt. versuche ich es
mal. Bin noch nicht solange mit Mikrocontrollern dran, wollte erstmal
mit Assembler herumspielen, da ich dazu viele Beispiele gefunden habe.
John:
Wie führe ich eine Binär-BCD-Wandlung am einfachsten durch? Durch
Schiebeoperationen? Sprich 4x rechts schieben = BCD_H und den
ursprünglichen Wert 4x links schieben für BCD_L? Oder muss ich 5x nach
links schieben wegen Carry-Bit?
Vielen Dank schonmal :)
Du kannst die 8bit/8bit Routine aus der an617 nehmen, die Schleife
ausrollen und die BTFSC rauswerfen. Dann bleiben die richtige Anzahl an
RLF und SUBWF übrig.
Dieter L. schrieb:> Wie führe ich eine Binär-BCD-Wandlung am einfachsten durch?
Hallo Dieter,
ich hab da mal kurz was zusammengetippt, ich hoffe es funktioniert.
Das macht praktisch das, was W.S. auch geschrieben hat.
Stephan schrieb:> wenn du immer durch denselben Wert teilst, dann kannst du> auch mit Rechtsshifts und Subtraktion arbeiten.
Statt durch 10 zu teilen könnte man ja auch mit 0.1
multiplizieren.
Dieter L. schrieb:> Hallo zusammen,>> ich suche nach einer Möglichkeit mit einem PIC in Assembler eine> Division durchzuführen. Konkret geht es darum, einen 8Bit Wert, der> allerdings nur Werte zwischen 5 und 90 liefert, durch einen ebenso 8Bit> Wert, genauer dez. 10 zu dividieren.
Was für ein PIC ist es denn genau ?
Hat der einen "HARDWARE MULTIPLIER" ?
Dann könntest du deine Zahl mit 26 multiplizieren und im PRODH würde das
Ergebnis stehen. Den "Rest" müsstest du dann noch berechnen ...
Volker SchK schrieb:> Dann könntest du deine Zahl mit 26 multiplizieren
Zu groß; das geht bei 89 schief: (89 * 26) / 256 = 9.039
(Genauer: Es geht bei 69, 79, 89 schief.)
Mit 205 multiplizieren und das High-Byte 3 bit rechtsschieben
geht aber:
(89 * 205) / 2048 = 8.909
Lohnt natürlich nur, wenn ein Hardware-Multiplizierer
vorhanden ist.
Volker SchK schrieb:> Mist, ja ab 69 müsste man dann wohl vorher eins abziehen.> (ab 128 -2, 197 -3)
Geht natürlich. Hat aber den Nachteil, dass man mit
Fallunterscheidungen herumlaborieren muss.
Die Methode, einfach einen genaueren Näherungsbruch zu
verwenden (205/2048) hat den Charme, ohne Fallunterscheidungen
auf 16 Bit verallgemeinerbar zu sein.
Der Bruch 52429/524288 ist auf ungefähr 17 Bit genau.
Possetitjel schrieb:> Geht natürlich. Hat aber den Nachteil, dass man mit> Fallunterscheidungen herumlaborieren muss.
In diesem speziellen Fall wäre die Fallunterscheidung ja nur
Volker SchK schrieb:> Possetitjel schrieb:>> Geht natürlich. Hat aber den Nachteil, dass man mit>> Fallunterscheidungen herumlaborieren muss.>> In diesem speziellen Fall wäre die Fallunterscheidung ja nur>> btfsc Zahl,6> decf Zahl
Ja, stimmt schon.
Meine Bemerkung hatte nix mehr mit der Ursprungsfrage zu tun.
Ich finde es einfach elegant, dass man mit einem Multiplizierer
auch in voller Genauigkeit dividieren kann :-)
Possetitjel schrieb:> Ich finde es einfach elegant, dass man mit einem Multiplizierer> auch in voller Genauigkeit dividieren kann :-)
Da hast du natürlich absolut recht ...
Oh, ist ja ganz schön was los hier, ich versuche mal alles wichtige der
Reihe nach zu kommentieren seit meinem letzten Post.
Noch einer:
Die Routine aus der AN617 habe ich mir angeschaut, bin jedoch nicht ganz
schlau daraus geworden. Auf den ersten Seiten gibt es ja diese Tabellen
je nachdem welchen PIC man benutzt, da war meiner nichtmal
aufgeführt...da stand PIC16C...., ich verwende allerdings einen
PIC16F887.
John und Wit G:
Werde eure Ideen bald mal ausprobieren, vielen Dank dafür.
Stephan/Possetitjel/Chregu:
An eine Multiplikation mit 0.1 hatte ich noch nicht gedacht, wobei ich
nicht weiß, ob das viel einfacher ist? Nochmal die Info: PIC16F887
Volker SchK:
So wie ich das gelesen habe, scheint mein PIC (s.o.) keinen Hardware
Multiplier zu besitzen.
Und an alle nochmal ein großes Dankeschön, hat mir schon viel geholfen
:)
Dieter L. schrieb:> Mit C habe ich mich noch gar nicht befasst, aber vllt...
Dieter, das was ich da gepostet habe, ist KEIN C, sondern ein
pascalartig formuliertes Prinzip. Du kannst es fast 1:1 in
Maschinenbefehle umsetzen.
1
akku:= inputwert; MOVWF akku
2
counter:= 0; CLRF counter
3
MOVLW 256-10
4
goto hierhinein; GOTO hierhinein
5
nochmal: nochmal:
6
inc(counter); INCF counter,f
7
hierhinein: hierhinein:
8
akku:= akku - 10; ADDWF akku,f
9
if akku >= 0 SKIP C
10
then goto nochmal; GOTO nochmal
11
rest:= akku + 10 MOVF akku,w
12
ADDLW 10
13
MOVWF rest
14
ergebnis:= counter; MOVF counter,w
15
MOVWF ergebnis
Tips:
1. SKIP ist BTFSS
2. C ist das Carrybit
3. akku + 246 ergibt C wenn das Ergebnis positiv oder 0 ist
Ich hänge dir mal ne steinalte, aber gut funktionierende
Gleitkomma-Routine in Assembler für die PIC16 dran - zum verstehenden
Lesen. Mit dem MicroChip-Assembler kannst du sie nämlich nur übersetzen,
wenn du sie passend editierst. Aber zum Verstehen des Prinzips ist sie
gut, weil gut lesbar.
W.S.
Hmmm, ich habe gerade nochmal überlegt und bin nicht darauf gekommen,
wie man das mit Rechtsshifts machen kann. Man kann ja sehr gut den
Booth-Algorithmus für die Multiplikation mit Konstanten verwenden, für
die Division scheint das nicht so einfach zu funktionieren.
Wenn du aber immer nur durch 10 dividieren willst, kannst du den
Dividend auch in BCD umwandeln und dann einen Rechtsshift machen und das
Ergebnis wieder in eine Binärzahl zurückwandeln. Der Vorteil wäre, das
du den Rest dann auch gleich mit einsammeln kannst und keinen
Rechenfehler hast.
Eine Abwandlung von der BCD-Darstellung wird heutzutage ja immernoch von
Großbanken verwendet.
BCD hätte aber auch den Vorteil, dass du das Ergebniss auch gleich auch
einen Characterdisplay darstellen kannst.
Für die Umwandlung von Binär- nach BCD-Zahl gibt es übrigens den
Double-Dabble-Algorithmus.
Stephan schrieb:> Hmmm, ich habe gerade nochmal überlegt und bin nicht darauf> gekommen, wie man das mit Rechtsshifts machen kann. Man kann> ja sehr gut den Booth-Algorithmus für die Multiplikation mit> Konstanten verwenden, für die Division scheint das nicht so> einfach zu funktionieren.
Naja, durch 10 teilen ist dasselbe wie mit 0.1 multiplizieren.
Dezimal 0.1 ist aber gerade binär 0.00011001100110011...
Multipliziere also einfach Deine Zahl mit 51, schiebe 9 Bit
nach rechts - und Du hast (näherungsweise) durch 10 dividiert.
Richtig gut ist, mit 205 malzunehmen und 11 Bit nach rechts
zu schieben.
Hallo zusammen,
habe es nicht früher geschafft zu antworten, aber besser spät als nie.
Habe mich letztendlich für die Idee von John entschieden, sprich immer
10 subtrahieren und dabei mitzählen, wie oft das geht bis das Ergebnis
<=0 ist und dann den Rest als Einerstelle nehmen. Danke aber auch an
alle anderen, auch an die letzten neuen Antworten hier mit diesem
Algorithmus, interessante Sache auf jeden Fall! Weiter so!
Viele Grüße
Noch eine Möglichkeit:
zu teilender Wert in ein Register (A)
160 in weiteres Register (B)
Ergebnis in Register (E)
CLR E
x:
Schiebe E 1 nach oben
Wenn A > B, dann A = A - B und E++
Schiebe B 1 nach unten in Carry
Wenn Carry nicht gesetzt springe zu x
In E befindet sich nun :
0 0 160er 80er 40er 20er 10er 5er
Für nur 10er E wieder ein bit nach unten schieben (5er fliegt raus)
Wenn sichergestellt ist, dass A nicht größer als 90 ist, kann B auch mit
80 initialisiert werden.
Gruß
Jobst