; ------------------------------------------------------------- ; Based on 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 ; 5 colour pixel working 1 red, 2 green, 3 blue, 4 amber, 5 white. ; 17/01/2009 DMX program Address , using a jumper on A2 ; 18/01/2009 DMX Address works reading out of eeprom, just got to get the writing to eeprom sorted ; ; 16F688 Chip Connections ; ; PORT PIN CONFIGURATION ; ================================== ; A0 13 PGM Header ; A1 12 PGM Header ; A2 11 DMX program header ; 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 ; Brownout = Enabled, SBODEN disabled ; Switchover = Disabled ; Failsave = Disabled ; Code = As needed ; EEPROM = As needed ; bits should be 33F4 CBLOCK 0x20 wsave ssave psave redval greenval blueval amberval whiteval pwmpins rednew greennew bluenew ambernew whitenew IntCount dmxhighbyte dmxlowbyte skiphigh skiplow temp timer COUNT dmxproghigh dmxproglow pskiphigh pskiplow newdmxhigh newdmxlow checkdmxlow checkdmxhigh loopcnt loopcnt2 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 ;bcf pwmpins,0 ;bcf pwmpins,1 ;bcf pwmpins,2 ;movf pwmpins,w ;movwf PORTC 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 ;btfsc STATUS,Z btfss STATUS,Z goto ambercheck bsf pwmpins,2 ; movf pwmpins,w ; movwf PORTC 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 memclear ; check if the programming jumper is set. call loop1sec call loop1sec btfss PORTA,2 ; is the jumper in place ? call dmxprogram ; if the jumper is there (ie porta2 = 0) goto dmxprogram ; else continue ; changed line 209 form goto to a call for auto return from programing-RC- call ledflash ; flash the leds in a funky way. call pwminit ; Initialize TIMER0 and enable TMRO interrupts call rxinit ; Set up the EUSART to receive DMX at 250,000 baud call dmxread ;read the dmx address from the eeprom 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,OERR goto hardwareor 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 andlw 0xff bnz dmxcapture ; 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 five bytes? Repeat Ad Absurdum. ; ----------- Routines for Starting the Chip --------- chipinit BANK0 ; Memory bank 0 clrf PORTA ; All PORTA Pins off movlw 07h movwf CMCON0 ; disable the comparators bcf WDTCON,0 ; turn off WDT bcf ADCON0,0 ; turn off AD Convert clrf PORTC ; All PORTC Pins off BANK1 ; Switch to memory bank 1 clrf ANSEL ; Turn off A/D Converters ; setup porta & portc bcf TRISC,0 ; Red Drive Pin bcf TRISC,1 ; Green Drive Pin bcf TRISC,2 ; Blue Drive Pin bcf TRISC,3 ; c3 output needs to low go read dmx. bsf TRISA,2 ; A2 as input (programing jumper) bcf TRISA,4 ; White Drive Pin bcf TRISA,5 ; Amber Drive Pin bsf WPUA,2 ; Set weak pull up for porta2 ; setup internal oscillator bsf OSCCON,6 ; Set these three bsf OSCCON,5 ; bits to enable the bsf OSCCON,4 ; 8 MHz internal oscillator BANK0 ; Memory bank 0 ;bsf PORTC,0 ;BSF PORTC,1 ;bsf PORTC,2 return ; ----------- Memory Clear --------- memclear BANK0 movlw 00h ; reset all memory movwf redval movwf rednew ; reset rednew movwf greenval movwf greennew ; reset greennew movwf blueval movwf bluenew ; reset bluenew movwf amberval movwf ambernew ; reset ambernew movwf whiteval movwf whitenew ; reset whitenew return ; ----------- Rx init --------- rxinit BANK0 bcf RCSTA,CREN ;ADDED THIS TO RESET OERR FLAG WHEN RETURNING FROM ;PROGRAMING bsf PORTC,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 ; ----------- pwm init --------- pwminit movlw B'10100000' ; Enable global and TMR0 interrupts movwf INTCON BANK1 clrf OPTION_REG ; No prescaler for TMR0 needed BANK0 return ; ---- SRM fancy flash when power is first applied. ripped after seeing a bought pixel from JEC. ledflash bcf PORTC,0 ; Turn on Red call loop bcf PORTC,0 ; Turn off Red bsf PORTC,1 ; Turn on Green call loop bcf PORTC,1 ; Turn off Green bsf PORTC,2 ; Turn on Blue call loop bcf PORTC,2 ; Turn off Blue bsf PORTA,4 ; Turn on White call loop bcf PORTA,4 ; Turn off White bsf PORTA,5 ; Turn on amber call loop bcf PORTA,5 ; Turn off amber bsf PORTA,4 ; Turn on White call loop bcf PORTA,4 ; Turn off White bsf PORTC,2 ; Turn on Blue call loop bcf PORTC,2 ; Turn off Blue bsf PORTC,1 ; Turn on Green call loop bcf PORTC,1 ; Turn off Green bsf PORTC,0 ; Turn on Red call loop bcf PORTC,0 ; Turn off Red return ; ----------- loop1sec ------------ loop1sec call loop call loop call loop call loop return ; ----------- 250ms delay --------- loop movlw d'250' ; 250mS call WaitNms ; return ; ----------------- WaitNms -------- WaitNms movfw timer ; timer_local=W loopNms movlw d'248' ; revised value due to extras call Wait1ms ; goto $+1 ; 2 clock cycles decfsz timer,f ; Dec loop variable and check goto loopNms ; No, keep looping return ; Yes, Nms done ; ----------------- 1 ms delay ------- Wait1ms movlw d'255' ; Initial value - tweak if req. movwf COUNT loop1ms decfsz COUNT,1 goto loop1ms ; No, keep looping return ; Yes, 1ms done ; ----------------- maxcheck ------- 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 ;----------------hardware error--------- hardwareor ; ADDED TO RESET USART FOR DMX DATA bcf RCSTA,CREN ;IF DATA LINE FAILURE, WORKS NOT SURE IF IT IS THE bsf RCSTA,CREN ;RIGHT WAY goto dmxcapture ;----------------progerror------------------ progerror ; ADDED TO RESET USART FOR DMXPROGRAM bcf RCSTA,CREN ; IF DATA LINE FAILURE same as above bsf RCSTA,CREN goto dmxprogcapture ; ----------------- dmxread ------- dmxread ; read the dmx address from the eeprom ; dmxhighbyte from eeprom addr 00 ; dmxlowbyte from eeprom 01 ; first read the dmxhighbyte BANK1 movlw 0x00 ; eeprom addr movwf EEADR ; data address to read bcf EECON1, EEPGD ; point to adata memory bsf EECON1, RD ; EE read movf EEDAT, w ; w = eeprom data. BANK0 movwf dmxhighbyte ; now in dmxhighbyte mem location ; now read the dmxlowbyte BANK1 movlw 0x01 ; eeprom addr movwf EEADR ; data address to read bcf EECON1, EEPGD ; point to adata memory bsf EECON1, RD ; EE read movf EEDAT, w ; w = eeprom data. BANK0 movwf dmxlowbyte ; now in dmxlowbyte return ; ----------------- dmxprogram ------- dmxprogram ;call rxinit dmxprogramstart BANK0 bsf PORTC,0 ; Turn on amber call loop1sec bcf PORTC,0 ; Turn off amber call loop1sec bsf PORTC,0 ; Turn on amber call loop1sec bcf PORTC,0 ; Turn off amber call loop1sec bsf PORTC,0 ; Turn on amber call loop1sec call loop1sec call loop1sec call loop1sec call loop1sec bcf PORTC,0 ; Turn off amber call loop1sec ; first turn on rxinit ; bcf RCSTA,CREN ;REMOVED AND ADDED TO rxinit RESETS USART OERR call rxinit ; ---------- Set DMX Program Start Address Here ------------------------- movlw D'0' ; Set the DMX Address here. It's a 16 bit movwf dmxproghigh ; number in two 8-bit bytes. Highbyte can be movlw D'1' ; [0 2] and lowbyte can be [0 256]. Overall, then, movwf dmxproglow ; the range is from [0 512]. ; ---------- End DMX Program Start Address Programming ------------------- BANK0 dmxprogcapture movf dmxproghigh,w ; Skipcounter is used to detmine how many movwf pskiphigh ; received data bytes are skipped before the RGB movf dmxproglow,w ; data is collected. Load skipcounter with movwf pskiplow ; the DMX address from above... movf pskiplow,f ; ... then decrement it by one btfsc STATUS,Z ; so we know how many channels to ignore before the decf pskiphigh,f ; useful data arrives. We'll see more of the decf pskiplow,f ; skipcounter a bit farther down the page. progbreak 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 progbreak ; 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 progstart btfss PIR1,RCIF ; Now that a break signal is detected, goto progstart ; loop until a new byte is received *without* btfsc RCSTA,OERR goto progerror btfsc RCSTA,FERR ; a framing error. If all is well AND the goto progstart ; new byte is zero (which means the start code movf RCREG,w ; is also zero, it's okay to begin gethering channel ; data andlw 0xff ;check zero start code bnz dmxprogcapture ; RIGHT NOW WE'RE NOT TESTING FOR A ZERO START CODE. THIS WILL BE CHANGED IN FUTURE ; VERSIONS OF THE CODE. BUYER BEWARE! movf dmxproghigh,1 ; Here check to see if the highbyte is btfss STATUS,Z ; zero. If it is,check to see if the goto progcapture ; lowbyte is 1. If 1, grab the next three bytes movf dmxproglow,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 waitforhigh progcapture btfss PIR1,RCIF ; If we're here, it's because the start address is goto progcapture ; greater than one. Hover until a byte is received. movf RCREG,w ; Then, capture & move to 'w'... movf pskiplow,f ; ...decrement the skip counter... btfsc STATUS,Z ; (all sixteen bits of it) decf pskiphigh,f decf pskiplow,f ; ...and see if we've reached the start address. movf pskiplow,1 ; If the skip counter now equals zero, we know btfss STATUS,Z ; that we need to gather the next three bytes goto progcapture ; and save them as RGB data. If the counter is movf pskiphigh,1 ; still nonzero, loop back and do it again. btfss STATUS,Z goto progcapture waitforhigh btfss PIR1,RCIF ; Wait until 'high' byte is received goto waitforhigh ; once it arrives, store it in 'newdmxhigh' movf RCREG,w movwf newdmxhigh waitforlow btfss PIR1,RCIF ; process is repeated for 'newdmxlow' goto waitforlow movf RCREG,w movwf newdmxlow ; goto dmxloop ; now we've got both bytes in locations newdmxhigh & newdmxlow this will be the new dmx address to be ; programmed. ;---------------checking to make sure no zero address------------- ; HAD A PROBLEM WITH MISSING THE PROGRAMED ADDRESS, EACH TIME IT WOULD MISS ; WHEN READING THE EEPROM WITH PICKIT2 IT WOULD SHOW ZEROS IN THE ADDRESS BLOCKS 0x00 AND 0x01 ; THIS WAS ADDED CHECKS IF BOTH newdmxhigh AND newdmxlow WERE ZERO IT GOES BACK TO START OF dmxprogram ; THIS CAN BE CHECKED BY SETTING BOTH HIGH AND LOW BYTES TO ZERO ON DMX TRANSMITTER, IT WILL KEEP LIGHTING RED LED ; AND CHECKING FOR ADRESS ABOVE 0 ;HIGH BYTE ZERO CHECK movlw .0 ;LOAD 0 INTO w REG subwf newdmxhigh,w ;SUBTRACT newdmxhigh btfss STATUS,Z ; IF Z = 1 CHECK LOW BYTE goto progprom ; IF Z = 0 VALID ADDRESS PROGRAM eeprom ;LOW BYTE ZERO CHECK movlw .0 ;LOAD 0 INTO W REG subwf newdmxlow,w ;SUBTRACT newdmxlow btfss STATUS,Z ; IF Z = 1 GOTO dmxprogram goto progprom ; IF Z = 0 VALID ADDERSS PROGRAM eeprom ;DMXPROGRAM goto dmxprogram ;IT MISSED ADDRESS GO BACK TO START ; First Cut, not testing for invalid addresses or if its the same as what is already programmed. ; things to do later on. progprom bsf PORTC,2 ; Turn on Blue call loop1sec call loop1sec bcf PORTC,2 ; Turn off Blue call loop1sec ; Program the high byte ; clear the EEIF interupt bit bcf PIR1,EEIF BANK1 movlw D'0' ;DMX HIGH ADDRESS movwf EEADR ;Data Memory Address to write BANK0 movf newdmxhigh,w ; BANK1 movwf EEDAT ;Data Memory Value to write bcf EECON1, EEPGD ;Point to DATA memory bsf EECON1, WREN ;Enable writes bcf INTCON, GIE ;Disable INTs. btfsc INTCON, GIE ;SEE AN576 goto $-2 movlw 0x55 ; movwf EECON2 ;Write 55h movlw 0xAA ; movwf EECON2 ;Write AAh bsf EECON1, WR ;Set WR bit to begin write ; need to keep checking for EEIF being set (write complete.) BANK0 ee_wri btfss PIR1,EEIF goto ee_wri bcf PIR1,EEIF ; clear the EEIF write complete interupt BANK1 bcf EECON1, WREN ;Disable writes ; Program the low byte movlw D'1' ;DMX LOW ADDRESS movwf EEADR ;Data Memory Address to write BANK0 movf newdmxlow,w BANK1 movwf EEDAT ;Data Memory Value to write bcf EECON1, EEPGD ;Point to DATA memory bsf EECON1, WREN ;Enable writes bcf INTCON, GIE ;Disable INTs. btfsc INTCON, GIE ;SEE AN576 goto $-2 movlw 0x55 ; movwf EECON2 ;Write 55h movlw 0xAA ; movwf EECON2 ;Write AAh bsf EECON1, WR ;Set WR bit to begin write ; need to keep checking for EEIE being set (write complete.) BANK0 ee_wri1 btfss PIR1,EEIF goto ee_wri1 ; bcf EECON1, WREN ;Disable writes ; BANK0 bcf PIR1,EEIF ; clear the EEIF write complete interupt BANK1 bcf EECON1,WREN BANK0 movlw .5 movwf loopcnt progend bsf PORTC,1 ; Turn on Green call loop call loop bcf PORTC,1 ; Turn off Green call loop call loop decfsz loopcnt,f goto progend return ; ----------------- end! ------- END