PDA

View Full Version : DMX512 Reception + 3 * 8-Bit PWM Outputs



JEC
- 25th June 2007, 14:36
Code is written for a 14-pin 16F688 using the internal 8 MHz oscillator.

It receives DMX-512 and outputs 3 channels of PWM at a nearly 100 Hz refresh rate. Resolution is 8 bits minus 1, or 255 discrete states. PWM data comes from three consecutive DMX channels, and the start address can be set at compile time.

No crystal required! The internal PLL module in the EUSART oversamples the incoming serial data string at a high enough rate that the reception is very, very stable.

I tried like crazy to make this all work in PicBasic Pro, but even using assembly interrupts the timing wasn't working out. The PWM routine needs a 40 uS tick and it was just taking too long (even with an assembly interrupt routine) to get things done smoothly.

Nice thing is that it compiles to just a few hundred words. The chip has 4K of space, so there's lots of room for expansion.

This is my first-ever 100% assembly program, so there may be some rough edges in the code.

Source code, schematics & more can be found here:

http://www.response-box.com/rgblights

Comments and suggestions are always appreciated.

- JEC

Bronx68
- 10th September 2007, 17:10
JEC,
Do you sell these pcbs and devices?
Ray

JEC
- 10th September 2007, 18:28
Hi Ray,

We're working on a group buy for the pixels through an overseas fabricator.

I've ordered samples and they should be here shortly.

Pricing isn't final yet but there's a very good chance we'll be under $10 per pixel, assembled and tested. You provide a stable +12v supply and valid DMX.

The discussion about the group buy is over here:

http://www.christmasinshirley.com/forum/viewtopic.php?t=563

John

JEC
- 18th January 2008, 23:07
EDIT: Pixels are for sale now.

www.response-box.com/rgblights

picchip
- 14th February 2015, 02:44
hi,
is it possible to still get to see the code for this project? link does not work.
thanks

JEC
- 16th February 2015, 15:21
hi,
is it possible to still get to see the code for this project? link does not work.
thanks

Wow, it's been a long time since I wrote this. Sorry for the dead link. You might be interested in www.response-box.com/rgb these days.

This code was written to compile under MPLAB 8.6 (?) or so. No idea how it will run under the new X complier, but it's probably fine.

The code is pretty raw. Not a lot of error correction. But since I was controlling the DMX TX side as well, it was ok. My 25 copies have run flawlessly for six weeks every Christmas season since then. Back in 2006-2007 we shipped many hundreds of circuit boards with this code loaded inside, to say nothing of what people may have designed and programmed themselves. I expect there are thousands of copies floating around out there.



; -------------------------------------------------------------
; JECPWM_3.asm
; (c) 2007 John Chapman, Engineering Solutions Inc
; Inspired somewhat by Microchip's application notes
; for receiving DMX and generating software PWM.
; Note: The reference designs for these pixels are distributed
; under a Creative Commons license Attribution-ShareAlike 2.5

;
;
; 16F688 Chip Connections
;
; PORT PIN CONFIGURATION
; ==================================
; A0 13 PGM Header
; A1 12 PGM Header
; A2 11 N/C
; C0 10 Red Drive
; C1 9 Green Drive
; C2 8 Blue Drive
; C3 7 N/C
; C4 6 N/C
; C5 5 DMX512 RX
; MCLR 4 Vcc w/4.7K pullup
; OSC2 3 N/C
; OSC1 2 N/C
; GND 14 GND
; VCC 1 Vcc
;

list p=16F688 ; list directive to define processor
#include <p16F688.inc> ; REMOVE THE EXTRA SPACES BETWEEN < and > - processor specific variable definitions
errorlevel -302 ; suppress message 302 from list file

; Here include Configuration settings

; OSC = INTOSCIO
; Watchdog = Disabled
; Power up Timer = Disabled
; MCLR = Reset
; Brownout = Enabled, SBODEN disabled
; Switchover = Disabled
; Failsave = Disabled
; Code = As needed
; EEPROM = As needed

CBLOCK 0x20

wsave
ssave
psave
redval
greenval
blueval
pwmpins
rednew
greennew
bluenew
IntCount
dmxhighbyte
dmxlowbyte
skiphigh
skiplow
temp

ENDC

#define BANK0 bcf STATUS,RP0
#define BANK1 bsf STATUS,RP0

ORG 0x000 ; Main Program

clrf PCLATH
goto start

ORG 0x004 ; Interrupt handler goes here

pwmdrive
movwf wsave ; Store the usual bits when interrupting
swapf STATUS, w
clrf STATUS
movwf ssave
movf PCLATH, w
movwf psave

