Thijs Assies DMX-512 met een PICmicro 1. Wat is DMX-512? DMX-512 (afgekort DMX) Staat door Digital MultipleXed. En dit omschrijft eigenlijk de functie al: Het is een boel signalen Gemultiplexed op een digitale manier. DMX wordt vooral gebruikt in de lichtwereld. Als je kijkt naar het verleden zie je dat er gebruik werd gemaakt van een systeem dat aan de hand van een 0.. 10 volt signaal bepaalde Hoe fel een lamp moest branden. Dit had een aantal nadelen: - Voor iedere lamp 1 sturingskabeltje, in geval van veel lampen dus erg dikke kabels -Spanningsverliezen over grote afstand. Naarmate het systeem uitgebreider werd, was er behoefte aan een eenvoudiger stuursysteem wat modulair was, oftewel: geen vaste opstelling vereiste. DMX houdt in dat alle signalen voor ieder kanaal door 1 kabel worden gestuurd. De informatie voor ieder apparaat wordt achter elkaar gestuurd. Het oude 0..10 volt systeem was een parrallel systeem, DMX is serieel. DMX-512 Heeft de mogelijkheid om informatie te versturen voor 512 verschillende apparaten (kanalen geheten). Deze worden achter elkaar verstuurd, ook als er geen veranderingen zijn in de gegevens. Om te zorgen dat elk apparaaat de juiste informatie ontvangt heeft ieder apparaat een eigen adres, en uitsluitend naar dat adres wordt geluisterd. Dit instellen van adressen gebeurd doormiddel van dipswitches of knopjes. Nu is het zo dat apparaten meer dan één kanaal kunnen gebruiken. Denk bijvoorbeeld aan een dimmer die 12 lampen onafhankelijk kan dimmen. Om ze onafhankelijk aan te sturen zijn dus 12 verschillende DMX kanalen nodig. Het is gebruikelijk dat in dat geval alleen het eerste adres wordt ingesteld en dat het apparaat vanaf dat punt 12 kanalen pakt. Hier moet je dus op letten met adressen geven aan de apparaten, dat niets elkaar overlapt. Er zijn ook meer apparaten dan dimmers. Denk aan scanners, movingheads, rookmachines. Allemaal bestuurbaar via DMX. Deze ondernemen een actie afhankelijk van de gegevens die dorogestuurd worden. Een DMX cyclus is als volgt opgebouwd: BREAK MARK AFTER BREAK Startcode0 Kanaal1 Kanaal2 Kanaal3...Kanaal 512 Startcode1 Kanaal1 Kanaal2 Kanaal3... Kanaal 512 BREAK MARK AFSTER BREAK Startcode0... Etc. Er wordt naast 512 kanalen dus nog één kanaal verstuurd, de zogeheten startcode. Deze startcode geeft aan voor wat voor apparaat de code bedoeld is. Startcode0 was bedoeld voor dimmers en 1 voor iets anders bijvoorbeeld. Echter zijn de startcodes nooit van de grond gekomen en is de enige gebruikelijke startcode 0. Andere codes worden niet gebruikt! Toch is het handig om er bewust van te zijn tijdens het programmeren! Het verzenden van 1 bit duurt in DMXwereld 4 microseconden (us). Dat komt neer op een frequentie van 250 Khz. Wat ook belangerijk is om te onthouden.
Een DMXbyte is als volgt vormgegeven: Startbit LAAG Stopbit Stopbit HOOG HOOG Per kanaal wordt 1 byte verstuurd, 8 bits. Dat betekend dat ieder kanaal 2^8 = 256 waarden aan kan nemen, 0... 255. Iedere byte wordt ingesloten door 1 bit laag en 2 bits hoog. Dit is zeer belangerijk voor het functioneren van de ontvangsthardware. De BREAK bestaat uit 88 us laag. Zonder startbits en stopbits. een BREAK is dus op geen enkele manier te intepreteren als databyte en is dus als zodanig eenvoudig te herkennen. Dit is ook essentieel voor het functioneren, immers als er geen break wordt gedetecteerd, kan er geen teller gestart worden die de kalen telt. Dit kan betekenen dat het apparaat helemaal niets ontvangt, of de verkeerde kanalen. Een BREAK is essentieel! Na een BREAK komt de Mark After break. Dit zijn 2 hoge bits, deze zijn er omdat de UART van een pic pas gaat ontvangen als er een overgang is van hoog naar laag. Als er geen MAB was, kreeg je eerst de lage BREAK, dan de lage startbit en dus geen hoog->laag overgang. En kon de uart dus niet beginnen met ontvangen. Nu de basis van DMX te hebben uitgelegd is het tijd voor implementatie in een microprocessor. In dit voorbeeld wordt gebruik gemaakt van een 16F628A, het principe is echter bij iedere PIC( en waarschijnlijk iedere andere microcontroller) hetzelfde. DMX is electrisch gezien gebaseerd op RS485, dit gebruikt voor communicatie andere spanningen dan waar de PIC mee werkt. Om deze systemen op elkaar aan te kunnen sluiten maak je gebruik van een converter IC, bijvoorbeeld de SN76175. De DMX bus wordt hierop aangesloten en de output van het IC gaat naar de Rx van de PIC (in geval van de 16F628A is dat pin 7 ). Er zijn 2 manieren om gebruik te maken van seriele connecties (zoals DMX): 1. Handmatig (bitbangen) 2. UART (USART / AUSART) Manier 1 houdt in dat je een bepaald pinnetje van de processor afwisselend hoog en laag maakt om zo de datastroom op gang te zetten. Je moet alles exact zelf timen, en er is in het programma eigenlijk geen tijd meer om berekeningen te doen. Manier 2 maakt gebruikt van een ingebouwd stuk hardware voor seriele connecties, Universal Asynchronus Reciever Transmitter. Dit houdt in dat je alleen maar hoeft aan te geven welke byte verzonden moet worden. De PIC houdt zelf de timing in de gaten en geeft aan wanneer een nieuwe byte verwacht wordt. Dit heeft als voordeel dat er in het programma tijd over is om berekeningen te doen met de ontvangen data. Het spreekt bijna voor zich dat manier 2 de voorkeur heeft, de 16f628A heeft een ingebouwde
USART en daar gaan we gebruik van maken. Eerst moeten er de volgende dingen worden ingesteld: 1. De frequentie waarop de verbinding moet werken (250 kbaud in dit geval) 2. De configuratie van de verbinding (asynchroon ed) 3. De configuratie van de ontvangen bytes ( startbits, stopbits etc) 4. Het aanzetten van de USART 5. Interrupts configureren. Dit alles wordt ingesteld in een aantal registers in de PIC. Hieronder is een stuk code die een DMX connectie insteld: INIT MOVLW 0x07 ;turn analog comparators off MOVWF CMCON CLRF PORTA CLRF PORTB BSF STATUS,RP0 ;Select Bank1 CLRF TRISA ;porta output CLRF TRISB ;portb output BSF TRISB,1 ;B1 input (UART Recieve) MOVLW 04h MOVWF SPBRG ;Set baud rate BSF TXSTA,BRGH ;high speed BCF TXSTA,SYNC ;Async connection CLRF PIE1 ;clear other interrupts BSF PIE1,RCIE ;Set recieve interrupt BCF STATUS,RP0 ;Return to bank0 BSF RCSTA,RX9 ;Enable 9bit data ;clear RCREG FIFO BSF RCSTA,CREN ;Enable recieve BSF RCSTA,SPEN ;Enable UART BSF INTCON,PEIE ;Enable interrupts GOTO MAIN ;Goto Main Program Hier een beschrijving wat wat doet, het DMX specifieke deel begint NA de witregel: CLRF TRISA ;porta output CLRF TRISB ;portb output BSF TRISB,1 ;B1 input (UART Recieve) Deze regels stellen porta en portb in als output, met uitzondering van RB1, welke de UART is, deze moet input zijn. MOVLW 04h MOVWF SPBRG ;Set baud rate BSF TXSTA,BRGH ;high speed Deze 3 regels stellen de snelheid van de verbinding in. De UART kent een aantal snelheids configuraties. High speed mode en low speed mode. Dit is gedaan om een grotere variatie aan snelheden aan te kunnen. In SPBRG staat de waarde die de snelheid voorsteld. Als volgt berekend: Highspeed: Fosc / ( 16 ( SPBRG + 1) = BAUDRATE
Lowspeed: Fosc / ( 64 (SPBRG +1 ) = BAUDRATE Als je gaat rekenen met een kristal van 20 Mhz, kom je erop uit dat 250 Khz exact kan in HIGH speed modus. SPBRG moet in dat geval 4 zijn. (in HEX is dat 04h). Daarom wordt de waarde 04h naar SPBRG geschreven. BCF TXSTA,SYNC ;Async connection Dit geeft aan dat we gebruik gaan maken van een Asynchrone connectie, (wat DMX is). CLRF PIE1 ;clear other interrupts BSF PIE1,RCIE ;Set recieve interrupt Hiermee zet je de interrupts aan. Met CLRF worden eerst alle interrupts uitgezet en alleen RCIE aangezet. Deze genereert een interrupt zodra er een byte wordt ontvangen door de UART. BSF RCSTA,RX9 ;Enable 9bit data Deze regel zet 9 bits data aan. Dat klinkt vreemd, DMX verstuurd immers toch maar 8 databits? Dat klopt, maar een PIC kan alleen omgaan met systemen waar maar één stopbit gebruikt wordt, DMX gebruikt echter 2 stopbits, en op deze manier laten we denken dat die ene extra bit gewoon bij de data hoort. In het programma wordt die verder gewoon genegeerd. ;clear RCREG FIFO Deze regels maken de FIFO (first in first out) leeg. DE PIC heeft een Buffer om ontvangen data in op te slaan, deze is maximaal 3 bytes diep, en vandaar 3 keer dezelfde regel. Om helemaal zeker te zijn dat de buffer leeg is. BSF RCSTA,CREN ;Enable recieve Door deze bit te zetten weet de PIC dat er ontvangen moet worden, Dat wordt echter pas gedaan als de seriele poort ge enabled wordt. BSF RCSTA,SPEN ;Enable UART Wat dus in deze regel gebeurd. Dit is de hoofschakelaar van de UART. Staat deze bit op 0 doet de UART helemaal niets. BSF INTCON,PEIE ;Enable interrupts Dit is de hoofschakelaar van de Interrupts, Pas als deze aanstaat zullen de interrupts werken. Nu hebben we de PIC klaar gemaakt om DMX te ontvangen. Iedere keer als er een byte binnenkomt zal er een interrupt gegenereerd worden. En deze moet afgevangen worden. In de interruptroutine moet er eerst gekeken worden of er geen fouten zijn geconstateerd. Dat gaat als volgt: Het register RCSTA kent 2 speciale bits. OERR en FERR. De eerste geeft aan dat er zich een overrun error heeft voorgedaan. Data werd sneller ontvangen dan verwerkt. Dit is in principe een ontwerpfout in het programma. De FERR geeft aan dat er een Framing error is geweest. Dat houdt in dat de PIC op het moment dat het een stopbit verwacht deze niet ontvangt. Dit kan 2 oorzaken hebben: De framerate staat verkeerd, waardoor er langzamer of sneller gelezen wordt als verzonden, en bytes dus dubbel gelezen worden of gemist. Maar het kan ook betekenen dat
die stopbit helemaal niet verzonden is. En hier zitten we op te wachten! Want een BREAK bestaat immers uit 88 microseconden laag, oftewel 2 volledige bytes. De PIC zal de eerste lage bit zijn als startbit, daarna 8 lage bits als data, de 9e databit laag. En dan moet er een hoge stopbit komen. Maar die komt er niet! De PIC zal dus een framing error geven. Op deze manier is een foutmeldig te gebruiken om een belangerijk punt te detecteren in de DMX cyclus. Hieronder een stukje (incomplete! ) voorbeeld code: dmx_break EQU 020h ORG 04h ;Int handler BTFSS PIR1, RCIF ;Check if interrupt is Recieve interrupt BTFSS RCSTA,OERR ;Check for Overrun error GOTO OVERRUN_HANDLER BTFSS RCSTA,FERR ;Check for frame error GOTO FRAMING_HANDLER BTFSC dmx_break ;Check if break has passed earlier GOTO RECIEVE_DATA FRAMING_HANDLER BTFSS RCREG,RX9 ;Check if 9 th bit is 0 BSF dmx_break ;set Var Hieronder een analyse van de code: BTFSS PIR1, RCIF ;Check if interrupt is Recieve interrupt Hier wordt gecontroleerd of de interrupt die gestart werd wel degelijk komt doordat de Uart iets ontvangen heeft. RCIF geeft aan dat er een recieve interrupt is. Is die niet geset? Negeer de interrupt dan. BTFSS RCSTA,OERR ;Check for Overrun error GOTO OVERRUN_HANDLER Hier wordt gekeken of er een overrun error is geconstateerd, als dat zo is wordt de functie aangeroepen die dat afhandeld (hier niet beschreven) BTFSS RCSTA,FERR ;Check for frame error GOTO FRAMING_HANDLER Hier wordt gecontroleerd of er een framing error is, dat kan het begin van een DMX cyclus berekenen, FRAMING_HANDLER wordt aangeroepen.
BTFSC dmx_break ;Check if break has passed earlier GOTO RECIEVE_DATA Indien er geen errors komen, betekend dat dus dat er een gewone databyte ontvangen is. Er wordt gecontroleerd of de flag dmx_break geset is. Indien ja: er is al een break geweest en de data kan ontvangen worden. Indien nee wordt de boel genegeerd. Belangrijk is bij een recieve interrupt: De ontvangen data moet ALTIJD worden ingelezen, ook als er niets mee gedaan wordt. Als dat niet gebeurd wordt de boel opgestapeld en loopt de ontvangst FIFO vol. In deze regel wordt de ontvangen byte (die niet nodig is) gekopieerd naar W. Er wordt verder niets gedaan maar de FIFO is geleegd BTFSS RCREG,RX9 ;Check if 9 th bit is 0 BSF dmx_break ;set Var Deze code wordt uitgevoerd als er een BREAK is geconstateerd. Er wordt gegeken of de ontvangen 9de bit, normaal dus de eerste stopbit, gelijk is aan 0. Als dit NIET zo is is de framing error te wijten aan een verkeerde baudrate, en daar kan verder naar gehandeld worden. Dit is echter niet noodzakelijk: zolang er Framing errors blijven komen komt het programma toch niet bij de databytes. Na een BREAK moet de startcode ontvangen worden. Hieronder staat een stukje wat verder gaat op de vorige lap code: startcode EQU 021h RECIEVE_DATA ;Called when valid data is recieved BTFSC startcode ;Check if a startcode has passed GOTO CHANNEL ;If so: recieve channel data ;Move recieved byte to W XORLW 00h ;Xor it with 0 BTFSC STATUS,Z ;check Z BSF startcode ;zero? Set flag ;return Hier de beschrijving: BTFSC startcode ;Check if a startcode has passed GOTO CHANNEL ;If so: recieve channel data Deze code controleerd of de flag startcode als geset is, als dat zo is is de ontvangen byte geen startcode maar channeldata, in dat geval wordt de functie aangeroepen die dat afhandeld ;Move recieved byte to W XORLW 00h ;Xor it with 0 BTFSC STATUS,Z ;check Z BSF startcode ;zero? Set flag De ontvangen byte wordt in W gestopt, en ge'xor'd met 00h. Het resultaat van deze berekening
zou 0 moeten geven indien de waarde van de ontvangen byte gelijk is aan 0. In dat geval wordt de flag geset dat de startcode is ontvangen. Na de Startcode komt de Channeldata. Dit zijn gewoon normale bytes. Het belangrijkste is dat de teller goed wordt bijgehouden! Hieronder wederom een stuk voorbeeldcode: CHANNEL XORWF BTFSS GOTO MOVWF INCF teller,w adres,w STATUS,Z NOT_USED recieved_data teller NOT_USED INCF teller En de uiteenzetting: XORWF teller,w adres,w De teller die bijhoudt welk kanaal op dit moment ontvangen wordt word verplaatst naar het werkgeheugen en ge XOR'd met de byte die het eigen adres bijhoudt. Levert dit 0 op dan is het adres hetzelfde. BTFSS GOTO STATUS,Z NOT_USED Stel: Z is NIET geset, in dat geval waren de 2 adressen dus niet gelijk en wordt een functie aangeroepen die de boel verder afhandeld. Indien Z wel geset is wordt de goto overgeslagen. MOVWF recieved_data De ontvangen byte wordt verplaatst naar het register recieved_data. Dit register is in het hele programma verder bruikbaar voor bewerkingen etc. INCF teller De teller wordt 1 opgehoogd (we hebben immers alweer een kanaal ontvangen), en de interruptroutine wordt afgeloten. INCF teller In dit geval was de ontvangen byte niet bruikbaar, maar om vollopen van de fifo te voorkomen wordt de byte toch ingelezen. Ook wordt de teller verhoogd om de kanaaltelling kloppend te
houden. In principe is dit de basismanier van DMX ontvangt met een PIC. Echter er moet nog wel met een aantal dingen rekening gehouden worden: -Zodra er een BREAK is moeten alle andere flags gereset worden -Er is op deze manier maar 1 kanaal te ontvangen -Dit werkt maar tot adres 255. Daarna geeft de INCF een overflow -Nadat de data van 1 cyclus ontvangen is moeten alle flags gereset worden, de rest is immers toch niet nodig. Deze dignen kunnen echter opgelost worden: Punt 1 wijst voor zich, dit komt puur neer op nauwkeurigheid. Punt 2 gaat als volgt: Direct zodra het startadres gevonden wordt, wordt een extra teller gestart, die de offset bijhoudt. Na het eerste ontvangen kanaal (het startadres) staat die op 1. Zodra er weer een channelbyte wordt ontvangen, moet gekeken worden of de offset groter is dan 0. Want als dat het geval is, valt de data precies in de range die ontvangen moet worden. Zodra de offset groter is dan 0 wordt een functie aangeroepen die dat afhandeld. De offset is te vergelijken met het aantal kanalen wat de pic moet ontvangen, is dat aantal gelijk, dan is alle informatie ontvangen en kan de pic wachten op een nieuw DMX cyclus. Hieronder een voorbeeld: #DEFINE chans 5 CHANNEL XORLW BTFSS GOTO XORWF BTFSC GOTO offset,w 00h STATUS,Z VALID teller,w adres,w STATUS,Z VALID VALID