Hallo zusammen, Da mein DMX-Krempel auf mehr Interesse als erwartet stieß, habe ich vor kurzem auf RDM (s.u.) aufgerüstet. Leider schluckt der Code beinahe 2kB, was ein Viertel vom Programmspeicher eines mega8 oder mega8515 ist. Da etliche Geräte sogar nur den TINY2313 verwenden, ist dies wohl letztlich einfach zu viel... Ich habe bereits ziemlich in die Trickkiste gegriffen und am Code gefeilt - hoffe aber, dass Ihr noch ein paar Einfälle habt. (Als angehender Maschi habe ich programmiertechnisch ja auch nicht die Weisheit mit Löffeln gefressen...) Die ISRs sollten neben der geringen Codegröße natürlich auch möglichst flott sein. Bei den anderen Funktionen spielt Geschwindigkeit keine derart wesentliche Rolle. Erläuterungen zu dem Code: http://www.hoelscher-hi.de/hendrik/light/ressources/AN017.pdf Der Code selbst: http://www.hoelscher-hi.de/hendrik/light/ressources/rdm_in.zip (Ansich ist nur die lib_rdm_in.c und der Header interessant...) Tausend Dank im Voraus! Hendrik Hier noch etwas Hintergrund, was aber nicht unmittelbar wichtig ist: RDM (remote device management) ist eine Erweiterung des DMX512 Lichtsteuerprotokolls und durch die ANSI E1.20 spezifiziert. (Ich darf sie aber wohl aus Copyright-Gründen nicht hochladen...) Durch RDM können angeschlossene Geräte konfiguriert werden und auch Status- und Fehlerinformationen können ausgelesen werden. RDM ist paketbasiert: Der Controller sendet ein Paket via RS485 an einen Responder und der antwortet. Der Ablauf geschieht folgendermaßen: Der Controller ermittelt die angeschlossenen Geräte über eine Discovery-Prozedur, bei der an alle Geräte eine Nachricht mit einem MAC-Adressbereich (Untergrenze, Obergrenze) gebroadcastet wird. Liegt die MAC eines Empfängers im Bereich antwortet er mit seiner encodierten Mac. (Da mehrere Empfänger antworten, bekommt der Controller also einen üblen Datensalat...) Nun engt er den Adressbereich immer weiter ein, bis nur noch ein Empfänger antwortet, der dann gemuted wird. (Treesearch) Sind alle Empfänger identifiziert, kann der Controller ein Paket gezielt verschicken: RDM start code (0xCC) RDM sub start code (0x01) Paketlänge Zieladresse Quelladresse Typ des Paketes (Discovery, Ändern eines Parameters, Auslesen eines Parameters) ID des Parameters Länge der Daten des Parameters Datenfeld additive Checksumme Es gilt dabei MSB first. Leute, die mir bei der Optimierung helfen, werde ich gern mit als Autor aufführen (wenn gewünscht).
Viel Kleinkram... Um zu beurteilen, wo Potential liegt, ist eigentlich ein Blick in den erzeugten Assembler notwendig. Weil die Software auf unterschiedlichen µC und auch verschiedenen Compiler-Versionen laufen soll (auch ältere gcc-Versionen sowie kommende), die Teilweise anderes Verhalten zeigen werden, sind allgemeint Tipps sehr schwer zu geben. Immerhin will man ja in C proggen, weil das halbwegs unabhängig ist von der Hardware, anstatt C als portierbaren Assembler zu verwenden. Ok, genug Prosa. -- Winz-Funktionen wie swapInt fühlen sich sehr warscheinlich wohler als static inline, bzw. der Aufrufer hat ne deutlich bequemere RegisterlAllokierung. -- isHigher ist als isLower doppelt vorhanden. Eine Vergleichs-Func, die je nach Gemengelage -1, 0 oder 1 liefert, ist evtl. praktikabler. -- getMsgP gibt's als memcpy_P, was wehlt ist nur Rumgecaste -- RxState ist volatile und Du machst 1000 Zugriffe, zB in einer ISR. Das ist sau teuer! --> Auf lokaler, temporärer Kopie arbeiten! Dito für den Mini-Zoo an anderen Globals -- dazu gehören evtl. auch Komposit-Elemente von Globals. Siehe auch Beitrag "Bitte um Hilfe bei Code-Optimierung" -- Indirekter Zugriff in C wird nicht unbedingt zu (möglicherweise platzsparenderem als direktem Zugriff) indirektem Zugriff in Assembler. Erkläuterungen dazu gibt's ebenfalls hinter obigem Link. Zum Code: Die ISRs sollen flott sein? delay-Zeug hat nix in einer ISR zu suchen, zumindest nicht bei deaktivierten IRQs und wenns schnell sein soll. In einer ISR IRQs zuzulassen und damit eine Prioritäts-Zwischenschicht einzuziehen ist möglich (hab ich in fast all meinen AVR-Anwendungen), das will aber genau überlegt sein. Es soll ja vermutlich noch ne Anwendung laufen, die Dein Zeug benutzt. Wenn die härtere Echtzeit-Anforderungen hat, kann sie diese wegen der erhöhten IRQ-Latenz uU nicht erfüllen.
Zunächst einmal Vielen Dank, Johann! Die Tipps der static inline Deklaration und memcpy_P werde ich später mal einbauen. Die Vergleichsfunktionen könnten etwas frickelig werden. Ich hatte dies schon mal versucht und wahrscheinlich einen Denkfehler bei den Abbruchbedingungen... Bei der Verwendung von RxState hast Du Dich wahrscheinlich verguckt: Bei jedem Durchlauf lese ich ein Mal den Wert ein und schreibe ein Mal einen Wert zurück. Der Compiler lädt dazu direkt den Wert in ein gerade freies Register und kopiert es direkt ins RAM. Der Compiler arbeitet da, meine ich, optimal... In allen anderen Fällen (echte Mehrfachzugriffe) hast Du vollkommen Recht und ich lege lokale Kopieen an. (Falls nicht, ist das ein Fehler von mir.) Zum indirekten Zugriff: Ich hatte zuvor mit Pointern gearbeitet, der GCC scheint den jetzigen Zugriff aber lieber zu mögen... (Es sei denn, ich habe Denkfehler gemacht und dadurch Overhead verzapft.) Deinen Link habe ich vor meinem Thread schon durchgearbeitet - er war eigentlich der Anlass, so einen Thread auch einmal zu versuchen... Zur TxISR: Ich bin eigentlich über Delays (besonders in ISRs) genauso verärgert wie Du. Der Hintergrund hier ist: Ich muss den Bus noch 20us IDLE halten, damit der Controller keine EMV als weitere Daten interpretiert. Warte ich kürzer, bekommt der Controller evtl Probleme - warte ich deutlich länger, verpasse ich evtl. ein folgendes Paket, falls der Controller zu schnell sendet. Ich kann also nicht einfach sofort wieder auf Empfang schalten. Ich kann auch nicht einen Counter für das Hauptprogramm starten, da ich dort keinen wirklichen Einfluss auf das Zeitverhalten habe. Ich möchte eigentlich auch keinen Timer opfern. Was bleibt mir dann noch?? (Als Entschuldigung sei gesagt, dass das Delay nur ein Mal pro zu beantwortendem Paket stört - also fast nie...) Nochmals Danke! Hendrik
Das ursprüngliche Projekt brauchte 1712 Bytes. Die Deklaration der Winz-Funktionen als static inline führte zu 1630 Bytes. Die Nutzung von memcpy_P anstatt meine eigenen Funktion führte zu 1628 Bytes. Das ist jetzt noch kein gewaltiger Sprung - aber doch ganz nett :-) Viele Grüße, Hendrik
Eine einzelne Vergleichsfunktion mit Übergabe des Leseoffsets vergrößerte den Code auf 1634 Bytes.
Mach einen byte Vergleich z.B. compare(char*a,char*b) { // sollte man ueberpruefen wegen flash pointer. uint16 res; char i=6; // compare 6 bytes while(i--) { res=*a++; res-=*b++; if (!res) return continue; } if (res<0) return 2; return res>0; }
Der Compare-Vergleich nach chris führt zu 1628 Bytes (identische Größe). (Entweder gibt es automatisches Inlining oder die Funktion ist aufwendiger als mein Konstrukt - müsste ich mir noch einmal im Disassembler anschauen.) Ich befürchte langsam, dass ich an genau der Stelle wohl nicht Hunderte von Bytes einsparen kann... Oder kennt jemand eine Alternative zu den delays in den ISRs für konstante inter byte gaps und den Senderichtungswechsel? Vielen Dank für Eure Mühe bislang, Hendrik
Hier der Disassembler-Code von "compare":
1 | @0000009C: compare |
2 | 161: uint8_t compare(uint8_t*a,const uint8_t*b) { // sollte man ueberpruefen wegen flash pointer. |
3 | +0000009C: 01DC MOVW R26,R24 Copy register pair |
4 | +0000009D: 01FB MOVW R30,R22 Copy register pair |
5 | +0000009E: E096 LDI R25,0x06 Load immediate |
6 | +0000009F: C00B RJMP PC+0x000C Relative jump |
7 | 164: res=*a++; |
8 | +000000A0: 918C LD R24,X Load indirect |
9 | +000000A1: 2F28 MOV R18,R24 Copy register |
10 | +000000A2: E030 LDI R19,0x00 Load immediate |
11 | 165: res-=*b++; |
12 | +000000A3: 8180 LDD R24,Z+0 Load indirect with displacement |
13 | +000000A4: 1B28 SUB R18,R24 Subtract without carry |
14 | +000000A5: 0931 SBC R19,R1 Subtract with carry |
15 | 166: if (res !=0) break; |
16 | +000000A6: 1521 CP R18,R1 Compare |
17 | +000000A7: 0531 CPC R19,R1 Compare with carry |
18 | +000000A8: F421 BRNE PC+0x05 Branch if not equal |
19 | +000000A9: 9611 ADIW R26,0x01 Add immediate to word |
20 | +000000AA: 9631 ADIW R30,0x01 Add immediate to word |
21 | 163: while(i--) { |
22 | +000000AB: 5091 SUBI R25,0x01 Subtract immediate |
23 | +000000AC: F798 BRCC PC-0x0C Branch if carry cleared |
24 | +000000AD: 01C9 MOVW R24,R18 Copy register pair |
25 | +000000AE: 2B23 OR R18,R19 Logical OR |
26 | +000000AF: F011 BREQ PC+0x03 Branch if equal |
27 | +000000B0: E081 LDI R24,0x01 Load immediate |
28 | +000000B1: E090 LDI R25,0x00 Load immediate |
29 | 170: } |
30 | +000000B2: 9508 RET Subroutine return |
Hier der Aufruf:
1 | if((compare(&(rdm->Data[0]), DevID) != 1) && (compare(&(rdm->Data[6]), DevID) != 2)) |
2 | +00000229: E660 LDI R22,0x60 Load immediate |
3 | +0000022A: E070 LDI R23,0x00 Load immediate |
4 | +0000022B: E889 LDI R24,0x89 Load immediate |
5 | +0000022C: E090 LDI R25,0x00 Load immediate |
6 | +0000022D: DE6E RCALL PC-0x0191 Relative call subroutine |
7 | +0000022E: 3081 CPI R24,0x01 Compare with immediate |
8 | +0000022F: F409 BRNE PC+0x02 Branch if not equal |
9 | +00000230: C082 RJMP PC+0x0083 Relative jump |
10 | +00000231: E660 LDI R22,0x60 Load immediate |
11 | +00000232: E070 LDI R23,0x00 Load immediate |
12 | +00000233: E88F LDI R24,0x8F Load immediate |
13 | +00000234: E090 LDI R25,0x00 Load immediate |
14 | +00000235: DE66 RCALL PC-0x0199 Relative call subroutine |
15 | +00000236: 3082 CPI R24,0x02 Compare with immediate |
16 | +00000237: F409 BRNE PC+0x02 Branch if not equal |
17 | +00000238: C07A RJMP PC+0x007B Relative jump |
========================================================= mein Geraffel:
1 | 176: rdm = (struct RDM_Packet *)&RdmField; |
2 | +00000216: E7A1 LDI R26,0x71 Load immediate |
3 | +00000217: E0B0 LDI R27,0x00 Load immediate |
4 | +00000218: E6E0 LDI R30,0x60 Load immediate |
5 | +00000219: E0F0 LDI R31,0x00 Load immediate |
6 | 180: buf= rdm->Data[i]; |
7 | +0000021A: 01ED MOVW R28,R26 Copy register pair |
8 | +0000021B: 8D98 LDD R25,Y+24 Load indirect with displacement |
9 | 181: if (buf < DevID[i]) return (0); |
10 | +0000021C: 8180 LDD R24,Z+0 Load indirect with displacement |
11 | +0000021D: 1798 CP R25,R24 Compare |
12 | +0000021E: F048 BRCS PC+0x0A Branch if carry set |
13 | 182: else if (buf > DevID[i]) return (1); |
14 | +0000021F: 1789 CP R24,R25 Compare |
15 | +00000220: F408 BRCC PC+0x02 Branch if carry cleared |
16 | +00000221: C091 RJMP PC+0x0092 Relative jump |
17 | +00000222: 9611 ADIW R26,0x01 Add immediate to word |
18 | +00000223: 9631 ADIW R30,0x01 Add immediate to word |
19 | 178: for (i=0; i<6; i++) |
20 | +00000224: E080 LDI R24,0x00 Load immediate |
21 | +00000225: 36E6 CPI R30,0x66 Compare with immediate |
22 | +00000226: 07F8 CPC R31,R24 Compare with carry |
23 | +00000227: F791 BRNE PC-0x0D Branch if not equal |
24 | 192: rdm = (struct RDM_Packet *)&RdmField; |
25 | +00000228: E7E1 LDI R30,0x71 Load immediate |
26 | +00000229: E0F0 LDI R31,0x00 Load immediate |
27 | 196: buf= rdm->Data[i +6]; |
28 | +0000022A: 8D96 LDD R25,Z+30 Load indirect with displacement |
29 | 197: if (buf > DevID[i]) return (0); |
30 | +0000022B: 01DB MOVW R26,R22 Copy register pair |
31 | +0000022C: 918C LD R24,X Load indirect |
32 | +0000022D: 1789 CP R24,R25 Compare |
33 | +0000022E: F050 BRCS PC+0x0B Branch if carry set |
34 | 198: else if (buf < DevID[i]) return (1); |
35 | +0000022F: 1798 CP R25,R24 Compare |
36 | +00000230: F408 BRCC PC+0x02 Branch if carry cleared |
37 | +00000231: C081 RJMP PC+0x0082 Relative jump |
38 | +00000232: 9631 ADIW R30,0x01 Add immediate to word |
39 | +00000233: 5F6F SUBI R22,0xFF Subtract immediate |
40 | +00000234: 4F7F SBCI R23,0xFF Subtract immediate with carry |
41 | 194: for (i=0; i<6; i++) |
42 | +00000235: E0B0 LDI R27,0x00 Load immediate |
43 | +00000236: 3666 CPI R22,0x66 Compare with immediate |
44 | +00000237: 077B CPC R23,R27 Compare with carry |
45 | +00000238: F789 BRNE PC-0x0E Branch if not equal |
Bin ich richtig in der Annahme, daß dein Code ge-Inlined wird, und compare nicht. Bei den Switch Anweisungen lassen sich ca 100-200 bytes einsparen, aber das ist zu wenig.
@chris: so ist es. Deinen Code brauche ich zwei Mal; bei mir jeden Vergleich ein Mal. Deshalb wird compare wohl nicht ge-inlined... Der Ersatz von switch (Sprungtabelle) durch einzelne Bedingungen machte die RxISR ein Stück langsamer und der switch in der Auswertung wird in einzelne Bedingungen aufgelöst.
@Henne Habe gerade bemerkt, daß du compare nur 2x aufrufst, wobei es 3x sein sollte, ich glaube forUs fehlt dir. Weiters könnte man die Konstante in compare laden, so spart man sich ein paar bytes. Auch eventuell mitgeben, auf was getestet werden muß, muß man sich anschauen, ob das was bringt, aber du hast recht, es bringt weniger als ich vermutete. Achso, habe einen fehler drin, res muß signed und nicht unsigned sein.
@chris: In "forUs" teste ich gleichzeitig auf einen broadcast - Da würde ich nicht viel raus holen können... Mit der Konstanten hast Du natürlich Recht - verbessert aber nichts, wie ich gerade gesehen habe. Viele Grüße, Hendrik
--swapInt ist überflüssig, weil nur gegen Compilezeit-Konstanten verglichen wird. Man kann den swap also vom Compiler auf den Konstanten machen lassen. -- isLower und isHigher werden nur 1x verwendet. Wenn die auserhalb nicht gebraucht werden, reicht ne Deklaration als static zum Inlinen. -- RxCount_16 in der einen ISR fühlt sich wohler als lokale Auto.
Moin Johann, swapInt wertet die Bedeutung eines eintreffenden Paketes (also die PID) aus. Ich kenne die einzelnen Pakete, die später bei meinem Gerät eintreffen, nicht zur Compilezeit... Man könnte allerdings die PID-Definitionen im Header umdrehen. Allerdings würde ich dann gegen die Norm verstoßen, weil ich für mich die PIDs geswappt haben möchte. Das dürfte dann bei PID-Erweiterungen von Dritten für ordentlich Verwirrung sorgen, oder? In RxCount_16 werden im DMX-Betrieb die eintreffenden Bytes bis zum Erreichen der Startadresse runtergezählt. Im RDM-Betrieb wird darin die additive Checksumme gebildet. Sie muss also im RAM gehalten werden. Bislang dachte ich, es mache keinen Unterschied, ob ich solche Variablen global in der Datei angebe oder innerhalb der Funktion als statisch markiere. Da werde ich noch einmal schauen... Vielen Dank, Hendrik
Der Unterschied zwischen den Deklarationen ist, dass ich bei lokal und static nicht außerhalb der Funktion auf die Variable zugreifen kann. Ich meine, dies wäre im Sinne der Data Encapsulation - ich seh da allerdings keinen Vorteil für mich. Bei größeren Dateien sieht das bestimmt anders aus...
uint8_t isLower(void) { struct RDM_Packet *rdm; rdm = (struct RDM_Packet *)&RdmField; uint8_t i ,buf; for (i=0; i<6; i++) { buf= rdm->Data[i]; // if (buf < DevID[i]) return (0); // else if (buf > DevID[i]) return (1); } return (0); } uint8_t isHigher(void) { struct RDM_Packet *rdm; rdm = (struct RDM_Packet *)&RdmField; uint8_t i ,buf; for (i=0; i<6; i++) { buf= rdm->Data[i +6]; // if (buf > DevID[i]) return (0); // else if (buf < DevID[i]) return (1); } return (0); Das ist mir aufgefallen. Könnte das nicht so gehen? MW }
@Michael: Stimmt - die andere Abbruchbedingung spart nur Zeit, auf die es hier aber eigentlich nicht ankommt...
@Michael,@Henne: Ich kenne jetzt nicht die gesamte Anwendung aber durch das Auskommentieren verändert sich das Verhalten der Funktion und unter Umständen das Ergebnis. DevID[] = {0,1,1} buf[] = {1,0,1} isHiger()_original liefert 0 isHigher()_modifiziert liefert 1 Falls ich mich jetzt nicht gerade vertue...
Hallo, einiges scheinen Manchestercodiert zu sein, andere nicht. ev. schick mir die RDM pdf auf scoxo3511514@yahoo.com, wobei die o und der 5 wegzulassen sind, vor dem domain. Habe ein paar optimierungideen bez. Messages, aber ohne Hintergrundinfo, ist es ein heikles thema.
Henne wrote: > swapInt wertet die Bedeutung eines eintreffenden Paketes (also die PID) > aus. Ich kenne die einzelnen Pakete, die später bei meinem Gerät > eintreffen, nicht zur Compilezeit... Brauchst auch nicht. Anstatt
1 | switch (SWAP(var)) |
2 | case A: |
3 | case B: |
geht doch
1 | switch (var) |
2 | case SWAP(A): |
3 | case SWAP(B): |
wobei in der zweiten Skizze SWAP zur Compilezeit auswertbar sein muss und dann auch zur CZ ausgewertet wird. Das spart also Laufzeit und Platz. > Man könnte allerdings die PID-Definitionen im Header umdrehen. Könnte man muss man aber nicht wie eben skizziert. > In RxCount_16 werden im DMX-Betrieb die eintreffenden Bytes bis zum > Erreichen der Startadresse runtergezählt. Im RDM-Betrieb wird darin die > additive Checksumme gebildet. Sie muss also im RAM gehalten werden. > Bislang dachte ich, es mache keinen Unterschied, ob ich solche Variablen > global in der Datei angebe oder innerhalb der Funktion als statisch > markiere. Da werde ich noch einmal schauen... Das macht auch keinen Unterschied. Zumindest, was Resourcen-Verbrauch angeht. Gemeint ist's so wie im Anhang in der einen ISR: Man operiert auf einer lokalen auto Variablen (nicht auf einem Static!). Gelesen und Geschrieben wird nur 1x, am Anfang und am Ende. Das typeof() ist Faulheit, würd ich in der Endversion nicht verwenden. Bei vielen Zugriffen auf das gleiche Ding spart man so Zugriffe, die der Compiler nicht immer wegoptimiert (oder nicht wegoptimieren darf wenn das Ding volatile ist). Um abzuwägen, ob sich das lohnt, schaut man das Listfile durch. "Nester" mit den 4-Byte-Zugriffen ins RAM sind beim Drüberscrollen schnell zu erkennen, weil sie aus den normalen 2-Byte-Zugriffen vorstechen. Wird in einer Funktion oft auf die selbe Adresse gegriffen, ist's ein potentieller Kandidat. Kosten für die lokale Variante sind die Regs, in denen man das Datum halten muss. In einer ISR muss das Reg zudem immer gesichert werden im Gegensatz zu einer normalen Funktion, wo nicht alle Regs gesichert werden mussen, und ein Umbau sich eigentlich immer lohnt, wenn noch call-used-Regs frei sind und die Funktion ein Blatt im Call-Baum ist. Johann
@chris: Es gibt nirgendwo eine Manchestercodierung... @Gast: Das Original ist richtig. Ich brauche die Abbruchbedingung, da ich andernfalls die Vergleichsergebnisse der höher wertigen Bytes nicht berücksichtige. @Johann: Die Idee mit der Swapumstellung ist vielversprechend. Mal schaun... Die "Nester" räucher ich eigentlich immer wie du es beschrieben hast aus - von daher wird es mich wundern, wenn ich da viel hohle. Was mir derzeit keine Ruhe lässt sind die delays: Mit 5us könnte ich noch leben - aber die 20 sind wirklich etwas viel. Wäre ein Sperren des TxC-IRQs, danach ein sei und dann die Schleife vertretbar? - halbwegs definierte Umschaltzeit des Ports - keine ungewollten rekursiven Aufrufe der TxISR - andere Interrupts freigegeben
Henne schrieb: > Was mir derzeit keine Ruhe lässt sind die delays: > Mit 5us könnte ich noch leben - aber die 20 sind wirklich etwas viel. > Wäre ein Sperren des TxC-IRQs, danach ein sei und dann die Schleife > vertretbar? Seh ich kein Problem. Vor Verlassen der ISR muss der IRQ aber wieder aktiviert werden, davor ein CLI. Auch möglich, ist am Ende der ISR abzudesten, ob das IRQ-Flag schon wieder gesetzt ist. Gegebenenfalls wird das Flag gelöscht in der ISR geschleift. Weil bei gesetztem IRQ-Flag die ISR eh nach Verlassen wieder angesprungen würde, spart diese Technik Zeit, nämlich einen ISR-Epilog und -Prolog. In Deinem Fall braucht man diese Technik aber kaum. CLI und IRQ-reaktivieren sollte vollauf genügen. Bei der Methode steigt die ISR-Tiefe um Eins, was bei der ISR, um die es geht, aber kaum was ausmacht. Die braucht ja nur ne handvoll Bytes aufm Stack, das sollte auch bei 128 Bytes RAM unkritisch sein.
Hallo Henne, ich greif das Thema nach langer Zeit mal wieder auf. Bin auch grad dabei eine RDM Implementation für einen Mega 162 controller zu schreiben. Deswegen habe ich mir deinen Code mal angesehen. Hab das auch soweit umgeschrieben bekommen das ich es auf dem Mega 162 ans laufen bekomme. DMX empfang läuft. RDM geht leider noch nicht. Kann es sein das du die Checksumme für die "discover unique branch" respond Message nicht richtig berechnet wird. Laut Ansi 1.20 steht da drin "Checksum is the sum of the previous 12 EUID slots. The checksum is an unsigned additive sum of the 8-bit fields into a 16-bit response value." In deinem code adierst du aber den preamble auch mit hinzu. und jede EUID auch nur einmal. Hast du das ganze schon mal mit einer echten RDM Anwendung getestet? Ich habe im Moment das Enttec RDM USB Pro Interface zum probieren hier. Mein controller Empfängt die RDM Pakete und sendet auch was zurück. Aber der Enttec RDM Controller findet das Device nicht. Und beim DMX Empfang würde ich auch noch nen kleinen schönheitsfehler ausbügeln. In Zeile 427 würde ich >= schreiben weil es sonst seihen kann das wenn du den letzten DMX Kanal abgreifen kannst der Status nicht gewechselt wird. case STARTADR_DMX: DmxField[RxCount_8++]= DmxByte; //get channel if (RxCount_8 > sizeof(DmxField)) { Flags |= (1<<EVAL_DMX); //reception finished RxState = IDLE; //all ch received -> wait for break } break;
Mit dem ersten Punkt liegst Du falsch. (Rechne nach ;-)) Beim zweiten liegst Du richtig. VG, Hendrik
Wenn Du die Lib mit dem Enttec-Controller zum Laufen gebracht hast, würden mich die Änderungen interessieren. Die Discovery und die Adressvergabe hatten bei mir damals funktioniert - das Auslesen der Device-Eigenschaften wollte jedoch nicht.
Also ich schau mir das mit der Checksumme noch mal an. Habs gestern dann auch noch soweit geschafft das mein RDM Device schon mal erkannt wird. Die ID wird im Enntec Tool angezeigt. Und dann meldet er die meiste Zeit "Selected Device did not respond to the DEVICE_INFO, No Information to display, Refer to the Manual for more info" Aber ich habs auch schon gehapt das er alle Parameter richtig ausgelesen hat. Ist aber mehr sporadisch. Das blöde ist ich kann nicht gleichzeitig RDM senden und mit dem Interface den Datenstrom mitsniffen. Aber irgendwie wird das wohl auch noch zum laufen zu bringen sein. :-) Wenn ich was rausfinde sag ich auf jeden Fall bescheid. Muss den Code dann auch noch etwas aufräumen. Wollte das eigentlich auch dann noch so machen das man das ganze noch etwas leichter für andere Prozessoren der AVR Reihe anpassen kann.
@Christian: Jetzt sind wir mit dem Enttec-Teil genau auf demselben Stand. Ich hatte deswegen auch schon bei rdmprotocol.org gepostet, wo mir empfohlen wurde, die Daten mit einem Oszi anzuschauen und die Pakete nachzurechnen. (Was anderes ist ja auch schlecht möglich...) Selbstverständlich habe ich auch als erstes die Response-Pakete nochmal durchgesehen (Oszi bringt mir dabei nicht viel): Es wird gesendet, was ich nach meiner Interpretation der E1.20 glaube, senden zu müssen. Abgesehen davon, sollte sonst nie irgendein Paket korrekt behandelt werden... Evtl. ist es sinnvoll, mit einem zweiten Rechner und meinem "OpenRDM"-Dongle den Bus mal abzuhorchen und vielleicht doch mal mit einem Oszi nachprüfen, wie sich der Bus während der Port-Turn-Arounds verhält. (Eigentlich müsste er über einen Pullup im ENTTEC-IF auf IDLE - also HI - gehalten werden.) Ich bin auf jeden Fall für alles dankbar - ich kann zwar mit meinen eigenen Systemen RDM fahren, möchte aber natürlich zum offiziellen Standard kompatibel sein. VG, Hendrik
zur CS: Präambel wird nicht berücksichtigt. Additive 16bit cs über: UID[0] |0xAA UID[0] |0x55 UID[1] |0xAA UID[1] |0x55 UID[2] |0xAA UID[2] |0x55 UID[3] |0xAA UID[3] |0x55 UID[4] |0xAA UID[4] |0x55 UID[5] |0xAA UID[5] |0x55 Dies entspricht mMn. 6*0xFF +UID[0] +UID[1] +UID[2] +UID[3] +UID[4] +UID[5] VG, Hendrik
Ich hab auch noch mal deine Paketreihenfolge mit dem Protokoll verglichen. Sieht eigentlich gut aus was du da rauschickst. Hab auch schon nen bißchen mit dem AVR simulator rumprobiert. Aber ich denke auch mal das es nen Timing Problem oder sowas ist. Weil er mir halt auch gestern zeitweise auch schon mal sämtliche Daten im enttec Programm dargestellt hat. Aber ich werd mir jetzt wohl erstmal noch dein OpenRDM-Dongel bauen damit ich das vernüftig Sniffen kann. Ich kann mit dem Enntec Interface ja leider nicht gleichzeitg RDM senden und die Pakete Analysieren. Aber werd dann mal mit deiner Software RDM Daten schicken und schauen was der Enttec RDM Sniffer dazu sagt. Ozzi hab ich auch da. Muss ich mal auf den Pin triggern der die Sendeumschaltung macht und gleichzeitig die Daten anschauen. Vielleicht krieg ich dann auch schon was raus. Meistens ist es ja immer nur ne Kleinigkeit die einem Kopfschmerzen bereitet. Gruß Christian
@Christian: Ich glaube, ich habe noch ein fertiges Modul bei mir liegen. Wenn Du mir Deine Adresse mailst, kann ich es Dir für die nächsten Monate gegen Erstattung der Versandkosten leihen. Meine eMail-Adresse findest Du auf meiner Seite. VG, Hendrik
Ich habe das Modul gerade gefunden. Es ist dies hier: http://www.nodna.com/xtc/product_info.php?info=p850_Devantech-USB-zu-RS485-Interface-Modul.html VG, Hendrik
@ Hendrik Danke für das Angebot und fürs rausuchen des Moduls. Aber ich denk mal ich werd mir das heute mal Anhand deines Schaltplans zusammen basteln. Der Materialwert liegt ja doch etwas unter 33 Euro. Und so hab ich noch nen bißchen was zu löten. Hab mir schon die passenden ICs besorgt. Von daher muss ich mir nur noch die Platine basteln. Aber Layout hab ich gestern schon gemacht. Hab gestern auch noch mal vorm Ozilloskop gesessen und gemessen. Aber so richtig nen Fehler erkennen konnte ich bisher nocht. Ist halt auch nicht so einfach damit nen Datenstrom zu analysieren. Aber ich denk mal wenn ich den RS485 umsetzer fertig hab sieht man weiter. Hab zwar hier haufenweise normale DMX Interfaces zum Testen. Aber das hilft einem bei RDM leider auch nicht wiklich weiter. (Arbeite für meine Lichtsteurungen unter anderem mit Martin Lightjockey, Ecue und seit neustem Cuelux. Ecue hat zwar angekündigt im laufe des Jahres auch RDM zu unterstüzen. Aber bisher ist mir da noch keine Version bekannt die das unterstüzt.
Meinen Schaltplan würde ich mittlerweile etwas erweitern: In der E1.20 ist in einem Anhang die Bus-Termination dargestellt, so dass er während der Port Turn-Arounds hochohmig auf IDLE hängt. Diese Widerstandsanordnung sollte übernommen werden. Tx und Rx LEDs würde ich auch noch spendieren. Falls es hakeln sollte: mein Angebot steht noch. VG, Hendrik
zum Oszi: Was geschieht in der Zeit zwischen dem Ende der Anfrage und dem Beginn der Antwort? Wenn der Bus da floatet haben wir die Antwort...
So, USB --> RS485 Umsetzer hab ich aufgebaut und fertig am Laufen. Inkl. LEDs und Widerstandskette. Hab dann also den neuen Dongel mit deiner Software zum discovern und Datenabfragen verwendet. Funktioniert soweit auch wunderbar. Dann hab ich das Enttec interface als Sniffer geflasht und die Daten mit der Enntec Sniffer Software mitgelockt. Die gute Nachricht ist, die Daten die übertragen werden sind soweit ok. Also die Pakete passen von den Anzahl und Cheksumme. Aber er meckert bei mir jedes Paket mit nem Timing fehler an. (Sowohl die gesendeeten vom PC wie auch die Response Nachrichten) Jetzt muss ich der Sache mal noch weiter auf den Grund gehen. Die Leitung floatet auf jeden Fall nicht. Haben jetzt immer nen definierten Zustand drauf. Ich werd mal noch nen bißchen experementieren. Sollte wohl in den Griff zu bekommen sein. MfG Christian
Auf der FTDI-Seite wirst Du nicht viel machen können: Der Port Turn-Around erfolgt automatisch. Interbyte Gaps sind nicht möglich. Der Break dauert mind. 1,X ms, da die Frequenz, mit der die USB-Pakete verschickt werden, wohl nicht mehr hergibt. Laut E1.20 dürfte ich aber eigentlich keine Grenzwerte verletzen. Auf der AVR-Seite haben wir freie Hand. Danke schonmal für das bisherige Feedback! Hendrik
So, komme jetzt auch langsam mit den ganzen Features der Enttec Sniffer Software klar. Also auf der FTDI Senderseite macht er folgende Fehlermeldung: RDM Paket: Timing Error Recorded Breakt Time: 2973 usek Allowed Break Time: Controller (176 usek --- 352 usek) Refer: Page 8, Table 3-1, Line 1&2 (RMD Spec) Timestamp(us) Count Event Data 0 - Start Break - 2973 - End Of Break - 5061 1 Data CC 5105 2 Data 01 5149 3 Data 18 5193 4 Data FF 5237 5 Data FF 5281 6 Data FF 5325 7 Data FF 5369 8 Data FF 5413 9 Data FF 5457 10 Data 41 5501 11 Data 64 5545 12 Data FF 5589 13 Data 00 5633 14 Data 00 5677 15 Data 01 5721 16 Data 01 5765 17 Data 01 5809 18 Data 00 5853 19 Data 00 5897 20 Data 00 5941 21 Data 10 5985 22 Data 00 6029 23 Data 03 6074 24 Data 00 6116 25 Data 08 6160 26 Data 99 Wobei die Zeit über nen ganzen Datensatz auf Senderseite zwischen 2000 und 3000 usek liegt. Wenn du sagst der Break liegt da immer bei mindestens 1,xx ms. Dann überschreitet man natürlich immer die Zeit. (Die Angabe aus der Ansi 1.20 stimmen) Aber ist ja auch erstmal nicht so tragisch. Zu Testzwecken reicht einem das natürlich erstmal. Auf AVR Seite habe ich derzeit erstmal die Delayzeiten rausgenommen die du eingepglegt hast. Dann hab ich mal mit der Baudrate für den Break gespielt. (90,9k) Mit deiner Einstellung produziert er folgenden Fehler: RDM Paket: Timing Error Recorded Breakt Time: 110 usek Allowed Break Time: Controller (176 usek --- 352 usek) Refer: Page 10, Table 3-3, Line 1&2 (RMD Spec) Hab das dann mal angepasst auf z.B. (voher UBRR0L = 0x05;) auf UBRR0L = 0x11; Dann meldet er keine Fehler auf AVR Seite im Sniffer: Timestamp(us) Count Event Data 0 - Start Break - 326 - End Of Break - 402 - Start Code CC 452 1 Data 01 501 2 Data 1A 551 3 Data 41 601 4 Data 64 651 5 Data FF 703 6 Data 00 752 7 Data 00 801 8 Data 01 851 9 Data 12 901 10 Data 34 951 11 Data 56 1005 12 Data 78 1054 13 Data 9A 1103 14 Data BC 1151 15 Data A8 1201 16 Data 00 1251 17 Data 00 1301 18 Data 00 1351 19 Data 00 1405 20 Data 11 1453 21 Data 00 1502 22 Data 03 1551 23 Data 02 1601 24 Data 00 1651 25 Data 00 1701 26 Data 05 1755 27 Data B4 Dann hab ich mit der Firmware im AVR mal wieder den RDM Controller gestartet und nen Discovery gemacht. Immer noch Fehler: "Selected Device did not respond to the DEVICE_INFO, No Information to display, Refer to the Manual for more info" Das ist dann allerdings merkwürdig. Wenn der Sniffer sagt das jetzt das Timing im grünen Bereich liegt, sollte es ja eigentlich sauber laufen. Muss ich wohl noch etwas weiter tüffteln.
Erst einmal vielen Dank für das Timing des Response Break! Das werde ich heute anpassen. Mögliche Fehlerquelle bei device info request: Wenn ich nach der Discovery gemuted werde, antworte ich nicht auf eine Device Info Request (oder irgendetwas anderes) Darf ich also zum Zeitpunkt der Anfrage überhaupt antworten oder bin ich gemuted? VG, Hendrik (Es gibt zum Muting die Interpretationen, dass es sich nur auf die Discovery oder auf alles außer Mute/Umute bezieht. Ich schließe mich der letzteren an, um sicherzustellen, dass meine Geräte nie aus Versehen dazwischenquaken...)
Also das Muting bezieht sich meiner Meinung nach nur auf Discovery Messages. Der Trick an der Sache ist ja das ein Device normal nur Antwortet wenn es angesprochen wird. Wenn man aber nicht die IDs der Geräte kennt werden alle Geräte unmuted per Broadcast. Dann spricht man einen bestimmten Adressbereich an und schaut ob jemand Antwortet. Wenn man ein vernüftiges Paket bekommt mit gültiger Checksumme dann hat man auch eine gültige ID. Man spricht also das erkannte Device an und schickt ihm eine Mute Message. Wenn das Device dieser erhält, reagiert es ab sofort nicht mehr auf Discovery Messages und sendet eine Respond das es den Mute Befehl verstanden hat. Jetzt ist dieses Device in dem Adressbereich Still wenn man ihn noch mal anspricht. Sollten jetzt noch andere Devices in dem Bereich seihen werden diese Antworten. Bekommt man bei der Abfrage keine gültige Nachricht zurück weil zwei Geräte gleichzeitig Antworten, muss man den Adressraum einschränken. Solange bis man ein gültiges Paket bekommt. Wenn niemand antwortet geht man davon aus das der Adressbereich in dem man sucht frei ist. Generell muss jedes Device immer Auf eine geziehlte Anfrage mit der passenden ID reagieren. Damit man testen kann ob es noch da ist. Das Muten gibts meiner meinung nach nur damit man halt bei der Erkennung erkannte Geräte ausblenden kann. Aber man möchte natürlich nach der Erkennung der "Seriennummern" anschliessend die Gerätedaten auslesen. Von daher macht es ja keinen Sinn Geräte dann auszuschalten. Generell Antwortet ein RDM Gerät ja nur dann wenn es danach gefragt wird. Somit besteht im Normalbetrieb eigentlich keine Gefahr das zwei Geräte durcheinader funken. (Außer der Hersteller pennt und liefert zwei Geräte mit der gleichen ID aus) Ich werd mir das gleich noch mal anschauen in deinem Code. Aber so wie ich das sehe ist es dann natürlich klar das sich das Device auf die Info Message nicht mehr meldet.
Jau, daran lags, hab grad deine IF Abfrage der Mute Flags bei der void respondMsg(void) rausgenommen. Jetzt funktioniert das wie es soll. Erklärung siehe oben :-) Dann kann ich jetzt mich ja dran machen die anderen RDM Funktionen ans laufen zu bekommen. MfG Christian
So, zu den verbessungen die noch gmacht werden sollten: SOFTWARE_VERSION_LABEL ist laut Norm auch ein Pflichtfeld, sollte also bei dir auch noch eingebaut werden: //*********** Deklaration als Beispiel const unsigned char Software_V_Label_Msg[] = { //Software Version Label msg (ASCII text up to 32 character) 'A','B' }; Dann musste deine Switch Case Funktion noch um nen Punkt erweitern: case SOFTWARE_VERSION_LABEL: getMsgP(Software_V_Label_Msg, sizeof(Software_V_Label_Msg)); rdm->PDLen= sizeof(Software_V_Label_Msg); rdm->PortID= RESPONSE_TYPE_ACK; //Acknolege respondMsg(); break; Und was mir in deinem Code auch noch aufgefallen ist. Es sind einige Variablen sind mitten in den Funktionen deklariert. Normal gehören die Deklarationen an den Anfang der Funktion. Hab das bei mir bereits geändert. Der WinAVR Compiler stört das scheinabr nicht. Aber andere Compiler meckern das schon an.
Ich werde das Muting und Break-Timing ändern. Zu dem SW-Version Label: Ist Pflicht, ich sehe darin keinen Nutzen, es raubt mir Flash (der mir an allen Ecken und Enden fehlt). -> Ich lass es weg. Zu Parametern in Funktionen: Meine WinAVR-Version ist nicht so schlau sich anzugucken, wann ich welche Variable in der Funktion brauche. Dadurch gibt es mehr SRAM-Zugriffe zum Puffern lok. Variablen als notwendig und der Code wird größer. Grundsätzlich hast Du natürlich Recht. Vielen Dank, Hendrik
Ok, ich geize halt normal nichzt so mit den Recourcen. Man kann sich ja aussuchen welchen Controller man nimmt. Und in nen Mega 128 passt schon ne Menge Code. Die kleinen Controller haben meistens auch zu wenig Hardware PWS mit 16 Bit. Da nutzt man dann doch eher das Teil ne Numer größer. Werd das ganze in nen LED Netzteil einbauen. Und da möchte ich dann schon alles was von der Norm vorgegebn wird auch drin zu haben. Dann kann man das Teil auch für drausen vergießen und alels schön übern Bus steuern. Das mit dem SOFTWARE_VERSION_LABEL hat aber schon nen Sinn. Weil es viele Geräte am Markt gibt die im laufe der Zeit neue Versionen bekommen. Und da die sich dann nicht gleich verhalten macht es auch Sinn das ganze zu Kennzeichnen. Damit man dann auch sehen kann wenn man unterschiedliche Geräte im Strang hat. IDENTYFY_DEVICE ist übrigens auch noch ein Pflichtfeld. Wenn du willst kannst du ja zumindest nen Komentar im Quelltext hinterlegen. Damit dann andere Leute das bei Bedarf mit aufnehmen können. Ist ja durch die Struktur sehr schön nen Befehl dazu zu bauen. Ansonsten muss man mal schauen ob sich RDM in der Lichtechnik weiter durchsetzt. Die neuen Robe Scheinwerfer habens zumindest auch mit drin. Ansonsten kommen die anderen großen Hersteller ja auch auf den Trichter ihre Geräte damit auszurüsten. Würd mich freuen wenn ich demnächt keine Dippschalter mehr adressieren muss g
Identify haben meine fertigen Geräte. Ist haber gerätespezifisch - deswegen habe ich es aus meiner GPL-Lib erst einmal rausgelassen. Zu den Kosten: Wenn die FW für eine Funzel ist, von der 10k Stäck/a gefertigt werden, bekommst Du ziemlich klare Kostenvorgaben. Und schon wird die jedes Byte lieb und teuer ;-) zur Durchsetzung: In einigen FWs läuft es versteckt schon mit, wenn der Platz da ist. Ich bin in einigen Mailverteilern zu dem Thema und konnte mal eine halbwegs akteuelle UID-Liste der ESTA sehen: RDM wird kommen.
Identify ist schon Gerätespezifisch, gibt sicherlich auch Geräte wo mans nicht so toll einbauen kann. Bei Lampen ist das ja nicht das Problem... lass es leuchten oder blinken. Also die Geräte die ich bisher gebaut habe gehen normal nie in 10k Stückzahlen. Von daher hat man den Vorteil das man nicht um jeden Cent sparen muss. Wenn man in Deutschland was produziert ist das Material meistens auch eher ein kleiner Faktor. Da versauen dann eher die Personal und Handlingskosten den Schnitt. Genrell kann man aber wenns wiklich mal was in größeren Stückzahlen läuft man bei fast jedem Hersteller auch was am preis drehen. Offtopic an Bei mir ist es halt so, ich toure schon seit 10 jahren als DJ und LJ durch die gegend. Habe Radio und Fernsehtechniker gelernt. Und nach der Ausbildung bei meiner jetzigen Firma in der Produktion angefangen. Dann nebenbei meinen Techniker gemacht. Technikerarbeit war dann ne Metallkernleiterplatte mit 4 stromgeregelten Luxeon 1 LEDs und DMX Eingang drauf. Nach dem Techniker bin ich dann in die Entwicklung gewechselt. Die Firma macht aber normal halt Motoren und Steuerungen für RWA (Rauch- und Wärmeabzug). Wir produzieren aber im Elektronikbereich auch viel OEM als Bestückung. Seit nen paar Jahren bestücken wir halt auch viel Hochleistungsleds für diverse Kunden (Lampenhersteller und Industriekunden). Dadurch kommen dann halt auch mal Anfragen für Kundenspezifische Lösungen. Das LED Netzteil ist halt auch ne Entwicklung für nen Lampenhersteller. Der bekommt das aber nur mit 1-10V Eingang. Da das aber Modular aufgebaut ist hab ich mir jetzt halt mal ne Controllerplatine gebaut mit RS485 Schnittstelle. DMX drauf ist ja kein Thema... Aber wollte mich jetzt halt auch mal mit RDM beschäftigen. Hat man halt schon ne Menge möglichkeiten auch wenns leider im Moment noch nicht so toll unterstüzt wird. Aber ich denk auch das es kommen wird. Weil der mehraufwand an der Hardware halt sehr gering ist. Die Aktuellen Hersteller IDs sind übrigens online veröffentlich: http://www.esta.org/tsp/working_groups/CP/mfctrIDs.php Sind eigentlich alle Namenhaften Hersteller dabei. Da die Registrierung auch nichts kostet steht der Sache auch nichts entgegen. Muss jetzt mal schauen ob ich für mich privat jetzt mal nen Satz Controllerplatine für die Netzteile bauen lasse. Das dumme ist halt das die Firma halt immer nur Zulieferteile herstellt und man leider nie ne komplette Leuchte hat. Muss man sich dann schon selbst was basteln. Bis jetzt hat auch bei uns noch niemand nach RDM gefragt. Aber wird vielleicht dann auch mal kommen das es ein Lampenhersteller haben will. aktuell baue ich grad erstmal an ner Motorsteuerung. Von daher ist nichts mit hell und bunt. Deine Libary werd ich dann auch noch mal ausprobieren. Hab mir spasseshalber auch mal eine von deinen Controllerplatinen bestellt. Dann kann ichs auch mal auf deiner Hardwarebasis den Code für dich testen. Ich selbst baue ja fast nur noch SMD. Obwohl ich auch schon ne DMX Platine mit nem Solid State Relais in bedratet gebaut habe. War auch für nen Kundenprojekt. Die brauchten da einmal 100 Stück von. Da lohnt das dann nicht extra nen Bestückungsautomat für anzuwerfen. Wenn du noch was wissen willst schreib einfach an dj AT turnthetable DOT de Vielleicht kannst mir dann auch mal nen paar Infos geben was du noch so treibst und was du so mit Lichtechnik zu tun hast. Würd mich auch mal intressieren. Und wenn du mal was produzieren lassen willst kann ich da vielleicht auch mal was für dich tun. Ist zwar mehr Aufgabe unseres Vertriebs. Aber die sitzen ja im Büro um die Ecke. Ansonsten hab ich halt ganz gute Kontakte im Bereich Hochleistungs LEDs. Wenn du da Infos brauchst oder Material brauchst kannst dich auch gerne melden. Offtopic aus
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.