DMX-Empfänger 1.1

Nachdem die Grundlagen geklärt sind jetzt noch ein bisschen GUI und IO ;-)

Zuerst wird eine Eingabemöglichkeit benötigt, damit der Empfänger weiß, auf welche 8 Kanäle des DMX-Signals er reagieren muß.

 

1. Fest einprogrammiert:

Das ist mir zu unflexibel.

 

2: Einstellung durch Codierschalter:

Um dem Controller eine Zahl zwischen 1 und 512 durch Codierschalter mitteilen zu können werden mindestens 9 Bit benötigt. Sollen alle Kanäle frei zuweisbar sein, wären also 9 mal 8 = 71 Leitungen nötig!! (Einsparungen wären durch eine Widerstandsmatrix möglich)

 

3: Navigationskreuz und Display:

Da LCD-Displays inzwischen sehr günstig zu haben sind und auch die Programmierung keine große Hürde darstellt ist das wohl die beste Lösung.

 

Jetzt zur Preipherie:

Das Display und die Taster könnte man direkt am ATMega betreiben, jedoch wäre dann ein größerer Controller als der 8er erforderlich, da der zu wenig Eingänge hat. Zudem wäre die Leiterbahnverlegung bei einer einseitig beschichteten Platine sehr kniffelig. Deshalb werden Taster und Display über I2C und einem IO-Port IC (PCA9555D) angesteuert.

Für die Ausgänge setze ich einen weiteren Spezialisten ein, den TDA8444P. Der D/A-Wandler wird ebenfalls über I2C angesprochen und stellt 8 Ausgänge mit 0,3 bis 10,5 Volt zur Verfügung. Damit wird eine Aufwendige PWM-Erzeugung im Controller und eine Glättung auserhalb umgangen. Die geringe Auflösung des TDA8444P von 6 Bit dürfte aber für die meisten Anwendungen locker ausreichen. Eine Anpassung auf andere oder mehrere D/A-Wandler ist dank I2C problemlos möglich.

Durch das LCD-Display und die 4 Taster kann jetzt der Empfänger komfortabel bedient werden.

 

Im Menü kann man bis jetzt:

-aktuelle Werte der 8 Kanäle anzeigen

-die Zuordnung DMX-> Ausgang konfigurieren und anzeigen

Das Menü ist mit einer State-Machine realisiert. Diese zeichnet sich durch einen gut lesbaren Code aus. Somit sind auch hier Erweiterungen oder Änderungen kein Problem. Weiteres zur State-Machine unter www.roboternetz.de/wissen/index.php/Bascom_State_Machine_Menu

 

Das DMX-Signal basiert auf einer RS485 Übertragung. Der MAX485 konvertiert es in ein microcontrollergerechtes 5V-Signal. Den Rest erledigt der Microcontroller. Weitere Grundlagen unter www.soundlight.de

 

Im Downloadbereich habe ich den Bascom Quellcode und den Schaltplan/ das Platinenlayout in Target bereitgestellt.

Praxis:

Bereits die 2. Versuchsplatine brachte den gewünschen Erfolg. Auch die erst befürchteten Timingprobleme blieben aus. Es ist erstaunlich was so ein kleiner ATMega bei 16MHz alles abarbeiten kann. Auch der Flashspeicher ist erst 66% voll. Weil der Microcontroller auch gut mit RAM bestückt ist, kann ich mir den Luxus leisten alle 512 Kanäle im RAM zu puffern.

Die SMDs lassen sich mit ein bisschen Übung gut löten. Nur der TDA8444 ist im DIL Gehäuse. Die SMD-Version wäre hier 3mal so teuer gewesen (Das ganze Material liegt bei ca. 20 Euro).

Menü

Mit den 4 Tastern kann durch das Menü navigiert werden. Die Struktur ist im folgenden Bild dargestellt:

Schaltplan:

Download

DMX-Empfang.zip

Quellcode

