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
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?
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.
> 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. ...
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.
Z.B. die optimierte Subtraktionsmethode: http://www.mikrocontroller.net/attachment/291/Bcd32b.asm Peter
;***** 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.
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
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?
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?
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:
1 | ...
|
2 | ldi r19, -1 + '0' |
3 | _bcd7: inc r19 |
4 | subi r28, low(1000) ;-1000 |
5 | sbci r29, high(1000) |
6 | brcc _bcd7 |
7 | |
8 | ldi r18, 10 + '0' |
9 | _bcd8: dec r18 |
10 | subi r28, low(-100) ;+100 |
11 | sbci r29, high(-100) |
12 | brcs _bcd8 |
13 | |
14 | ldi r17, -1 + '0' |
15 | _bcd9: inc r17 |
16 | subi r28, 10 ;-10 |
17 | brcc _bcd9 |
18 | |
19 | mov r16, r28 |
20 | subi r16, -10 - '0' |
21 | ret
|
Nun sollte es hinhauen. Komisch, daß das bisher keiner bemerkt hat. Peter
> 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:
1 | bin32_ascii: |
2 | ldi r25, -1 + '0' |
3 | _bcd1: inc r25 |
4 | subi r29, byte2(1000000000) ;-1000,000,000 until overflow |
5 | sbci r30, byte3(1000000000) |
6 | sbci r31, byte4(1000000000) |
7 | brcc _bcd1 |
8 | |
9 | ldi r24, 10 + '0' |
10 | _bcd2: dec r24 |
11 | subi r29, byte2(-100000000) ;+100,000,000 until no overflow |
12 | sbci r30, byte3(-100000000) |
13 | sbci r31, byte4(-100000000) |
14 | brcs _bcd2 |
15 | ... |
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
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
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
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
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.