; ------------------------------------------------------------- ; 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 White Drive ; OSC1 2 Amber Drive ; GND 14 GND ; VCC 1 Vcc ; list p=16F688 ; list directive to define processor #include ; 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 ; Code = As needed ; Brownout = Enabled, SBODEN disabled ; Switchover = Disabled ; Failsave = Disabled ; EEPROM = As needed CBLOCK 0x20 wsave ssave psave redval greenval blueval amberval whiteval pwmpins rednew greennew bluenew ambernew whitenew 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 clrf pwmpins ; We've been through 256 cycles of PWM, so it's time movf pwmpins,w ; to reset everything ; pwmpins is a byte variable. Bytes are cleared ; here (turning off red, gren and blue drive pins) movwf PORTC ; and the entire byte is pushed out to PORTC. ; (added amber and white) movwf PORTA ; and the entire byte is pushed out to PORTA. 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 movf ambernew,w ; (added amber and white) movwf amberval movf whitenew,w movwf whiteval 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 btfss STATUS,Z goto ambercheck bsf pwmpins,2 ambercheck movf amberval,w ; (added amber and white) subwf IntCount,w btfss STATUS,Z goto whitecheck bsf pwmpins,5 whitecheck movf whiteval,w subwf IntCount,w btfsc STATUS,Z bsf pwmpins,4 movf pwmpins,w ; move the pin variable out to PORTA & PORTC movwf PORTC movwf PORTA ; 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 256]. 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 synchronzie 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! waitforamber btfss PIR1,RCIF ; process is repeated for amber data goto waitforamber movf RCREG,w movwf temp call maxcheck movwf ambernew waitforwhite btfss PIR1,RCIF ; process is repeated for white data goto waitforwhite movf RCREG,w movwf temp call maxcheck movwf whitenew 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 PORTA ; All PORTA 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 bcf TRISC,3 bcf TRISA,4 ; White drive pin bcf TRISA,5 ; Amber 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 bcf TRISA,2 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