Forum: Projekte & Code Schnelle 8-Bit Binär nach BCD für 8Bit-AVR


von Christof Rieger (Gast)


Lesenswert?

Hi,
hier ein Kleiner Aufruf zum Wettkampf, wer kann es schnller 8-Bit Binär 
in BCD wandeln. Eine gewöhnliche Routine mit Schleifen benötigt auf dem 
8-Bit AVR je nach zu wandelder Zahl 9 bis 64 Taktzyklen (Dafür aber sehr 
kompakter Code). Meine Vorschlag benötigt 7 bis 42 Taktzyklen. In beiden 
Fällen ohne call und ret gezählt.

Register r15;r16;r17;r18
Argument: r16: Binärwert
Ergebnis: r15: Hunderter; r16 HighNibbel: Zehner; r16 LowNibbel: Einer
1
BIN2BCD:
2
  clr r15
3
BIN2BCDL:        ;Hunderter in r15 schreiben
4
  cpi r16,100    ;maximal 2 Durchläufe
5
  brlo BIN2BCD1
6
  subi r16,100
7
  inc r15
8
  rjmp BIN2BCDL
9
BIN2BCD1:        ;Prüfen ob 96 bis 99 bzw. 196 bis 199
10
  cpi r16,0x60
11
  brlo BIN2BCD2
12
  subi r16, -0x36    ;wenn ja nur 0x36 addieren
13
  ret      ;r16 HighNibbel Zehner LowNibbel Einer
14
BIN2BCD2:        ;sonst LowNibbel isolieren
15
  mov r17,r16
16
  andi r17,0b00001111
17
  cpi r17,10
18
  brlo BIN2BCD3    ;Wenn LowNibbel größer 9  
19
  subi r17,-6    ;Dezimaladjust durchfüren
20
BIN2BCD3:
21
  clr r18      ;Da der Rest kleiner 96 ist
22
  sbrc r16,6    ;kann nur 2^6
23
  subi r18,-0x64    ;sprich 6 Zehner und 4 Einer
24
  sbrc r16,5    ;oder 2^5
25
  subi r18,-0x32    ;sprich 3 Zehner und 2 Einer
26
  add r17,r18    ;vorhanden sein.
27
  mov r18,r17    ;BCD aus LowNibbel dazu addieren
28
  andi r18,0b00001111  ;Prüfen ob Dezimaladjust nötig.
29
  cpi r18,10
30
  brlo BIN2BCD4  
31
  subi r17,-6
32
BIN2BCD4:
33
  sbrc r16,4    ;zuletzt noch 2^4 prüfen    
34
  subi r17,-0x16    ;entsprich 1 Zehner und 6 Einer
35
  mov r16,r17
36
  andi r17,0b00001111  ;Prüfen ob Dezimaladjust nötig.
37
  cpi r17,10
38
  brlo BIN2BCD5  
39
  subi r16,-6    ;r16 HighNibbel Zehner LowNibbel Einer
40
BIN2BCD5:
41
  ret

von Simon K. (simon) Benutzerseite


Angehängte Dateien:

Lesenswert?

Mit dem Klassiker Lookup-Table komme ich auf 12 Takte statische 
Laufzeit.

Assembler Code (Ohne die Initialisierung vom Y Pointer)
1
  uint8_t Bin = 233;
2
3
  volatile uint16_t BCD = pgm_read_word(&BcdTab[Bin]);
4
 29c:  e6 e2         ldi  r30, 0x26  ; 38
5
 29e:  f2 e0         ldi  r31, 0x02  ; 2
6
 2a0:  85 91         lpm  r24, Z+
7
 2a2:  94 91         lpm  r25, Z+
8
 2a4:  9a 83         std  Y+2, r25  ; 0x02
9
 2a6:  89 83         std  Y+1, r24  ; 0x01

von Peter D. (peda)


Lesenswert?

Die erste Frage ist, wozu braucht man gepacktes BCD von einem Byte. Ich 
habe es bisher nirgends gebraucht.

Meistens braucht man größeren Zahlen (2 oder 4 Byte) und dann nicht als 
gepacktes BCD sondern ASCII oder 7-Segment. Dann müßte man die gepackten 
Nibble erst wieder mühsam auseinanderfummeln.



Die zweite Frage ist, welches Auge+Gehirn ist in der Lage, Zahlen 
innerhalb weniger µs zu erfassen?