'--------------------------------------------------------------
'                        DMX-Empfang10.bas
'               Empfangsmodul für das DMX-512 Signal
'                  (c) 2008, Günter Gerold
'                        Fileversion 1.1
'--------------------------------------------------------------
$regfile = "m8def.dat"
$crystal = 16000000                                         'selbst mit 8MHz kommt der Code noch locker mit
$baud = 250000
'$framesize = 32                                             'Stack
'$swstack = 32
'$hwstack = 64
$lib "gg.lib"
Const Gg_displayport = &H40
Const Gg_da = &H48
Config Scl = Portc.5                                        'Configure i2c SCL
Config Sda = Portc.4
'USART auf DMX512 einstellen
Config Com1 = Dummy , Synchrone = 0 , Parity = None , Stopbits = 2 , Databits = 8 , Clockpol = 1
On Urxc Empfang
Enable Urxc
Enable Interrupts
'
'              [Key_up   ]
' [Key_prev]   [         ]    [Key_next]
'              [Key_down ]
'
'
'              [ Taste_8 ]
' [Taste_4 ]   [         ]    [Taste_6 ]
'              [ Taste_2 ]
'
'bzw. im ASCII-Code:

Const Key_buttons = 4                                       'Anzahl der Tasten
Dim Keycode_string As String * Key_buttons
'Key_up|Key_down|Key_prev|Key_next
Keycode_string = "{056}{050}{052}{054}"                     'see scan codes in ASCII chart
Dim Keycode(key_buttons) As Byte At Keycode_string Overlay
Const Key_null = 0                                          'keine Taste gedrückt

Dim Channel_buffer(8) As Word
Dim Value_buffer(8) As Byte
Dim Address As Word

'******** LCD Settings ***********************************************
Dim Lcd_textbuffer As String * 32
Dim Lcd_textbuffer1 As String * 16

'Pins des LCD-Modules setzen ggf. an eigene Anschlüsse anpassen
Config Lcd = 16 * 2
Initlcd
Cls

'******** Joystick/Key Settings ***********************************************
Dim Key As Byte                                             'key in Mainloop

'********* State Variables ****************************************************
Dim State As Word                                           'aktueller State
Dim State_renew As Byte                                     'Flag
Dim State_gosub As Word                                     'aktuelles Unterprogramm

'Initial state variables
State_renew = 1
State = Loadlabel(s1)                                       'Startbildschirm
Key = Key_null                                              'keine Taste gedrückt

'******** allg Variablen ******************************************************
Dim I As Byte , J As Byte
Dim W As Word
Dim Wert As String * 8


'Puffer für das angekommene Byte aus dem DMX-Signal
Dim X As Byte

'Alle 512 Kanäle werden in diesem Array gehalten
Dim Buffer(512) As Byte

'Der aktuelle Kanal in der Schleife
Dim Kanal As Word
Dim Ax(2) As Byte
Gosub Init_channels
Cls
Do
    'Menüeintrag und Tastencodes finden und ggf. State wechseln
    'hier nur Tastendruck auswerten
    Gosub Change_state
    'Pointer nach Statuswechsel neu setzen
    If State_renew = 1 Then Gosub Change_state

    'Unterprogramm des aktuellen State anspringen
    LDS R31, {State_gosub+1}                                'High see Bascom-Doc Mixing ASM and BASIC
    LDS R30, {State_gosub}                                  'Low
    LSR R31                                                 'Division durch 2 (Alternativ ADR verwenden)
    ROR R30
    'Call Zum Sub
    ICALL
    For I = 1 To 8
      Value_buffer(i) = Buffer(channel_buffer(i))
    Next
   '---------------------------------------------------------------
   'place for your own code in main loop
   '---------------------------------------------------------------
   'LCD refresh wenn Menü verändert
   If State_renew = 1 Then
      State_renew = 0
      Gosub Lcd_print
   End If
   'Tastaturabfrage mit Halt nur für Simulator, ansonsten Sleep+Timer_ISR verwenden!!
For I = 0 To 7
   J = Value_buffer(i + 1)
   Shift J , Right
   Shift J , Right
   I2cstart
   I2cwbyte Gg_da
   I2cwbyte I
   I2cwbyte J
   I2cstop
Next
    Ax(1) = 1
 I2creceive Gg_displayport , Ax(1) , 1 , 1
 If Ax(1).3 = 0 Then
      Key = Keycode(3)
 Elseif Ax(1).4 = 0 Then
      Key = Keycode(1)
 Elseif Ax(1).5 = 0 Then
      Key = Keycode(4)
 Elseif Ax(1).6 = 0 Then
      Key = Keycode(2)
 End If
 Waitms 50
Loop

'--------------------------------------------------------------
'
'Empfang:
'
'steht in der USART ein Byte an, wird ein Interrupt
'ausgelöst und folgender Code abgearbeitet:
'--------------------------------------------------------------
Empfang:
X = Udr
   If Ucsra.fe = 1 Then
      Kanal = 0
   Else
      Incr Kanal
      If Kanal < 513 And Kanal > 0 Then Buffer(kanal) = X

   End If

