Forum: Mikrocontroller und Digitale Elektronik Dmx senden mit einem 89C52 (8052)


von Andre T. (andret)


Lesenswert?

Hallo Forum,

ich weiß ja, dass das Thema mit den "alten" 89c52 uC´s und Dmx schon 
sehr oft hier behandelt wurde, ich komme aber trotzdem nicht weiter.
Nachdem ich nun DMX mit dem 8052 empfangen konnte, will ich nun wissen 
wie man DMX Daten mit dem 8052 sendet. Ich habe dazu dem Henning seine 
Seite, sowie diverse andere Seiten plus dieses Forum durchforstet. Zu 
beginn, ich kann nicht gut Assembler programmieren, deshalb suche ich 
eine Lösung in C, ich habe versucht das Skript von hier: 
http://www.hoelscher-hi.de/hendrik/light/ressources.htm anzupassen.

Heraus kam dieser C Code:
1
#include <REG52.h>
2
3
enum {BREAK, STARTB, DATA};
4
volatile unsigned char gCurDmxCh;
5
volatile unsigned char gDmxState;
6
volatile unsigned char DmxField[6];            //array of DMX vals
7
volatile unsigned int CurDmxCh;
8
9
//Delay - Warte x mal 10 uSec
10
void delay(const unsigned int ms) {
11
  unsigned int x;
12
  for(x = 0; x<ms ; x++) {
13
      TMOD=0x01;     //Timer Mode 1 =   16-bit timer
14
      TL0=0xFF;     //203
15
      TH0=0xF3;     //250
16
      TR0=1; // starte timer1
17
      while(TF0==0); // Warte bis fertig
18
      TR0=0; // stop timer
19
      TF0=0; // reset timer flag
20
  }
21
}
22
23
void dmxSender (void) interrupt 4 {
24
    
25
  unsigned char DmxState = gDmxState;
26
27
if (DmxState == BREAK)
28
        {
29
          SBUF = 0;    //sende break  
30
          while(!TI);     
31
          gDmxState= STARTB;
32
          delay(8);      //88us Pause , umsetzung wahrscheinlich Quatsch
33
    
34
        }
35
else if (DmxState == STARTB)
36
        {  
37
          SBUF = 0;     //sende startbyte
38
          while(!TI);  
39
          gDmxState= DATA;
40
          gCurDmxCh= 0;
41
       }
42
else
43
       {
44
        delay(1); ////interbyte gap
45
        CurDmxCh = gCurDmxCh;
46
        SBUF = DmxField[CurDmxCh++];                        //sende data
47
        while(!TI);
48
        if (CurDmxCh == sizeof(DmxField)) gDmxState= BREAK; //wieder zurück zu break
49
        else gCurDmxCh= CurDmxCh;
50
       }
51
52
}
53
54
int x = 0;
55
56
void main(void) {
57
58
      PCON = 0x00;                                 // 250 Baud bei 16MHz
59
      SCON = 0x88;                                 // 10001000                                              
60
      IE |= 0x90;                                  // Interrupt freigeben 
61
      TI = 1;                                      // TxD setzen
62
      gDmxState= BREAK;                            //start with break
63
64
    DmxField[0] = 88;
65
      DmxField[1] = 200;
66
      DmxField[2] = 50;
67
      DmxField[3] = 90;
68
      DmxField[4] = 127;
69
      DmxField[5] = 30;
70
     
71
    
72
    while(1) {
73
    for (  x = 0 ; x<6 ; x++) {
74
      DmxField[x] = ~DmxField[x] ;
75
    }
76
      }
77
78
}

Der 8052 ist angeschlossen an einen Differentialbus TI SN75176A, Pin 3.1 
zu SN75176A Pin 4 (D) , Pin 3+2 (DE/RE) auf Masse, VCC an 5V, Gnd auf 
Masse, und 6+7 an DMX stecker gehen. (Die müssten stimmen da ich mit der 
selbern Verbindung auch senden kann, vorausgesetzt natürlich Pin 1 auf 
Port 3.0 am uC)

Es kommt nur nix dabei raus... Ich wäre für jede Art von Hilfestellung 
so dankbar, denn ich hänge da einfach völlig in der Luft, mangels 
Kenntnisse. Ich würde aber sehr gerne dazulernen.

Bin für jeden Tipp, wo Fehler in meinem Aufbau / Code sind offen.

Viele Grüße Andre

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

Delay in Interrupts, ganz schlecht.

Interrupts benutzen, wie wärs dann mit freigeben?

Du verwendest nur int, sind wirklich überall 16 Bit nötig?

von Andre T. (andret)


Lesenswert?