Der Mensch ist >10.000-mal langsamer als der MC, daher ist für 
Zahlenausgaben immer die codeoptimierte Lösung am effektivsten.
Ob ein Ausgabe nun 0,001% oder 0,002% CPU-Last bewirkt, interessiert 
nicht die Bohne.


Peter

von Simon K. (simon) Benutzerseite


Lesenswert?

Peter Dannegger wrote:
> ...

Du kannst aber auch echt jedem den Spaß vermiesen ;)

Wenn der Controller erstmal genug zu tun hat, dann kommt es vielleicht 
doch hier und da noch ein bisschen auf die Geschwindigkeit an.

Wenn man dann plötzlich 1000000 Zahlen nach BCD umwandeln will, fällt's 
doch ins Gewicht ;)

von Peter D. (peda)


Lesenswert?

Simon K. wrote:

> Wenn man dann plötzlich 1000000 Zahlen nach BCD umwandeln will, fällt's
> doch ins Gewicht ;)

Nö, die CPU-Last ist ganz genau die gleiche (nämlich nicht 
erwähnenswert).

Sie hängt nicht von der Anzahl Werte ab, sondern von dem Verhältnis der 
Zeiten, die der MC braucht, um sie darzustellen und die der Mensch 
braucht, um sie abzulesen.

Bei 1000000 Zahlen braucht der Mensch eben mehrere Tage zum Ablesen.

Ich benutze dazu oft ne Timertask die nur alle 200ms nen neuen Wert 
ausgibt, damit die Anzeige ablesbar bleibt, sonst sieht man ja nur 
flackernde Achten.


Peter

von Simon K. (simon) Benutzerseite


Lesenswert?

Peter Dannegger wrote:
> Bei 1000000 Zahlen braucht der Mensch eben mehrere Tage zum Ablesen.

Vielleicht will er die Zahlen ja nicht ablesen sondern irgendwo 
speichern... ;)

von Peter D. (peda)


Lesenswert?

Simon K. wrote:

> Vielleicht will er die Zahlen ja nicht ablesen sondern irgendwo
> speichern... ;)

Dann macht es natürlich Sinn, sie erstmal durch BCD-Wandlung unnötig 
aufzublähen ;)


Peter

von Christof Rieger (Gast)


Lesenswert?

Die Aufgabe hat eher ein sportlichen Aspekt.
Zur Frage:"Für was eine 8-Bit Umwandlung"
Wenn ich eine Prozentwert auf einem Display ausgeben will, genügt mir 
eine 8-Bit Umwandlung.
Zur Frage:"Warum schnell"
Es gibt Situationen in denen es sinnvoll ist einen Prozess vor dem 
nächsten Interrupt abzuschließen um sich z.B. eine evetuelle 
Zwischenspeicherung von Werten zu sparen, dass auch wieder Zeit kostet.
Desweiteren gibt es hier einige Projekte die aus einem Mega8 ein 
Schwarz-Weiß Fernsehbild raus kitzel. Hier kann es sinnvoll sein, die 
BCD-Umwandlung und die Zeichenaufbereitung innerhalb eine 
Zeilenaustastung abzuschließen.

Liebe Grüße Christof

PS: Es gibt viele Freizeitbeschäftigungen die nicht wirklich einen Sinn 
machen aber sie machen einfach Spaß.

von Christof Rieger (Gast)


Lesenswert?

@Simon
Ich glaube, noch schneller geht es kaum. Gut ist auch, dass jede 
Wandlung die gleich dauer hat. Allerdigs sind 524 Bytes für die 
kleineren AVR'ne menge Holz.

Aber moment, die Perversion ist noch steigebar.

Binwert in ZL:
1
  ldi ZH, High(TabellenBasis) ;(Low(TabellenBasis) muß 0 sein!)
2
  ijmp
3
4
TabellenBasis:
5
  rjmp BCD_1
6
  .
7
  .
8
  rjmp BCD_256
9
10
Ein Tabelleneintrag von 256:
11
BCD_X:
12
  ldi r16,(Passeder Zehner/Einer)
13
  ldi r17,(Passender Hunerter)
14
  ret
7 Zyklen ohne call und ret !
Wenn ZH Grundsätzlich vorbelegt bleiben kann, dann sogar nur 6 Zyklen ! 
Aber dafür satte 2052 Bytes verschlungen