Return

'********* State routines ***************************************************
'---------------------------------------------------------------
'Subroutine: Change_state
'Call from:  main loop
'Purpose:    Status der State Machine feststellen und ggf. Wechseln
'Result:     Pointer auf State / Variable State_renew
'---------------------------------------------------------------
Change_state:
    lds R8, {State}
    lds R9, {State + 1}
    For I = 1 To Key_buttons
      Read W
      If Key = Keycode(i) Then
           If W <> Loadlabel(null) Then
              State_renew = 1
              Key = Key_null                                'reset key status after get a new state of state machine (prevent influence on GOSUBs)
              State = W
           End If
      End If
    Next I
    Read State_gosub                                        'Adresse des akt. Unterprogramms einlesen
    Read Lcd_textbuffer                                     'read LCD text
Return

'---------------------------------------------------------------
'Subroutine: Change_state_by_sub
'Call from:  subroutine
'Purpose:    change state of state machine by a subbroutine
'---------------------------------------------------------------
Change_state_by_sub:
   State_renew = 1
   Gosub Change_state
Return

'---------------------------------------------------------------
'DATA:       State Machine
'Result:     Pointers auf States , Unterprogramme
'            Menütexte
'Format:     1. Zeile: für jede Taste genau eine Verzeigung ADR2 (see Key_buttons=xx)
'            2. Zeile: Sprunglabel als ADR2 für ein ggf. anzuspringendes Unterprogramm
'            3. Zeile: Displaytext des akt. Status als DATA Feld
'Hinweis:    wenn nichts passieren soll wird das Label NULL eingetragen
'---------------------------------------------------------------

Null:
         'Null is a dummy flag for State and Gosub -> do nothing
         Return

S1:
         Adr2 Null : Adr2 S2 : Adr2 Null : Adr2 Null        'Key_up|Key_down|Key_prev|Key_next
         Adr2 Null                                          'Subroutine for current State
         Data "DMX-Empf{225}nger1.0G{245}nter Gerold"       'Menue Display Text

S2:
         Adr2 S1 : Adr2 S3 : Adr2 Null : Adr2 Null
         Adr2 Val1to4
         Data "   1   2   3   4"

S3:
         Adr2 S2 : Adr2 S4 : Adr2 Null : Adr2 Null
         Adr2 Val5to8
         Data "   5   6   7   8"
S4:
         Adr2 S3 : Adr2 Null : Adr2 Null : Adr2 S10
         Adr2 Null
         Data "DMX-            Einstellungen"

   S10:
            Adr2 Null : Adr2 S20 : Adr2 S4 : Adr2 Null      'Key_up|Key_down|Key_prev|Key_next
            Adr2 Set1to4                                    'Subroutine for current State
            Data "   1   2   3   4"                         'Menue Display Text

   S20:
            Adr2 S10 : Adr2 S30 : Adr2 S4 : Adr2 Null
            Adr2 Set5to8
            Data "   5   6   7   8"

   S30:
            Adr2 S20 : Adr2 Null : Adr2 S4 : Adr2 S31
            Adr2 Store_channels
            Data "DMX-Kan{225}le      zuweisen"
      S31:
               Adr2 Null : Adr2 S32 : Adr2 S30 : Adr2 S31a
               Adr2 Null
               Data "DMX-Kanal f{245}r   Ausgang 1"
         S31a:
                  Adr2 Null : Adr2 Null : Adr2 S31 : Adr2 Null
                  Adr2 Setvalue1
                  Data "Ausgang 1 =     "
      S32:
               Adr2 S31 : Adr2 S33 : Adr2 S30 : Adr2 S32a
               Adr2 Null
               Data "DMX-Kanal f{245}r   Ausgang 2"
         S32a:
                  Adr2 Null : Adr2 Null : Adr2 S32 : Adr2 Null
                  Adr2 Setvalue2
                  Data "Ausgang 2 =     "
S33:
             Adr2 S32 : Adr2 S34 : Adr2 S30 : Adr2 S33a
             Adr2 Null
             Data "DMX-Kanal f{245}r   Ausgang 3"
S33a:
               Adr2 Null : Adr2 Null : Adr2 S33 : Adr2 Null
               Adr2 Setvalue3
               Data "Ausgang 3 =     "
