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