Hallo peda,

vielen Dank erstmal für die Hilfe.
Ich weiß das uint evtl zu viel RAM nimmt, wobei ja DMX 512 Kanäle haben 
könnte und da käme ich mit einem unsigned char nur bis 255, oder habe 
ich da schon was falsch verstanden? Denn mein "Gerät" könnte ja auch auf 
dem Kanal 500 hängen. Ich habe das Array DmxField auf uchar geändert 
sowie die "States", denn hier können nur Werte bis 255 kommen, das ist 
die einzige Optimierung die mir hier ins Auge gefallen ist.

Delay in Interrupts ist sehr schlecht, das weiß ich leider auch, aber 
ich habe noch keine andere Lösung dafür, mangels Fachwissen. Ich würde 
den delay auch lieber anderst machen, nur weiß ich ehrlich gesagt nich 
nicht wie. Theoretisch schon, nur kann ich es nicht umsetzen. Ich bin 
schon froh das es im Keil-Simulator tatsächlich 88ms und 10ms bei 16.0 
Mhz waren, denn die sind für das DMX Protokol wichtig.

Was mich nun stark interessiert ist der Quote "Interrupts wieder 
freigeben", ich möchte ja das Signal immer senden, also ständig. Falls 
du das auf die Zeile
1
void dmxSender (void) interrupt 4
 beziehst, dass ist eigentlich gewollt. Ich will das Signal, wenn es mal 
beim "SLAVE" ankommt, immer senden und verändern können.

Ich bin für mehr Tipps wirklich dankbar. Auch was die 88ms + 10ms 
Verzögerung betrifft, ich würde das gerne anderst lösen.

Gruß Andre

: Bearbeitet durch User
von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

DMX beschreibt als Standard, das du mindestens 24 Kanäle senden musst, 
bis dann eben zu max. 512. 6 Kanäle sind also zu wenig.
Andre Thomas schrieb:
> Ich bin
> schon froh das es im Keil-Simulator tatsächlich 88ms und 10ms bei 16.0
> Mhz waren, denn die sind für das DMX Protokol wichtig.

Alle meine Unterlagen sagen, das der Break eine Länge von min. 88µs hat, 
nicht 88ms. Kann aber auch 100µs werden:
https://en.wikipedia.org/wiki/File:Annotated_trace_of_DMX-512_signal.png
http://www.theater-technisch-lab.nl/dmxdu.htm

Soweit ich die DMX Sender von Henning verfolgt habe, sendet er den Break 
mit einer niedrigeren Baudrate (sendet die 0) und stellt dann wieder auf 
250kBaud vor dem ersten Nutzbyte (Kanal 0). Dann brauchst du nämlich gar 
keine Delays im Interrupt, sondern wartest einfach in der Hauptschleife, 
bis der Sendebuffer leer ist, bzw. der Interrupt wieder zuschlägt, weil 
das Breakbyte weg ist.
Jetzt müsste man mal rechnen, wie hoch die Baudrate für den Break sein 
muss...
88µs/9bit (Denn das Stopbit geht auf High und markiert das Ende von 
'Break') sind 9,7µs - rechnerisch eine Baudrate von 102 kBit/s. 
Praktisch wäre es zudem, wenn die Länge des Stopbit gleich die 12µs hat, 
die das Ende von Break markieren. Also ist eine Baudrate von 83kBits/s 
gar nicht so unpassend.

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

@ Andre Thomas (andret)

>Nachdem ich nun DMX mit dem 8052 empfangen konnte, will ich nun wissen
>wie man DMX Daten mit dem 8052 sendet.

Das ist fast noch einfacher als Empfangen.

>beginn, ich kann nicht gut Assembler programmieren, deshalb suche ich
>eine Lösung in C,

Beitrag "Re: CTC-Modus beim ATmega32 ?1us mit 10MHz?"

ISt für AVR, aber kann man leicht anpassen.

von Peter D. (peda)


Lesenswert?

Andre Thomas schrieb:
> Was mich nun stark interessiert ist der Quote "Interrupts wieder
> freigeben"

Steht nirgends, sondern überhaupt erstmal freigeben.
Stichwort IE-Register.

Andre Thomas schrieb:
> wobei ja DMX 512 Kanäle haben
> könnte und da käme ich mit einem unsigned char nur bis 255, oder habe
> ich da schon was falsch verstanden?

Nimm int nur wenn nötig.


Ein Break sendet man am einfachsten, indem man P3.1. für die gewünschte 
Zeit auf Low setzt.

Gibt es im Web wirklich kein DMX mit 8051 Beispiel?

von Andre T. (andret)


