Hallo,
ich bin gerade dabei das ASM AVR Tutorial durchzuarbeiten.
Jetzt habe ich schonmal 2 Fragen.
1)
Ich habe mal im Datenblatt des ATMega geschaut, ob man irgendwo eine
Angabe findet, mit welcher max. Frequenz man den uC betreiben kann. habe
leider nichts gefunden. Gibt es eine solche Angabe oder gibt es eine
Verallgemeinerung?
2)
Ich habe vor (später) eine LED über Timer und PWM zu dimmen und via DMX
zu steuern. Dafür möchte ich zwei Kanäle verwenden. Einmal einen
Masterkanal der die Grundhelligkeit der LED's vorgibt und einmal jeweils
den Dimmerkanal für RGB.
RGB jetzt einzeln dimmen zu können ist ja nicht wirklich schwer. Einfach
das DMX-Byte einlesen, mit dem Timer verarbeiten und das PWM Signal an
die einzelnen LED's geben.
Wie realisiere ich allerdings den Masterkanal?
Ich hatte mir überlegt, das mit einer Multiplikation zu lösen.
Also den Masterkanal multipliziert mit dem Byte der einzelnen LED-PWM.
Und da stellt sich mir die Frage.
Ich müsste ja rein theortisch, wenn z.B. der Kanal für Rot auf 255 ist
der Master allerdings nur auf 127 mit 0,5 multiplizieren?!?
Wie stelle ich dass denn an? Ich glaube nicht,dass es "wirtschaftlich"
sinnvoll ist, eine Tabelle anzulegen in dem jedes Master Byte (0...255)
einen Wert zwischen 0 und 1 bekommt, wobei 0 eben aus ist
(multiplikation mit 0 = 0 und 1 volle Helligkeit vom Master -> Es wird
direkt das ausgegeben, was für RGB anliegt)
Könnt Ihr mir die 2 Sachen vielleicht erklären?
Danke!
Das mit der Mutliplikation habe ich gerade mal auf dem Papier
durchgespielt.
Angenommen der Master steht auf 255 und LED-ROT auf 91.
Das wäre ja 255*91 = 23205
In Binär:
11111111 * 1011011 = 101101010100101
Wäre das oberste Byte also "01011010". Das sind ja nach erneuter
Umwandlung 90 und nicht 91.
Ich denke mal, dass sind die Rundungsfehler. Gibt es jetzt die
Möglichkeit diese zu minimieren?
Sorry,
hatte deine zweite Antwort zu spät gelesen.
Ich habe nmlich gerade das Interface von Digital-Enlightenment aufgebaut
(es geht auch) aber dort wird ja ein 24MHz Quarz verwendet. Ist das
nicht reichlich übertaktet?
@ Erik (Gast)
>leider nichts gefunden. Gibt es eine solche Angabe
Ja, schau mal un AC-Characteristics.
>Wie realisiere ich allerdings den Masterkanal?>Ich hatte mir überlegt, das mit einer Multiplikation zu lösen.
Genau so.
>Ich müsste ja rein theortisch, wenn z.B. der Kanal für Rot auf 255 ist>der Master allerdings nur auf 127 mit 0,5 multiplizieren?!?>Wie stelle ich dass denn an?
Mit einer Passenden Saklierung Master * Slave / Faktor
Faktor ist sinnvollerweise 256. Oder auch weniger, nur dass man dann
ggf. übersteuert.
MFG
Falk
Falk Brunner schrieb:> @ Erik (Gast)> Faktor ist sinnvollerweise 256. Oder auch weniger, nur dass man dann> ggf. übersteuert.
Ich denke ideal ist ein Faktor von 255. Dann erreicht man auch die volle
Helligkeit, sonst maximal 255*255/256 = 254. Kann man natuerlich nicht
durch einen einfachen rechts-shift um 8 ersetzen :-(
ZigZeg
Dankeschön für all die Antworten. Max. Frequenz ist jetzt klar -> an
mich selbst RTFM :-)
Zur Multiplikationssache:
Ich habe das nochmal kurz probiert.
Master: 91
DMX-Get-In-Byte: 155
Mit Rechnung und Faktor:
91*155 = 14105
14105 / F = 55,3
(F-Faktor -> 255)
Wenn ich das jetzt nach der Methode mit der 16 BIT Zahl mache, und nur
das oberste byte nehme kommt aber raus:
91*155 = 14105 = 11011100011001 (binär)
Davon NUR das oberste Byte = 01101110 = 110 (dez)
Wo ist jetzt hier mein Fehler? Oder funktioniert das doch nicht so
"einfach", dass man nur das oberste Byte nimmt?
Wenn ich es mit einem Faktor F (sowie in meiner ersten Rechnung) mache,
was macht der Assembler dann mit den Kommazahlen, wie rundet er da?
Erik schrieb:> 91*155 = 14105 = 11011100011001 (binär)> Davon NUR das oberste Byte = 01101110 = 110 (dez)
Zähl nochmal nach, wieviele Bits dein ausmultipliziertes Ergebnis hat
:-)
Mach solche Dinge nicht auf Bitebene. Da verzählt man sich ständig.
Unterstes Byte wegwerfen ist gleichbedeutend mit einer Division durch
256.
14105 / 256 = 55
Das ist auch binär nix anderes. Nur dass dein µC keinen Fehler dabei
macht.
Oh, auch grad bemerkt. Stimmt, diese Rechnerei ist echt "nervig" auf
Dauer ^^
Da mein programm später auf einem Tiny2313 laufen soll gibt es ja dort
nicht die Möglichkeit der Hardware Multiplikation bzw. Division.
Wie das dann gemacht werden muss, habe ich bereits in den Tutorials
gelesen.
Aber wie könnte ich noch diesen kleinen "Fehler" mit der 256 umgehen, so
dass die Teilung durch 255 und nicht durch 256 erfolgt?
Erik schrieb:> Aber wie könnte ich noch diesen kleinen "Fehler" mit der 256 umgehen, so> dass die Teilung durch 255 und nicht durch 256 erfolgt?
Indem man rundet
Wie machst du es denn mit der Hand?
Wenn deine Kommazahlen kleiner 0.5 sind, dann zählst du zum Ergebnis
noch 1 dazu.
Du hast hier auch 'Kommazahlen'. Das sind die Bits im Low-Byte, die du
verwirfst. Nur bewegen sich die nicht zwischen 0 und 10 sondern zwischen
0 und 256. Ergo: Wenn im Low-Byte was größeres als 127 steht, dann
zählst du noch 1 zum Ergebnis dazu. Was größeres als 127 ist aber
einfach: Wenn im Low-Byte das Bit 8 gesetzt ist, dann kommt zum Ergebnis
noch 1 dazu.
Man kanns auch so ansehen:
Im Grunde hast du mit deinem 16-Bit Ergebnis ein Fixed Point Ergebnis.
abcdefghabcdefgh , abcdefghabcdefgh
Der Teil links vom Komma ist dein ganzzahliges Ergebnis. Der Teil rechts
vom Komma sind deine Kommazahlen. Wobei jedes Bit wieder nach Wertigkeit
aufgeschlüsselt werden kann.
abcdefghabcdefgh , abcdefghabcdefgh
^^^^....
||||
|||+---- das sind die 1/16 im Ergebnis
||+----- das sind die 1/8 im Ergebnis
|+------ das sind die 1/4 im Ergebnis
+------- das sind die 1/2 im Ergebnis
Das habe ich mir auch schon überlegt, allerdings habe ich dann immernoch
das Problem beim "FullOn"
255*255 = 65025, da würde das MSB beim zweiten Byte doch garnicht
gesetzt sein?
Erik schrieb:> Das habe ich mir auch schon überlegt, allerdings habe ich dann immernoch> das Problem beim "FullOn"> 255*255 = 65025, da würde das MSB beim zweiten Byte doch garnicht> gesetzt sein?
Das musst du in Kauf nehmen, wenn du dir das Leben mit einer einfachen
"Division" leichter machen willst.
Du kannst natürlich die Rundung auch vor der Division schon machen,
indem die Häöfte des Multiplikanden mit eingerechnet wird.
anstelle von
a * b
rechnet man dann eben a * ( b + b/2)
Das Problem: Das passt dann nicht mehr in 16 Bit
Die Frage ist halt immer: Merkt man das im Ergebnis überhaupt?
Die meisten Dinge haben logarithmische Kennlinien (Helligkeit,
Lautstärke). Ob du eine LED mittels einer PWM mit einem OCR Wert von 255
oder mit einem OCR Wert von 254 ansteuerst, kann man optisch visuell
nicht mehr unterscheiden.
Ich will nicht extra nochmal einen neuen Thread aufmachen.
Ich versuche gerade einige Routinen komplett zu verstehen. Am meisten
macht mir im Moment noch die "Bitmanipulation" bzw. "Bitoperatoren" zu
schaffen.
Ich habe mir den Artikel dazu hier bereits durchgelesen, und da ist ja
der obere Teil extrem interessant für mich.
Ich habe ja verstanden das << = a*2^b bedeutet. Und wenn man mehrere
dieser Anweisungen verknüpft mit einem | (oder) werden sozusagen die
entstandenen Werte "addiert".
Aber leider sehe ich noch keinen Vorteil dieser Schreibweise bzw. wüsste
ich garnicht wie ich drauf komme?!?
Bsp.:
ldi temp, (1<<URSEL)|(3<<UCSZ0)
Damit wird URSEL gesetzt werden und 8Datenbit eingestellt für die UART.
Das habe ich aber erst nach "relativ langem" rechnen herrausgefunden.
URSEL ist Bit 7 -> 1*2^7 = 128
UCSZ0 ist Bit 1 -> 3*2^1 = 6
Die beiden Sachen sind mit | verknüpft, also 128 + 6 = 134.
Das in eine Binärzahl gewandelt ergibt dann das Muster: 10000110
Wenn ich dieses Bitmuster sehe, ist mir doch eigtl. klar, welche Bits
gesetzt sind und welche nicht, dann schaue ich kur in das Register, was
diese bedeuten und "fertig".
Ich wüsste jetzt nicht wie der Programmierer der Zeile:
ldi temp, (1<<URSEL)|(3<<UCSZ0)
überhaupt darauf gekommen ist, das so auszudrücken. Gibt es da einen
"Trick"?
Warscheinlich liegt es daran, das ich den wirklichen "Sinn" dieser
Schreibweise noch nicht verstanden habe, und deshalb auch nicht weiß wie
ich auf diese Ausdrücke kommen soll
Erik schrieb:> Ich wüsste jetzt nicht wie der Programmierer der Zeile:> ldi temp, (1<<URSEL)|(3<<UCSZ0)> überhaupt darauf gekommen ist, das so auszudrücken. Gibt es da einen> "Trick"?
OK. Die 3 sind ein 'Trick' und hätten so nicht sein müssen.
Besser wäre es gewesen das so zu schreiben
ldi temp, (1<<URSEL) | (1<<UCSZ1) | (1<<UCSZ0)
> Warscheinlich liegt es daran, das ich den wirklichen "Sinn" dieser> Schreibweise noch nicht verstanden habe,
Ist ganz einfach:
Schau ins Datenblatt. Bei jedem Register steht dabei, wie die einzelnen
Bits heissen. Du willst eines dieser Bits auf 1 setzen? Dann führe es
mit einem (1<<Bitnamen) Ausdruck auf.
Du brauchst nichts rechnen, du brauchst keine Bits zählen.
Super,
wieder was verstanden!
Ich hoffe mir kommt das demnächst nicht allzu oft über den Weg, das dort
höhere Ziffern als 1<< auftauchen, dann muss ich doch noch mal das Hirn
anstrengen und überlegen, wie die Programmierer darauf kommen und warum
die das machen.
Erik schrieb:> Super,> wieder was verstanden!> Ich hoffe mir kommt das demnächst nicht allzu oft über den Weg, das dort> höhere Ziffern als 1<< auftauchen, dann muss ich doch noch mal das Hirn> anstrengen und überlegen, wie die Programmierer darauf kommen und warum> die das machen.
Die 3 stammen einfach nur daher, das UCSZ0 und UCSZ1 2
nebeneinanderliegende Bits sind. Aber wie gesagt: schreibs aus, dann
stellt sich das Problem erst gar nicht.
Erik schrieb:> Könnte man anstatt der oben genannten Methode eigtl. auch folgendes> anwenden:>> sbi UCSRC, URSEL> sbi UCSRC, UCSZ0> sbi UCSRC, UCSZ1>> ?
kann man
Wenn das Register bitadressierbar ist ja (weiss ich nicht, kann sein,
kann aber auch nicht sein)
Aber auf jeden Fall braucht das mehr Programmspeicher, als das auf einen
Rutsch zu erledigen. Übersichtlicher ist das auch nicht - warum also?
Hallo,
Die Namen verallgemeinern das ganze. D.h. obwohl ich nur diese Zeile
kenne, weiß ich, dass es um die serielle Schnittstelle geht. Und
eigentlich in jedem Datenblatt kommen diese Bezeichnungen vor, egal ob
Tiny2313 oder Mega8. Bei 'ldi temp, 0x11' muss ich dagegen genau
wissen, für welchen µC das geschrieben ist, weil sich die
Bit-Reihenfolge ja ändern kann.
ldi temp, 0b10000110
out 0x20, temp
und
ldi temp, (1<<URSEL) | (1<<UCSZ1) | (1<<UCSZ0)
out UCSRC,temp
machen auf dem Atmega8 das selbe. Kompiliert man beides für den
Tiny2313, dann produziert das eine einen Compiler-Fehler, weil URSEL
nicht definiert ist. Das sagt einem der Compiler und nach einer Minute
hat man den Fehler.
Die andere Version wird ohne Fehler Compiliert, das Programm ändert die
Pin-Change-Interrupts, weil die an 0x20 definiert sind. Also passiert
irgendetwas, nur nicht das, was man will. Und finde jetzt den Fehler
ohne Compiler-Warnung.
login gerade weg :( schrieb:> Und finde jetzt den Fehler> ohne Compiler-Warnung.
Stimmt, das wird deutlich übersichtlicher wenn man die Namen des BIT's
da hat. Ich denke mal wenn man länger mit uC's arbeitet (bin jetzt 3
Tage dabei), dann weiß man irgendwann auch so ungefähr (und vor allen
Dingen die "wichtigsten") was diese Bezeichnungen bedeuten und worauf
sie Bezug nehmen.
Und das mit weniger Programmplatz ist auch klar.
-----
Im Genauen "analysiere" ich grad eine DMX Routine, die ich hier im Forum
gefunden habe, Schritt für Schritt. Klappt bis jetzt auch ganz gut, muss
halt nur vielvim Datenblatt suchen bzw. googlen.
Vor dem schlafen gehen möchte ich allerdings noch gerne die letzte Frage
klären (sonst schläft man so schlecht ein ^^)
In der Reset-Routine kommt dann mal das vor:
1
clr dmx_status ;setzt DMX-Status Byte auf 'wait for Reset'
2
in dmx_Byte, UDR ;setzt UART Empfangs-Interrupt Flag zurück
3
out UCSRA, dmx_status ;setzt alle error-flags zurück
Ich verstehe, was in diesen Schritten passiert. Ich frage mich nur
warum?
(Der vollständige Code ist hier:
http://www.mikrocontroller.net/attachment/34108/DMX.asm)
clr dmx_status löscht sozusagen dieses Register und setzt es 0
in dmx_Byte, UDR -> Das ganze ist doch noch in der Resetroutine, d.h.
für mich nach dem Neustart des System (z.B. Spannung angelegt), eigtl.
müsste UDR doch "leer" sein, denn auch Interrupts werden ja gesperrt.
Warum läd der Autor also den "leeren" Inhalt von UDR in das Register
dmx_Byte?
out UCSRA, dmx_status -> das soll laut Kommentar die Error-Flags
zurücksetzten. Da in dem Schritt weiter oben dmx_status ja 0 gemacht
wurde, werden also die BITS im UCSRA alle 0 gesetzt.
Jetzt habe ich mal gegooglet und in UCSRA sind doch eigtl. nur TXC,U2X
und MPCM Read UND Write fähig. Und der InitialWert wird von vornerein
mit 0 angegeben. Warum dann also dieser Schritt im Programm?
clr dmx_status ;setzt DMX-Status Byte auf 'wait for
2
> Reset'
3
> in dmx_Byte, UDR ;setzt UART Empfangs-Interrupt Flag zurück
4
> out UCSRA, dmx_status ;setzt alle error-flags zurück
5
>
> Ich verstehe, was in diesen Schritten passiert. Ich frage mich nur> warum?> (Der vollständige Code ist hier:> http://www.mikrocontroller.net/attachment/34108/DMX.asm)>> clr dmx_status löscht sozusagen dieses Register und setzt es 0>> in dmx_Byte, UDR -> Das ganze ist doch noch in der Resetroutine, d.h.> für mich nach dem Neustart des System (z.B. Spannung angelegt), eigtl.> müsste UDR doch "leer" sein, denn auch Interrupts werden ja gesperrt.> Warum läd der Autor also den "leeren" Inhalt von UDR in das Register> dmx_Byte?
Das ist nur Nebeneffekt.
Wie der Kommentar verrät geht es dem Autor darum, ein eventuell von
vorher gesetzter UART-RX Interrupt Flag zu löschen. Das wird gelöscht,
indem man aus dem UDR liest.
> out UCSRA, dmx_status -> das soll laut Kommentar die Error-Flags> zurücksetzten. Da in dem Schritt weiter oben dmx_status ja 0 gemacht> wurde, werden also die BITS im UCSRA alle 0 gesetzt.
Den Teil versteh ich auch nicht ganz.
> Jetzt habe ich mal gegooglet und in UCSRA sind doch eigtl. nur TXC,U2X> und MPCM Read UND Write fähig. Und der InitialWert wird von vornerein> mit 0 angegeben. Warum dann also dieser Schritt im Programm?
Ich hab jetzt das Program nicht analysiert.
Könnte sein, dass der Autor auch dann für klare Verhältnisse sorgen
wollte, wenn ein Reset auftritt oder er sonst irgendwo an diese Stelle
kommt.
Hey,
ich bin schon wieder am "weitermachen" :)
Jetzt hänge ich an einer eher kleinen Sache, weil ich glaube,
unterschiedliche Angaben im Internet gefunden zu haben.
Was passiert denn nun genau bei einem Überlauf bei "inc". Wird das
Z-Flag 1 oder 0?
Da geht es einfach nur um eine einfache Erhöhung.
inc addrL
brne Label
inc addrH
brne lässt mich ja zum Label springen, wenn das ZeroFlag Ungleich 0 ist,
also 1.
Angenommen das untere Register stand vorher auf 0 wird jetzt mit inc um
1 erhöht, dann wird das Zero Flag ja nicht gesetzt (da kein überlauf)
also Z=0
Dann kommt der Befehl brne, was dann ja heißt:
"0 Ungleich 0?, dann gehe zu label"
Aber 0 ist ja gleich 0, also springt er ja nicht zum neuen Label und
erhöht falscherweiße auch noch das obere adrrH Register.
Ich habe diese Angaben so im Netz zusammen gesucht wo es einmal heißt:
"Die Erhöhung um Eins wird mit der Instruktion INC erledigt. Wenn dabei
ein Überlauf auftritt, also 255 zu 0 wird, dann ist, dann ist das im
Anschluss an die Durchführung am gesetzten Z-Bit im Statusregister zu
bemerken."
"brne Zahl bewirkt, dass das Programm an eine neue Adresse springt, wenn
das Zero-Flag ungleich 0 ist."
Ich hab das jetzt mehrmals versucht durchzuspielen, aber bei mir ist das
immer umgedreht vom logischen her.
Fall1
- addrL wird erhöht ohne Überlauf -> Z=0
- Vergleich "0 ungleich 0" -> passt nicht, also nicht zum Label, sondern
zu inc addrH
- addrH wird erhöht obwohl nicht nötig.
Fall2
- addrL wird erhöht MIT Überlauf -> Z=1
- Vergleich "1 ungleich 0" -> passt, also springe zum Label
- addrH wird also nicht mehr erhöht obwohl es notwendig gewesen wäre
Hmm?!?
Hi
>"brne Zahl bewirkt, dass das Programm an eine neue Adresse springt, wenn>das Zero-Flag ungleich 0 ist."
Das Z-Flag wird bei 0 auf 1 gesetzt.
breq springt wenn Z=1
brne springt wenn Z=0
MfG Spess
spess53 schrieb:>>"brne Zahl bewirkt, dass das Programm an eine neue Adresse springt, wenn>>das Zero-Flag ungleich 0 ist."
Das heißt dieser Text stimmt eigtl. gar nicht? Der sagt mir ja das brne
springt wenn Z=1 (weil Ungleich 0)
Hi
>Das heißt dieser Text stimmt eigtl. gar nicht? Der sagt mir ja das brne>springt wenn Z=1 (weil Ungleich 0)
Der Satz ist Müll.
Das Z-Flag wird gesetzt (auf 1), wenn das Ergebnis einer Operation Null
ist. Welche Operationen das Flag (und auch wie) setzen, sagt dir das
Instuction Set.
http://www.atmel.com/dyn/resources/prod_documents/doc0856.pdf
MfG Spess
Ah cool,
werde jetzt wohl doch leiber nur in die Instruction Set PDF schauen, da
verstehe ich das recht gut und kann mir sicher sein, dass es passt :-)
Vielleicht nochmal kurz zu dieser Routine von oben:
http://www.mikrocontroller.net/attachment/34108/DMX.asm
Ich blick da soweit jetzt einigermaßen durch, was die Befehle machen.
Allerdings finde ich dort nicht herraus, wann dmx_status endlich mal auf
1 gesetzt wird um in den nächsten Status zu kommen.
Dazu muss die Routine ja in frame_error springen, denn danach soll
dieser status ja erreicht werden.
Allerdings finde ich nirgendwo einen Sprung in Frameerror
1
sbrc temp,DOR ; Overrun überprüfen -> wait for reset rjmp overrun // Überspringe nächsten Befehl, wenn DOR 0 ist
Er schreibt zwar hier ins Kommentar das rjmp overun bzw. rjmp
frame_error aber im eigentlich code taucht es doch garnicht auf. Und
sbrc testet ja nur die Bits FE und DOR um dann den nächsten Schritt
jeweils zu überspringen.
Aber wann gehts denn dann mal rein in die Labels Overrun bzw.
Frame_Error?
Hi
Da fehlen irgendwie die Sprünge zu den Behandlungsroutinen.
Versuche mal:
sbrc temp, DOR
rjmp overrun
sbrc temp, FE
rjmp frame_error
Ob die Routinen richtig sind kann ich jetzt nicht sagen.
MfG Spess
Bis zum Punkt "Framezähler zurücksetzen" ist mir hier alles klar, und
das passt auch in den Ablauf.
Dann allerdings liest "er" die Adresse des Empfängers ein. DMX braucht
ja 9 Bit (da 512 mögliche Adresse). Dafür verwendet er also den PORTC um
von dort die Adresse einzulesen. Warum dreht er diese dann mit "com" um?
Und dann liest er das neunte Bit garnicht ein sondern "löscht" einfach
das Register adresseHigh. Warum denn das?
Ich überlege auch die ganze Zeit, wo der vorteil ist, die Adresse erst
im Interrupt einzulesen? Wäre es nicht besser diese gleich in der
RESET/INIT Routine einzulesen und fertig?
Zweite Sache ähnlich:
1
startadr:
2
clc ; Carry-Flag C in SREG löschen
3
cp dmxcountL, adresseL ;
4
cpc dmxcountH, adresseH
5
brne gb_exit
6
....
Es wird erst das CarryFlag auf 0 gesetzt.
Dann vergleich er mit cp das unterere Adressregister mit dem
Framezähler.
Dann vergleicht er mit cpc das obere Adressregister mit "oberem
Framezähler".
Warum wird beim Vergleichen der oberen Adresse cpc genommen?
Und zum schluss brne. Damit soll doch, falls die Adressen nicht gleich
sind der nächstefolgende Befehl übersprungen werden.
Wirkt sich das BRNE auf beide Befehle aus? z.B. cp sagt ist nicht gleich
aber cpc sagt ist gleich. Welches Argument wird dann gewertet?
Dankeschön für die starke hilfe bis hier hin!
Hi
> Warum dreht er diese dann mit "com" um?
Das hängt von dem ab, was der Port einliest. Wenn dort Schalter sind,
die nach GND schalten, muss der Wert negiert werden.
>Und dann liest er das neunte Bit garnicht ein sondern "löscht" einfach>das Register adresseHigh. Warum denn das?
Das kann nur der Autor wissen. Ich vermute eine unvollständige
Implementierung.
Bist du sicher, das der Urheber des Programms wusste, was er macht?
>Es wird erst das CarryFlag auf 0 gesetzt.
Das ist unsinnig, da durch cp das Flag entsprechend des Vergleichs
gesetzt wird.
>Dann vergleich er mit cp das unterere Adressregister mit dem Framezähler.>Dann vergleicht er mit cpc das obere Adressregister mit "oberem>Framezähler". Warum wird beim Vergleichen der oberen Adresse cpc genommen?
Ein cp ist eine Subtraktion ohne Veränderung der Operatoren. Um
16-Bit-Werte zu vergleichen muss bei den High-Werten der 'Übertrag' der
ersten 'Subtraktion' berücksichtigt werden. Das passiert mit cpc.
MfG Spess
Okay,
dass heißt also die Zeile CLC ist überflüssig.
Ich habe diese Routine hier im Forum gefunden, leider kann ich nicht
beurteilen ob das Vertrauenswürdig ist oder nicht. Auf jefden Fall war
es auch eine gute Übung für mich das ganze zu analysieren und zu
verstehen.
Wäre noch die Frage für mich, ob es wirklich sinnvoll ist, die Adresse,
die über die Schalter eingestellt wird im Interrupt auszulesen, oder
nicht doch lieber in der ResetRoutine.
spess53 schrieb:> Ein cp ist eine Subtraktion ohne Veränderung der Operatoren. Um> 16-Bit-Werte zu vergleichen muss bei den High-Werten der 'Übertrag' der> ersten 'Subtraktion' berücksichtigt werden. Das passiert mit cpc.
Eigentlich muss ich doch aber garkein 16Bit Wert Vergleichen, es reicht
doch wenn ich jedes Register einzeln Vergleiche, da ja beide
"gleichzeitig" gleich sein müssen.
(Also wenn in dmxcountL schon was anderes drin steht als adresseL dann
weiß ich ja, dass die Adresse nicht stimmt, ebenso mit den oberen
Registern)
Aber auch im Instruction Set wird bei cp und cpc ja das Beispiel
gebracht das eine 16Bit zahl vergleicht werden soll.
Wenn ich jetzt die Routine ändern würde nach meinen Vorstellungen würde
ich das so machen:
Adresse wird eingelesen über (z.B.) den kompletten PortB und Pin 1 von
(z.B.) PortD, andere Pins von PortD sind evtl. für andere Sachen
genutzt.
Gibt es hier denn die Möglichkeit nur BIT0 also diesen einen PORT-Pin
von D auszulesen?
um an b ranzukommen ist ja alles schon vorhanden:
in adrL, PINC
Aber über welchen Umweg kann ich denn in adrH nur den einen PIN
einlesen?
Hab grad mal in den Wiki "Bitmanipulation" geschaut, aber da kann wird
ja das Bit direkt gesetzt. Ich muss ja erstmal auslesen, ob der PIN "von
außen" überhaupt gesetzt oder nicht gesetzt ist.
Hi
>Wäre noch die Frage für mich, ob es wirklich sinnvoll ist, die Adresse,>die über die Schalter eingestellt wird im Interrupt auszulesen, oder>nicht doch lieber in der ResetRoutine.
Das dürfte egal sein.
>Eigentlich muss ich doch aber garkein 16Bit Wert Vergleichen, es reicht>doch wenn ich jedes Register einzeln Vergleiche, da ja beide>"gleichzeitig" gleich sein müssen.
cp/cpc ist die universelle Variante. Damit kann man nicht nur auf
Gleichheit sondern auch auf größer/kleiner testen. In deinem Fall egal.
>Aber über welchen Umweg kann ich denn in adrH nur den einen PIN>einlesen?
in adrH,PinD
(com adrH)
andi adrH,0b00000001
MfG Spess
Ach ja, der Vorteil des einlesens der Geräteadresse im Interrupt besteht
darin, sie auch während des Betriebes zu ändern.
Das kann man allerdings auch außerhalb des Interruptes machen, z.b. in
der Main-Loop.
Vielen Dank an alle die mir bis hier hin so super geholfen haben!!! Ich
habe alles verstanden. Super Forum!
Jetzt schreibe ich mir die Routine gerade etwas um. Da fällt mir noch
eine Kleinigkeit in Sachen UART Konfiguration auf.
Im Tutorial von dieser Seite habe ich gerlernt, dass es bei "größeren"
Megas (z.B.Mega8515) zwei Reigster für die Baudrate gibt:
UBRRH & UBRRL. Auch heißt es, das zuerst UBRRH beschrieben werden muss
und danach UBRRL.
In der Routine die ich bearbeite ist es aber so:
1
ldi temp, bddv ;bddv = berechneter Wer nach Formel
2
out UBRRL, temp
3
ldi temp, 0x00
4
out UBRRH, temp
Es wird also erst Low und dann High beschrieben. Auch finde ich die
Methode, von vorne rein 0 für UBRRH zu nehmen nicht so schön.
Im Tutorial wird es so gemacht.
1
ldi temp, HIGH(UBRR_VAL)
2
out UBRRH, temp
3
ldi temp, LOW(UBRR_VAL)
4
out UBRRL, temp
Das ist doch eigentlich besser, oder? Denn wenn der Wert für UBRR (durch
ändern der Frequenz) mal doch nicht nur in das untere Register passt,
dann wird UBRRH nicht automatisch 0 gesetzt sondern es wird der richtige
Wert reingeschrieben?
Dabei habe ich noch die Frage zu dieser Schreibweise:
ldi temp, HIGH(UBRR_VAL)
Bedeutet HIGH(UBRR_VAL) das automatisch das obere Register von UBRR_VAL
genommen wird?
Hi
>Bedeutet HIGH(UBRR_VAL) das automatisch das obere Register von UBRR_VAL>genommen wird?
Nein. UBRR_VAL ist eine Zahl (16 Bit) . HIGH(UBRR_VAL) sind die oberen 8
Bit dieser Zahl.
MfG Spess
okay.
und wie sieht es aus mit dem initialisieren? Sollte ich das besser
ersetzen so wie oben geschrieben oder so lassen?
Stimmen denn meine Vermutungen das es sinnvoller ist die Methode aus dem
uart-Tutorial zu verwenden, oder würde das hier nicht passen?
Erik schrieb:> okay.> und wie sieht es aus mit dem initialisieren? Sollte ich das besser> ersetzen so wie oben geschrieben oder so lassen?
ersetzen.
Die Initialisierung im Originalprogramm ist definitiv falsch. Die Gründe
dafür hast du ja schon genannt. Gute Arbeit.
Habe gerade noch eine was.
andi lässt sich nur auf die Register 16-31 anwenden. Allerdings nicht
auf die unteren Register.
Gibt es für die unteren Register einen gleichen Befehl?
Warum ist das eigtl. so, das nicht alle Register die selben Befehle
beherschen, wäre doch einfacher für den Programmierer?!?
Einige Befehle gibt es ja wirklich in unterschiedlicher Ausführung für
die unteren und oberen Register.
Gibt es irgendwo eine offizielle Ersatzliste?
also in etwa xyz für obere Register (16-31) = zyx für untere
Arbeitsregister
In den InstructionSet konnte ich sowas nicht ausfindig machen.
Erik schrieb:> Habe gerade noch eine was.>> andi lässt sich nur auf die Register 16-31 anwenden. Allerdings nicht> auf die unteren Register.> Gibt es für die unteren Register einen gleichen Befehl?
nein
> Warum ist das eigtl. so, das nicht alle Register die selben Befehle> beherschen, wäre doch einfacher für den Programmierer?!?
sieh dir in der Doku bei jedem Befehl die Befehlscodierung an. Es gibt
im Opcode des Befehls einfach nicht genug Bits um alle 32 Register
anzusprechen.
Erik schrieb:> Habe gerade noch eine was.>> Warum ist das eigtl. so, das nicht alle Register die selben Befehle> beherschen, wäre doch einfacher für den Programmierer?!?
Genau. So was nennt man dann auch "orthogonalen Befehlssatz", und wird
von Programmierern (und auch Compilerbauern) geliebt. Manche
Befehlssaetze haben eher die Struktur eines Barock-Schloesschens: Hier
noch ein Tuermchen, da noch ein Erker. Da kann man als Programmierer
schon mal die Krise kriegen, und der Compiler hat auch sein liebe Not.
>> Einige Befehle gibt es ja wirklich in unterschiedlicher Ausführung für> die unteren und oberen Register.> Gibt es irgendwo eine offizielle Ersatzliste?> also in etwa xyz für obere Register (16-31) = zyx für untere> Arbeitsregister
Das nennt sich dann "Erfahrung", etsteht meistens nur durch Kopfarbeit
(z.B. Kopf auf Tischplatte aufschlagen ;-) )
ZigZeg