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.
;***** 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
ldir19,-1+'0'
3
_bcd7:incr19
4
subir28,low(1000);-1000
5
sbcir29,high(1000)
6
brcc_bcd7
7
8
ldir18,10+'0'
9
_bcd8:decr18
10
subir28,low(-100);+100
11
sbcir29,high(-100)
12
brcs_bcd8
13
14
ldir17,-1+'0'
15
_bcd9:incr17
16
subir28,10;-10
17
brcc_bcd9
18
19
movr16,r28
20
subir16,-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