Lesenswert?

Peter Dannegger schrieb:
> Gibt es im Web wirklich kein DMX mit 8051 Beispiel?

Ich habe nur 2 Beispiele in ASM gefunden, alle anderen Fragen oder 
Threats oder Tutorials sind entweder offline, ungeklärt oder für andere 
Prozessoren. Ich habe das vom Henning genommen, da dies das einzig 
komplette C script ist, welches auch läuft (jedoch nur auf AVR).

Matthias Sch. schrieb:
> Alle meine Unterlagen sagen, das der Break eine Länge von min. 88µs hat,
> nicht 88ms. Kann aber auch 100µs werden:

Oh mein Gott, ich verspreche besseren Code zu schreiben, denn das ist 
ein großer Patzer... Du hast vollkommen recht, und eigentlich wollte ich 
auch es genau so machen. Ich habe es dann vor lauter Timer ausrechnen 
auf 1ms berechnet statt auf 1uS.

Falk Brunner schrieb:
> Beitrag "Re: CTC-Modus beim ATmega32 ?1us mit 10MHz?"
>
> ISt für AVR, aber kann man leicht anpassen.

Ich schaue es mir gerne mal an, aber meine Konvertierung von Hennings 
script ging ja mangels Verständnis für Timer,Interrupts und SFR´s ja 
auch schon in die Hose.

Trotzdem @ alle, vielen Dank für eure Unterstützung.

Gruß Andre

von Andre T. (andret)


Lesenswert?

Peter Dannegger schrieb:
> Steht nirgends, sondern überhaupt erstmal freigeben.
> Stichwort IE-Register.

Danke, das müsste dann geOdert werden mit IE |= 0x90;
um den Interrupt auf die serielle Schnittstelle zu legen, das heisst 
dann immer wenn gesendet wurde wird ein Interrupt ausgelöst, was im 
Moment nicht passiert.

Und als ich dann noch TI=1 eingesetzt habe, hat mir der Debugger sogar 
mal was auf UART ausgegeben.

So lächerlich es klingen mag, aber das nun die RX-Led flackert ist für 
mich schon was. Wieder etwas näher am Ziel. Vielen Dank !

: Bearbeitet durch User
von Andre T. (andret)


Lesenswert?

Matthias Sch. schrieb:
> 88µs/9bit (Denn das Stopbit geht auf High und markiert das Ende von
> 'Break') sind 9,7µs - rechnerisch eine Baudrate von 102 kBit/s.
> Praktisch wäre es zudem, wenn die Länge des Stopbit gleich die 12µs hat,
> die das Ende von Break markieren. Also ist eine Baudrate von 83kBits/s
> gar nicht so unpassend.

Danke für den Tipp: 83kBits/s wäre dann ja einfach bei 16.0Mhz

von Serial Port Mode 2 auf Mode 3 umschalten und Timer 1 / Mode 2 
(Smod=1)
TH1 auf 0xFF (255) macht 83.33333

das versuche ich mal.

: Bearbeitet durch User
von Andre T. (andret)


Lesenswert?

Es geht, wunderbar. Ich möchte mich für die Hilfe bei allen im Threat 
bedanken.

Richtig geholfen haben mir die Tipps von Peter Danneger und den Wink mit 
dem Zaunpfahl (ms != uS) von Matthias.

Ein Problem habe ich noch,
es geht nur wenn man kurz das DMX-Kabel aus dem Slave zieht, ob das nun 
am Slave liegt oder nicht, weiß ich erst wenn ich einen anderen 
anschliesse. Falls jemand da noch eine Idee hätte, wäre ich dankbar.


Viele Grüße von einem glücklichen Dilletant !

: Bearbeitet durch User
von Andre T. (andret)


Lesenswert?

Leider zu früh gefreut, ich habe nun noch einen anderes DMX Gerät hier. 
Das erste ging zwar, wenn man den Stecker kurz zog. Und dann lief es auf 
den ersten Werten die durchgegeben wurden. Das zweite reagiert gar 
nicht.

