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
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
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 ;)
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
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... ;)
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
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ß.
@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:
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...
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.
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.
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 ;)
@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
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
ldir17,-1;ldir18,'0'-1
3
_pbcd1:
4
incr17;incr18
5
subir16,100
6
brcc_pbcd1
7
ldir18,0xA0;ldir17,'0'+10
8
_pbcd2:
9
subir18,0x10;decr17
10
subir16,-10
11
brcs_pbcd2
12
orr16,r18;subir16,-'0'
13
14
;9..53cycle,9words
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
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