Quote Originally Posted by picchip View Post
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.


Code:
; -------------------------------------------------------------
; 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