Hier ist mal wieder mein Code, vielleicht mag sich den jemand mal 
ansehen und mir nochmal einen Tipp geben:
1
#include <REG52.h>
2
3
enum {BREAK, STARTB, DATA};
4
volatile unsigned int gCurDmxCh;
5
volatile unsigned int gDmxState;
6
volatile unsigned int DmxField[24];            //array of DMX vals
7
volatile unsigned int CurDmxCh;
8
9
10
//Delay - Warte x Millisekunden
11
void delay_10uS(const unsigned int ms) {
12
  unsigned int x;
13
  for(x = 0; x<ms ; x++) {
14
          //SCON = 0x50;
15
      TMOD=0x01;     //Timer Mode 1 =   16-bit timer
16
      TL0=0xF4;     //
17
      TH0=0xFF;     //
18
      TR0=1; // starte timer0
19
      while(TF0==0); // Warte bis fertig
20
      TR0=0; // stop timer
21
      TF0=0; // reset timer flag
22
  }
23
} 
24
25
void dmxSender (void) interrupt 4 {
26
    
27
  unsigned int DmxState = gDmxState;
28
29
if (DmxState == BREAK)
30
        {
31
      TMOD=0x20;     //Timer Mode 2
32
      TH1=0xFF;     //
33
      TR1=1; // starte timer1
34
          SCON = 0xC8;                                    // 11001000    
35
         PCON = PCON | 128;  // SMOD = 1
36
        SBUF = 0;    //sende break
37
      EA = 1;  
38
      while(!TI);  
39
        gDmxState= STARTB;
40
      TR1=0; // stop timer
41
      TF1=0; // reset timer flag
42
      
43
        }
44
else if (DmxState == STARTB)
45
        {  
46
      PCON = PCON & 0x01;    // SMOD = 0
47
      SCON = 0x88; 
48
      SBUF = 0;     //sende startbyte
49
      while(!TI);  
50
          gDmxState= DATA;
51
          gCurDmxCh= 0;
52
       }
53
else
54
       {
55
        delay_10uS(3); ////interbyte gap
56
      SCON = 0x88; 
57
    CurDmxCh = gCurDmxCh;
58
        SBUF = DmxField[CurDmxCh++];                        //sende data
59
    while(!TI);
60
        if (CurDmxCh == sizeof(DmxField)) gDmxState= BREAK; //wieder zurück zu break
61
      else gCurDmxCh= CurDmxCh;
62
       }
63
64
}
65
66
int x = 0;
67
68
void main(void) {
69
    
70
     PCON = 0x00;                                    // 250 Baud bei 16MHz
71
      SCON = 0x88;                                    // 10001000                                              
72
    gDmxState= BREAK;                            //start with break
73
74
    for(x=0;x<24;x++) {
75
      DmxField[x]  = 0;
76
    }
77
     
78
      x=0;
79
    
80
    
81
    IE |= 0x90;                                     // Enable RX I
82
      TI = 1;
83
    while(1) {
84
85
        if (x<15000) {
86
          DmxField[0] = 100;
87
          DmxField[1] = 200;
88
          DmxField[2] = 0;
89
          DmxField[3] = 90;
90
          DmxField[4] = 127;
91
          DmxField[5] = 0;
92
        x++;     
93
     }  else{
94
        DmxField[0] = 230;
95
          DmxField[1] = 80;
96
          DmxField[2] = 6;
97
         x++;
98
        }
99
100
        if(x==30000) {x=0;}
101
        
102
103
      }
104
105
}

Ich habe im Debugger mal die Zeiten angeschaut.
in der Funktion dmxSender beim Break wo es 88uS sein sollten geht es von 
0.00037050 sec - 0.00050400 sec , also 134uS oder ? Das wäre dann 
zuviel, was müsste ich den ändern.

und bei dem 10uS interbyte gap sinds 0.000826 - 00091575 nahezu 9 uS ...

Ich denke daran wird es wohl liegen, könnte mir dabei jemand helfen.

Hrüße Andre

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Andre Thomas schrieb:
> delay_10uS(3); ////interbyte gap

Normalerweise reicht bei der Übertragung ein 2tes Stopbit, ein extra gap 
ist nicht nötig. Du machst dir auch das Leben unnötig schwer, indem du 
Timer 1 sowohl für die Baudrate als auch für die Delay Schleife 
einsetzt. Wenn du einen 8052 hast, nimm besser Timer2 für die Baudrate, 
der läuft dann völlig unabhängig und du musst nicht ständig rumschalten.
Der serielle Port läuft dann in Mode 3 und als 9. Bit sendest du eine 1. 
Mit dem folgenden Stopbit hast du effektiv die nötige Lücke.
Wenn der Break zu lang ist, erhöhe die Baudrate im Breakmode, bis es 
stimmt. Vermutlich wäre es sinnvoll, den Baudratengenerator schon nach 
dem letzten Datenbyte umzustellen, damit das Timing besser wird, ist 
aber nicht entscheidend.
Behalte nur im Hinterkopf, das der MC mit 1/12 der Quarzfrequenz intern 
läuft, er ist also (wenn es kein WARP 51 ist) nicht der schnellste.

: Bearbeitet durch User
von Andre T. (andret)


