Hmm. Also dann entweder DVI oder 8 bit DMX oder was?
Alternative zu ws2801 lpd6803 tm.... u.s.w gesucht!
-
-
8 Bit DMX rein in den Controller, der guckt in der Tabelle nach und setzt das auf 16 Bit Werte um...
in meiner Kugelmatrix habe ich auch ne Gammakorrektur drin, weil ich festgestellt habe, dass die Werte vom Bildschirm (die das Matrix-Steuer-Programm erzeugt) auf den LEDs viel zu hell, und die Farben dadurch auch zu blass rüberkommen.
bei mir wird halt von 8 Bit auf 8 Bit umgesetzt, wodurch ein paar Stufen verloren gehen - da wäre ne Umsetzung von 8 auf 12 oder 16 Bit eben besser...
siehe dazu auch hier...
und Zeit dafür haettest
Das ist das Problem, die habe ich i.M. leider überhaupt nicht - wird schon knapp, dass mein Zeug alles rechtzeitig fertig wird... diese Matrix soll am 13.1. auf ner Party hängen, und es sind gerade mal 2 Ketten fertig, 1/2 von 4 Controllern, und die SW zu 70%...
-
huh, da gibt es ja schon auch ein code für 12bit gamma von nenni
bis am 13.1 kriegst DU doch die kette locker hin
Na gut, ich werd mal demnaechst gucken wie ich die DMX routine an die TLC einbinden kann. Wird ein laaaanger Weg dahin sein. Kann ja hier nach tipps fragen wenns kompliziert für mich wird (heisst: alle 5 minuten poste ich dann ne frage für jede zeile code ;P )
-
Gehört zwar in den Marktplatz, aber haette da jemand von euch noch ein paar von den tlc5947's übrig? Dip version waer geil! Wenn ja, dann bitte per PM melden. Danke..
EDIT: Sorry glaube von der 5947 gibts gar keine dip version.
-
Also hab mal ein bisschen assembler studiert
Wie ich das so sehe, müsste ich mit deinem tlc5947ram ausgabe code folgendes tun:
(Haut das alles eigentlich mit nem Mega16 und 16mhz hin?)1- Register & Konstante Definieren für die TLC's in deinem code
2- ISR für DMX und die Konfiguration dafür sollte kein problem sein
3- Ram puffer daten vom DMX in die ARR_TLC_Out reinschreiben lassen
3- Wenn ich das richtig verstehe, müsste dann TLC_Out_Loop auch daten vom Ram bekommen statt von der Tabelle oder?Für die Ausgabepins hab ich mal sowas festgelegt:
Code.equ TLC_Data = 6 ; Ausgabepin TLC5947 Data .equ TLC_Clk = 7 ; Ausgabepin TLC5947 Clock .equ TLC_Blank = 21 ; Ausgabepin TLC5947 Blank .equ TLC_Lat = 22 ; Ausgabepin TLC5947 Latch .equ TLC_Out = 23 ; Ausgabepin TLC5947 Out
Meint ihr ich bin auf dem richtigen Weg ?
-
Fast....
bei den Pins ist nicht die Nummer am IC-Gehäuse gemeint, sondern die vom Port.
Der Port muss auch definiert werden, bei "TLC_Port" - also z.B. bei
.equ TLC_Port = PortC
.equ TLC_Data = 0
.equ TLC_Clk = 1kommen die Daten dann an PortC0 raus und Clock an PortC1, usw.
das ist noch ne alte Routine, sorry, bei meinen neuen stehen oben im Header diese ganzen Definitionen drin zum rauskopieren...
genau, die DMX-Empfangsroutine muss dann die Daten in's RAM schreiben ab Adresse ARR_TLC_Out (diese muss eben auch definiert werden, bei nem Mega16 z.B. .equ ARR_TLC_Out = 0x0060)
und die Patchtabelle ist dann dazu da, die Daten bei der Ausgae umzusortieren, also wenn Du die LEDs in ner bestimmten Reihenfolge, so wie's vom Layout her passt, an den TLC5947 angeschlossen hast, dass dann jede DMX-Adresse zur richtigen LED passt..
-
Danke für die Tipps.
Du benutzt hier SW SPI oder? Man kann also die Data & Clk von irgendeinem Pin rausgeben?
Wegen der Patchtabelle:
Zeigen die Zahlen die DMX kanaele oder bedeutet jeder Zahl 1 RGB led?
zb.:
.db 8, 7, 6, 11, 10, 9, 23, 22
.db 21, 20, 19, 18, 17, 16, 15, 14
.db 13, 12, 2, 1, 0, 5, 4, 3ist also 5,4,3 ein RGB Led oder 3 RGB Leds?
Edit 1: Braucht der TLC überhaupt auch so ne Warteschleife wie bei den 2801's?
Edit 2: TLC_kanaele definieren sollte doch .equ TLC_kanaele = 24 sein oder nicht? -
genau, wenn Du einen TLC hast, dann sind das 24 Kanäle - Du kannst mit dieser Routine auch mehrere kaskadierte TLC ansteuern, dann halt die Zahl der Kanäle entsprechend erhöhen (und die Patchtabelle vergrößern).
Warten ist hier nicht nötig, da gibt's ja ein extra Latch-Signal, das geht hier nicht über eine Pause - Du kannst also in der Hauptschleife einfach immer die Daten aus dem RAM ausgeben.
Hier beachten: der TLC hat ja 12 Bit, also ist der Puffer dort auf 16 Bit ausgelegt, also 2 Byte pro Ausgang (erst LSB, dann MSB). Du kannst also nicht direkt (bzw. hängt halt von Deiner Empfangsroutine ab) die empfangenen Bytes da rein schreiben.
ich würde das so machen: Du schreibst die empfangenen Kanäle mit der DMX-ISR in nen extra RAM-Bereich. In der ISR setzt Du ein Flag, wenn ein kompletter Frame empfangen wurde. In der Hauptschleife wartest Du immer drauf, dass dieses Flag gesetzt ist, dann lscht Du das Flag, kopierst Du die Daten in den Ausgabepuffer und rechnest sie dabei auf 12 Bit um. Dann ausgeben, und wieder auf das Flag warten.
Dann läuft das Ganze synchron, und es kann auch kein Flackern geben, weil sich Empfangs- und Sende-Routine gerade irgendwo überschneiden...
das umrechnen beim Kopieren kann dann eben nachsehen in ner Tabelle sein (für Gammakorrektur), oder im einfachsten Fall das empfangene Byte 4 Bit nach links schieben, um nen Pseudo-12-Bit-Wert (letzte 4 Bits immer 0) draus zu machen...
Weißt Du wie das geht mit der Bitschieberei und Flags setzen/abfragen...?
Die Zahlen in der Tabelle sind die DMX-Kanäle, richtig - mit einer Änderung: es geht bei 0 los, nicht bei 1... also 1 Zahl ist ein Pin am TLC, für ne RGB-LED also 3 Zahlen
wie dort steht, das ist in umgekehrter Reihenfolge, weil der TLC praktisch ein normales Schieberegister ist, d.h., was Du als erstes rein schiebst, kommt am letzten Pin an... wenn also z.B. die *letzten* 3 Ausgänge des TLC auf Kanal 22, 23 und 24 reagieren sollen, dann *beginnt* die Tabelle mit 24, 23, 22...
und, ja, es ist SW-SPI, Du kannst also die Daten irgendwo raus schicken - nur nicht vergessen, die Pins auch auf Ausgang zu stellen... hier sind's nicht nur Data und Clock, sondern eben auch noch Latch und Blank, also insg. 4 Signale...
-
ich würde das so machen: Du schreibst die empfangenen Kanäle mit der DMX-ISR in nen extra RAM-Bereich. In der ISR setzt Du ein Flag, wenn ein kompletter Frame empfangen wurde. In der Hauptschleife wartest Du immer drauf, dass dieses Flag gesetzt ist, dann lscht Du das Flag, kopierst Du die Daten in den Ausgabepuffer und rechnest sie dabei auf 12 Bit um. Dann ausgeben, und wieder auf das Flag warten.
Dann läuft das Ganze synchron, und es kann auch kein Flackern geben, weil sich Empfangs- und Sende-Routine gerade irgendwo überschneiden...
das umrechnen beim Kopieren kann dann eben nachsehen in ner Tabelle sein (für Gammakorrektur), oder im einfachsten Fall das empfangene Byte 4 Bit nach links schieben, um nen Pseudo-12-Bit-Wert (letzte 4 Bits immer 0) draus zu machen...
Weißt Du wie das geht mit der Bitschieberei und Flags setzen/abfragen...?
Leider Nein Mit dem Bits und Flags wirds mir dann schon wie du geahnt hast zu viel. Hab keine Ahnung wie ich das umsetzen soll.
EDIT:
Bin mal beim rumstöbern auf ein thread aufmerksam geworden bzgl. 5947 und BLANK flackern. Da schreibt TI support dass 5947 eher für bilder statt videos designed wurde und dass der 5940 besser dafür ist. Was soll das denn heissen? -
k.A. - kann die Seite nicht ansehen, da haut's bei mir total den Text durcheinander...
war hier auch schon mal im Gespräch, dieses Problem mit dem Flackern bei der Datenübernahme, tritt aber mit meiner Routine nicht auf, da dort eben der Blank benutzt wird...
das mit den Bits etc. ist ganz einfach - erst mal zu den Flags:
ein Flag ist einfach ein Bit in einem Register, das Du setzt oder löscht, um nen bestimmten Zustand anzuzeigen/zu merken - wie eben hier, dass ein kompletter Frame empfangen wurde...
am Besten lädst Du Dir mal bei Atmel.com die AVR-Befehlsübersicht runter, da ist zu jedem Befehl erklärt, was der macht, da kannst Du das leichter nachvollziehen...
Du definierst also ein Register, das Du für die Flags benutzt, z.B.:
.def Flags = R24 ; Diverse Flags (oder was halt noch frei ist, muss aber größer 15 sein)
und Dein Flag mit z.B.:
.equ DMX_FRAME_COMPLETE = 0
das sind letztlich nur Merkhilfen für Dich, der Assembler ersetzt intern also überall "Flags" durch R24, und "DMX_FRAME_COMPLETE" durch 0, für ihn ist es also das selbe, egal ob Du
sbrs Flags, DMX_FRAME_COMPLETE oder
sbrs R24, 0
schreibst - wichtig: für Register muss man .def nhemen, für alles andere .equ - Gross- und Kleinschreibung ist egal, es ist aber so ne Konvention, dass man Konstanten in Grossbuchstaben schreibt.
Du kannst also nun mit:
sbr Flags, (1<<DMX_FRAME_COMPLETE) das Flag auf "1" setzen (in der DMX-Routine, wenn der Frame durch ist), und mit
cbr Flags, (1<<DMX_FRAME_COMPLETE) das Flag auf 0 setzen (nachdem Du es abgefragt hast)warum es einmal (beim setzen/löschen) "(1<<DMX_FRAME_COMPLETE)" heisst, und einmal nur "DMX_FRAME_COMPLETE" (beim abfragen), dazu siehe eben die Befehlsübersicht...
in der Hauptschleife oben machst Du einfach die Abfrage rein:
_main:
sbrs Flags, DMX_FRAME_COMPLETE ; nächsten Befehl überspringen, wenn das Flag gesetzt ist
rjmp _main ; Flag nicht gesetzt, weiter abfragen
cbr Flags, (1<<DMX_FRAME_COMPLETE)das heisst also, so lange das Flag nicht gesetzt ist, springt er wieder zu _main zurück, wenn's dann gesetzt wurde (durch die DMX-ISR), macht er weiter, lscht das Flag wieder - danach dann die Kopier-Routine und Ausgabe, dann wieder zu _main, also wieder drauf warten, dass das Flag gesetzt wird.
zum Kopieren erst mal zwei Pointer setzen:
ldi YL, low(DMX_Buffer) ; Y auf DMX-Buffer
ldi YH, high(DMX_Buffer)ldi ZL, low(Arr_TLC_Out) ; Z auf Ausgabepuffer
ldi ZH, high(Arr_TLC_Out)dann in einer Schleife die Werte kopieren - immer erst den Wert aus dem DMX-Buffer holen mit Post-Increment (bedeutet, der Zeiger zeigt nach dem holen auf das nächste Byte):
ld temp0, Y+
ein Register löschen:
clr temp1
um in diesen beiden Registern den 12-Bit-Wert zusammenzubasteln - dazu muss eben der 8-Bit-Wert in temp0 um 4 nach links geschoben werden:
rol temp0
rol temp1
rol temp0
rol temp1
rol temp0
rol temp1
rol temp0
rol temp1es werden also die 8 Bit in temp0 um eins nach links geschoben, das höchste Bit dabei in's Carry - dann das selbe mit temp1, wobei das unterste Bit aus dem Carry kommt - das Ganze eben 4 mal.
dann diese beiden Bytes in den Ausgabepuffer schreiben:
st Z+, temp0 ; Low-Byte speichern
st Z+, temp1 ; High-Byte speichernauch hier wird wieder der Pointer automatisch erhöht, um eben immer nach dem Speichern auf das nächste Byte zu zeigen
das war's dann auch schon, da drum rum halt ne Schleife basteln, die 510x durch läuft, wie man das macht, da kannst Du nun mal selbst gucken (Mikrocontroller.net o.ä.)
-
Hi Pesi,
Wow, noch nie gesehen, dass da jemand wirklich Zeit nimmt und alles bis ins letzte Detail schreibt! Hast mich echt überrascht. Respekt!!!
Ich habe es mir mal ein bisschen einfacher gemacht und als Vorlage dein Code für den 2801 genommen. Alle veraenderungen mache ich auf diesem Code. Wenn Du oder andere in den Code (was ich hoffentlich noch zu ende bringen werde) mal was editieren wollen, dann habt ihr das ajf einfacher. Sonst waers echt n mischmasch und unmöglich für mich das hier zu Realisieren Ich finde halt das System wie du programmierst sehr hilfreich, und ist besonders mit den einzelnen .inc dateien sehr Übersichtlich.
So nun mal wieder ein paar Fragen:
.def Flags = R24
.equ DMX_FRAME_COMPLETE = 0und
push YL
push HLhabe ich Definiert.
Ist der "Dmx_Buffer" bei YL und HL der gleiche wie "DMX_Data" in der ISR_Byte_received.inc? Also kann ich wieder statt "Dmx_Buffer" den "DMX_Data" nehmen, weil die ja im gleichen Ram Bereich liegen? Oder muss ich trotzdem zusaetzlich mit ".equ DMX_Buffer = 0x0060" den Ram Bereich festlegen.
Wozu brauchen wir YL und YH wenn in der DMX/ISR die "DMX_Data" ZL und ZH ist? Vielleicht echt ne bescheuerte Frage, aber nur so kann ich ein bisschen was verstehenDu kannst also nun mit:
sbr Flags, (1<<DMX_FRAME_COMPLETE) das Flag auf "1" setzen (in der DMX-Routine, wenn der Frame durch ist), und mit
cbr Flags, (1<<DMX_FRAME_COMPLETE) das Flag auf 0 setzen (nachdem Du es abgefragt hast)warum es einmal (beim setzen/löschen) "(1<<DMX_FRAME_COMPLETE)" heisst, und einmal nur "DMX_FRAME_COMPLETE" (beim abfragen), dazu siehe eben die Befehlsübersicht...
in der Hauptschleife oben machst Du einfach die Abfrage rein:
_main:
sbrs Flags, DMX_FRAME_COMPLETE ; nächsten Befehl überspringen, wenn das Flag gesetzt ist
rjmp _main ; Flag nicht gesetzt, weiter abfragen
cbr Flags, (1<<DMX_FRAME_COMPLETE)das heisst also, so lange das Flag nicht gesetzt ist, springt er wieder zu _main zurück, wenn's dann gesetzt wurde (durch die DMX-ISR), macht er weiter, lscht das Flag wieder - danach dann die Kopier-Routine und Ausgabe, dann wieder zu _main, also wieder drauf warten, dass das Flag gesetzt wird.
Ist da von dir vielleicht ein Tippfehler drin?
Weil im ersten Paragraph heisst es "sbr" und unten "sbrs".Wieder im ersten Paragraphhast du 1<<DMX_FRAME_COMPLETE in klammern, weiter unten sind aber die klammern weg und der 1<< ist auch nicht mehr da. Welche ist denn nun richtig?
Ist die Hauptschleife so ok?
(DMX-zu-TLC5947.asm)Code
Alles anzeigenMain: rcall SUB_TLC5947_Out ; Daten ausgeben sbrs Flags, DMX_FRAME_COMPLETE ; Nächsten Befehl überspringen, wenn das Flag gesetzt ist rjmp Main ; Flag nicht gesetzt, weiter abfragen - Hauptschleife von vorne cbr Flags, (1<<DMX_FRAME_COMPLETE)
Und in der Datenausgabe aus RAM an TLC5947 ( das ist ja nun SUB_TLC5947_Out.inc geworden) habe ich es jetzt so umgesetzt (Kopiert habe ich hier nur den Teil mit dem Registern und dem Loop Bereich):
Code
Alles anzeigenSUB_TLC5947_Out: push temp2 ; Register sichern push temp1 push temp0 push ZL push ZH push YL ; Für Bitshifting DMX_Buffer Low push YH ; Für Bitshifting DMX_Buffer High clr temp0 ; temp0 = aktuell bearbeiteter Kanal TLC_Out_Loop: ldi ZH, high(TLC_Patchtable*2) ; Position des Kanals im Ram aus Tabelle ldi ZL, low(TLC_Patchtable*2) add ZL, temp0 adc ZH, Null lpm temp1, Z ; in temp1 ldi YL, low(DMX_Buffer) ; Y auf DMX-Buffer ldi YH, high(DMX_Buffer) ld temp0, Y+ clr temp1 rol temp0 ; 12bit Umwandlung rol temp1 rol temp0 rol temp1 rol temp0 rol temp1 rol temp0 rol temp1 st Z+, temp0 ; 12Bit Low-Byte speichern st Z+, temp1 ; 12Bit High-Byte speichern ldi ZH, high(ARR_TLC_Out) ; Zeiger auf auszugebendes Byte legen ldi ZL, low(ARR_TLC_Out) add ZL, temp1 ; 2x addieren, da Wörter im RAM adc ZH, Null add ZL, temp1 adc ZH, Null adiw Z, 1 ; High Byte ist eins weiter ld temp1, Z ; auszugebendes HB in temp1 laden swap temp1 ; nur 4 LSB interessieren ldi temp2, 4 ; Ausgabe-Schleife für 4 höchste Bits
Das wars erstmal...
To be continued
EddiePS: Vielleicht lachen die Profis mich jetzt bestimmt aus, aber ich lache echt mit Euch waehrend ich die Fragen hier tippe. Trozdem ist es mir nicht peinlich, weil es mir einfach spass macht ein bisschen zu lernen
-
Ja, wenn Du (und evtl. andere auch noch) was dabei lernst, dann hat sich's ja auch rentiert.
push YL
push HLhabe ich Definiert.
Da gibt es aber nix zu "definieren" - "push" ist ein Befehl, und bedeutet, dass das Register auf den Stapel gelegt wird...
Ist der "Dmx_Buffer" bei YL und HL der gleiche wie "DMX_Data" in der ISR_Byte_received.inc? Also kann ich wieder statt "Dmx_Buffer" den "DMX_Data" nehmen, weil die ja im gleichen Ram Bereich liegen? Oder muss ich trotzdem zusaetzlich mit ".equ DMX_Buffer = 0x0060" den Ram Bereich festlegen.
Das kommt daher, dass die eine Routine halt 2, 3 Jahre älter ist als die andere, da habe ich halt mal so, mal so geschrieben... aber wie gesagt, das sind ja nur Namen für Konstanten, Du kannst also nun entweder überall "Dmx_Buffer" durch "DMX_Data" ersetzen, oder du definierst für "Dmx_Buffer" eben die selbe Zahl (0x0060)...
Wozu brauchen wir YL und YH wenn in der DMX/ISR die "DMX_Data" ZL und ZH ist? Vielleicht echt ne bescheuerte Frage, aber nur so kann ich ein bisschen was verstehen
Hier ebenso: ein mal habe ich halt den Y-Pointer benutzt, ein mal den Z-Pointer - das ist völlig egal... evtl. ist's halt damals so besser ausgegangen, bei kleinen Projekten, wo man nicht viele Register braucht, reserviert man manchmal die Pointer fest, also z.B. Y für die Empfangsroutine und Z für die Ausgaberoutine...
Ist da von dir vielleicht ein Tippfehler drin?
Weil im ersten Paragraph heisst es "sbr" und unten "sbrs".Wieder im ersten Paragraphhast du 1<<DMX_FRAME_COMPLETE in klammern, weiter unten sind aber die klammern weg und der 1<< ist auch nicht mehr da. Welche ist denn nun richtig?
siehe hier, es ist beides richtig:
warum es einmal (beim setzen/löschen) "(1<<DMX_FRAME_COMPLETE)" heisst, und einmal nur "DMX_FRAME_COMPLETE" (beim abfragen), dazu siehe eben die Befehlsübersicht...
das ist kein Tippfehler, "sbr" heisst "set bit(s) in register" und dient zum setzen der Bits, "sbrs" heisst "skip if Bit in Register is set", eben, den nächsten Befehl überspringen, wenn das Bit gesetzt ist (zur Abfrage)...
der Mechanismus ist da unterschiedlich, setzen kann man mehrere Bits auf einmal, deswegen das 1<< - abfragen kann man aber nur eins, deswegen da dirket die Bit-Nummer...
wie schon gesagt, am Besten schaust Du Dir erst mal die Befehlsübersicht an, was die Befehle überhaupt bedeuten und was sie machen - ausserdem das Tutorial bei Mikrocontroller.net - weil mit reinem Copy&Paste funktioniert das nicht, wenn man nicht mal weiß, was der µC nun macht, wenn man "sbrs" hin schreibt...
-
Habe gestern Tutorial von MC und AVR Befehlsübersicht schon runtergeladen. War wohl zu schnell mit der Antwort. Haette erstmal die Dokumente checken müssen....Ich werde versuchen ab Mitternacht keine codes mehr zu schreiben (copy&paste), wird wohl nur noch schlimmer
Über die Hauptschleife und TLC_Out_Loop Codes hast du nichts geschrieben. Ich geh mal davon aus dass die total falsch sind?
BB & Danke nochmal
-
Da war's mir dann doch zu spät, da auch noch was dazu zu schreiben... aber, ja es ist falsch...
Grundsätzlich: Du baust Dir hier ja ne SW zusammen unter Verwendung bereits bestehender Routinen - das ist ja auch ganz üblich, keiner erfindet immer wieder das Rad neu...
nur, da gibt es zwei Möglichkeiten: entweder man nimmt ne Routine, so wie die geschrieben ist, und benutzt sie als Programmteil - oder man nimmt Codeschnipsel und modifiziert sich die nach seinen Anforderungen - dazu muss man dann aber genau wissen, was man da macht... und dazu erst mal nachvollziehen, was der Code überhaupt bewirkt... sollte bei meinem recht einfach sein, da gut kommentiert...
zur Augaberoutine, wo Du diese Umrechnung mit rein geflickt hast: Die setzt ja erst mal nen Zeiger auf die Patchtabelle, der dann in der Schleife erhöht wird - ein Zeiger ist praktisch ne Adresse im RAM oder Flash... der zeigt also zuerst auf den ersten Wert in der Patchtable und holt den, beim nächsten Durchlauf den 2., usw.
dieser Wert wird dann zur Basisadresse des Ausgabepuffers dazu addiert - dadurch geschieht das umsortieren. Eben wieder mit nem Zeiger, der erst auf die Startadresse des Puffers gesetzt wird, und dann der Wert aus der Patchtabelle dazu addiert wird.
Und der Teil fehlt hier nun, Du holst einfach der Reihe nach Bytes aus dem DMX-Puffer, verschiebst die, und speicherst die dann an ne komplett falsche Adresse, weil der Z-Pointer ja eigentlich auf's Flash gesetzt ist (wo die Patchtabelle drin ist)...
Das ist nun für nen Anfänger evtl. nicht so leicht nachvollziehbar, was da in der Routine genau passiert - deswegen ist es eben besser, die einfach so zu benutzen, *wie sie ist*, *da* nix dran zu ändern!
Genauso, wie Du in Bascom o.ä. z.B. For...next schreibst, ohne dass es Dich groß interessiert, was der Compiler draus macht...
Beim Programmieren generell (egal welche Sprache), empfiehlt es sich, ein größeres Problem erst mal in mehrere kleinere zu zerlegen - wie sieht das hier aus..?
Programm insgesamt läuft so:
- in einer ISR werden per DMX empfangene Bytes in's RAM geschrieben - diese ISR hast Du ja schon fertig, da musst Du (ausser der Sache mit dem Flag) nix dran machen, die kannst Du einfach benutzen
- in der Hauptschleife wird drauf gewartet, dass das Flag gesetzt wird, das sind die 4 Zeilen oben, das ist nur der *Beginn* der Hauptschleife...
- dann werden die Daten "umgerechnet", so wie oben erklärt
- dann werden sie ausgegeben - dazu gibt's ja eben die fertige Routine von mir, die Du einfach so verwenden kannst, wie nen Befehl "Daten ausgeben"
- dann wieder nach oben, warten auf Flag gesetzt
und so größere Programmteile (eben die Ausgaberoutine, auch die zum Bits verschieben) macht man eben in ne Subroutine, dann ist das Ganze übersichtlich und leicht änderbar - willst Du z.B. doch mal die Gammakorrektur rein machen, musst Du nur die Bitschieberoutine durch eine ersetzen, die die Gamma-Umrechnung macht, ohne am Code sonst was zu ändern - was aber eben nicht mehr geht, wenn die Bitschieberei bereits in der Ausgaberoutine mit drin ist...
die Hauptschleife, das "eigentliche Programm" sieht dann also so aus:
Code
Alles anzeigenMain: sbrs Flags, DMX_FRAME_COMPLETE ; Nächsten Befehl überspringen, wenn das Flag gesetzt ist rjmp Main ; Flag nicht gesetzt, weiter abfragen cbr Flags, (1<<DMX_FRAME_COMPLETE) ; Flag wieder löschen - diese 3 Zeilen warten also immer drauf, dass das Flag gesetzt wird rcall Change_8Bit_to_12Bit ; hier werden die Daten dann aus dem DMX-Buffer gelesen, Bits verschoben, und Daten in den Ausgabepuffer kopiert rcall SUB_TLC5947_Out ; bearbeitete Daten ausgeben rjmp Main ; das Ganze wieder von vorne
so macht man sich das Ganze eben übersichtlich, ist dann fast wie ne Hochsprache, das Programm in der Hauptschleife besteht eben nur aus 3 Anweisungen: Warten auf Flag gesetzt, dann Daten umkopieren mit Bitschieberei, dann Daten ausgeben, das ist alles...und diese Routine "Change_8Bit_to_12Bit" eben auch in ne extra Datei, die Routine an sich habe ich Dir oben ja schon erklärt, ist einfach der Code ohne die Erklärungen dazwischen:
Code
Alles anzeigenChange_8Bit_to_12Bit: ldi YL, low(DMX_Buffer) ; Y auf DMX-Buffer ldi YH, high(DMX_Buffer) ldi ZL, low(Arr_TLC_Out) ; Z auf Ausgabepuffer ldi ZH, high(Arr_TLC_Out) ; *** Hier noch Kopf der Schleife einfügen *** ld temp0, Y+ clr temp1 rol temp0 rol temp1 rol temp0 rol temp1 rol temp0 rol temp1 rol temp0 rol temp1 st Z+, temp0 ; Low-Byte speichern st Z+, temp1 ; High-Byte speichern ; *** hier noch Fuß der Schleife einfügen *** ret ; zurück aus der Subroutine
die Schleife fehlt hier noch, wie gesagt, die kannst Du mal selbst machen - entweder zwei extra Register zum zählen benutzen, wie oft das Ganze schon durchlaufen wurde, man kann ja aber auch z.B. den Y-Pointer nehmen, der ja eh' imer um eins erhöht wird - Anfangs hat er den Wert DMX_Buffer, nach 510 übertragenen Werten eben (DMX_Buffer+510), also kann man einfach immer wieder nach oben springen, so lange Y kleiner ist als (DMX_Buffer+510)...EDIT: Du machst das Ganze j ain AVR Studio, oder..? - wenn'smal nahe an fertig ist, ist's dann besser, mal das komplette Projekt rein zu stellen, als nur Ausschnitte aus Quelltexten, damit man sieht, ob das überhaupt alles zusammen passt (z.B. eben auch die Definitionen, INterrupt-Vektor-Tabelle, etc.)
-
Hi Pesi,
Du machst das Ganze j ain AVR Studio, oder..?
Genau. AVR Studio 4. Oft nehme ich aber auch das Programm "Programmer's Notepad". Ist ziemlich fix.
Ja, ich depp hab also die flags zwischen die SUB_TLC_Out schleife rein getackert
Die Pointer wie Y, Z usw verstehe ich jetzt besser.Die Idee die 8bit-12bit Routine in die SUB_TLC5947_Out einzubauen war dann auch doof. So ne sub Routine sieht schon ordentlicher aus. Vor allem wenn du schon alles vorgegeben hast. Bin noch nicht mal in der Lage korrekt zu Copy&Pasten
Ne im ernst, wenn ich irgendwann mal ein DMX-zu-XXXXX Code basteln sollte, wirds definitiv einfacher gehen. Man lernt eben einiges durch diese Trial & Error Geschichten.
Habe jetzt Stundenlang die AVR Befehle gelesen und bin dabei hoffentlich fündig geworden.
Mal sehen ob das ok ist. War mir nur nicht so sicher ob ich temp3 und temp4 benutzen durfte.Habe übrigens schon alle .inc dateien entsprechend für TLC editiert. Beim Kompilieren kommen auch keine Errors.
Hier der Code mit meinem Schleifchen
Code
Alles anzeigenChange_8Bit_to_12Bit: ldi YL, low(DMX_Buffer) ; Y auf DMX-Buffer ldi YH, high(DMX_Buffer) ldi ZL, low(Arr_TLC_Out) ; Z auf Ausgabepuffer ldi ZH, high(Arr_TLC_Out) ;Start Kopf der Schleife Bit_Loop: ld temp3, Y+ ; DMX Buffer Daten Laden ldi temp4, DMX_Channels cp temp4, temp3 ; Daten mit DMX_Channels Vergleichen brlo Bit_Ende ; DMX Channel Zahl erreicht, weiter zu ret ;Ende Kopf der Schleife ld temp0, Y+ clr temp1 rol temp0 rol temp1 rol temp0 rol temp1 rol temp0 rol temp1 rol temp0 rol temp1 st Z+, temp0 ; Low-Byte speichern st Z+, temp1 ; High-Byte speichern ;Start Fuß der Schleife rjmp Bit_Loop Bit_Ende: ;Ende Fuß der Schleife ret ; zurück aus der Subroutine
-
So ne sub Routine sieht schon ordentlicher aus. (...)
Ne im ernst, wenn ich irgendwann mal ein DMX-zu-XXXXX Code basteln sollte, wirds definitiv einfacher gehen.Genau - das musste ich auch erst lernen, anfangs waren meine Programme auch einfach alles in eine meterlange Datei geschrieben - so ist das eben übersichtlicher, man weiß bei jeder Routine, was sie macht, und muss ggfs. nur noch Routinen austauschen - also hier eben die mit der Bitschieberei gegen eine, die Gamma-Korrektur macht, wenn man das will, oder eben ne andere Ausgaberoutine statt TLC für andere Chips - der Rest vom Code ist davon eben unberührt...
War mir nur nicht so sicher ob ich temp3 und temp4 benutzen durfte.
Du darfst benutzen, was Du willst - musst nur den Überblick behalten....
Hier z.B., in der Hauptschleife passiert weiter nix, da werden sonst keine Register gebraucht, also kann auch keins überschrieben werden - normal macht man das so, dass man zu Beginn einer Subroutine alle verwendeten Register auf den Stapel sichert, und nachher wiederherstellt, dann kann nix passieren in der Art, dass es seltsame Fehler gibt, weil in irgendeinem Register was drin steht, was da nicht soll...
also eben oben z.B.:
push temp0
push temp1
push temp2 usw. - damit werden die Inhalte der Register auf den Stack gelegt... und unten dann:pop temp2
pop temp1
pop temp0 - damit werden sie dann wieder vom Stapel geholt, die Inhalte dieser Register sind also nach dem Aufruf der Subroutine exakt so wie vorher, egal was diese mit den Registern gemacht hat...ich habe es mir schon länger angewöhnt, in die Routinen oben rein zu schreiben, was die eigentlich wie machen, welche Register sie benutzen und ggfs. Konstanten brauchen - so in der Art z.B.:
Code
Alles anzeigen; ================================================================================================ ; Mini-DMX-Empfangsroutine - fest auf 256 Kanäle eingestellt - werden nicht alle gespeichert! ; by Pesi - V 1.0 - 25.12.2010 ; ================================================================================================ ; benutzte Register : temp0, temp1, ZL, ZH, werden gesichert ; gem. benutzte Register : keine ; reservierte Register : MDMX_State, MDMX_Count, MDMX_L, MDMH_H ; ; Definitionen (zum Kopieren): ; ;.def MDMX_State = R16 ; Status-Byte, Register muss >15 sein ;.def MDMX_Count = R17 ; Zähl-Byte, Register muss >15 sein ;.def MDMX_L = R2 ; Low-Byte Adress-Pointer ;.def MDMX_H = R3 ; High-Byte Adress-Pointer ; ;.equ MDMX_Buffer = 0x60 ; RAM-Bereich für empfangene Daten ;.equ MDMX_Kanaele = 192 ; Zahl der zu empfangenden MDMX-Kanaele (max 252) ; ------------------------------------------------------------------------------------------------ ISR_Byte_Received: push temp1 ; Register sichern in temp1, SREG push temp1 push temp0 push ZL push ZH in temp0,UCSRA ; Status und Daten holen in temp1,UDR ; Daten in temp1
da hat man dann alles auf einen Blick, und weiß auch nach 2 Jahren sofort wieder, wie man die Routine einbauen muss...das mit den Pointern hast Du anscheinend noch nicht ganz verstanden: Ein Pointer ist eben ein Zeiger auf eine Speicherzelle im RAM, der enthält eine Adresse - Du kannst Dir das so vorstellen, das RAM ist ein Kasten mit lauter Schubladen, in jeder liegt ein Zettel mit einer Zahl drauf. Der Pointer enthält dann eben die Nummer der Schublade, also wenn Y z.B. 15 ist, dann holst Du mit ld temp0, Y+ den Wert aus der 15. Schublade in temp0, anschließend ist Y dann 16.
Jetzt schau Dir mal Deine Schleife an: Du holst da also nen Wert aus dem RAM, und vergleichst *den* (der kann irgendwas sein) mit DMX_Channels - so kannst Du ja nicht feststellen, wie viele Schubladen Du schon geöffnet hast...
Du musst also die Nummer der Schublade (= der Y-Pointer) überprüfen, ob Du schon alle durch hast - also z.B. die erste Schublade hat die Nummer 0x0060 (dezimal 96 = DMX_Buffer), und Du musst 512 Schubladen öffnen (512 DMX-Kanäle = DMX_Channels), dann bist Du also fertig, wenn Du bei Schublade Nr. 608 (= 96+512 = DMX_Buffer+DMX_Channels) angelangt bist.
also musst Du eben die *Nummer* der Schublade (= Y) mit DMX_Buffer+DMX_Channels vergleichen, *nicht* das, was da drin ist...
wundert mich, dass sich das assemblieren lassen hat, wenn DMX_Channels größer als 255 ist, muss ldi temp4, DMX_Channels nen Fehler ergeben, weil temp4 ja nur Zahlen von 0 bis 255 enthalten kann (8 Bit)...
deswegen muss der Vergleich mit dem Y-Pointer auch in 16 Bit gemacht werden - "Y" ist auch nur ein Name, das bedeutet praktisch "das 16-Bit-Register, das aus ZL und ZH zusammengesetzt ist" - daher muss man die auch einzeln benutzen beim laden und vergleichen etc., so wie oben eben auch:
da kann man eben nicht ldi Y, DMX_Buffer schreiben... (leider, wenn der Assembler "schlau" wäre, wüsste er schon, dass damit eben gemeint ist, low-Byte von "DMX_Buffer" nach YL und High-Byte nach YH...)um nun Y mit der Nummer der letzten Schublade zu vergleichen, muss man auch Low- und High-Byte getrennt vergleichen - so wie das in der DMX-Empfangsroutine ja auch gemacht wird (hier halt mit dem Z-Pointer, Prinzip ist das selbe):
Codecpi ZL, low(DMX_Data+DMX_Channels) brne dmx_finish cpi ZH, high(DMX_Data+DMX_Channels) brne dmx_finish
bau' das doch mal ein, überlege Dir dabei, wann was passiert, und wo er bei welchem Vergleichsergebnis hin springt, dann bekommst Du die Schleife schon zusammen... -
Hi,
So! Haben ja noch kein Mitternacht, also kann ich jetzt mal meine Hausaufgabe posten
Danke nochmal für die ganzen Tipps und Tricks, wobei ja das, was du alles da schreibst schon viel mehr sind als nur einfache Tipps u. Tricks u.s.w.
Ich muss zugeben: ich lerne jedes mal viel mehr über die Sprache und vor allem was die leds vom uc brauchen und was der uc den leds geben muss damit sie Leuchtenwundert mich, dass sich das assemblieren lassen hat, wenn DMX_Channels größer als 255 ist, muss ldi temp4, DMX_Channels nen Fehler ergeben, weil temp4 ja nur Zahlen von 0 bis 255 enthalten kann (8 Bit)...
Ja, sorry habe es vorher nicht erwaehnt, aber ich habe erstmal 24 DMX kanaele eingestellt, weil ich die Patch Tabelle ja noch nicht auf 512 erweitert habe. Werde ich natürlich demnaechst auch machen. Im moment laeufts halt noch mit 24. Lieber klein Anfangen wie Du auch schon vor kurzem über Sachen zerkleinern u.s.w. geschrieben hast. Dieser Vorschlag von Dir passt jetzt natürlich nicht so richtig rein Vielleicht ist es sogar besser wenn mann sofort 512 einstellt und erst die Patch Tabelle macht, statt da dann spaeter temp4 wieder zu aendern oder erstmal stundenlang suchen wo es nun hakt Siehst du, ich lern wirklich was von dir :)) Ich möchte damit eigentlich nur sagen, dass Logik hier und da eine sehr grosse Rolle spielt. Die Logik, um vom Baecker 2 stck. semmel zu besorgen, klappt schon von alleine im Alltag. Für die Mikrokontroller Geschichte aber, braucht man halt einfach vieeel mehr Logik. Genau das glaube ich, ist der grösster Fehler was viele machen, die vor haben eigene codes zu Basteln. Die jetzt mal auch vielleicht was kleines Programmieren wollen und noch nicht mit der ganzen Logik umgehen können, weil halt entweder keine Erfahrung oder Lehre, oder einfach zu dumm dafür ;). Ohne Plan einfach alles reinschreiben und dann wieder alles schön löschen
Na ja, nun zum code. Ich werde jetzt dazu nicht viel schreiben da ja eh die comments existieren, kann manschön vom code lesen was ich da alles gemacht habe. Bin jetzt nur noch gespannt ob ich nach so vielen infos von dir immer noch fehler habe oder nicht. Aber von der ordnung im code her finde ich mich als ein gehorsamer Schüler gegenüber seinen Lehrer. (wenigestens dass ;))
Code
Alles anzeigen; ================================================================================================ ; ; TLC5947 8Bit -> 12Bit Routine V0.001/Ultra Alpha ; Leddie's Prüfungsarbeit, die unter Aufsicht von Pesi geschrieben wird. ;) ; ; ================================================================================================ ; benutzte Register : temp0-3, ZL, ZH, YL, YH werden gesichert ; gem. benutzte Register : keine ; reservierte Register : keine ;. ; Definitionen (zum Kopieren): ; ;.equ DMX_Channels= 24; Anzahl zu empfangender Kanäle (max 512) ;.equ DMX_Buffer= 0x0060; RAM-Bereich für 8Bit Daten Verarbeitung ;.equ ARR_TLC_Out= 0x0060; RAM-Puffer für Ausgabedaten ; Change_8Bit_to_12Bit: push temp0 ; Register sichern (ausleihen) push temp1 push temp2 push temp3 push ZH push ZL push YH push YL Bit_Loop: ldi YL, low(DMX_Buffer) ; Y auf DMX-Buffer ldi YH, high(DMX_Buffer) ldi ZL, low(Arr_TLC_Out) ; Z auf Ausgabepuffer ldi ZH, high(Arr_TLC_Out) ;Start Kopf der Schleife cpiYL, low(DMX_Buffer+DMX_Channels); Die LOW Schublade vergleichen, ob dies jetzt endlich den Wert vom angegebenen DMX_Channels erreicht hat. breq Bit_Ende; Endlich! Abgang. cpiYH, high(DMX_Buffer+DMX_Channels); Hier geht es um die HIGH Schublade. breq Bit_Ende; Juhu! Nix wie weg hier! ;Ende Kopf der Schleife ld temp0, Y+ clr temp1 rol temp0 rol temp1 rol temp0 rol temp1 rol temp0 rol temp1 rol temp0 rol temp1 st Z+, temp0 ; Low-Byte speichern st Z+, temp1 ; High-Byte speichern ;Start Fuß der Schleife rjmp Bit_Loop; Leider noch nicht ganz, bitte oben nochmal probieren. Bit_Ende:; Da sind wir also endlich! ;Ende Fuß der Schleife pop temp0 ; Register wieder herstellen, Die brauchen wir spaeter naemlich. pop temp1 pop temp2 pop temp3 pop ZH pop ZL pop YH pop YL ret; Das wars dann wohl - Und tschüss! ; ================================================================================================
EDIT: Hab nur mein Text da oben bissl korrigiert weil viele Schreibfehler u.s.w. Bzw immer noch;)
-
Ganz kurz noch:
Eine Stelle wo ich mir immer noch nicht so sicher bin:
Obwohl du eigentlich den platz von der
angegeben hast, habe ich die "Bit_Loop", über die beiden Pointer:Codeldi YL, low(DMX_Buffer) ; Y auf DMX-Buffer ldi YH, high(DMX_Buffer) ldi ZL, low(Arr_TLC_Out) ; Z auf Ausgabepuffer ldi ZH, high(Arr_TLC_Out)
reingeschrieben, weil ich gedacht habe, warum soll der Pointer jetzt zweimal in die schublade nachschauen. Guckt er doch oben schon reinEDIT:
Deshalb habe ich in der Schleife nicht schon wieder den Code hier Wiederholt... -
passt fast...
nur, das Label "Bit_Loop:" muss nach diesen 4 Zeilen mit "ldi" hin - so wie jetzt, setzt Du den Zeiger ja immer wieder auf Anfang, d.h. Du schaust ständig immer wieder in der ersten Schublade nach, da kannst Du ja nie fertig werden...
Und die Pop-Befehle müssen in der umgekehrten Reihenfolge kommen wie die Push-Befehle - warum, dazu mal bei Wikipedia o.ä. gucken, was ein "Stack"/"Stapel" in einem µC/Prozessor eigentlich ist....
EDIT: Ach, sorry, der Vergleich mit high und low ist auch falsch - das kannst Du Dir so vorstellen, wie zwei Stellen einer Zahl - also z.B. Du vergleichst "xy" mit "27" - gleich ist es dann, wenn x = 2 *und* y = 7
im Moment beendest Du ja die Schleife, wenn auch nur *eine* Bedingung erfüllt ist - also, in diesem Beispiel, Du zählst xy hoch, und vergleichst die letzte Stelle ("y") zuerst - wenn Du also 7 erreicht hast, ist die Bedingung y = 7 erfüllt, und die Schleife also schon bei 7 beendet, obwohl sie erst bei 27 aufhören sollte...
also musst Du das "andersrum" machen - so lange x *nicht* 2 ist *und/oder* y *nicht* 7, geht es weiter mit der Schleife... brauchst also den Befehl "brne", und wie das genau geht, kannst Du jetzt mal als Denksportaufgabe machen....
in obigem Beispiel so, wenn x nicht 2 ist, und/oder y nicht 7, dann kann ja xy nicht 27 sein... d.h. an einer Stelle (egal ob low oder high), wenn das Byte nicht gleich ist, kann auch die ganze 16-Bit-Zahl nicht gleich sein...
-
Ok.
reicht es dann also wenn ich einfach sowas machen würde?
CodeBit_Loop: cpi YL, low(DMX_Buffer+DMX_Channels) ; Low Wert vergleichen, ob dies den Wert vom angegebenen DMX_Channels erreicht hat. brne Bit_Ende ; cpi YH, high(DMX_Buffer+DMX_Channels) ; High Wert vergleichen. brne Bit_Ende
oder müsste ich sowas in der Art machen?
CodeBit_Loop: cpi YL, low(DMX_Buffer+DMX_Channels) ; Low Wert vergleichen, ob dies den Wert vom angegebenen DMX_Channels erreicht hat. brne Bit_Loop ; cpi YH, high(DMX_Buffer+DMX_Channels) ; High Wert vergleichen. brne Bit_Loop rjmp Bit_Ende