Forum: Mikrocontroller und Digitale Elektronik 2 Einsteigerfragen - Arithmetik & Frequenz


von Erik (Gast)


Lesenswert?

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?

von Skua C. (skua)


Lesenswert?

Einfach die Bytes Multiplizieren und vom entstehenden wert nur das obere 
Byte nehmen.

von Skua C. (skua)


Lesenswert?

Zur Frequenz

z.b.
ATMEGA32
Seite 1 bzw. 26
http://www.atmel.com/dyn/resources/prod_documents/doc2503.pdf
suchbegriff maximun frequency

von Erik (Gast)


Lesenswert?

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?

von Erik (Gast)


Lesenswert?

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?

von Falk B. (falk)


Lesenswert?

@  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

von Kai S. (zigzeg)


Lesenswert?

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

von Erik (Gast)


Lesenswert?

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?

von Karl H. (kbuchegg)


Lesenswert?

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.

von Erik (Gast)


Lesenswert?

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?

von Karl H. (kbuchegg)


Lesenswert?

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

von Erik (Gast)


Lesenswert?

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?

von Karl H. (kbuchegg)


Lesenswert?

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.

von Erik (Gast)


Lesenswert?

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

von Karl H. (kbuchegg)


Lesenswert?

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.

von H.Joachim S. (crazyhorse)


Lesenswert?

die 3 gehört da nicht hin :-)

von Erik (Gast)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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.

von Erik (Gast)


Lesenswert?

Könnte man anstatt der oben genannten Methode eigtl. auch folgendes 
anwenden:

sbi UCSRC, URSEL
sbi UCSRC, UCSZ0
sbi UCSRC, UCSZ1

?

von Karl H. (kbuchegg)


Lesenswert?

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

von H.Joachim S. (crazyhorse)


Lesenswert?

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?

von login gerade weg :( (Gast)


Lesenswert?

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.

von Erik (Gast)


Lesenswert?

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?

von Karl H. (kbuchegg)


Lesenswert?

Erik schrieb:

>
1
  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.

von Erik (Gast)


Lesenswert?

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?!?

von spess53 (Gast)


Lesenswert?

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

von Erik (Gast)


Lesenswert?

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)

von spess53 (Gast)


Lesenswert?

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

von Erik (Gast)


Lesenswert?

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
2
    sbrc temp,FE              ; Frame Error abfragen -> got reset rjmp frame_error

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?

von spess53 (Gast)


Lesenswert?

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

von Erik (Gast)


Lesenswert?

So ergibt das ganze dann auch einen Sinn!
Dann läuft alles nämlichso ab, wie ich es mir jetzt auf meine 2 DINA4 
Blätter geschmiert habe ^^

von Erik (Gast)


Lesenswert?

Soo, ich hoffe das sind erstmal die letzten Fragen, die ich jetzt stelle 
:-
1
startbyte: 
2
    cpi  dmx_byte, 0          ; wenn null, dann Startbyte 
3
    brne overrun 
4
    inc  dmx_status           ; nächster Statuswert -> Startadresse 
5
    clr  dmxcountH            ; Framezähler zurücksetzten 
6
    clr  dmxcountL 
7
    in   adresseL, PinC       ; Einlesen der Startadresse 
8
    com  adresseL         
9
    clr  adresseH         
10
 
11
    reti
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!

von spess53 (Gast)


Lesenswert?

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

von Erik (Gast)


Lesenswert?

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.

von Steffen (Gast)


Lesenswert?

Zum Beispiel:
.equ   PORT_9._Bit = PORTA
.equ   9.Bit       = PA0

sbic  PORT_9._Bit, 9.Bit
ldi   adrh, 1
sbis  PORT_9._Bit, 9.Bit
clr   adrh

Gruß Steffen

von spess53 (Gast)


Lesenswert?

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

von Steffen (Gast)


Lesenswert?

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.

von Erik (Gast)


Lesenswert?

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?

von spess53 (Gast)


Lesenswert?

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

von Erik (Gast)


Lesenswert?

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?

von Karl H. (kbuchegg)


Lesenswert?

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.

von Erik (Gast)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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.

von Kai S. (zigzeg)


Lesenswert?

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

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
Noch kein Account? Hier anmelden.