Lesenswert?

Hallo Matthias,
danke für die Unterstützung, ich habe wie empfohlen das ganze mal mit 
dem Timer 2 versucht. ich habe an allen DMX-Slaves leider immernoch das 
Problem das ich erst das Kabel ziehen muss, dann übernimmt der Slave 
einmalig den Wert. Also fehlt meiner Meinung nach noch irgendeine 
Unterbrechung. Evtl das Mark After Break - das sollte glaube ich das 9te 
bit (TB8=1) sein.

Ich habe bei ein paar anderen Scripten ( AVR und Arduinos ) gesehen, 
dass die teilweise den Port abschalten nach dem Break. Vielleicht hilft 
das ? Nur wie schalte ich "kurz" mal den Port aus/an ?

Hier der verbesserte Quelltext mit Timer 2  60 Werten  alles chars + 
weniger gewurschtel hoffentlich.
1
/*
2
Send DMX 
3
4
___  Break 88uS        _________  ST ____________________
5
   \_________________/Mark>8us \__/\/\/\/\/\/\/\/2Stop\______ ...
6
                   Bits01234567    >8 usec
7
    88.000baud/8bit | 9th-bit  |250.000baud start|ch1-ch512
8
*/
9
10
#include <REG52.h>
11
12
enum {BREAK, STARTB, DATA};
13
volatile unsigned char gCurDmxCh;
14
volatile unsigned char gDmxState;
15
volatile unsigned char DmxField[60];    //array of DMX vals
16
volatile unsigned char CurDmxCh;
17
char x = 0;
18
19
void dmxStart (void) {
20
     //Initialsiere Timer 2
21
    SCON    = 0xC8  ;                // 10001000         
22
    TCLK    = 1    ;     
23
    RCAP2H  = 255  ;
24
    RCAP2L  = 254  ;
25
    ET2     = 1    ;                // Enable Timer 2 Interrupts 
26
    TR2     = 1    ;                // Start Timer 2 Running 
27
      EA     = 1    ;                 // Global Interrupt Enable
28
    IE     |= 0x90  ;                // Enable RX I
29
}
30
31
void dmxSender (void) interrupt 4 {
32
  char i = 0;
33
  unsigned char DmxState = gDmxState;
34
  TI=0;
35
36
if (DmxState == BREAK)
37
        {
38
        RCAP2H = 255    ;
39
        RCAP2L = 250    ;   // 250 = 83.333 b  249 = 71428baud
40
          SBUF   = 0x00    ;    // sende break + 9bit = 1
41
          while(!TI)    ;  
42
           gDmxState= STARTB  ;
43
    }
44
else if (DmxState == STARTB)
45
        {  
46
          RCAP2H = 255    ;
47
        RCAP2L = 254    ;
48
          SBUF    = 0x00    ;     // sende startbyte
49
          while(!TI)    ;  
50
          gDmxState = DATA  ;
51
              gCurDmxCh = 0    ;
52
       }
53
else
54
       {  //INTERBYTE GAP ???
55
         //for(i=0;i<1;i++)  ;
56
      //
57
         CurDmxCh = gCurDmxCh;
58
        SBUF = DmxField[CurDmxCh++];                        //sende data
59
        while(!TI);
60
        if (CurDmxCh == sizeof(DmxField)) gDmxState= BREAK; //wieder zurück zu break
61
        else gCurDmxCh= CurDmxCh;
62
       }
63
64
}
65
66
void main(void) {
67
    
68
    for(x=0;x<60;x++) {
69
        DmxField[x]  = 0;
70
    }
71
   
72
    DmxField[0] = 0;
73
      DmxField[1] = 0;
74
      DmxField[2] = 254;
75
      DmxField[3] = 0;
76
      DmxField[4] = 0;
77
      DmxField[5] = 0;
78
 
79
80
    dmxStart();
81
82
    gDmxState= BREAK;                            //start with break
83
       TI  =  1;
84
      for(;;) { 
85
86
      }
87
88
}

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Andre Thomas schrieb:
> void dmxStart (void) {
>      //Initialsiere Timer 2
>     SCON    = 0xC8  ;                // 10001000
>     TCLK    = 1    ;
>     RCAP2H  = 255  ;
>     RCAP2L  = 254  ;
>     ET2     = 1    ;                // Enable Timer 2 Interrupts
>     TR2     = 1    ;                // Start Timer 2 Running
>       EA     = 1    ;                 // Global Interrupt Enable
>     IE     |= 0x90  ;                // Enable RX I
> }