von Benedikt K. (benedikt)


Lesenswert?

Unter Verwendung eines externen 256x16bit ROMs, dessen Adressen an PORTA 
und die Daten an PORTB & PORTC angeschlossen sind:
out PORTA, r16
nop
in r16, PINB
in r17, PINC

Benötigt nur 4 Takte und 8 Bytes, aber halt ein externes ROM und 24 IO 
Pins...

von Christof Rieger (Gast)


Lesenswert?

O.K. !!!!!!!!!!!!!!!!!!!
Da Sprach der Elektrotechniker. Ich kann mich dunkel erinnern. Benedikt 
K. baut gerne schnelle Hardware und nutzt den AVR nur zur Steuerung der 
Hardware.
Wenn du einen CPLD oder FPGA mit entsprechender Programmierung dran 
hängst, kannst du die auch noch den NOP sparen ;-)

Interesannt wären aber noch Lösungen die weniger als 512 Bytes 
verschlingen und ohne externe Hardware auskommen und unter 42 Takten 
liegt.

von Benedikt K. (benedikt)


Lesenswert?

Es gäbe da noch die Möglichkeit einer Mischung aus deiner Lösung und der 
von  Simon:
Die Hunderter vergleichen (sind ja nur 2 Vergleiche), und die Zener und 
Einer über Tabelle machen. Benötigt 100Bytes für die Tabelle + ein paar 
für den Code. Sollte also mit etwa mit etwa 130-140Bytes und rund 20 
Takten machbar sein.

von Christof Rieger (Gast)


Lesenswert?

Änderung am Post 1.
Die Berechnug der Hunderter ersätzen. Takte 8-35. Der Code verlängert 
sich um 6 Bytes (3 Instructions).
1
BIN2BCD:
2
  clr r15
3
  subi r16,100
4
  brlo BIN2BCDE
5
  inc r15
6
  subi r16,100
7
  brlo BIN2BCDE
8
  inc r15
9
  rjmp BIN2BCD1
10
BIN2BCDE:
11
  subi r16,-100

von Christof Rieger (Gast)


Lesenswert?

@Benedikt

Das sähe dann so aus:
Argument in ZL
1
BIN2BCD:
2
  clr r15
3
  subi ZL,100
4
  brlo BIN2BCDE
5
  inc r15
6
  subi ZL,100
7
  brlo BIN2BCDE
8
  inc r15
9
  rjmp BIN2BCD1
10
BIN2BCDE:
11
  subi ZL,-100
12
BIN2BCD1:
13
ldi  ZH, High(BasisTabelle) ;BasisTabelle mit Low(BasisTabelle)=0
14
lpm  r16, Z
15
ret

9-13 Takte und 122 Bytes wie immer ohne call und ret
Wenn ZH vorbelegt bleiben kann
8-12 Takte und 120 Bytes.

von Simon K. (simon) Benutzerseite


Lesenswert?

Gute Idee! Wie du siehst muss man stark abwägen, welcher Verbrauch von 
ROM und Rechenleistung für den speziellen Fall am besten ist. Du 
schriebst in deinem ersten Post nur von "schnellste" Version. Da habe 
ich dann natürlich nicht mit dem ROM geknausert ;)

von Christof Rieger (Gast)


Lesenswert?

@Simon

So etwas entwickelt sich! Ich konte ja beim schreiben des ersten 
Beitrags noch nicht ahnen, welche Ansätze kommen werden. Der Beitrag 
wird auf jedem Fall der Rubrik Code-Sammlung gerecht. Wenn jetzt jemand 
nach so einer Routine sucht kann er schon die passende für seine 
Anwendug raussuchen. Bin mal gespannt was noch für Ideen kommen.

Ersteinmal Danke an Alle

von Christof Rieger (Gast)


Lesenswert?

Argument in r17
1
BIN2BCD1:
2
  clr r15
3
  mov ZL,r17
4
  swap ZL
5
  andi ZL,0b00001111
6
  ldi  ZH, High(BasisTabelle) ;BasisTabelle mit Low(BasisTabelle)=0
7
  lpm  r16, Z
8
  lsl r16
9
  rol r15
10
  andi r17,0b00001111
11
  subi r17,-6
12
  brhc BIN2BCD1    ;Wenn LowNibbel kleiner 10  
