'' ================================================================================================= '' '' File....... jm_dmx_tx.spin '' Purpose.... DMX-512 transmitter with configurable transmission parameters '' Author..... Jon "JonnyMac" McPhalen '' Copyright (c) 2009-2023 Jon McPhalen '' -- see below for terms of use '' E-mail..... jon.mcphalen@gmail.com '' Started.... 05 JUL 2009 '' Updated.... 26 OCT 2021 '' '' ================================================================================================= {{ Simple Example Circuit +5v +5v   │ │ 10k │ 4k7 │ ┌─────────┐ │ dmxrx ────┻─┤1° 8├─┘ txrx ──────┳─┤2 7├──────┳──────┳─────── Pin 2 XLR-F ┣─┤3 6├──────┼─┳────┼─┳───── Pin 3 XLR-F DMX OUT dmxtx ──────┼─┤4 5├─┐ │ │ │ │ ┌─── Pin 1 XLR-F │ └─────────┘ │ │ └ ┐ │ │ │ 10k ST485BN │ 120 ┌ ┘ └─┼─┼─── Pin 2 XLR-M │ │ │ │ └─┼─── Pin 3 XLR-M DMX IN   └─┘ ┣─── Pin 1 XLR-M └ ┐ ┌ ┘  ST485BN (Mouser 511-ST485BN) pins 1 RO Receive output 2 /RE Receive enable (active low) 3 DE Transmit enable (active high) 4 DI Transmit input 5 Vss ground 6 A differential IO 7 B differential IO 8 Vdd +5v Resources * http://www.erwinrol.com/index.php?stagecraft/dmx.php * http://www.dmx512-online.com/packt.html * http://en.wikipedia.org/wiki/DMX512-A Author's Note I have found that most DMX devices will tolerate a short packet, that is, a packet of less than 512 slots that runs faster than every 23ms. The .startx() method allows the appliction to set the size of the packet and the break-to-break timing. I inserted some temporary test code to determine the actual time required for short packet settings. I use these values for guidance when configuring an app with a short packet. #Slots Suggested B2B Refresh (Hz) 10 0_7 1428.5 25 1_4 714.2 50 2_5 400.0 100 4_7 212.7 150 6_9 144.9 200 9_1 109.8 250 11_4 87.7 300 13_6 73.5 400 18_0 55.5 512 23_0 43.4 The programmer is responsible for testing B2B timing with target device(s). }} con { fixed io pins } PGM_RX = 31 { I } ' serial / programming PGM_TX = 30 { O } EE_SDA = 29 { I/O } ' i2c / eeprom EE_SCL = 28 { I/O } con BREAK_US = 92 MAB_US = 12 var long cog ' cog running DMX UART long txpin ' transmit pin long txepin ' transmit enable pin long numslots ' last slot of packet, 1 to 512 long bufaddr ' address of brightness levels long b2bticks ' ticks, break-to-break long breakticks ' ticks in BREAK period long mabticks ' ticks in MAB period long baudticks ' ticks in one bit (4us) pub null '' This is not an application pub start(tx, txe, p_buf) '' Standard initialization of DMX transmitter cog '' -- tx...... transmit pin '' -- txe..... transmit enable pin; make high to transmit (-1 if not used) '' -- p_buf... pointer to DMX buffer (513 bytes) return startx(tx, txe, 0, 512, 23_0, p_buf) pub startx(tx, txe, scode, nslots, b2bt, p_buf) | us001 '' Extended initialization of DMX transmitter cog '' -- tx...... transmit pin '' -- txe..... transmit enable pin; make high to transmit (-1 if not used) '' -- scode... system start code (usually $00) '' -- nslots.. number of slots after start byte (1 to 512) '' -- b2bt.... break-to-break timing, 1.0ms to 1000.0ms '' -- timing expressed in 0.1ms units '' -- p_buf... pointer to DMX buffer (must be at least nslots+1 bytes) stop txpin := tx ' set pins txepin := txe numslots := 1 #> nslots <# 512 ' set slots bufaddr := p_buf ' point to dmx buffer ' User is responsible for correct break-to-break setting for size of packet ' -- approximately 92us + 12us + 44us { start byte} + (44us x slots) ' -- use 0 for auto retransmit with no delay between packets us001 := clkfreq / 1_000_000 ' ticks in 1us b2bticks := (0 #> b2bt <# 1000_0) * (clkfreq / 10_000) ' set break-to-break timing breakticks := us001 * BREAK_US ' ticks in standard break mabticks := us001 * MAB_US ' ticks in standard mab baudticks := clkfreq / 250_000 ' tick per bit @ 250kBaud write_all(scode, 0) ' initialize buffer to 0s cog := cognew(@dmx_tx, @txpin) + 1 ' start DMX cog return cog pub stop '' Stops DMX TX driver; frees a cog if (cog) ' if running cogstop(cog - 1) ' stop cog := 0 ' mark stopped write_all(0, 0) pub write(slot, level) '' Writes level to slot (0 - 512) '' -- slot 0 is DMX start code '' -- slot 1 to slot N are DMX levels if ((slot => 0) and (slot =< numslots)) ' valid slot? byte[bufaddr][slot] := 0 #> level <# 255 ' set it pub write_all(scode, level) '' Fills DMX buffer with start code and level (all slots) '' -- scode is the start code for stream (DMX is $00) '' -- level is the brightess level (0..255) for all slots byte[bufaddr][0] := 0 #> scode <# 255 ' limit start code level := 0 #> level <# 255 ' limit level bytefill(bufaddr+1, level, numslots) ' fill slots pub read(slot) : result '' Reads value in DMX slot (from tx buffer) '' -- slot 0 is DMX start code '' -- slot 1 to slot N are DMX levels if ((slot => 0) and (slot =< numslots)) return byte[bufaddr][slot] else return 0 pub high(slot) '' Writes 255 to slot '' -- max level '' -- does not work with start byte if ((slot > 0) and (slot =< numslots)) byte[bufaddr][slot] := 255 pub low(slot) '' Writes 0 to slot '' -- off '' -- does not work with start byte if ((slot > 0) and (slot =< numslots)) byte[bufaddr][slot] := 0 pub toggle(slot) '' Inverts slot level based on 50% threshold '' -- does not work with start byte if ((slot > 0) and (slot =< numslots)) if (byte[bufaddr][slot] < 128) byte[bufaddr][slot] := 255 else byte[bufaddr][slot] := 0 dat { dmx transmit driver } org 0 dmx_tx mov t1, par ' copy address of parameters rdlong t2, t1 ' read tx pin mov txmask, #1 ' create pin mask shl txmask, t2 or outa, txmask ' set to idle or dira, txmask add t1, #4 ' point to txe pin rdlong t2, t1 ' read txe pin cmps t2, #0 wc, wz ' in use (=> 0)? if_b mov txemask, #0 ' no, clear mask if_ae mov txemask, #1 ' yes, make mask if_ae shl txemask, t2 if_ae or outa, txemask ' enable device for tx if_ae or dira, txemask add t1, #4 ' point to packet size rdlong slots, t1 ' get slots in packet add slots, #1 ' +1 for start code add t1, #4 ' point to bufaddr rdlong hub, t1 ' read address of dmx buffer in hub add t1, #4 ' point to b2btix rdlong b2b, t1 ' read break-to-break timing add t1, #4 ' point to breaktix rdlong break, t1 ' read break timing add t1, #4 ' point to mabtix rdlong mab, t1 ' read mab timing add t1, #4 ' point to baudtix rdlong bit1x0tix, t1 ' read bit timing mov t1, mab ' let transmitter turn on add t1, cnt waitcnt t1, #0 mov b2btimer, b2b ' set b2b timing (ms) add b2btimer, cnt ' sync with system cntr ' dmx transmit loop tx_break andn outa, txmask ' tx := 0 mov t1, break ' setup for BREAK add t1, cnt waitcnt t1, mab ' hold for BREAK; setup for MAB tx_mab or outa, txmask ' tx := 1 waitcnt t1, #0 ' hold for MAB mov bufpntr, hub ' bufpntr := @dmxbuf[0] mov count, slots ' send N bytes tx_packet rdbyte t1, bufpntr ' get byte from buffer mov bittimer, bit1x0tix ' load bit timing add bittimer, cnt ' sync with system counter ' tx unrolled for best speed tx_bits andn outa, txmask ' start bit (0) waitcnt bittimer, bit1x0tix ' let timer expire, reload test t1, #(1 << 0) wc ' lsb first muxc outa, txmask waitcnt bittimer, bit1x0tix test t1, #(1 << 1) wc muxc outa, txmask waitcnt bittimer, bit1x0tix test t1, #(1 << 2) wc muxc outa, txmask waitcnt bittimer, bit1x0tix test t1, #(1 << 3) wc muxc outa, txmask waitcnt bittimer, bit1x0tix test t1, #(1 << 4) wc muxc outa, txmask waitcnt bittimer, bit1x0tix test t1, #(1 << 5) wc muxc outa, txmask waitcnt bittimer, bit1x0tix test t1, #(1 << 6) wc muxc outa, txmask waitcnt bittimer, bit1x0tix test t1, #(1 << 7) wc muxc outa, txmask waitcnt bittimer, bit1x0tix or outa, txmask ' 2 stop bits waitcnt bittimer, bit1x0tix waitcnt bittimer, bit1x0tix add bufpntr, #1 ' update buffer pointer djnz count, #tx_packet ' update tx'd count tjz b2b, #tx_break ' no waiting; restart now waitcnt b2btimer, b2b ' let b-to-b timer expire jmp #tx_break ' -------------------------------------------------------------------------------------------------- txmask res 1 ' mask for TX pin txemask res 1 ' mask for TXE pin slots res 1 ' slots to tx hub res 1 ' pointer (in hub) to levels b2b res 1 ' break-to-break (23 to 1000ms) break res 1 ' ticks in BREAK period (92us) mab res 1 ' ticks in MAB period (12us) bit1x0tix res 1 ' ticks in one bit (4us) b2btimer res 1 ' break-to-break timer bufpntr res 1 ' hub address of byte to tx count res 1 ' bytes to tx bittimer res 1 ' bit timer t1 res 1 ' work vars t2 res 1 fit 496 con { license } {{ Terms of Use: MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. }}