title mpxplayer serial interface ;by tim worthington processor 16F873 ;at 20Mhz radix dec __config b'11111101000010' include ; ; ;functions of i/o ;0a i analog for pushbuttons ;1a i cd loaded? (high = loaded, low = empty) ;2a o nc ;3a o nc ;4a o nc ;5a o nc ;0b o power on/off (relay in parallel with power switch) ;1b o led 10 (sled1) ;2b o led 9 (sled0) ;3b o led 5 ;4b o led 1 ;5b o led 2 ;6b o led 3 ;7b o led 4 ;0c i rotary A ;1c i rotary A ;2c i rotary B ;3c i rotary B ;4c o monitor on/off (screen saver) ;5c i power switch status (high = off, low = on) ;6c o serial tx ;7c i serial rc ; ;flags #define _scan flag, 0 ;1: waiting for the adc to become ready and privide a reading #define _keysent flag, 1 ;1: a key has been pressed - will clear when adc reads a zero (no key) #define _dira flag, 2 ;0: clockwise 1: anti-clockwise #define _dirb flag, 3 ;previous direction status #define _ssenable flag, 4 ;1: screen saver enabled 0: disabled #define _cdstatus flag, 5 #define _startup flag, 6 #define _powstatflg flag, 7 ;inputs/outputs #define _scrsav portc, 4 ;0: screen saver active (blanking) 1: not active (counting?) #define _skey0activ portb, 2 ;1: special key 0 is active 0: it isn't #define _skey1activ portb, 1 ;1: special key 1 is active 0: it isn't #define _cdload porta, 1 ;1: loaded 0: empty #define _powstat portc, 5 ;constants ENC_DIV equ 12 ;(t * 4) / ENC_DIV = outputs per revolution (t is no. of teeth on encoder wheel) DBTIME equ 8 ;DBTIME * 0.8mS = debounce stable time SKEY0 equ 11 ;which key is special key 0? (1-20 (ascii value)) SKEY1 equ 12 ;which key is special key 1? (1-20 (ascii value)) VACLWISE equ 21 ;value sent when turning enca clockwise VAANTICLW equ 22 ;value sent when turning enca anti-clockwise VBCLWISE equ 23 ;value sent when turning encb clockwise VBANTICLW equ 24 ;value sent when turning encb anti-clockwise VBSKEY0CW equ 25 ;value sent when turning encb clockwise while special key 0 active VBSKEY0ACW equ 26 ;value sent when turning encb anti-clockwise while special key 0 active VBSKEY1CW equ 27 ;value sent when turning encb clockwise while special key 1 active VBSKEY1ACW equ 28 ;value sent when turning encb anti-clockwise while special key 1 active VCDLOAD equ 29 ;value sent when cd loaded (delayed by CDTIME) VCDEJECT equ 30 ;value sent when cd ejects VPOWEROFF equ 31 ;value sent when power switch has been turned off SSTIME equ 67 ;SSTIME * 26.84 seconds = screen saver time CDTIME equ 38 ;CDTIME * 0.105 seconds = pause before cd load byte sent POWTIME equ 20 ;POWTIME * 0.105 seconds = time allowed to power off gracefuly cblock 0x20 flag key key.old debounce enca ;holds current encoder data in bits 0 and 1 enca.old ;holds previous encoder data in bits 0 and 1 enca.dir ;holds current direction status in bit 1 enca.div ;used for dividing the encoder's output encb ;holds current encoder data in bits 2 and 3 encb.old ;holds previous encoder data in bits 2 and 3 encb.dir ;holds current direction data in bit 3 encb.div tmp ssdiv ssdiv2 cddiv powdiv endc org 0x00 goto init ;receive byte ;use a goto table to convert rec byte --> action recbyte btfss pir1, rcif ;check if byte has been received goto tick.1sec ;bcf pir1, rcif btfsc rcsta, ferr goto rec_error btfsc rcsta, oerr goto rec_error movf rcreg, w movwf tmp sublw 29 btfss status, c goto tick.1sec clrf pclath ;sets pclath to 0x00 movf tmp, w addwf tmp, w ;double the value of received byte addwf pcl, f ;output lookup table- ;it's positioned specialy to avoid a page boundary bcf portb, 0 ;0 goto tick.1sec bsf portb, 0 ;1 goto tick.1sec bcf portb, 1 ;2 goto tick.1sec bsf portb, 1 ;3 goto tick.1sec bcf portb, 2 ;4 goto tick.1sec bsf portb, 2 ;5 goto tick.1sec bcf portb, 3 ;6 goto tick.1sec bsf portb, 3 ;7 goto tick.1sec bcf portb, 4 ;8 goto tick.1sec bsf portb, 4 ;9 goto tick.1sec bcf portb, 5 ;10 goto tick.1sec bsf portb, 5 ;11 goto tick.1sec bcf portb, 6 ;12 goto tick.1sec bsf portb, 6 ;13 goto tick.1sec bcf portb, 7 ;14 goto tick.1sec bsf portb, 7 ;15 goto tick.1sec nop ;16 not used goto tick.1sec nop ;17 not used goto tick.1sec bcf porta, 2 ;18 goto tick.1sec bsf porta, 2 ;19 goto tick.1sec bcf porta, 3 ;20 goto tick.1sec bsf porta, 3 ;21 goto tick.1sec bcf porta, 4 ;22 goto tick.1sec bsf porta, 4 ;23 goto tick.1sec bcf porta, 5 ;24 goto tick.1sec bsf porta, 5 ;25 goto tick.1sec bcf portc, 4 ;26 goto tick.1sec bsf portc, 4 ;27 goto tick.1sec goto ssdisable ;28 disable screen saver nop ssenable clrf ssdiv ;29 enable screen saver when song finished movlw SSTIME movwf ssdiv2 bsf _startup ;enable tx bsf _ssenable bsf _scrsav goto tick.1sec ssdisable bcf _ssenable bsf _scrsav ;if it somehow got here while asleap... goto tick.1sec rec_error movf rcreg, w bcf rcsta, cren bsf rcsta, cren goto tick.1sec ;initialisation init movlw b'10010000' movwf rcsta bsf status, rp0 movlw b'11010011' movwf option_reg movlw b'00000000' movwf trisb movlw b'10101111' movwf trisc movlw b'00000011' movwf trisa movlw b'00001110' movwf adcon1 movlw b'00100110' movwf txsta movlw 129 movwf spbrg bcf status, rp0 clrf porta clrf portb clrf portc clrf tmr1l clrf tmr1h movlw b'01000001' movwf adcon0 movlw b'00110001' movwf t1con movlw SSTIME movwf ssdiv2 movlw DBTIME movwf debounce movlw ENC_DIV movwf enca.div movwf encb.div movlw CDTIME movwf cddiv movlw POWTIME movwf powdiv ;start of 0.1s clock for the cd, power and screen saver routines tick.1sec btfss pir1, tmr1if ;tmr1 overflow? goto keyscan bcf pir1, tmr1if ;check to see if a cd has been inserted into (or ejected from) the cd-rom drive cdload btfss _cdload goto cdempty btfsc _cdstatus goto power decfsz cddiv ;wait a few seconds for the cd to spin up goto power movlw VCDLOAD ;somebody just inserted a cd call txbyte ;let's send a byte to celebrate movlw CDTIME movwf cddiv bsf _cdstatus goto power cdempty btfss _cdstatus goto power movlw VCDEJECT call txbyte bcf _cdstatus ;take a look at the state of the power switch power btfss _powstat goto scrsav movlw VPOWEROFF ;it's off!!! shutdown! shutdown! shutdown! movwf txreg decfsz powdiv goto scrsav bcf portb, 0 ;shutdown taking too long...try to force power off ;screen saver will shut off monitor after it counts to SSTIME * 26.8 seconds scrsav btfss _ssenable goto keyscan incfsz ssdiv, f ;divider 1 goto keyscan decfsz ssdiv2, f ;divider 2 goto keyscan movlw SSTIME movwf ssdiv2 bcf _ssenable bcf _scrsav ;save screen (turn off monitor) ;end of 0.1s clock ;button inputs ;reads voltage on an0 and determines which button has been depressed (if any) keyscan btfss intcon, t0if goto adcwait adcsetup bsf adcon0, go bsf _scan bcf intcon, t0if adcwait btfss _scan ;has the adc finished? goto a_checkenc btfsc adcon0, not_done goto a_checkenc bcf _scan call fetchkey movwf key movf key, f btfss status, z goto notzero zero bcf _keysent movlw DBTIME movwf debounce goto a_checkenc notzero btfsc _keysent ;has the previous key been released? goto a_checkenc movf key, w subwf key.old, f btfsc status, z goto keysame keydiff movlw DBTIME movwf debounce movf key, w movwf key.old goto a_checkenc keysame movf key, w movwf key.old decfsz debounce, f goto a_checkenc movlw ENC_DIV ;let's reset enca/b dividers movwf enca.div movwf encb.div movf key, w xorlw SKEY0 btfsc status, z goto is_skey0 movf key, w xorlw SKEY1 btfsc status, z goto is_skey1 normalkey movf key, w call txbyte bsf _keysent goto a_checkenc is_skey0 bsf _keysent ;toggle skey0 btfsc _skey0activ goto clr_skey0 bsf _skey0activ bcf _skey1activ goto a_checkenc clr_skey0 bcf _skey0activ goto a_checkenc is_skey1 bsf _keysent ;toggle skey1 btfsc _skey1activ goto clr_skey1 bsf _skey1activ bcf _skey0activ goto a_checkenc clr_skey1 bcf _skey1activ ;goto a_checkenc ;rotary encoder a ;has the control been rotated? if so find out which direction a_checkenc movf portc, w andlw b'00000011' movwf enca xorwf enca.old, w btfsc status, z goto b_checkenc ;nothing changed - do nothing subwf b'00000011', w btfsc status, z goto b_checkenc ;both bits changed - pretend nothing happened bcf status, c rlf enca.dir, f movf enca, w movwf enca.old xorwf enca.dir, f btfss enca.dir, 1 goto a_anticlw a_clwise btfsc _dira goto a_ndirclw decfsz enca.div, f goto b_checkenc movlw VACLWISE call txbyte movlw ENC_DIV movwf enca.div goto b_checkenc a_ndirclw movlw ENC_DIV - 1 ;change of direction, reload enc.div movwf enca.div ;reset divider movwf encb.div ;might as well reset encb's divider while we're here bcf _dira goto b_checkenc a_anticlw btfss _dira goto a_ndiranti decfsz enca.div, f goto b_checkenc movlw VAANTICLW call txbyte movlw ENC_DIV movwf enca.div goto b_checkenc a_ndiranti movlw ENC_DIV - 1 ;change of direction, reload enc.div movwf enca.div ;reset divider movwf encb.div ;might as well reset encb's divider while we're here bsf _dira ;goto b_checkenc ;rotary encoder b ;same as rotary enc a except byte can be modified by the special buttons' status b_checkenc movf portc, w andlw b'00001100' movwf encb xorwf encb.old, w btfsc status, z goto recbyte btfsc status, z goto recbyte bcf status, c rlf encb.dir, f movf encb, w movwf encb.old xorwf encb.dir, f btfss encb.dir, 3 goto b_anticlw b_clwise btfsc _dirb goto b_ndirclw decfsz encb.div, f goto recbyte movlw VBCLWISE btfsc _skey0activ movlw VBSKEY0CW btfsc _skey1activ movlw VBSKEY1CW call txbyte movlw ENC_DIV movwf encb.div goto recbyte b_ndirclw movlw ENC_DIV - 1 movwf encb.div movwf enca.div bcf _dirb goto recbyte b_anticlw btfss _dirb goto b_ndiranti decfsz encb.div, f goto recbyte movlw VBANTICLW btfsc _skey0activ movlw VBSKEY0ACW btfsc _skey1activ movlw VBSKEY1ACW call txbyte movlw ENC_DIV movwf encb.div goto recbyte b_ndiranti movlw ENC_DIV - 1 movwf encb.div movwf enca.div bsf _dirb goto recbyte ; subroutines ;send serial byte ;check to see if the screen saver is active. if not then reset ss dividers + tx byte ;if so then deactivate the screen saver (turn monitor back on) and discard byte txbyte btfss _scrsav goto ssactive movwf txreg clrf ssdiv movlw SSTIME movwf ssdiv2 return ssactive btfss _startup ;don't let monior come on until _startup bit received return bsf _ssenable bsf _scrsav return ;lookup table converts (ADC output)/4 --> ascii byte for tx fetchkey bcf status, c rrf adresh, f bcf status, c rrf adresh, f movlw low keytable ;adds one to pclath if crossing a page boundary addwf adresh, w ; movlw high keytable btfsc status, c addlw 1 movwf pclath movf adresh, w call keytable return keytable addwf pcl, f ; ascii adc value retlw 20 ;0-3 retlw 20 ;4-7 retlw 20 ;8-11 retlw 19 ;12-15 retlw 19 ;16-19 retlw 19 ;20-23 retlw 18 ;24-27 retlw 18 ;28-31 retlw 18 ;32-35 retlw 17 ;36-39 retlw 17 ;40-43 retlw 17 ;44-47 retlw 16 ;48-51 retlw 16 ;52-55 retlw 16 ;56-59 retlw 15 ;60-63 retlw 15 ;etc, etc retlw 15 retlw 14 retlw 14 retlw 14 retlw 13 retlw 13 retlw 13 retlw 12 retlw 12 retlw 12 retlw 11 retlw 11 retlw 11 retlw 10 retlw 10 retlw 10 retlw 9 retlw 9 retlw 9 retlw 8 retlw 8 retlw 8 retlw 7 retlw 7 retlw 7 retlw 6 retlw 6 retlw 6 retlw 5 retlw 5 retlw 5 retlw 4 retlw 4 retlw 4 retlw 3 retlw 3 retlw 3 retlw 2 retlw 2 retlw 2 retlw 1 retlw 1 retlw 1 retlw 0 retlw 0 retlw 0 retlw 0 end