; ------- Actual Interrupt Routine Here --------------------

movlw D'223' ; TMR0 will roll over every 40.0 uS
movwf TMR0 ; which allows for 97.65 Hz PWM frequency
; Nice thing here is that even if we're receiving
; full-on DMX (44, 512-byte frames per second)
; the PWM routine can easily keep up. So some
; exciting effects should be possible.

DecIntCount
decfsz IntCount,F ; Decrement IntCount register. If it's zero, reset
goto redcheck ; the PWM routine. Otherwise, check each PWM value

PWMReset ; We've been through 256 cycles of PWM, so it's time
bcf pwmpins,0 ; to reset everything
bcf pwmpins,1 ; pwmpins is a byte variable. Bytes are cleared
bcf pwmpins,2 ; here (turning off red, gren and blue drive pins)
movf pwmpins,w ; and the entire byte is pushed out to PORTC.
movwf PORTC
movlw D'255' ; Reset the counter to allow 255 values per channel
movwf IntCount ; Also, since we're resetting, it's time to transfer
movf rednew,w ; the *new variables (which came from the DMX routine)
movwf redval ; to the working PWM variables.
movf greennew,w ; If the transfer takes place at any time *other* than
movwf greenval ; during a PWM reset, the LEDs flicker unflatteringly
movf bluenew,w ; We're guaranteed that new DMX data is made available
movwf blueval ; to the PWM routine as soon as possible
goto pwmexit ; Exit ISR

; Here we compare each of the duty cycles to the IntCount. If the values
; match, the corresponding pin is driven high. It's a bit counter-intuitive
; but it works. Note that if a value of 255 is received, it won't work. So
; the DMX routine limits PWM values to [0 254]. Which is good enough.

redcheck
movf redval,w
subwf IntCount,w
btfss STATUS,Z ; Are they equal?
goto greencheck ; Note that the *val variables are used only within
bsf pwmpins,0 ; the PWM routine. The actual DMX data is stored
greencheck ; in the *new variables, and transferred to these
movf greenval,w ; working variables only when the PWM rolls over
subwf IntCount,w
btfss STATUS,Z ; If the values are equal, set the bit (which turns
goto bluecheck ; on the corresponding drive pin)
bsf pwmpins,1

bluecheck
movf blueval,w
subwf IntCount,w
btfsc STATUS,Z
bsf pwmpins,2
movf pwmpins,w ; move the pin variable out to PORTC
movwf PORTC ; which will drive / clear the LEDs as needed

pwmexit

BCF INTCON,T0IF ; Clear the TMR0 interrupt flag
movf psave,w ; Restore whatever was happening prior
movwf PCLATH ; to the interrupt and get back to
swapf ssave,w ; gathering DMX data.
movwf STATUS
swapf wsave,f
swapf wsave,w

retfie ; Back to gathering DMX data



; ---------- Main Program Starts Here ----------------------------

start

call chipinit ; Initialize pins, oscillator, etc
call pwminit ; Initialize TIMER0 and enable TMRO interrupts
call rxinit ; Set up the EUSART to receive DMX at 250,000 baud

; ---------- Set DMX Start Address Here -------------------------
movlw D'0' ; Set the DMX Address here. It's a 16 bit
movwf dmxhighbyte ; number in two 8-bit bytes. Highbyte can be
movlw D'7' ; [0 2] and lowbyte can be [0 255]. Overall, then,
movwf dmxlowbyte ; the range is from [0 512].
; ---------- End DMX Start Address Programming -------------------

BANK0

dmxcapture

movf dmxhighbyte,w ; Skipcounter is used to detmine how many
movwf skiphigh ; received data bytes are skipped before the RGB
movf dmxlowbyte,w ; data is collected. Load skipcounter with
movwf skiplow ; the DMX address from above...

movf skiplow,f ; ... then decrement it by one
btfsc STATUS,Z ; so we know how many channels to ignore before the
decf skiphigh,f ; useful data arrives. We'll see more of the
decf skiplow,f ; skipcounter a bit farther down the page.