S34:
             Adr2 S33 : Adr2 S35 : Adr2 S30 : Adr2 S34a
             Adr2 Null
             Data "DMX-Kanal f{245}r   Ausgang 4"
S34a:
               Adr2 Null : Adr2 Null : Adr2 S34 : Adr2 Null
               Adr2 Setvalue4
               Data "Ausgang 4 =     "
S35:
             Adr2 S34 : Adr2 S36 : Adr2 S30 : Adr2 S35a
             Adr2 Null
             Data "DMX-Kanal f{245}r   Ausgang 5"
S35a:
               Adr2 Null : Adr2 Null : Adr2 S35 : Adr2 Null
               Adr2 Setvalue5
               Data "Ausgang 5 =     "
S36:
             Adr2 S35 : Adr2 S37 : Adr2 S30 : Adr2 S36a
             Adr2 Null
             Data "DMX-Kanal f{245}r   Ausgang 6"
S36a:
               Adr2 Null : Adr2 Null : Adr2 S36 : Adr2 Null
               Adr2 Setvalue6
               Data "Ausgang 6 =     "
S37:
             Adr2 S36 : Adr2 S38 : Adr2 S30 : Adr2 S37a
             Adr2 Null
             Data "DMX-Kanal f{245}r   Ausgang 7"
S37a:
               Adr2 Null : Adr2 Null : Adr2 S37 : Adr2 Null
               Adr2 Setvalue7
               Data "Ausgang 7 =     "
S38:
             Adr2 S37 : Adr2 Null : Adr2 S30 : Adr2 S38a
             Adr2 Null
             Data "DMX-Kanal f{245}r   Ausgang 8"
S38a:
               Adr2 Null : Adr2 Null : Adr2 S38 : Adr2 Null
               Adr2 Setvalue8
               Data "Ausgang 8 =     "

'********* LCD SUB routines ***************************************************
'---------------------------------------------------------------
'Subroutine: Lcd_print
'Call from:  anywhere
'Purpose:    gibt Lcd_textbuffer auf dem LCD-Display aus
'Result:     LCD
'---------------------------------------------------------------
Lcd_print:                                                  'Print lcd_textbuffer
    Upperline
    If Len(lcd_textbuffer) > 16 Then
      Lcd Left(lcd_textbuffer , 16) ; "        "
      Lowerline
      Lcd Mid(lcd_textbuffer , 17 , 16) ; "        "
    Else
      Lcd Lcd_textbuffer
      Lowerline
      Lcd Lcd_textbuffer1 ; "        "
      Lcd_textbuffer1 = ""
    End If
    State_renew = 0
Return


Setvalue1:
   If Key = Key_null Then
      State_renew = 0
   Else
      State_renew = 1
   End If
    If Key = Keycode(1) And Channel_buffer(1) < 512 Then
        Channel_buffer(1) = Channel_buffer(1) + 1
        Key = Key_null
    Elseif Key = Keycode(2) And Channel_buffer(8) > 1 Then
        Channel_buffer(1) = Channel_buffer(1) - 1
        Key = Key_null
    End If
    Wert = Str(channel_buffer(1))
    Lcd_textbuffer1 = "DMX-Kanal: " + Wert
Return
Setvalue2:
   If Key = Key_null Then
      State_renew = 0
   Else
      State_renew = 1
   End If
    If Key = Keycode(1) And Channel_buffer(2) < 512 Then
        Channel_buffer(2) = Channel_buffer(2) + 1
        Key = Key_null
    Elseif Key = Keycode(2) And Channel_buffer(2) > 1 Then
        Channel_buffer(2) = Channel_buffer(2) - 1
        Key = Key_null
    End If
    Wert = Str(channel_buffer(2))
    Lcd_textbuffer1 = "DMX-Kanal: " + Wert
Return
Setvalue3:
   If Key = Key_null Then
      State_renew = 0
   Else
      State_renew = 1
   End If
    If Key = Keycode(1) And Channel_buffer(3) < 512 Then
        Channel_buffer(3) = Channel_buffer(3) + 1
        Key = Key_null
    Elseif Key = Keycode(2) And Channel_buffer(3) > 1 Then
        Channel_buffer(3) = Channel_buffer(3) - 1
        Key = Key_null
    End If
    Wert = Str(channel_buffer(3))
    Lcd_textbuffer1 = "DMX-Kanal: " + Wert
