; MAXIM MAX3100 SPI interface to address/data bus. title "PC serial port bus expander using MAX3100" ;History: ;20-03-2001 rewritten to use lookup table to decode command bits. ; implemented EE read and write, status query and loopback tests. ;21-03-2001 disabled interrupts while preparing for EE write operation. ; changed status response to "011 TO WRERR PD WR MF". ;22-03-2001 F2 & F3 control state of RTS line, handshake removed. ; UART FIFO enabled and queue checked by reading UART "R" bit. ; echo facility added. ; disable port initialisation / initialise from EE implemented. ;23-03-2001 status response changed to 011,WRERR,WR,/TO,CTS,MF. ; No bugs found when tested! ;01-02-2004 added routines to scan input channels, looking for ; changes in the output of the sync detector. If a change is ; found, the byte derived from the detector output is sent to ; the host. ;07-02-2004 Implemented an 8-byte input buffer to remove the need to pace ; commands being sent from the host or SAP. Various bug fixes. ; Tested and working. ;24-02-2004 Increased Baud rate to 57.6K to help with problem on SAP. ;27-02-2004 Added command 'FB' to force sending of sync status ;28-02-2004 Fixed bug where continuous MF tone would hold-up the sync ; detection routine. Fixed bug where MF tones could sometimes ; be reported twice. Added ability to enable and disable the ; auto sync detecting and reporting function ('F6' & 'F7'). __config _CP_OFF & _PWRTE_OFF & _WDT_ON & _XT_OSC #include "\repeater\include\P16F84.INC" #include "\repeater\include\3100.inc" #define bank_0 bcf STATUS,5 #define bank_1 bsf STATUS,5 page_0 macro bcf PCLATH,0 bcf PCLATH,1 endm page_1 macro bsf PCLATH,0 bcf PCLATH,1 endm ;PIC register usage: TXL equ H'10' ;UART transmit word TXH equ H'11' RXL equ H'12' ;UART receive word RXH equ H'13' SHFTO equ H'14' ;transmit shift data to UART SHFTI equ H'15' ;receive shift data from UART LPCNT equ H'16' ;bit shift counter PADDR equ H'17' ;parallel bus address PDATA equ H'18' ;parallel bus data SAVEW equ H'19' ;store to preserve W during interrupts SAVES equ H'1A' ;store to preserve STATUS during interrupts SAVEA equ H'1B' ;store to preserve PORTA (address port) during UART access SAVET equ H'1C' ;store to preserve TRISB during UART access EPORT equ H'1D' ;store for externally generated bus address EDATA equ H'1E' ;store for externally generated data/command SYSTAT equ H'1F' ;system status flags SYSTMP equ H'20' ;used to build SYSTAT from various bit sources ROTCMD equ H'21' ;used to shuffle command bits MODE equ H'22' ;mode configuring bits RTSMASK equ H'23' ;holds RTS bit for merging into TXH during Transmit SDCHAN equ H'24' ;channel number (0-7) connected to sync detector SDDEL1 equ H'25' ;used to delay sync detector channel switching SDDEL2 equ H'26' ;used to delay sync detector channel switching SDDATA equ H'27' ;one bit per SD port input. 0=no sig, 1=sig present SDBYTE equ H'28' ;one bit per video input. 0=no sig, 1=sig present SDSAVE equ H'29' ;previous state of SDBYTE (to see if it has changed) FSRSAVE equ H'2A' ;saves FSR contents during interrupts TXHSAVE equ H'2B' ;saves TXH contents during interrupts TXLSAVE equ H'2C' ;saves TXL contents during interrupts RXQHEAD equ H'2D' ;input queue write location RXQTAIL equ H'2E' ;input queue read location RXQ1 equ H'2F' ;top of queue RXQ2 equ RXQ1+1 RXQ3 equ RXQ2+1 RXQ4 equ RXQ3+1 RXQ5 equ RXQ4+1 RXQ6 equ RXQ5+1 RXQ7 equ RXQ6+1 RXQ8 equ RXQ7+1 ;bottom of queue ; macro to switch the data bus direction databus_out macro bank_1 movlw H'0F' ;ensure RB4:7 are outputs andwf TRISB,f bank_0 endm databus_in macro bank_1 movlw H'F0' ;ensure RB4:7 are inputs iorwf TRISB,f bank_0 endm org 0000 ;tell assembler to start at 000H start clrwdt ;clear watchdog clrf PORTA ;initial port values clrf PORTB goto init ; this routine is called whenever an interrupt occurs because a byte has ; arrived at the UART. If bits 7:5 specify the command type, other bits are ; data (3:0) or addresses (4:0). org 0004 ;interrupt vector rx_int movwf SAVEW ;preserve W during interrupt swapf STATUS,w ;preserve STATUS during interrupt movwf SAVES movf FSR,w ;save the FSR movwf FSRSAVE movf TXH,w ;save TXH and TXL movwf TXHSAVE movf TXL movwf TXLSAVE rx_byte clrf TXH ;0000 = UART read data command clrf TXL call utlk ;send command and receive the data bcf SYSTMP,1 ;pre-clear the copy of CTS bit btfsc RXH,1 ;check if UART detected CTS set bsf SYSTMP,1 ;if set, copy to the status byte ; add the received byte to the 'head' of the queue movf RXQHEAD,w ;use FSR to point to memory movwf FSR movf RXL,w ;pick up the data to write movwf INDF ;store in the queue incf RXQHEAD,f ;advance head position movf RXQHEAD,w ;check for wrap around sublw RXQ8+1 ;Z if beyond end of the queue btfsc STATUS,Z ;skip if not beyond the end goto reset_head int_end movf TXHSAVE,w ;restore TXH and TXL movwf TXH movf TXLSAVE,w movwf TXL movf FSRSAVE,w ;restore the FSR movwf FSR swapf SAVES,w ;restore STATUS movwf STATUS swapf SAVEW,f ;restore W swapf SAVEW,w bcf INTCON,INTF ;clear the INTF interrupt flag retfie ;ISR finished reset_head movlw RXQ1 ;wrap head position back to start movwf RXQHEAD goto int_end ; UART serial support routines. These are derived from the MAX3100 data ; sheet with port numbers modified for this application. ; send 16 bits from TXH and TXL, receive 16 bits to RXH and RXL. ; Bits 4 & 5 of TRISB may be changed by this routine so they are saved ; and restored before exit. utlk movf PORTA,w ;save current address port contents movwf SAVEA bank_1 movf TRISB,w ;save TRISB contents movwf SAVET bcf TRISB,D0 ;configure D0 for output bsf TRISB,D1 ;configure D1 for input bank_0 movlw UARTCS ;lower the UART CS movwf PORTA bcf PORTB,RW ;lower the R/W line movf TXH,w ;get TXH to w call byt8 ;send byte 1, receive byte 1 movwf RXH ;stash receive byte 1 movf TXL,w ;get TXL to w call byt8 ;send byte 2, receive byte 2 movwf RXL ;stash byte 2 bsf PORTB,RW ;raise R/W line movlw PARK ;use top address to deselect UART movwf PORTA bank_1 movf SAVET,w ;restore TRISB movwf TRISB bank_0 movf SAVEA,w ;restore address port contents movwf PORTA return ;done byt8 movwf SHFTO ;put in shift out movlw D'8' ;load loop counter movwf LPCNT b8lp rlf SHFTO,f ;get high bit to send bcf PORTB,D0 ;pre-clear D0 (data out to UART) btfsc STATUS,C ;see if D0 should be set bsf PORTB,D0 bsf PORTB,SCLK ;raise the SCLK line bcf STATUS,C ;pre-clear the carry flag btfsc PORTB,D1 ;check D1 (data in from UART) bsf STATUS,C ;set carry flag rlf SHFTI,f ;rotate D1 into shift in reg. bcf PORTB,SCLK ;drop the SCLK line decfsz LPCNT,f ;loop for all 8 bits goto b8lp movf SHFTI,w ;put result in w return ; UART transmit byte to host routine. Enter with byte to be sent in W. tx_to_host movwf TXL ;prepare for sending to UART movlw H'80' ;UART TX command iorwf RTSMASK,w ;merge the RTS bit movwf TXH ;prepare for sending to UART call utlk return ; Parallel bus support routines. Expects port address in PADDR, data in PDATA write_port databus_out movf PORTB,w ;get old data (ddddxxxx) andlw H'0F' ;lose data part (0000xxxx) swapf PDATA,f ;move new bits 0:3 to 4:7 (nnnn0000) iorwf PDATA,w ;merge old 0:3 with new 4:7 (nnnnxxxx) movwf PORTB ;set up data bus movf PADDR,w ;get the address movwf PORTA ;set up address bus bcf PORTB,RW ;pull R/-W low (write enable) nop ;allow time for polling port to read nop nop nop nop nop bsf PORTB,RW ;pull R/-W high again movlw PARK ;parking address (all deselected) movwf PORTA return ;done read_port databus_in movf PADDR,w ;get the address movwf PORTA ;set up address bus movf PORTB,w ;get the data from the bus andlw H'F0' ;mask out lower bits movwf PDATA ;save the data swapf PDATA,f ;move bits 4:7 to 0:3 movlw PARK ;parking address (all deselected) movwf PORTA return ;done ; EE memory support routines ; enter with data to be written to EE in EEDATA and EE address in W. write_to_EE movwf EEADR bank_1 ;to access EE control regs bcf INTCON,GIE ;disable interrupts bcf EECON1,WRERR ;clear the write error flag bsf EECON1,WREN ;write enable active movlw H'55' ;unlock sequence... movwf EECON2 movlw H'AA' movwf EECON2 bsf EECON1,WR ;initiate write to EE bsf INTCON,GIE ;re-enable interrupts bcf EECON1,WREN ;write enable inactive bank_0 return ; routines to decode and process commands and data to/from the ; host system or SAP. check_rx_queue movf RXQHEAD,w ;if head=tail theres nothing to do subwf RXQTAIL,w btfsc STATUS,Z ;Z if nothing queued goto update_status_byte decode_incoming movf RXQTAIL,w ;pick up pointer into queue movwf FSR incf RXQTAIL,f ;advance tail position movf RXQTAIL,w ;check for wrap around sublw RXQ8+1 ;Z if beyond end of the queue btfss STATUS,Z ;skip if not beyond the end goto decode movlw RXQ1 ;reset tail to start of queue movwf RXQTAIL decode movf INDF,w ;load data from queue tail andlw H'1F' ;isolate data bits movwf EDATA ;save them for later movf INDF,w ;load data from queue tail andlw H'E0' ;isolate command bits movwf ROTCMD ;move command bits 7:5 to 2:0 swapf ROTCMD,f bcf STATUS,C ;mustn't shift carry into b7 rrf ROTCMD,w ;leaving result in W addwf PCL,f ;form jump into table goto read_data ;000 = read current port goto read_EE ;001 = read from current EE address goto write_data ;010 = write current port goto write_EE ;011 = write to current EE address goto set_address ;100 = select address to use goto read_status ;101 = read current status goto loopback ;110 = loopback (echo) enable goto setmode ;111 = system configuration / reset setmode movf EDATA,w ;check data bits were 01010 (EA) sublw H'EA' ;111 = reset 01010 = validated btfsc STATUS,Z ;skip next if not the reset command wait_wdt goto wait_wdt ;wait until WDT cause a reset btfss EDATA,4 ;if bit 4 = 1, decode mode setting bits goto setmode_end ;otherwise abort movf EDATA,w ;pick up the command again (=1110xxxx) andlw H'0F' ;mask to keep only data bits addwf PCL,f ;form jump table goto echo_off ;F0 = data loopback mode off goto echo_on ;F1 = data loopback mode on goto rts_off ;F2 = disable RTS handshake goto rts_on ;F3 = enable RTS handshake goto EE_setup_off ;F4 = do not initialise ports at reset goto EE_setup_on ;F5 = set ports from EE table at reset goto autosync_off ;F6 = turn auto sync mode off goto autosync_on ;F7 = turn auto sync mode on goto setmode_end ;F8 = goto setmode_end ;F9 = goto setmode_end ;FA = goto syncs_to_TX ;FB = force sync status read and TX goto mode_to_TX ;FC = send mode byte to host goto mode_to_EE ;FD = copy mode setting to EE memory goto defs_to_EE ;FE = reset EE mode store to all off goto wait_wdt ;FF = for debugging - alternative reset ; set up the PIC configuration, interrupts and default port contents. init bank_1 clrf TRISA ;PORTA is always an output movlw B'00001001' ;PORTB 0(int) and 3(strobe) are inputs movwf TRISB bank_0 ; initial values for sync detection movlw H'07' ;first channel when checking sync i/p movwf SDCHAN clrf SDBYTE clrf SDSAVE movlw SDHOLD movwf SDDEL2 ; set power-up defaults: ; if mode set to initialise ports, use values stored in an EE memory table. ; The UART is then programmed to set speed and protocol. ; set mode of operation from value stored in EE memory location H'20' movlw H'20' ;address of mode byte store movwf EEADR ;select EE memory location bank_1 ;access bank 1 to reach EECON1 bsf EECON1,RD ;set read EE bit bank_0 ;back to bank 0 movf EEDATA,w ;retrieve the EE data movwf MODE ;set the mode btfss MODE,2 ;set up from EE table if bit 2 set goto init_UART ;otherwise skip initialisation stage defaults movlw H'20' ;counter for port addresses movwf PADDR init_ports decf PADDR,f ;count backwards through addresses movf PADDR,w ;copy address to EE address reg. movwf EEADR bank_1 ;access bank 1 to reach EECON1 bsf EECON1,RD ;set read EE bit bank_0 ;back to bank 0 movf EEDATA,w ;retrieve the EE data movwf PDATA ;prepare to write to port call write_port movf PADDR,f ;set status flags btfss STATUS,Z ;skip if all done goto init_ports init_UART clrf RTSMASK ;make sure unused bits are clear movlw H'E4' ;top of config word movwf TXH movlw BAUD ;bottom of config word movwf TXL call utlk ;write config to UART movlw H'40' ;confirm config by reading back movwf TXH clrf TXL call utlk movf RXL,w ;compare baud rates sublw BAUD btfss STATUS,Z ;Z set if baud bits match goto init_UART ;try again init_rx_queue movlw RXQ1 ;point head and tail to the queue movwf RXQHEAD movwf RXQTAIL set_interrupt bank_1 clrf OPTION_REG ;RB PU on, int on RB0 falling edge bank_0 bsf INTCON,INTE ;INTO(RB0) ints enabled bsf INTCON,GIE ;enable global ints ; announce program has started movlw H'F5' ;F5 = "ready" to host call tx_to_host ; this is the main program loop, it waits for a strobe signal to indicate ; that DTMF tones have been decoded then transmits the digit over the ; serial link to the host system. While idle, it updates the SYSTAT register ; in case it is requested by the host system. Bits are prepared in SYSTMP ; before being copied to SYSTAT as a status request is likely to be invoked ; while this code is executing and the bit assembly process is incomplete. ; b0 = MF in progress, b1 = copy of CTS, b2 = /TO, b3 = WR, b4 = WRERR. ; The routine also sequentially routes each the 8 video input signals to ; the sync detector and monitors for any changes in their state. If a change ; is noted, the current input status is sent to the host as two 4-bit bytes. ; Inputs 7:4 have response type H'1000' and bits 3:0 have response H'1010'. ; A "1" in the response indicates signal is present. main_loop clrwdt ;reset the watchdog timer btfss SYSTMP,MFSD ;have we already detected an mf digit goto check_new_strobe ;no, see if a new one has arrived... btfss PORTB,MFINT ;..yes, is it still there? bcf SYSTMP,MFSD ;strobe has gone, prepare for next goto check_syncs ;continue check_new_strobe ;strobe not already in process btfss PORTB,MFINT ;has a new strobe appeared? goto check_syncs ;no so continue ;a new MF strobe signal has ben recognised bsf SYSTMP,MFSD ;set status bit (= processing MF) movlw CS8870 ;safe address to select DTMF decoder movwf PADDR ;activate DTMF chip select call read_port ;pick up the digit movf PDATA,w andlw H'0F' ;mask to keep only data bits iorlw H'40' ;010 = data from 8870 header call tx_to_host ;send to host ;sync detector is only read once per (256 x SDHOLD) loops and is ;read prior to selecting the next input channel to allow as long ;as possible for the presence of sync to be seen. ;sync detector has active low o/p. (0 = video present) check_syncs btfss MODE,3 ;bypass if auto-sync mode is off goto check_rx_queue decfsz SDDEL1,f ;only switch once in 256xSDHOLD loops goto check_rx_queue decfsz SDDEL2,f goto check_rx_queue movlw SDHOLD movwf SDDEL2 movlw SYNCDET ;sync detector port movwf PADDR ;read the sync detector input call read_port movf PDATA,f ;set Z flag if no bits set bsf STATUS,C ;pre-set carry flag btfss STATUS,Z ;skip if all zero bcf STATUS,C ;clear carry if any bits were 1 rlf SDBYTE,f ;rotate carry into sync byte decf SDCHAN,f ;move on to next video i/p btfss SDCHAN,7 ;check for roll back to -1 goto set_sync_channel ;not all inputs checked yet movf SDBYTE,w ;pick up the bit map subwf SDSAVE,w ;see if pattern matches last scan btfsc STATUS,Z ;skip if inputs changed goto restart_sync_det ;sync detector changed since last scan of video inputs, ;TX two bytes (8x and 9x) where 'x' is sync hi and lo bits movf SDBYTE,w ;send b3:0 with B'1000' header andlw H'0F' iorlw H'80' call tx_to_host swapf SDBYTE,w ;exchange b7:4 with b3:0 andlw H'0F' iorlw H'A0' ;send b7:4 with B'1010' header call tx_to_host movf SDBYTE,w ;update with new scan info movwf SDSAVE restart_sync_det clrf SDBYTE ;ready for next scan movlw H'07' ;first sync channel to check movwf SDCHAN set_sync_channel movlw VSEL1s ;address sync channel input movwf PADDR movf SDCHAN,w ;set new input number movwf PDATA call write_port goto check_rx_queue ; routines to manipulate the program mode and special functions read_data movf EPORT,w ;get port number movwf PADDR call read_port ;read the data bus movf PDATA,w ;transfer read byte to TXL call tx_to_host ;send byte to host goto check_loopback read_EE movf EPORT,w ;read current address movwf EEADR ;select EE memory location bank_1 ;access bank 1 to reach EECON1 bsf EECON1,RD ;set read EE bit bank_0 ;back to bank 0 movf EEDATA,w ;retrieve the EE data andlw H'1F' ;mask to keep data bits only iorlw H'20' ;001xxxxx = data from EE header call tx_to_host ;send byte to host goto check_loopback write_data movf EPORT,w ;get port number movwf PADDR movlw H'0F' ;data bits mask andwf EDATA,w ;mask out all but valid data bits movwf PDATA call write_port goto check_loopback ; note: interrupts are disabled during EE write "unlock" sequence write_EE clrwdt ;don't restart while writing EE movf EDATA,w ;read the new data movwf EEDATA movf EPORT,w call write_to_EE goto check_loopback set_address movf EDATA,w ;RX data is a port or EE address movwf EPORT goto check_loopback read_status movf SYSTAT,w ;transfer system status byte to TXL andlw H'1F' ;mask to allow only status bits iorlw H'60' ;011xxxx = status response header call tx_to_host ;send byte to host check_loopback btfss MODE,0 ;skip next if echo mode is on goto update_status_byte loopback movf EDATA,w ;retrieve the last data received andlw H'1F' ;mask to keep only data bits iorlw H'C0' ;C0 = loopback header call tx_to_host ;send the data back with loopback hdr. goto update_status_byte ; mode settings and miscellaneous commands (jumps from 'setmode') echo_off bcf MODE,0 ;loopback enable bit = 0 goto setmode_end echo_on bsf MODE,0 ;loopback enable bit = 1 goto setmode_end rts_off bcf MODE,1 ;use RTS flow control bit = 0 clrf RTSMASK ;clear RTS bit in mask goto setmode_end rts_on bsf MODE,1 ;use RTS flow control bit = 1 bsf RTSMASK,1 ;set RTS bit in mask goto setmode_end EE_setup_off bcf MODE,2 ;don't use EE to initialise at reset goto setmode_end EE_setup_on bsf MODE,2 ;set ports from EE table at reset goto setmode_end autosync_off bcf MODE,3 ;disable auto detection of syncs goto setmode_end autosync_on bsf MODE,3 ;enable auto detection of syncs goto setmode_end mode_to_EE movf MODE,w ;get the current mode settings movwf EEDATA mode_to_EE_2 movlw H'20' ;address of mode byte in EE call write_to_EE goto setmode_end defs_to_EE clrf EEDATA ;put 00 in mode storage adress goto mode_to_EE_2 syncs_to_TX clrf SDSAVE ;force TX of sync status. Always... goto restart_sync_det ;...sends because TCG syncs present mode_to_TX movf MODE,w ;pick up the mode byte andlw H'1F' ;mask to keep only bits 4:0 iorlw H'20' ;make it look like EE read response call tx_to_host setmode_end goto check_loopback update_status_byte movlw H'03' ;mask to keep CTS & MF bits only andwf SYSTMP,f ;clear all bits but CTS copy btfsc STATUS,NOT_TO ;check timeout bit bsf SYSTMP,2 ;bit 2 = copy of timeout bit bank_1 ;to access EECON1 btfsc EECON1,WR ;check write in progress bit bsf SYSTMP,3 ;bit 3 = copy of WE bit btfsc EECON1,WRERR ;check EE write error bit bsf SYSTMP,4 ;bit 4 = copy of WRERR bit bank_0 movf SYSTMP,w ;update the real SYSTAT reg. movwf SYSTAT goto main_loop ; this table is not part of the program. It is a table of initialisation ; data to be written to the system ports when the processor starts up. org H'2100' ;EE storage area in .hex file de 00 ;00 tcg -> vmux1 iso channel de 00 ;01 tcg -> vmux1 monitor channel de 00 ;02 tcg -> vmux1 TX channel de 00 ;03 tcg -> vmux1 sync detector de 08 ;04 vmux2 0-3 all disabled de 08 ;05 de 08 ;06 de 08 ;07 de 07 ;08 amux1 to chan 7 de 07 ;09 amux2 to chan 7 de 00 ;0A UART,but not yet initialised de 00 ;0B 8870 (R/O) de 0F ;0C OSG message off de 04 ;0D TCG to page 4 de 0B ;0E IDSEL short ID de 00 ;0F USRB relays off de 00 ;10 VCR commands off de 00 ;11 METSAT lines off de 00 ;12 Antenna select 0 de 00 ;13 USRA relays off de 00 ;14 Syncdet (R/O) de 00 ;15 Spares all to zero de 00 ;16 de 00 ;17 de 00 ;18 de 00 ;19 de 00 ;1A de 00 ;1B de 00 ;1C de 00 ;1D de 00 ;1E de 00 ;1F de 0E ;20 mode control byte store ; note: the Concs ports are not completely initialised by the data ; above. R/O ports do not care if they are written to but it ; makes coding the init routine much nicer. end