mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik 26 bit umwandlung in ASCII


Autor: Manuel (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo!

Ich habe hier eine ganz harte Nuß, die ich wohl alleine nicht knacken 
werde.
In meinem Programm habe ich einen zähler der auf interupt signale 
reagiert der mit 4 registern arbeitet. Da ich nur 26 bit benötige habe 
ich die obersten 6 bits mit einer und verknüpfung ausgeblendet.
Ich habe jetzt also 4 register (zaehler1, 2, 3 und zaehler4) die ich in 
eine 8 stellige ASCII zahl umwandeln will.
Mir ist es bereits gelungen die umwandlung mit 8 bit durchzuführen, 
indem ich solange 100 subtrahiere (und gleichzeitig das "hunderter" 
register inkrementiere) bis die zahl unter hundert ist.
Das gleiche mit den zehnern und einern.
Doch mit den 26 bit kommen unheimlich viele probleme auf.
Die höchste zahl beträgt 2^26 = 67108864
nun muss ich also als erstes nicht die hunderter, sondern die "10 
millionener" subtrahieren ;-) . Das ist meines wissens nach mit einem 
Mega16 garnicht möglich oder?
Ich habe mir überlegt es nach dem ausschlussverfahren zu machen, wenn 
also bit 23 oder höher gesetzt ist habe ich aufjedenfall über 10 
millionen aber da gibt es halt noch den fall das wenn zum beispiel bit 
23,20,19,15,12,10,9 und 7 gesezt sind (00010011000100101101000000) es 
genau 10 mega sind...

Das ist schon ein haufen holz für so einen kleinen mega16, aber 
irgendwie muss das doch funktionieren!?

Über welchen weg wäre es am vernünftigsten eine lösung anzugehen?
Ich wäre über jeden hilfreichen Tipp dankbar!
Vielleicht hat ja jemand sogar eine passende routine!?

MfG
Manuel Weikert

Autor: Manfred B. (vorbeigeschlendert)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hast du uns irgendwo verraten mit welcher Programmiersprache zu 
arbeitest? Für die ein oder andere gibt es ja vielleicht fertige 
Routinen die das für dich erledigen?

Autor: Manuel (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ohhh, sorry!
Ich arbeite mit Assembler...

Autor: crazy horse (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
geh mal zu atmel.com, da gibts die avr200.asm. u.a. 
16bit-Divisionsroutinen. Die kann man leicht auf 32bit aufbohren - falls 
du es nicht schaffst, melde dich nochmal, habe ich irgendwo.
Verfahren:
Zahl durch 10 teilen, Rest ergibt die Einer. Das Ergebnis wieder durch 
10 usw.

Autor: Hannes Lux (hannes)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Verfahren:
> Zahl durch 10 teilen, Rest ergibt die Einer. Das Ergebnis wieder durch
> 10 usw.

Ist auf dem AVR die Subtraktionsmethode nicht schneller?

Hier ist z.B. ein Lösungsansatz in ASM:
Beitrag "Ausgabe Binärzahl 8, 16, 24 und 32Bit auf LCD!"

Darauf bauen z.B. die Ausgaberoutinen für LCD auf, wie sie hier zu 
finden sind:
Beitrag "Re: Display Menü  LCD 4x20"
Sind allerdings nur bis 24 Bit implementiert, lassen sich aber leicht 
auf 32 Bit erweitern, wenn man die Vorgehensweise verstanden hat.

Es gibt aber noch mehr (und vermutlich auch bessere) Beispiele in der 
Codesammlung.

...

Autor: Manuel (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Ok, ich habe mir das angehängte file mal angeschaut, ist doch das was du 
meinst oder? Da muss ich dann den loop counter statt 17 auf 33 stellen 
und noch jeweils 2 zwischenregister (zwischen low und high register) mit 
einbaun oder?
aber dann hab ich ja 15 register verpulvert, sehe ich das richtig?
Eigentlich kann man doch, bis auf das counterregister das mit ldi 
geladen wird, die unteren 16 register benutzen oder?

Was ich überhaupt nicht verstehe ist warum die für r16 und r17 jeweils 2 
verschiedene definitionen gemacht haben.

Falls du die 32 bit routine in griffnähe hast wärs echt super wenn du 
die mal anhängen könntest.

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Z.B. die optimierte Subtraktionsmethode:

http://www.mikrocontroller.net/attachment/291/Bcd32b.asm


Peter

Autor: crazy horse (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
;***** Subroutine Register Variables

.def    drem32u_0=r12     ;remainder
.def  drem32u_1=r13
.def    drem32u_2=r14
.def  drem32u_3=r15

.def  dres32u_0=r16      ;result
.def  dres32u_1=r17
.def  dres32u_2=r18
.def  dres32u_3=r19

.def  dd32u_0  =r16  ;dividend
.def  dd32u_1  =r17
.def  dd32u_2  =r18
.def  dd32u_3  =r19

.def  dv32u_0  =r20  ;divisor
.def  dv32u_1  =r21
.def  dv32u_2  =r22
.def  dv32u_3  =r23
.def  dcnt32u  =r24    ;loop

;***** Code

div32u:  clr  drem32u_0  ;clear remainder Low byte
  clr  drem32u_1
  clr  drem32u_2
  sub  drem32u_3,drem32u_3  ;clear remainder High byte and carry
  ldi  dcnt32u,33  ;init loop counter
d32u_1:  rol  dd32u_0    ;shift left dividend
  rol  dd32u_1
  rol  dd32u_2
  rol  dd32u_3
  dec  dcnt32u    ;decrement counter
  brne  d32u_2    ;if done
  ret      ;    return
d32u_2:  rol  drem32u_0  ;shift dividend into remainder
  rol  drem32u_1
        rol   drem32u_2
        rol   drem32u_3
  sub  drem32u_0,dv32u_0  ;remainder = remainder - divisor
  sbc  drem32u_1,dv32u_1
        sbc  drem32u_2,dv32u_2
  sbc  drem32u_3,dv32u_3  ;
  brcc  d32u_3      ;if result negative
  add  drem32u_0,dv32u_0  ;    restore remainder
  adc  drem32u_1,dv32u_1
  adc  drem32u_2,dv32u_2
  adc  drem32u_3,dv32u_3
  clc      ;    clear carry to be shifted into result
  rjmp  d32u_1    ;else
d32u_3:  sec      ;    set carry to be shifted into result
  rjmp  d32u_1

das ist das, was ich auf die Schnelle gefunden habe, steht aber noch mit 
nicht getested. Das heißt aber nicht unbedingt was :-)
Du lädst einmal deine zu wandelnde Zahl in r16-r19, r20 bekommt eine 10 
verpasst, r21-r24 ist in deinem Fall immer 0. Du rufst die Funktion 8 
mal auf und holst jedesmal nur drem32u_0 ab.
Falls Geschwindigkeit eine Rolle spielt, kann man das noch optimieren.

Autor: Manuel (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ok, erstmal danke für alle tipps und routinen vor allem an Peter, die 
ist kompakt, verständlich und für meinen verwendungszweck perfekt.

Ich habe mir das ganze viel umständlicher vorgestellt und hätte nie 
gedacht das es auch so einfach geht.

Also nochmal vielen dank, ihr habt mir wirklich weitergeholfen!

Manuel

Autor: Manuel (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Ok, habe den code mal angehängt.
Man sollte den interuppt 1 erstmal garnicht berücksichtigen, sondern nur 
den int0.
den int1 werde ich wohl auch mit subi machen müssen, da dec ja keine 
carry flag beeinflusst oder?

Aber jetz zum eigentlichen problem.
Wenn ein int0 signal kommt, beginnt er nicht bei eins zu zählen, sondern 
das lcd zeigt 0000q280 an. beim nächsten interupt zeigt es 1612q280 an, 
und ab da ändert sich nichts mehr, egal wieviele int0 noch kommen.

Ich habe bei der routine von Peter die obersten 2 digits auskommentiert, 
da die zahl eh nicht höher sein kann als 67108864 (durch die 
aus-maskierung der obersten 6 bits im register "zaehler4".
Aber die routine sollte doch trozdem funktionieren...

Wo könnte der fehler liegen?

Autor: Manuel (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
  ldi  r19, -1 + '0'
_bcd7:  inc  r19
  subi  r30, low(1000)    ;-1000
  sbci  r31, high(1000)
  brcc  _bcd7

Das ist der codeabschnitt in dem ein "q" berechnet wird.
Ich kann mittlerweile die ganzen zähler und alles ausschließen, da ich 
zaehler1 bis 3 mit 0xff und zaehler4 mit 0x03 geladen habe, und das 
direkt aufs display ausgebe. Eigentlich müsste er also 67108864 
ausgeben...
Die obersten 4 digits stimmen auch, aber ab dem 5. kommt ein q und die 
zahlen dahinter stimmen auch nicht mehr (6710q280).

Daher vermute ich, das ab dem Teil-code _bcd7 irgendwas nicht hinhaut.
Peter weißt du vielleicht wo der fehler liegen könnte?
wofür ist eigentich das r30, low und r31, high?

hat das was mit dem zh und zl zu tun?

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ja, das kommt von copy&paste.

Ich habs nur bis 16 Bit wirklich benutzt und beim Aufbohren auf 32 Bit 
die falschen Register genommen.

Es ist ja R28 das LSB und nicht R30, also gehts so weiter:
...
  ldi  r19, -1 + '0'
_bcd7:  inc  r19
  subi  r28, low(1000)    ;-1000
  sbci  r29, high(1000)
  brcc  _bcd7

  ldi  r18, 10 + '0'
_bcd8:  dec  r18
  subi  r28, low(-100)    ;+100
  sbci  r29, high(-100)
  brcs  _bcd8

  ldi  r17, -1 + '0'
_bcd9:  inc  r17
  subi  r28, 10      ;-10
  brcc  _bcd9

  mov  r16, r28
  subi  r16, -10 - '0'
  ret

Nun sollte es hinhauen.

Komisch, daß das bisher keiner bemerkt hat.


Peter

Autor: Hannes Lux (hannes)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Komisch, daß das bisher keiner bemerkt hat.

@Peter:
Ich hatte nicht verstanden, warum bei einer 32-Bit zu ASCII-Umwandlung 
schon bei der höchstwertigen Ziffer nur drei der vier Register behandelt 
werden:
bin32_ascii:
  ldi  r25, -1 + '0'
_bcd1:  inc  r25
  subi  r29, byte2(1000000000)  ;-1000,000,000 until overflow
  sbci  r30, byte3(1000000000)
  sbci  r31, byte4(1000000000)
  brcc  _bcd1

  ldi  r24, 10 + '0'
_bcd2:  dec  r24
  subi  r29, byte2(-100000000)  ;+100,000,000 until no overflow
  sbci  r30, byte3(-100000000)
  sbci  r31, byte4(-100000000)
  brcs  _bcd2
         ...
Ich war immer der Meinung, dass man zu Beginn die Konstante von allen 
Registern subtrahieren (bzw. addieren, wenn vorher Unterlauf war) muss. 
In Deinem Beispiel sehe ich aber nur die drei höherwertigen Register.

Und da ich das nicht verstanden habe (ich bin noch nicht soweit, dass 
ich jeden Deiner Codes verstehe), habe ich nicht mehr auf weitere 
Einzelheiten  geachtet. Ich benutze nunmal keinen Code, den ich nicht 
verstehe. Und da nicht alles, was ich nicht verstehe, falsch sein muss, 
habe ich da auch nicht weiter nachgehakt. ;-)

Gruß...
Hannes

Autor: Manuel (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Es werde ja nur die register subtrahiert, die auch im wertebereich des 
subtrahenden (oder wie das ding heißt :) liegen.
Ist mir auch erst an diesem sehr netten Beispiel klar geworden.

Nach der änderung funktioniert alles wunderbar!
Vielen dank für die schnelle Hilfe!

Manuel

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hannes Lux wrote:

> In Deinem Beispiel sehe ich aber nur die drei höherwertigen Register.

-1000000000 = C4653600
100000000 = 05F5E100

Das untere Byte ist 0 und daher braucht man damit auch nicht zu rechnen.
X + 0 oder X - 0 ändert nichts.


Peter

Autor: Hannes Lux (hannes)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Peter Dannegger wrote:
> -1000000000 = C4653600
> 100000000 = 05F5E100
>
> Das untere Byte ist 0 und daher braucht man damit auch nicht zu rechnen.
> X + 0 oder X - 0 ändert nichts.

Ohhh... - Das habe ich nicht überprüft.
Ich hätte auch nicht erwartet, dass bei einer Zehnerpotenz das untere 
Byte der Binärzahl 0 sein könnte. So dumm, wie es manchmal kommt, kann 
man gar nicht denken... :-)

Vielen Dank für die Aufklärung.

Beste Grüße...
Hannes

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.