13
  subi r17,6       ;kein Dezimaladjust durchfüren
14
BIN2BCD1:
15
  add r16,r17
16
  brhc BIN2BCD2    ;Wenn HalfCarry
17
  subi r16,-6      ;auf jeden Fall ein Dezimaladjust
18
  rjmp BIN2BCD3    ;LowNibbel ist hier immer kleiner 10
19
BIN2BCD2:
20
  subi r16,-6
21
  brhc BIN2BCD3    ;Wenn LowNibbel kleiner 10
22
  subi r16,6       ;kein Dezimaladjust durchfüren
23
BIN2BCD3:
24
  subi r16,-0x60   
25
  brcs BIN2BCD4    ;Wenn HighNibbel größer 9
26
  inc r15          ;ein Hunderter mehr
27
  ret
28
BIN2BCD4:
29
  subi r16,-0x60   ;Wenn nicht, kein Dezimaladjust
30
  ret
31
.org 0xy0/8
32
.db 0x00,0x0B,0x19,0x24,0x32,0x40,0x4B,0x89
33
.db 0x94,0xA2,0xB0,0xBB,0xC9,0xD4,0xE2,0xF0
34
;Bit 7 => Dezimaler Hunderter
35
;Bit 3-6 => Zehner Binär
36
;Bit 0-2 => Hälfte der Dezimalen Einer
37
;Das HighNibbel hat niemals einen Ungeraden Einer
22-24 Takte  66 Bytes Damit Kürzer und Schneller als mein Erster 
Vorschlag
ich hoffe das geht auch habe es nicht getestet ;-)

von Peter D. (peda)


Lesenswert?

Christof Rieger wrote:
> Desweiteren gibt es hier einige Projekte die aus einem Mega8 ein
> Schwarz-Weiß Fernsehbild raus kitzel. Hier kann es sinnvoll sein, die
> BCD-Umwandlung und die Zeichenaufbereitung innerhalb eine
> Zeilenaustastung abzuschließen.

Sinnvoll ist dann aber nicht als packed BCD sondern als ASCII zum 
Adressieren des Zeichengenerators.

Sinnvoll ist dann aber die Wandlung einmal im Main, wenn sich die Zahl 
ändert und nicht in jedem Bildaufbau neu. Man muß ja die Rechenzeit 
nicht mit Gewalt vergeuden.
Die Optimierung eines Algorithmus nützt garnichts, wenn man ihn dann 
unnötiger Weise viel oft ausführt.

Hier mal die auf Effizienz (=Codesparend) getrimmte Version:
1
packed_bcd:
2
        ldi     r17, -1         ; ldi r18, '0'-1
3
_pbcd1:
4
        inc     r17             ; inc r18
5
        subi    r16, 100
6
        brcc    _pbcd1
7
        ldi     r18, 0xA0       ; ldi r17, '0'+10
8
_pbcd2:
9
        subi    r18, 0x10       ; dec r17
10
        subi    r16, -10
11
        brcs    _pbcd2
12
        or      r16, r18        ; subi r16, -'0'
13
14
; 9 .. 53 cycle, 9 words
Bis auf die 5 geänderten Zeilen entspricht sie der (sinnvolleren) 
ASCII-Ausgabe.


Die Frage stellt sich aber weiterhin, wozu man packed BCD überhaupt 
brauchen könnte?


Peter

von Christof Rieger (Gast)


Lesenswert?

Peter,
gefällt mir auch gut, die Zeile
or      r16, r18        ; subi r16, -'0'
schenke ich dir. Es hängt sowiso von der Anwendug ab, ob gepackt oder 
ungepakt gebraucht wird.

Was mich etwas fuchst, dass ich nicht selbst darauf gekommen bin, mal 
über subtrahieren und mal über addieren die Hunderter und Zehner zu 
bestimmen.

Was natürlich auch die Takt-Bilanz verbessrt ist die Möglichkeit der 
direkten ASCII-Codierung wenn man sie zur Ausgabe benötigt.

Die Umwandung eines Pached BCD braucht

r16:LowBCD r17:HighBCD
1
mov r18,r16
2
andi r16,0b00001111
3
subi r16,-'0'
4
swap r18
5
andi r18,0b00001111
6
subi r18,-'0'
7
subi r17,-'0'

auch nochmal 7 Takte und weitere 7 Words

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
Noch kein Account? Hier anmelden.