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
Bookmarks