Return
Setvalue4:
   If Key = Key_null Then
      State_renew = 0
   Else
      State_renew = 1
   End If
    If Key = Keycode(1) And Channel_buffer(4) < 512 Then
        Channel_buffer(4) = Channel_buffer(4) + 1
        Key = Key_null
    Elseif Key = Keycode(2) And Channel_buffer(4) > 1 Then
        Channel_buffer(4) = Channel_buffer(4) - 1
        Key = Key_null
    End If
    Wert = Str(channel_buffer(4))
    Lcd_textbuffer1 = "DMX-Kanal: " + Wert
Return
Setvalue5:
   If Key = Key_null Then
      State_renew = 0
   Else
      State_renew = 1
   End If
    If Key = Keycode(1) And Channel_buffer(5) < 512 Then
        Channel_buffer(5) = Channel_buffer(5) + 1
        Key = Key_null
    Elseif Key = Keycode(2) And Channel_buffer(5) > 1 Then
        Channel_buffer(5) = Channel_buffer(5) - 1
        Key = Key_null
    End If
    Wert = Str(channel_buffer(5))
    Lcd_textbuffer1 = "DMX-Kanal: " + Wert
Return
Setvalue6:
   If Key = Key_null Then
      State_renew = 0
   Else
      State_renew = 1
   End If
    If Key = Keycode(1) And Channel_buffer(6) < 512 Then
        Channel_buffer(6) = Channel_buffer(6) + 1
        Key = Key_null
    Elseif Key = Keycode(2) And Channel_buffer(6) > 1 Then
        Channel_buffer(6) = Channel_buffer(6) - 1
        Key = Key_null
    End If
    Wert = Str(channel_buffer(6))
    Lcd_textbuffer1 = "DMX-Kanal: " + Wert
Return
Setvalue7:
   If Key = Key_null Then
      State_renew = 0
   Else
      State_renew = 1
   End If
    If Key = Keycode(1) And Channel_buffer(7) < 512 Then
        Channel_buffer(7) = Channel_buffer(7) + 1
        Key = Key_null
    Elseif Key = Keycode(2) And Channel_buffer(7) > 1 Then
        Channel_buffer(7) = Channel_buffer(7) - 1
        Key = Key_null
    End If
    Wert = Str(channel_buffer(7))
    Lcd_textbuffer1 = "DMX-Kanal: " + Wert
Return
Setvalue8:
   If Key = Key_null Then
      State_renew = 0
   Else
      State_renew = 1
   End If
    If Key = Keycode(1) And Channel_buffer(8) < 512 Then
        Channel_buffer(8) = Channel_buffer(8) + 1
        Key = Key_null
    Elseif Key = Keycode(2) And Channel_buffer(8) > 1 Then
        Channel_buffer(8) = Channel_buffer(8) - 1
        Key = Key_null
    End If
    Wert = Str(channel_buffer(8))
    Lcd_textbuffer1 = "DMX-Kanal: " + Wert
Return

Init_channels:

Address = 8176
For I = 1 To 8
    Readeeprom Channel_buffer(i) , Address
    If Channel_buffer(i) > 512 Or Channel_buffer(i) < 1 Then Channel_buffer(i) = 1
    Address = Address + 2
Next
Return

Store_channels:

Address = 8176
For I = 1 To 8
    Writeeeprom Channel_buffer(i) , Address
    If Channel_buffer(i) > 512 Or Channel_buffer(i) < 1 Then Channel_buffer(i) = 1
    Address = Address + 2
Next
Return

Set1to4:
Lcd_textbuffer1 = ""
For I = 1 To 4
   Wert = Str(channel_buffer(i))
   Wert = Format(wert , "   0")
   Lcd_textbuffer1 = Lcd_textbuffer1 + Wert
Next
State_renew = 1
Return
Set5to8:
Lcd_textbuffer1 = ""
For I = 5 To 8
   Wert = Str(channel_buffer(i))
   Wert = Format(wert , "   0")
   Lcd_textbuffer1 = Lcd_textbuffer1 + Wert
Next
State_renew = 1
Return

Val1to4:
Lcd_textbuffer1 = ""
For I = 1 To 4
   Wert = Str(value_buffer(i))
   Wert = Format(wert , "   0")
   Lcd_textbuffer1 = Lcd_textbuffer1 + Wert
Next
State_renew = 1
Return
Val5to8:
Lcd_textbuffer1 = ""
For I = 5 To 8
   Wert = Str(value_buffer(i))
   Wert = Format(wert , "   0")
   Lcd_textbuffer1 = Lcd_textbuffer1 + Wert
Next
State_renew = 1
Return