waitbreak
btfsc PIR1,RCIF ; Here we're waiting to see if a break occurs
movf RCREG,w ; in the data signal. Since we're *only*
btfss RCSTA,FERR ; receiving DMX, anything which generates a
goto waitbreak ; framing error in the EUSART will count as a break.
movf RCREG,w ; If a byte is received correctly, dump it and loop
; back until we get the error we need
; without being able to synchronize to the break signal
; there's no way to extract valid DMX data
waitforstart
btfss PIR1,RCIF ; Now that a break signal is detected,
goto waitforstart ; loop until a new byte is received *without*
btfsc RCSTA,FERR ; a framing error. If all is well AND the
goto waitforstart ; new byte is zero (which means the start code
movf RCREG,w ; is also zero, it's okay to begin gethering channel
; data

; RIGHT NOW WE'RE NOT TESTING FOR A ZERO START CODE. THIS WILL BE CHANGED IN FUTURE
; VERSIONS OF THE CODE. BUYER BEWARE!

movf dmxhighbyte,1 ; Here check to see if the highbyte is
btfss STATUS,Z ; zero. If it is,check to see if the
goto bytecapture ; lowbyte is 1. If 1, grab the next three bytes
movf dmxlowbyte,w ; which come through. If <> 1, go to the routine
xorlw D'1' ; which receives and discards bytes until the
btfsc STATUS,Z ; DMX address has been reached.
goto waitforred

bytecapture

btfss PIR1,RCIF ; If we're here, it's because the start address is
goto bytecapture ; greater than one. Hover until a byte is received.
movf RCREG,w ; Then, capture & move to 'w'...

movf skiplow,f ; ...decrement the skip counter...
btfsc STATUS,Z ; (all sixteen bits of it)
decf skiphigh,f
decf skiplow,f
; ...and see if we've reached the start address.
movf skiplow,1 ; If the skip counter now equals zero, we know
btfss STATUS,Z ; that we need to gather the next three bytes
goto bytecapture ; and save them as RGB data. If the counter is
movf skiphigh,1 ; still nonzero, loop back and do it again.
btfss STATUS,Z
goto bytecapture

waitforred
btfss PIR1,RCIF ; Wait until 'red' byte is received
goto waitforred ; once it arrives, store it in 'temp'
movf RCREG,w ; and call the 'maxcheck' routine. Since the
movwf temp ; PWM code only works for values between [0 254]
call maxcheck ; maxcheck will set levels of 0xFF to 0xFE
movwf rednew ; then store them in the proper bit bucket.
; remember the the *new variables are converted
; to *val variables when the PWM routine resets
; itself
waitforgreen
btfss PIR1,RCIF ; process is repeated for green data
goto waitforgreen
movf RCREG,w
movwf temp
call maxcheck
movwf greennew

waitforblue ; ...and for blue data
btfss PIR1,RCIF ; It is assumed that there will be enough DMX
goto waitforblue ; channels available to capture three bytes
movf RCREG,w ; per pixel. For this reason, we don't check to
movwf temp ; see if the DMX string has timed out anywhere.
call maxcheck ; Rather, once all three bytes have been received,
movwf bluenew ; the code loops back and waits for a new start code.
; Were we to 'run out' of channel data in here somewhere
; the code may behave strangely. Caveat Emptor!

goto dmxcapture ; Got all three bytes? Repeat Ad Absurdum.


; ----------- Routines for Starting the Chip ---------

chipinit
BANK0 ; Memory bank 0
clrf PORTC ; All PORTC Pins off
clrf CMCON0 ; Comparators aren't used either

BANK1 ; Switch to memory bank 1
bcf TRISC,0 ; Red Drive Pin
bcf TRISC,1 ; Green Drive Pin
bcf TRISC,2 ; Blue Drive Pin
clrf ANSEL ; Turn off A/D Converters
bsf OSCCON,6 ; Set these three
bsf OSCCON,5 ; bits to enable the
bsf OSCCON,4 ; 8 MHz internal oscillator
bcf TRISA,0 ; PORTA.0 and PORTA.1 were used
bcf TRISA,1 ; for testing and debugging, so set as outputs
return

rxinit
BANK0
bsf TRISC,5 ; PORTC.5 is input for DMX data to EUSART
clrf TXSTA ; Clear TXSTA register
movlw B'10010000' ; Serial Port and continuous receive enabled
movwf RCSTA
movlw D'1' ; for baud rate generator
movwf SPBRG
clrf SPBRGH ; This combination assures 0% error when
bsf BAUDCTL,BRG16 ; receiving DMX at 250,000 bits per second
return ; the PLL makes it possible to grab such
; high speed data without any error

pwminit
movlw B'10100000' ; Enable global and TMR0 interrupts
movwf INTCON
BANK1
clrf OPTION_REG ; No prescaler for TMR0 needed
BANK0
return



; ----------------- Other Subroutines Go Here

maxcheck

; Processes value which is stored in W.
; New value is also in W when routine exits

xorlw D'255' ; Here we're checking to see if a received
btfsc STATUS,Z ; byte is greater than 254. If it is,
goto exit ; set it to 254. If it's less than 254
movf temp,w ; leave it alone and the PWM routine will deal with
return ; it shortly
exit
movlw D'254'
return

END