Irgendwie geht da noch was durcheinander. Den Timer 2 Interrupt brauchst 
du nicht, aber den Sendeinterrupt des UART.
Es müsste also heissen:
1
 SCON = (1<<SM0) | (1<<SM1) | (1<<TB8) | (1<<TI) // = 0xCA
2
 IE = (1<<EA) | (1<<ES)  // global and serial irq enable  = 0x90
Der Vektor für den seriellen Interrupt liegt auf 0x0023, wie man das bei 
deinem Kompiler einstellt, weiss ich aber nicht.

: Bearbeitet durch User
von Andre T. (andret)


Lesenswert?

Matthias Sch. schrieb:
> Der Vektor für den seriellen Interrupt liegt auf 0x0023, wie man das bei
> deinem Kompiler einstellt, weiss ich aber nicht.


Ich denke das habe ich schon so, bei der Zeile:
1
void dmxSender (void) interrupt 4 {...

interrupt 4 ^= Serial Port 0023h

Vielen Dank, Matthias, für den weiteren Tipp mit dem Sendeinterrupt des 
UART, ich probiere es später am mC selber aus. Wo ich noch ein bisschen 
mit hadere, und das könnte evtl auch der Fehler sein, sind die Timings 
im Debugger. Habe nun noch einen anderen Debugger genommen und da kommt 
so ziemlich das gleich raus. Mit den Änderungen am SendeIR von oben wäre 
dann 133.5 uS für das BREAK inkl. 9.bit bei 88.000b, dann dauert es 
6.5uS bis zum gesendeten Startbit (9.bit), und dann von Ende Startbit 
bis Ende Ch1. 8,5 uS - wobei der eigentliche Sendevorgang 4,35uS dauert.

Leider steht mir kein Oszilloskop zur Verfügung, damit könnte ich 
schneller sehen was los ist. Ich probiere heute abend einfach weiter es 
zum laufen zu bekommen.

Grüße André

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

du musst dich beim Break senden noch entscheiden, ob du auch ein 8 bit 
mit 2 Stopbit senden willst, oder ein 8bit mit einem Stopbit. Wenn du 
weiterhin die 9 bit aussenden willst mit einem Stopbit, musst du nochmal 
neu rechnen. Mark soll 12µs sein. Würdest du jetzt mit 1/12µs = 83kBaud 
sendest, bleibt für Break  9 * 12µs (du sendest ja weiterhin 9 bit, 
wobei diesmal das 9te Bit 0 sein sollte) = 108µs.
Entscheidend ist im Moment die Frage, wann das UART sein IRQ setzt. 
Müsste im Datenblatt stehen.
Wenn es schon während des Stopbit zündet, wären die 108µs vermutlich 
akzeptabel, anderenfalls käme das Startbyte der Daten zu spät. Es ist 
also evtl. sinnvoll, zwar bei den 83kbit/s zu bleiben (um MARK gut 
einzuhalten), aber das Datenformat auf 8bit, 1 Stopbit zu setzen.
Das sind dann 8*12µs = 96µs Breakzeit.

von Andre T. (andret)


Lesenswert?

So jetzt bin ichs wieder. Ich habe rumprobiert und versucht alle Tipps 
zu berücksichtigen. Rausgekommen ist im Moment ein recht übersichtliches 
C-Skript was aber leider immernoch den Dienst versagt.

Es funktioniert mit beipspielsweise 10 Werten die von diversen 
DMX-Slaves auch richtig eingelesen werden, jedoch nur wenn man am ersten 
Slave das Kabel zieht und wieder einsteckt.

Und es ist schon eine Weile so. Das lässt mich vermuten das irgendwo so 
etwas wie ein "Unterbrecher" fehlt. Ich bin aber ziemlich am Ende meines 
Verständnisses, sowie besitze ich leider kein Oszilloskop um mal 
anzuschaun was da rauskommt.

An der Schaltung an sich ist ein SN75176bp mit einem 
Stützkondensator(104) zum DMX Kabel. Ich habe noch einen 100 Ohm 
Widerstand zw Pin1 und Masse sowie die anderen Pins des 75176 mit 
Sicherheit richtig angeschlossen.

Hier ist das aktuelle Skript, vielleicht kann mir nochmal jemand helfen.
Vielen Dank an dieser Stelle an Matthias der mir geduldig gute Tipps 
gab.
Ich habe auf jedenfall bis jetzt eine Menge über DMX, Timer und Serielle 
Schnittstellen gelernt - von daher was es nicht ganz sinnlos sich damit 
zu beschäftigen.
1
#include <REG52.h>
2
3
enum {BREAK, STARTB, DATA};
4
volatile unsigned char gCurDmxCh  ;
5
volatile unsigned char gDmxState  ;  //
6
volatile unsigned char dmxField[24]  ;  //Array aus DMX Werten
7
volatile unsigned char CurDmxCh    ;
8
char x = 0;
9
10
//Initialsiere DMX BAUDRATE mit Timer 2
11
void dmxStart (void) {
12
      SCON        =  0xCA      ;  // 11001010 
13
    RCAP2H     =   255       ;  // Setze Timer 2 auf 83.333b   
14
    TCLK        =  1        ;   // Timer2 Transmit Clock -> Serielle Schnittstelle 
15
      TR2        =  1       ;  // Start Timer 2 Running 
16
      IE         |=  0x90     ;  // Enable RX I
17
}
18
19
//dmxSender = Senderoutine auf Seriellen Interrupt (4) gelegt ^= 0x0023
20
void dmxSender (void) interrupt 4 {
21
    unsigned char DmxState = gDmxState;
22
  
23
  if (DmxState == BREAK)
24
          {// BREAK + Mark after Break 
25
          SCON        =  0x40      ;  // 10000000 = 40 Mode 2 8bit // 11000000 = C0 Mode 3 9-bit -> letztes = 0
26
      RCAP2L    =   250        ;   // Setze Timer 2 auf 83.333b 250 
27
      SBUF      =   0x00       ;   // sende break 
28
        gDmxState  =  STARTB    ;  // weiter mit Startbit
29
         }
30
  else if (DmxState == STARTB)
31
          {// STARTB(yte) 8n2
32
        SCON        =  0xC8      ;  // 11001000    
33
          RCAP2L     =  254       ;  // Setze Timer 2 wieder auf 250.000b  
34
        SBUF       =  0x00       ;   // Sende Startbyte 0x00     
35
          gDmxState  =  DATA      ;  // Weiter mit DATA
36
      gCurDmxCh  =  0        ;  // Setzte Kanalzähler zurück  
37
      }
38
  else
39
      {//Sende DMX Werte 1-512 
40
      CurDmxCh  = gCurDmxCh    ;  // DMX Kanäle senden 
41
          SBUF = dmxField[CurDmxCh++] ;  // sende Daten
42
          TI      =  0      ;  // TxD Interrupt
43
      if (CurDmxCh == sizeof(dmxField)){
44
45
      gDmxState = BREAK; // wieder zurück zu BREAK
46
      } 
47
          else{
48
      gCurDmxCh = CurDmxCh  ;  // andernfalls weitermachen mit nächstem Kanal
49
      }
50
      }
51
}
52
53
//Main Function
54
void main(void) {
55
  
56
  for(x=0;x<sizeof(dmxField);x++) {
57
        dmxField[x]  = 0        ;  // Fülle Array mit Werten
58
    }
59
60
  //Werte zum Testen
61
  dmxField[0] = 0;
62
  dmxField[1] = 0;
63
  dmxField[2] = 30;
64
  dmxField[3] = 250;
65
  dmxField[4] = 0;
66
  dmxField[5] = 0;
67
68
  gDmxState= BREAK;                 // Starte mit BREAK
69
  dmxStart();
70
      
71
  // Superloop
72
  for(;;){
73
  // Silence !
74
  }  
75
76
}

von Andre T. (andret)


Lesenswert?

Andre Thomas schrieb:
> An der Schaltung an sich ist ein SN75176bp mit einem
> Stützkondensator(104) zum DMX Kabel

Natürlich meinte ich zw VCC und GRD, nicht am DMX Kabel...

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

24 DMX Kanäle sind zwar das erlaubte Minimum, es kann aber sein, das 
dann die Wiederholrate für die Slaves zu schnell wird, wenn sie sich 
nicht genau an die Specs halten. Sende doch einfach mal ein paar mehr 
Kanäle, 100 oder so, damit sinkt die Framerate ab.

von Falk B. (falk)


Lesenswert?

@ Matthias Sch. (Firma: Matzetronics) (mschoeldgen)

>nicht genau an die Specs halten. Sende doch einfach mal ein paar mehr
>Kanäle, 100 oder so, damit sinkt die Framerate ab.

Wer sagt denn, dass die DMX-Sender immer mit maximal möglicher 
Wiederholrate arbeiten? Und mit 1 kHz schon gar nicht, auch wenn es 
prinzipiell möglich ist.

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Falk Brunner schrieb:
> Wer sagt denn, dass die DMX-Sender immer mit maximal möglicher
> Wiederholrate arbeiten?

Der aus diesem Thread tut es jedenfalls. Deswegen ja mein Vorschlag. Man 
kann jetzt die Zeit zwischen zwei Datenbytes strecken, mehr Bytes senden 
oder bis zu einer gewissen Grenze den Break verlängern, allerdings sind 
mehr als 100-120µs für Break anscheinend nicht üblich.

: Bearbeitet durch User
von Andre T. (andret)


Lesenswert?

Endlich ! Habe den Fehler,

das c-script geht einwandfrei.

Ich weiß nicht warum, aber an meinem hardwareseitigen Versuchsaufbau war 
ein Massefehler. Als ich alles neu gesteckt habe, ging auf einmal alles 
wie gewollt. Also das og script kann als DMX-sender eingesetzt werden. 
Es können/sollten noch ein paar Änderungen mit einfliessen, ich habe 
eine interrupt unabhängige version geschrieben die ca. 10x / sec einen 
DMX-Frame sendet, die ging auch.

Grüsse Andre.

PS: Matthias, vielen Dank fürs helfen ! Ohne Deine Tipps wäre ich nicht 
soweit gekommen.

von Andre T. (andret)


Lesenswert?

Hier noch abschliessend das Script mit welchem ich testweise einen 
Farbwechsel machte, Ch 2=rot, 3=gr, 4=bl.


1
#include <REG52.h>
2
3
volatile unsigned char dmxField[50]  ;  //Array aus DMX Werten
4
char x = 0;
5
6
7
void delay (unsigned char n) {   //49ms
8
      unsigned int c = 0;
9
      for(c=0;c<n;c++) {
10
        TMOD =0x01;
11
        TL0 = 0xCB;
12
        TH0 = 0x00;
13
        TR0 = 1;
14
        while(!TF0);
15
        TR0 = 0;
16
        TF0 = 0;      
17
      }
18
}
19
20
21
void delay_33us (unsigned char n) {   
22
      unsigned int c = 0;
23
      for(c=0;c<n;c++) {
24
        TMOD =0x01;
25
        TL0 = 0xFD;
26
        TH0 = 0xFF;
27
        TR0 = 1;
28
        while(!TF0);
29
        TR0 = 0;
30
        TF0 = 0;      
31
      }
32
}
33
 
34
//Initialsiere DMX BAUDRATE mit Timer 2
35
void dmxStart (void) {
36
      SCON        =  0xCA      ;  // 11001010 
37
    RCAP2H     =   255       ;  // Setze Timer 2 Hbit   
38
    TCLK        =  1        ;   // Timer2 Transmit Clock -> Serielle Schnittstelle 
39
      TR2        =  1       ;  // Starte Timer 2 
40
      IE         |=  0x90     ;  // Enable 
41
42
     
43
}
44
45
//dmxSender = Senderoutine
46
void dmxSender (void)  {
47
   unsigned int i = 0;
48
     
49
    // BREAK + Mark after Break 
50
51
          SCON        =  0xC0      ;  // 10000000 = 40 Mode 2 8bit // 11000000 = C0 Mode 3 9-bit -> letztes = 0
52
      RCAP2L    =   250        ;   // Setze Timer 2 auf 83.333b 250 
53
      SBUF      =   0x00       ;   // sende break 
54
      while(!TI);
55
         delay_33us(4);
56
57
  // STARTB(yte) 8n2
58
        SCON        =  0xC8      ;  // 11001000    
59
          RCAP2L     =  254       ;  // Setze Timer 2 wieder auf 250.000b  
60
        SBUF       =  0x00       ;   // Sende Startbyte 0x00     
61
      while(!TI);
62
      delay_33us(1);
63
64
  //Sende Kanäle
65
      
66
      for(i=0;i<sizeof(dmxField);i++) {
67
          SBUF = dmxField[i] ;  // sende Daten
68
          TI=0;
69
      while(!TI);
70
      delay_33us(1);
71
      }
72
73
    
74
}
75
   
76
//Main Function
77
void main(void) {
78
  
79
  for(x=0;x<sizeof(dmxField);x++) {
80
        dmxField[x]  = 0        ;  // Fülle Array mit Werten
81
    }
82
83
84
85
86
    dmxStart();
87
      
88
  // Superloop
89
  for(;;){
90
   dmxField[3] = 0;
91
  dmxField[1] = 254;
92
  dmxSender ();  
93
  delay   (40);
94
  
95
  dmxField[1] = 0;
96
  dmxField[2] = 254;
97
  
98
  dmxSender ();  
99
  delay   (40);
100
  
101
  dmxField[2] = 0;
102
  dmxField[3] = 254;
103
  dmxSender ();  
104
  delay   (40);
105
106
107
  // Silence !
108
  }  
109
110
}

: Bearbeitet durch User
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.