;----------------------------------------------------------------------------------------------------- ; Serial port multiplexer ; Main processor ; ; This is the PIC16C74 which is connected to the SRAM as well as all the other (slave) processors. ; It is responsible for reading and writing from/to the other processors, buffering data in the ; SRAM, and packaging and unpackaging data to/from routing packets. ; ; Code developed 04/22/98 through 08/09/98 ; ; ;**************************************************************************************************** ; Overview: ;**************************************************************************************************** ; ; The serial port multipler board is used to allow one RS-232 port on a PC to be shared among 7 ; devices requiring simple (no handshake) serial I/O. Any of these 7 devices may themselves be ; another (cascaded) multiplexer board. ; ;**************************************************************************************************** ; Packets: ;**************************************************************************************************** ; ; All data sent to or from the PC is wrapped in packets which facilitate routing and data integrity ; verification. Packets contain the following components: ; ; 1). Prefix ; The prefix provides a means by which the receive logic can be easily synchronized ; with the packet transmission phase. It also identifies the type of packet. ; ; 2). Header ; The header contains routing and/or sequencing information. ; ; 3). Body ; The body contains the data itself. ; ; 4). Suffix ; The suffix contains checksums used to verify the integrity of the data in the header and body. ; ; Note that some types of packet may lack a header, body, or suffix. ; ; ; ;------------------------------------------------------------------------------------------------------ ; Nomenclature for "Data packets": (next topic below this one) ; ; When elements of a packet are listed or when elements of a component of a packet are listed, ; the items occuring closer to the top of the page are sent/received first. ; ; <byte> ; An 8-bit (single character) value ; ; <word> ; A 16-bit (2 character) value with the following components: ; <byte> least-significant byte (bits 0..7 of the word, sent/received first) ; <byte> most-significant byte (bits 8..15 of the word, sent/received last) ; ; ; ;------------------------------------------------------------------------------------------------------ ; Data Packets: ; ; Data packets may be sent to or from the PC. They contain data which came from or ; will be sent to one of the slave ports or the control channel (this MCU's command ; interpreter). ; ; Data packets may be streamed out back-to-back and are not acknowledged. If the ; checksum fails, the data packet is simply ignored. Lost data packets are detected by ; checking the serial number of each packet. ; ; ; a). Prefix = @P ; ; The prefix consists of the 'at' character (0x40) followed by an uppercase 'P'. ; All packet prefixes consist of the '@' followed by one or more letters. ; ; ; b). Header: ; ; <byte> address/length ; ; Bits 0..4 specify the length: ; Values 0..31 specify 1..32 bytes ; ; Bits 5..7 specify the address: ; Values 0..6 specify slave ports 0..6 (labeled Aux1..Aux7 on the board) ; The value 7 specifies the control channel (messages sent to/from this MCU) ; ; <word> serial number ; ; The serial number is used to identify each packet. It starts at an arbitrary ; number then increases in value by 1 for each subsequent packet sent over the ; RS232 connection to/from the PC. ; ; ; c). Body ; ; The body contains the data (1 to 32 bytes, as specified in the header). ; ; The data bytes themselves are modified via the following algorithm: (using C syntax) ; output_byte = input_byte ^ (cyclic_checksum & 0xff); ; apply_to_checksums( input_byte ); ; ; To restore the data to its original value: ; input_byte = output_byte ^ (cyclic_checksum & 0xff); ; apply_to_checksums( input_byte ); ; ; This modification of the data is done to prevent packets being sent to cascaded ; multiplexers from being misinterpreted as packets destined for this multiplexer if the ; receive logic gets out of phase. ; ; ; d). Suffix ; ; <byte> plain checksum of header and pre-modified body ; ; <word> cyclic checksum of header and pre-modified body ; The cyclic checksum algorithm is difficult to explain in words but the code ; speaks for itself (see the code for details). ; ; ; The length of a data packet ranges from 9 to 40 bytes ; ; ; ;**************************************************************************************************** ; Data flow: ;**************************************************************************************************** ; ; PC --> IRQ --> Rx_pipe --> Rx_input() # --> Receive_Buffer[address] --> Service_slave() * --> slave port ; slave port --> Service_slave() ** --> Transmit_Buffer --> Tx_output() ## --> Tx_pipe --> IRQ --> PC ; ; * = packet extracted ; # = prefix and checksum examined and stripped ; ** = packet encoded ; ## = prefix and checksum generated ; ; ;------------------------------------------------------------------------------------------------------ ; Data flow from PC to slave port: ; ; ; 1). PC transmits packet addressed to a slave port ; ; 2). IRQ handler buffers data in Rx_pipe ; ; 3). Rx_input() checks and discards the packet prefix ("@P") and header and moves the contents ; of the packet from Rx_pipe to Receive_Buffer for the addressed slave ; If the checksum fails or Receive_Buffer is full then the packet is discarded ; ; 4). Service_slave() spools the contents of the packet off to the slave port ; ; ;------------------------------------------------------------------------------------------------------ ; Data flow from slave port to PC: ; ; ; 1). Service_slave() transfers data from the slave processor to a new packet and puts the packet in Transmit_Buffer ; The packet is encoded (except for "@P" prefix and checksums which are generated later) ; Bandwidth useage is optimized by not beginning a packet transfer from a slave processor ; until a full packet worth of data is available or Transmit_Buffer is close to empty ; ; 2). Tx_output() moves data from Transmit_Buffer to Tx_pipe ; The "@P" prefix and checksums are added by Tx_output() ; ; 3). IRQ handler writes data from Tx_pipe to the PC port ; ; 4). PC receives packet addressed from a slave port ; ; ; ; ;**************************************************************************************************** ; Data structures: ;**************************************************************************************************** ; ; Rx_pipe and Tx_pipe are simple FIFO buffers in register space with one enqueue pointer and ; one dequeue pointer each. ; ; Transmit_Buffer is a 64k FIFO buffer implemented in external SRAM. ; ; There is one 8k Receive_Buffer for each slave port plus one for the control channel. ; The Receive buffers are FIFOs implemented in external SRAM. Each receive buffer has an ; enqueue pointer and a dequeue pointer. There is one global tentative enqueue pointer which is ; used to receive each packet. It is initialized to the value of the enqueue pointer for the ; buffer to which the packet is addressed and then advanced for each character received. Once an ; entire packet has been received and the checksum verified, the enqueue pointer for the addressed ; buffer is set to the value of the tentative enqueue pointer. ; ; ; ; ;**************************************************************************************************** ; Control channel messages: ;**************************************************************************************************** ; ; The control channel is mapped to address 7. It is written in the same manner as writing to any ; slave port. Data sent from it arrives in the same manner as data arriving from a slave port. ; Rather than being connected to an external port, however, the control channel is "connected" ; to this MCU's command-interpreter. The messages the command-interpreter understands and the ; responses it sends are detailed below: ; ; ; 1). Read registers ; ; "@R" (prefix, 2 bytes) ; ; <byte> Address/length ; Bits 0..1 specify the length: ; Values 0..3 specify 1..4 bytes ; ; Bits 2..4 are 0s ... the packet will be ignored if they are non-zero ; ; Bits 5..7 specify the address: ; Values 0..6 specify slave MCUs 0..6 (corresponding to Aux1..Aux7 ports on the board) ; The value 7 specifies this MCU ; ; <byte> Register start address ; This specifies the first of <length> registers to read ; ; <byte> plain checksum of Address/length and register start address ; ; <word> cyclic checksum of Address/length and register start address ; ; ; Response: ; ; "@r" (prefix, 2 bytes) ; ; <byte> plain checksum of Address/length and register start address (from command packet) ; ; <word> cyclic checksum of Address/length and register start address (from command packet) ; ; length * <byte> data ; ; <byte> plain checksum of data ; ; <word> cyclic checksum of data ; ; ; ; 2). Write registers ; ; "@W" (prefix, 2 bytes) ; ; <byte> Address/length ; Bits 0..1 specify the length: ; Values 0..3 specify 1..4 bytes ; ; Bits 2..4 are 0s ... the packet will be ignored if they are non-zero ; ; Bits 5..7 specify the address: ; Values 0..6 specify slave MCUs 0..6 (corresponding to Aux1..Aux7 ports on the board) ; The value 7 specifies this MCU ; ; <byte> Register start address ; This specifies the first of <length> registers to write ; ; length * <byte> data ; ; <byte> plain checksum of data and header ; ; <word> cyclic checksum of data and header ; ; ; Response: ; ; "@w" (prefix, 2 bytes) ; ; <byte> plain checksum from command packet ; ; <word> cyclic checksum from command packet ; ; ; ; Note that up to 4 registers are read and written via a holding buffer which is copied to/from ; register space all at once so that 32-bit words can be read or written with no danger of ; having the value modified by the operating program in between bytes read or written. ; ; ; 3). Change baud rate ; ; "@B" (prefix, 2 bytes) ; ; <byte> new TXSTA value XOR'd with 0X6C (causes checksums to fail if this packet is mistaken for "read" packet or vice versa) ; ; <byte> new SPBRG value XOR'd with 0X35 ; ; <byte> plain checksum of TXSTA and SPBRG (original non-XOR'd values) ; ; <word> cyclic checksum of TXSTA and SPBRG (original non-XOR'd values) ; ; ; This command causes all buffers to be flushed prior to writing TXSTA and SPBRG. It also ; resets the serial port to clear any error flags. ; ; ; Response: ; ; None -- if the command was properly received then the baud rate will be reset and the ; multiplexer will then respond to commands at the new baud rate. ; ; ; ; ;**************************************************************************************************** ; Slave processor bus operation: ;**************************************************************************************************** ; ; The master controls the bus. It has 8 bits of bidirectional data connected to RB0..7 of each ; slave processor. It also has the following control lines: ; ; ; Name Brief description Driven by port bit ; ----------------------------------------------------------------------------------- ; CID1 Cycle ID bit 1 Master RA3 ; CID0 Cycle ID bit 0 Master RA2 ; R/W High for read cycle, Low for write cycle Master RA1 ; E Enable -- active high strobe Master RA0 ; Ack Acknowledge -- active low, common-drain Slave RA4 ; ; ; Each slave may either be addressed to respond to normal bus read/write cycles or ; to ignore normal bus cycles. A special bus cycle is used to address a specific ; slave processor to "listen" or "unlisten" to normal bus cycles. Only one slave ; processor at a time should be addressed to "listen". ; ; ; All bus cycles (including the special "listen" / "unlisten" one) fall into one ; of two categories: read cycles or write cycles. Note that "read" and "write" ; are from the master's point-of-view. ; ; Write cycle timing: ; 1). Master sets up CID1, CID0, and R/W ; 2). Master waits if Ack is being driven low (prior cycle not yet ended by slave) ; 3). Master drives data onto RB0..7 ; 4). Master asserts E ; 5). The addressed slave samples RB0..7 ; 6). The addressed slave drives Ack low ; 7). The master de-asserts E (and is then free to change any other signal) ; 8). The addressed slave stops driving Ack low ; ; Read cycle timing: ; 1). Master sets up CID1, CID0, and R/W ; 2). Master waits if Ack is being driven low (prior cycle not yet ended by slave) ; 3). Master asserts E ; 4). The addressed slave drives data onto RB0..7 ; 5). The addressed slave drives Ack low ; 6). The master samples RB0..7 ; 7). The master de-asserts E (and is then free to change any other signal) ; 8). The addressed slave stops driving RB0..7 and Ack ; ; ; Types of bus cycles: ; ; CID1 CID0 R/W Cycle name Description ; ------------------------------------------------------------------------------------------------- ; 0 0 0 Write Tx Data is written to the Transmit Pipe ; 0 0 1 Read Rx Data is read from the Receive Pipe ; 0 1 0 Write IAddr Data is written to the indirect addressing register (IAddr) ; 0 1 1 Poll Tx Reads the number of free bytes in the Transmit Pipe ; 1 0 0 Chip Select Special cycle which addresses a slave to "listen" or "unlisten" ; 1 0 1 Poll Rx Reads the number of bytes waiting in the Receive Pipe ; 1 1 0 Write Indirect Writes the register pointed to by the IAddr register ; 1 1 1 Read Indirect Reads the register pointed to by the IAddr register ; ; ; The Chip Select cycle requires further description: ; Data bits 0..3 contain the address of the slave which should respond to this cycle (and assert Ack) ; Data bit 7 is: ; 1 if the addressed slave should "listen" (respond) to all other types of bus cycle ; 0 if the addressed slave should "unlisten" to (ignore) all other types of bus cycle ; Data bits 4..6 are unused ; ; If a slave is addressed to "unlisten" when it is already in the "unlistened" state then it will ; reset all internal registers (thus disabling the serial port receiver and flushing all buffers). ; This special double unlisten condition is sent by the master as part of its BREAK reset ... ; thus a BREAK sent to the master will reset the entire board to its power-up state. ; ; ; The Write IAddr cycle has special meaning for the following values: ; 0X00 -- Transfers a block of up to 4 registers into the word buffer (read transfer) ; 0X80 -- Transfers a block of up to 4 registers from the word buffer (write transfer) ; ; Prior to executing either of the above commands, the word buffer address and length registers ; should be written in the normal manner. The word buffer itself should be written prior to ; executing a write transfer. It is read to fetch the results of a read transfer. ; ; The data in the word buffer is right-justified (WordBuf3 is always filled, WordBuf2 is ; filled if 2 or more bytes are transfered, etc.) ; ; These special commands facilitate "snapshot" transfers of 32-bit words in order to avoid ; having the value modified by the operating program partway through a read or write. ;|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ;#################################################################################################### ;**************************************************************************************************** ; Definitions, macros, variables, constants ;**************************************************************************************************** ;#################################################################################################### ;|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| LIST P=PIC16C74A, R=DEC ; PIC16C74A, radix = base 10 INCLUDE <P16C74A.INC> ;----------------------------------------------------------------------------- ;Software registers (RAM) in bank0 wtemp EQU 0x20 ;EQU 0XA0 -- both banks -- storage for w register for interrupt context save CBLOCK 0X21 stemp ;for saving status register in IRQ handler PCLATHTEMP ;for saving PCLATH in IRQ handler FSRTEMP ;for saving FSR in IRQ handler Flags1 ;miscellaneous flags -- see individual bit definitions below Flags2 SlaveFlags ;bits 0..6 correspond to slaves at addresses 0..6 -- if a bit is on then the slave MCU is installed SlaveAddress ;address of slave being serviced on each iteration of the "service slaves" algorithm SlaveMask ;bitmask into SlaveFlags byte of slave being serviced on each iteration of the "service slaves" algorithm SoftTimer1 ;countdown timer which is decremented every 100ms by the timer interrupt until it reaches zero temp ;temporary register temp2 ; note that these registers are generally reserved for the highest-level code (main loop and rx, tx state machines) temp3 temp4 temp5 temp6 mem_temp ;temporary register for use by memory-interface subroutines mem_temp2 TxBufPCount0 ;counter for number of packets in the transmit buffer TxBufPCount1 ;high byte of word TxSerial0 ;serial number to use for next transmit packet encoded TxSerial1 ;high byte of word RxSerial0 ;serial number of last packet received RxSerial1 ;high byte of word TxBufVCount0 ;counter for number of packets lost due to transmit buffer overflow TxBufVCount1 ;2nd byte of long word TxBufVCount2 TxBufVCount3 RxBufMissCount0 ;counter for number of packets detected missing on receive RxBufMissCount1 RxBufMissCount2 RxBufMissCount3 rx_state ;state of serial port receive logic -- index into jump table rx_substate ;substate ... used by some state handlers rx_substate2 rx_substate3 rx_substate4 r232 ;char received from serial port r232xs ;simple checksum for receive r232cl ;cyclic checksum low byte for receive r232ch ;cyclic checksum high byte for receive tx_state ;state of serial port transmit logic -- index into jump table tx_substate ;substate ... used by some state handlers tx_substate2 t232 ;char to transmit to serial port t232xs ;simple checksum for transmit t232cl ;cyclic checksum low byte for transmit t232ch ;cyclic checksum high byte for transmit crx_state ;state of command interpreter receive logic -- index into jump table rCmd ;char to process from channel receive buffer rCmdxs ;simple checksum for rCmd rCmdcl ;cyclic checksum low byte for rCmd rCmdch ;cyclic checksum high byte for rCmd ctx_state ;state of command interpreter transmit logic -- index into jump table ctx_substate ;substate ... used by some state handlers tCmd ;char to transmit via command channel tCmdxs ;simple checksum for tCmd tCmdcl ;cyclic checksum low byte for tCmd tCmdch ;cyclic checksum high byte for tCmd CTxPipeRptr ;Read pointer for command-interpreter transmit pipe CTxPipeWptr ;Write pointer for command-interpreter transmit pipe TentativeCTxWptr ;tentative CTxPipe write pointer CTxPipeCount ;number of bytes in CTx pipe tmr_softper ;software timer period register for matching 12.8ms interrupt with 100ms timer event led_code ;LED error code -- 0 = off, 1 = 1PPS, 2 = blink code 2, 3 = blink code 3, etc. led_codephs ;LED blink code phase led_bphs ;LED blink phase ferr_count ;Number of framing errors detected since reset (maxes out at 255) rc_break_counter ;counter for detecting BREAK condition on Rx line ;Read and write pointers for Tx and Rx pipes (FIFO buffers between IRQ and foreground processes for serial port) ;Note: "transmit" and "receive" are relative to this processor's serial port which connects to the PC, ;thus, all "transmit" buffers hold data which will be sent to the PC and all "receive" buffers hold ;data which came from the PC. TxPipeRptr ;Read pointer for Transmit pipe TxPipeWptr ;Write pointer for Transmit pipe TentativeTxWptr ;tentative TxPipe write pointer RxPipeRptr ;Read pointer for Receive pipe RxPipeWptr ;Write pointer for Receive pipe ;Read and write pointers for SRAM queues (FIFO buffers) ; ;The transmit buffer holds all the data received from all the slaves pending transmission ;to the PC. It contains packets lacking the prefix and suffix (checksums). ; ;Each individual receive buffer holds data received from the PC which is waiting to be ;sent to the slave processor it was addressed to. ; ;Receive buffer size = 8k ;Transmit buffer size = 64k ;Transmit_Buffer pointers: Txwl ;low byte of write pointer for Transmit_Buffer (Enqueue pointer) Txwh ;high byte of write pointer for Transmit_Buffer Txrl ;low byte of read pointer for Transmit_Buffer (Dequeue pointer) Txrh ;high byte of read pointer for Transmit_Buffer RxBufEmptyFlags ;bits 0..7 correspond to the Receive_Buffer for packet addresses 0..7 -- bit is set if buffer is empty RxBufIndex ;packet address * 2 of receive buffer read by ReadRxBuf RxBufMask ;mask for RxBufEmptyFlags bit corresponding to RxBufIndex RxTwl ;low byte of tentative write pointer for an Rxbuf RxTwh ;high byte of tentative write pointer for an Rxbuf CTxPipe ;command-interpreter transmit pipe (runs up through end of bank0 ... should be at least 33 bytes long for optimum efficiency) ENDC CTxPipeEnd EQU 0X80 ;first byte past the end of CTxPipe CBLOCK 0XA1 ;Receive_Buffer pointers: ;Note that pointers are grouped so that each pointer belongs to an array of 8 pointers ;which can be indexed by the packet address (* 2) Rx0rl ;low byte of read pointer for Rx0buf Rx0rh ;high byte of read pointer for Rx0buf Rx1rl ;low byte of read pointer for Rx1buf Rx1rh Rx2rl Rx2rh Rx3rl Rx3rh Rx4rl Rx4rh Rx5rl Rx5rh Rx6rl Rx6rh RxMrl ;read pointer for buffer for control channel RxMrh Rx0wl ;low byte of write pointer for Rx0buf Rx0wh ;high byte of write pointer for Rx0buf Rx1wh Rx1wl Rx2wl Rx2wh Rx3wl Rx3wh Rx4wl Rx4wh Rx5wl Rx5wh Rx6wl Rx6wh RxMwl RxMwh ;Registers used to transfer data between master or slave MCU registers and the host computer ;via command packets and responses sent to/from the control channel RegXferMCUAddr ;MCU address of register block to transfer (0..6 are slaves 0..6, 7 is this MCU) RegXferAddr ;base address of register block to transfer RegXferLen ;length of register block to transfer (1..4) RegXfer0 ;low byte of block to transfer RegXfer1 RegXfer2 RegXfer3 ;high byte of block to transfer ;Note that these registers are used by both the command recieve logic and the command transmit logic -- ;receipt and transmission of command messages may not be overlapped... the host PC must wait for a reply ;to arrive for each command it sends or wait until a reasonable timeout elapses before sending another ;command. ENDC ;------------------------------------------------------------------------------ ; Flags contained in the Flags1 register: POST EQU 0 ;set durring Power-On Self Test -- disables blink-code driver so error LED stays lit RxPipeEmpty EQU 1 ;receive pipe is empty TxPipeFull EQU 2 ;transmit pipe is full Receiving EQU 3 ;serial port is receiving data -- used by interrupt routine to update Rx LED Transmitting EQU 4 ;serial port is sending data -- used by interrupt routine to update Tx LED TxBufOverflow EQU 5 ;transmit buffer overflowed -- signal from WriteTxBuf subroutine to its caller SlaveSelected EQU 6 ;set if the slave at SlaveAddress is addressed to "listen" CTxPipeFull EQU 7 ;command-interpreter channel transmit pipe is full ;------------------------------------------------------------------------------ ; Flags contained in the Flags2 register: CTxPipeEmpty EQU 0 ;Control channel Tx pipe is empty RC_FERR EQU 1 ;Framing error detected on last char received RC_BREAK EQU 2 ;BREAK condition detected on Rx line -- reset baud rate RC_OERR EQU 3 ;TESTING -- send diagnostic message if overrun detected ;------------------------------------------------------------------------------ ;Pipes for buffering data between IRQ and foreground processes ; ;Note: "transmit" and "receive" are relative to this processor's serial port ;which connects to the PC, thus, all "transmit" buffers hold data which will ;be sent to the PC and all "receive" buffers hold data which came from the PC. ; ;The receive pipe ensures that no data is lost when characters come in while ;the foreground process is busy. ; ;The transmit pipe helps keep data streaming out at the maximum rate allowed ;by the serial port. ; ;Since a receive pipe overflow will result in a lost packet whereas a transmit ;pipe underflow only results in a small delay, the receive buffer is larger ;than the transmit buffer. ; ;Receive buffer size = 40 bytes ;Transmit buffer size = 16 bytes TxPipe EQU 0XC8 ;first byte within the buffer TxPipeEnd EQU 0XD8 ;first byte past the end of the buffer RxPipe EQU 0XD8 ;first byte within the buffer RxPipeEnd EQU 0X00 ;first byte past the end of the buffer (buffer goes up through 0XFF) ;------------------------------------------------------------------------------ ;SRAM buffer addresses ; ;The transmit buffer holds all the data received from all the slaves pending transmission ;to the PC. It contains packets lacking the prefix and suffix (checksums). ; ;Each individual receive buffer holds data received from the PC which is waiting to be ;sent to the slave processor it was addressed to. ; ;Receive buffer size = 8k ;Transmit buffer size = 64k ; ;Tx buffer is at 0x10000 + given address (A16 high) ;Rx buffer is at given address (A16 low) Rx0buf EQU 0X0000 ;holding buffer for data being streamed to slave 0 Rx1buf EQU 0X2000 ;holding buffer for data being streamed to slave 1 Rx2buf EQU 0X4000 ;etc. Rx3buf EQU 0X6000 Rx4buf EQU 0X8000 Rx5buf EQU 0XA000 Rx6buf EQU 0XC000 RxMbuf EQU 0XE000 ;data being streamed to master -- control messages Txbuf EQU 0X0000 ;holding buffer for data pending transmission to PC ;---------------------------------------------------------------------------------- ; Special addresses within the slave processors' register space. ; These are register locations which the master must access in order to facilitate ; data transfer requests made by the host computer. ; CBLOCK 0X20 ;Word buffer registers ;These registers are used to facilitate "snapshot" transfers of 16 or 32 bit words so ;that the values can be read or written without danger of the value being modified by ;the operating program partway through a read or write. WordBufAddr ;address base for word transfer WordBufLen ;length, 1..4 WordBuf0 ;low databyte of word WordBuf1 ;note that data is right-justified, i.e. a 16-bit word WordBuf2 ; occupies WordBuf2 and WordBuf3 WordBuf3 ;high databyte of word ENDC ;------------------------------------------------------------------------------ ;Macros Move MACRO destination,source MOVFW source MOVWF destination ENDM Movlf MACRO destination,source MOVLW source MOVWF destination ENDM ;Clear global interrupt enable -- note that an IRQ may be acknowledged ;before the BCF instruction and the IRQ may be executed after the BCF ;instruction and leave gie set upon return (thus the BTFSC instruction ;to make sure this hasn't happened) ClrI MACRO LOCAL lpclri lpclri BCF INTCON,GIE BTFSC INTCON,GIE GOTO lpclri ENDM ;Set global interrupt enable SetI MACRO BSF INTCON,GIE ENDM Bank0 MACRO BCF STATUS,RP0 ENDM Bank1 MACRO BSF STATUS,RP0 ENDM LowPage MACRO BCF PCLATH,3 ENDM HighPage MACRO BSF PCLATH,3 ENDM ;------------------------------------------------- ; LEDs LightStatus MACRO BSF PORTE,0 ENDM DouseStatus MACRO BCF PORTE,0 ENDM LightError MACRO BSF PORTA,5 ENDM DouseError MACRO BCF PORTA,5 ENDM LightRx MACRO BSF PORTC,1 ENDM DouseRx MACRO BCF PORTC,1 ENDM LightTx MACRO BSF PORTC,2 ENDM DouseTx MACRO BCF PORTC,2 ENDM ;--------------------------------- ; Blink codes: ; The "error" LED is used as a simple output device for reporting errors. ; When an error occurs, a blink-code is set which describes the error. ; The "error" LED is then flashed by the timer interrupt routine. It blinks ; the number of times specified by the code, pauses, then repeats. ; ; Codes: ERR_RX_PIPE_OVERRUN EQU 2 ;Rx pipe overflowed ERR_RXREG_OVERRUN EQU 3 ;Hardware Rx overrun ERR_NO_SLAVE_PORTS EQU 4 ;No slave ports detected ERR_MEMORY_TEST EQU 5 ;Memory test failed ERR_BUS_FAILURE EQU 6 ;Bus timeout on attempt to scan for slaves (Ack is asserted) Blink MACRO code MOVLW code CALL set_blink_code ENDM ;------------------------------------------------ ; Slave processor interface ; ; Write macros are invoked with the databyte to write in the W register. ; Read macros end with the data byte read in the W register. ; ; These macros do not time out. Slave processors should be scanned for with a different ; algorithm which does time out. These macros should be used only to communicate ; with slaves which are present. ; ; ; The macros available are: ; ; BusWriteTx writes W to the transmit pipe (of the addressed slave) ; BusReadRx reads W from the receive pipe ; BusWriteIAddr writes W to the IAddr register ; BusPollTx returns number of bytes free in transmit pipe ; BusChipSelect addresses a slave to listen or unlisten ; BusPollRx returns number of bytes buffered in receive pipe ; BusWriteIndirect writes W to the register pointed to by the IAddr register ; BusReadIndirect reads W from the register pointed to by the IAddr register ; BusWriteTx MACRO ;Write W to addressed slave's transmit pipe BusStartWrite ;wait for prior cycle to end then drive W onto data bus MOVLW 0XE0 ANDWF PORTA,F ;setup cycle ID and R/W bits for a "Write Tx" bus cycle NOP BSF PORTA,0 ;assert E to start the cycle BusWaitAck BusEndWrite ENDM BusReadRx MACRO BusStartRead ;wait for prior cycle to end MOVFW PORTA ANDLW 0XE0 IORLW 0X02 MOVWF PORTA ;setup cycle ID and R/W bits for a "Read Rx" bus cycle IORLW 0X01 MOVWF PORTA ;assert E to start the cycle BusWaitAck BusEndRead ENDM BusWriteIAddr MACRO BusStartWrite MOVFW PORTA ANDLW 0XE0 IORLW 0X04 MOVWF PORTA ;setup cycle ID and R/W bits for a "Write IAddr" bus cycle IORLW 0X01 MOVWF PORTA ;assert E to start the cycle BusWaitAck BusEndWrite ENDM BusPollTx MACRO BusStartRead MOVFW PORTA ANDLW 0XE0 IORLW 0X06 MOVWF PORTA ;setup cycle ID and R/W bits for a "Poll Tx" bus cycle IORLW 0X01 MOVWF PORTA ;assert E to start the cycle BusWaitAck BusEndRead ENDM BusChipSelect MACRO BusStartWrite MOVFW PORTA ANDLW 0XE0 IORLW 0X08 MOVWF PORTA ;setup cycle ID and R/W bits for a "Chip Select" bus cycle IORLW 0X01 MOVWF PORTA ;assert E to start the cycle BusWaitAck BusEndWrite ENDM BusPollRx MACRO BusStartRead MOVFW PORTA ANDLW 0XE0 IORLW 0X0A MOVWF PORTA ;setup cycle ID and R/W bits for a "Poll Rx" bus cycle IORLW 0X01 MOVWF PORTA ;assert E to start the cycle BusWaitAck BusEndRead ENDM BusWriteIndirect MACRO BusStartWrite MOVFW PORTA ANDLW 0XE0 IORLW 0X0C MOVWF PORTA ;setup cycle ID and R/W bits for a "Write Indirect" bus cycle IORLW 0X01 MOVWF PORTA ;assert E to start the cycle BusWaitAck BusEndWrite ENDM BusReadIndirect MACRO BusStartRead MOVFW PORTA ANDLW 0XE0 IORLW 0X0E MOVWF PORTA ;setup cycle ID and R/W bits for a "Read Indirect" bus cycle IORLW 0X01 MOVWF PORTA ;assert E to start the cycle BusWaitAck BusEndRead ENDM ;Sub-macros used by above macros ;These sub-macros perform common subfunctions BusStartWrite MACRO ;disables interrupts, waits for prior cycle to end, then drives W onto bus LOCAL bus_wait_no_ack ClrI MOVWF PORTB bus_wait_no_ack BTFSS PORTA,4 ;wait for prior cycle to finish if necessary GOTO bus_wait_no_ack MOVLW TRISB MOVWF FSR ;leaves FSR pointing to TRISB CLRF INDF ;drive data onto bus ENDM BusStartRead MACRO ;disables interrupts and waits for ack to be released LOCAL bus_wait_no_ack ClrI bus_wait_no_ack BTFSS PORTA,4 ;wait for prior cycle to finish if necessary GOTO bus_wait_no_ack ENDM BusWaitAck MACRO ;waits for slave to assert Ack LOCAL bus_wait_ack bus_wait_ack BTFSC PORTA,4 GOTO bus_wait_ack ENDM BusEndWrite MACRO ;Terminates a bus write cycle BCF PORTA,0 DECF INDF,F ;assumes FSR was set to TRISB at the start of the bus cycle SetI ENDM BusEndRead MACRO ;Terminates a bus read cycle BCF PORTA,0 MOVFW PORTB ;reads data bus ... although this happens after E is de-asserted it is ok since interrupts are disabled and the slave cannot respond within 200ns to stop driving the data bus SetI ENDM ;------------------------------------------------- ; SRAM interface SetMemCE1 MACRO BCF PORTE,1 ENDM ClrMemCE1 MACRO BSF PORTE,1 ENDM SetMemCE2 MACRO BSF PORTC,5 ENDM ClrMemCE2 MACRO BCF PORTC,5 ENDM SetMemWr MACRO BCF PORTC,3 ENDM ClrMemWr MACRO BSF PORTC,3 ENDM SetMemOE MACRO BCF PORTC,0 ENDM ClrMemOE MACRO BSF PORTC,0 ENDM SetMemA16 MACRO BSF PORTC,4 ENDM ClrMemA16 MACRO BCF PORTC,4 ENDM SetMemAClk MACRO BSF PORTE,2 ENDM ClrMemAClk MACRO BCF PORTE,2 ENDM ;|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ;#################################################################################################### ;**************************************************************************************************** ; Start of code ;**************************************************************************************************** ;#################################################################################################### ;|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ;****************************************************************************** ;Reset vector ORG 0 CLRF PCLATH GOTO start ;****************************************************************************** ;Interrupt vector ; ;Interrupt sources: ; TMR2IF -- Timer2 expired ; RCIF -- Serial port received a character ; TXIF -- Serial port is ready to transmit a character ORG 4 ;Save context MOVWF wtemp ;Save W and status registers SWAPF STATUS,W ;gets status into W without affecting flags Bank0 MOVWF stemp MOVF PCLATH,W MOVWF PCLATHTEMP MOVFW FSR MOVWF FSRTEMP ;Branch to appropriate handler ;If more than one interrupt is pending then the other(s) will be serviced after the RETFIE instruction is executed ;Interrupt sources are prioritized as follows: ; 1). RCIF ; 2). TMR2IF ; 3). TXIF LowPage BTFSC PIR1,RCIF GOTO receive_char BTFSS PIR1,TMR2IF GOTO transmit_char ;-------------------------------- ; Begin timer 2 interrupt handler ; ; ;Timer2 interrupt occurs every 12.8ms (78.125 Hz) and causes a timer event to be ;executed every 100ms (on average). ;Update Rx and Tx LEDs ;The Receiving and Transmitting flags are set by the foreground process and cleared here every 12.8ms BTFSC Flags1,POST GOTO skip_led_update ;don't do anything with the LEDs if the Power-On Self Test is in progress DouseRx BTFSC Flags1,Receiving LightRx BCF Flags1,Receiving DouseTx BTFSC Flags1,Transmitting LightTx BCF Flags1,Transmitting skip_led_update ;Test for "break" on Rx line BTFSC PORTC,7 GOTO no_break DECFSZ rc_break_counter,F GOTO tmr2int_c1 BSF Flags2,RC_BREAK ;BREAK detected -- trigger foreground routine to reset baud rate GOTO tmr2int_c1 no_break MOVLW 10 MOVWF rc_break_counter tmr2int_c1 ;Update software timer period register and generate 100ms event MOVLW 0X10 ;interrupt period = 128/1000 of 100ms = 16/125 of 100ms ADDWF tmr_softper,F BTFSS STATUS,C GOTO ret_tmr_int MOVLW 0X7D ;0x7d = 125 SUBWF tmr_softper,F ;----------- ;100ms event ;check for hardware overrun error BTFSS RCSTA,OERR GOTO no_overrun_err MOVLW ERR_RXREG_OVERRUN ;set blink code unless a higher code has previously been set SUBWF led_code,W BTFSC STATUS,C GOTO no_overrun_err MOVLW ERR_RXREG_OVERRUN MOVWF led_code no_overrun_err ;update timeout timers MOVF SoftTimer1,F BTFSS STATUS,Z DECF SoftTimer1,F ;decrement countdown timer if not already 0 ;Display blink codes on the "error" LED BTFSC Flags1,POST ;blink-code driver is disabled durring Power-On Self Test GOTO ret_tmr_int MOVF led_code,F ;code 0 = no error ... no blinking ... LED doused BTFSC STATUS,Z GOTO ret_tmr_dl DECFSZ led_bphs,F GOTO ret_tmr_dl LightError DECFSZ led_codephs,F GOTO set_led_bp MOVFW led_code MOVWF led_codephs DECFSZ led_codephs,W GOTO set_led_bp2 ;code 1 (1PPS)? MOVLW 10 ;code 1 -- blink once per second GOTO set_led_bpc set_led_bp2 MOVLW 20 ;code other than 1 -- delay 2 seconds between code repetitions GOTO set_led_bpc set_led_bp MOVLW 5 ;LED blinks every 500ms when flashing a code set_led_bpc MOVWF led_bphs GOTO ret_tmr_int ret_tmr_dl DouseError ret_tmr_int BCF PIR1,TMR2IF ;clear timer2 interrupt flag GOTO ret_int ; End timer 2 interrupt handler ;------------------------------ ;------------------------------------------------------ ; Begin serial port receive character interrupt handler ; receive_char BTFSS RCSTA,OERR ;check for overrun GOTO rc_no_oerr BSF Flags2,RC_OERR BCF RCSTA,CREN BSF RCSTA,CREN ;overrun occurred -- reset receive logic GOTO ret_int rc_no_oerr BCF Flags1,RxPipeEmpty BCF Flags2,RC_FERR BTFSS RCSTA,FERR ;Check for framing errors -- keep track of how many occur GOTO no_ferr BSF Flags2,RC_FERR INCF ferr_count,F BTFSC STATUS,Z DECF ferr_count,F ;count maxes out at 255 no_ferr MOVF RxPipeWptr,W ;Move character from RCREG to RxPipe MOVWF FSR MOVF RCREG,W MOVWF INDF MOVLW 10 ;must have 10 consecutive low Rx samples (on timer interrupt) with no characters received before resetting baud rate MOVWF rc_break_counter INCF RxPipeWptr,F ;Increment RxPipe write pointer MOVFW RxPipeWptr SUBLW RxPipeEnd MOVLW RxPipe BTFSC STATUS,Z MOVWF RxPipeWptr MOVFW RxPipeWptr ;Check for pipe overflow SUBWF RxPipeRptr,W BTFSS STATUS,Z GOTO ret_int DouseStatus ;Overflow has occurred -- turn off "ok" status LED MOVLW ERR_RX_PIPE_OVERRUN ;set blink code unless a higher code has previously been set SUBWF led_code,W BTFSC STATUS,C GOTO ret_int MOVLW ERR_RX_PIPE_OVERRUN MOVWF led_code GOTO ret_int ; End serial port receive character interrupt handler ;------------------------------------------------------ ;------------------------------------------------------- ; Begin serial port transmit character interrupt handler ; ; Move character from TxPipe to TXREG ; If TxPipe is empty then disable this interrupt (clear TXIE) ; TXIE is set when data is written to TxPipe transmit_char BCF Flags1,TxPipeFull MOVFW TxPipeRptr MOVWF FSR SUBWF TxPipeWptr,W BTFSC STATUS,Z GOTO clear_txie ;TxPipe is empty MOVFW INDF ;TxPipe contains data -- move one character to TXREG MOVWF TXREG INCF TxPipeRptr,F ;Increment TxPipe read pointer MOVFW TxPipeRptr SUBLW TxPipeEnd MOVLW TxPipe BTFSC STATUS,Z MOVWF TxPipeRptr ;Restore context and return from interrupt ret_int MOVFW FSRTEMP MOVWF FSR MOVF PCLATHTEMP,W MOVWF PCLATH SWAPF stemp,W ;gets original status to W without affecting flags MOVWF STATUS SWAPF wtemp,F ;swaps halves of wtemp SWAPF wtemp,W ;gets original W back without affecting flags RETFIE clear_txie Bank1 BCF PIE1,TXIE ;TxPipe is empty -- disable the "transmit buffer empty" interrupt Bank0 GOTO ret_int ; End serial port transmit character interrupt handler ;------------------------------------------------------ ;**************************************************************************************************** ; Entry point from reset vector ... initializations ;**************************************************************************************************** start ;----------------------------------------------------------------------------- ;Initialize all relevent special function registers to their reset states ... ;this is done incase a watchdog timer reset occurs and some SFRs have values ;which would otherwise impair normal operation CLRF STATUS ;Clear RP0, RP1 and IRP bits CLRF PCLATH CLRF INTCON CLRF T1CON CLRF T2CON CLRF SSPCON CLRF CCP1CON CLRF RCSTA CLRF CCP2CON CLRF ADCON0 Bank1 MOVLW 0XFF MOVWF 0X81 ;OPTION register -- symbol not defined in stock header MOVWF TRISA MOVWF TRISB MOVWF TRISC MOVWF TRISD MOVLW 0X07 MOVWF TRISE CLRF PIE1 CLRF PIE2 CLRF SSPSTAT CLRF TXSTA CLRF SPBRG CLRF ADCON1 ;----------------------------------------------------------------------------- ;Initialize some software registers (any that interrupt service routine uses) Bank0 MOVLW (1 << POST) | (1 << RxPipeEmpty) MOVWF Flags1 MOVLW (1 << CTxPipeEmpty) MOVWF Flags2 MOVLW 10 MOVWF rc_break_counter CLRF CTxPipeCount CLRF ferr_count CLRF TxBufPCount0 CLRF TxBufPCount1 CLRF TxSerial0 CLRF TxSerial1 CLRF TxBufVCount0 CLRF TxBufVCount1 CLRF TxBufVCount2 CLRF TxBufVCount3 MOVLW 1 MOVWF led_code MOVWF led_codephs MOVWF led_bphs Movlf TxPipeRptr,TxPipe ;Read pointer for Transmit pipe Movlf TxPipeWptr,TxPipe ;Write pointer for Transmit pipe Movlf RxPipeRptr,RxPipe ;Read pointer for Receive pipe Movlf RxPipeWptr,RxPipe ;Write pointer for Receive pipe Movlf CTxPipeRptr,CTxPipe ;Read pointer for command-interpreter Tx pipe Movlf CTxPipeWptr,CTxPipe ;Write pointer for command-interpreter Tx pipe ;-------------------------------------------------------------------------- ; Initialize special function registers (I/O port states, most peripherals) CLRF PORTA MOVLW 0X29 ;0010 1001 -- no memory rd or wr, mem CE2 set, A16 low, tx and rx LEDs doused MOVWF PORTC MOVLW 0X06 ;0000 0110 -- no error LED, mem CE1 clear, latch clk high MOVWF PORTE MOVLW 0X7F ;enable tmr2, prescale 16, postscale 16 MOVWF T2CON Bank1 MOVLW 0X10 MOVWF TRISA ;RA4 is the only input (/Ack) MOVLW 0XFF MOVWF TRISB ;port B is data bus MOVLW 0X80 MOVWF TRISC ;port C is all output except RC7/Rx CLRF TRISD ;port D is the memory address bus -- all output CLRF TRISE ;port E is all output Movlf ADCON1,6 ;configure RA0..5 and RE0..2 as digital I/O rather than analog inputs MOVLW 249 ;tmr2 period 250 x 256 x 4 (prescale, postscale, fixed /4) = 256000 = 12.8ms with 20MHz oscillator MOVWF PR2 MOVLW (1 << TMR2IE) | (1 << RCIE) ;enable tmr2 interrupt and receive character interrupt (TXIE is set when TxPipe is written to ... cleared when TxPipe becomes empty) MOVWF PIE1 Bank0 MOVLW (1 << GIE) | (1 << PEIE) ;enable peripheral interrupts (tmr2, serial port) MOVWF INTCON ;********************************************************************************************************************** ;Begin Power-On Self Test ;********************************************************************************************************************* LightStatus ;turn on all LEDs durring the POST NOP LightError NOP LightRx NOP LightTx ;------------------------------------------------------------ ;Test the SRAM ;First, fill the memory with pseudo-random numbers... CLRF r232xs ;checksums and checksum algorithm is used to produce pseudo-random numbers for memory test CLRF r232cl CLRF r232ch CLRF t232xs CLRF t232cl CLRF t232ch CLRF Txwl CLRF Txwh Movlf FSR,Txwl ClrMemA16 lprtw1 CLRWDT MOVFW t232xs XORWF t232cl,W XORWF t232ch,W CALL update_rx_checksums MOVFW r232xs XORWF r232cl,W XORWF r232ch,W CALL update_tx_checksums MOVFW t232xs XORWF t232cl,W XORWF t232ch,W CALL WriteMem INCF Txwl,F BTFSC STATUS,Z INCFSZ Txwh,F GOTO lprtw1 SetMemA16 lprtw2 CLRWDT MOVFW t232xs XORWF t232cl,W XORWF t232ch,W CALL update_rx_checksums MOVFW r232xs XORWF r232cl,W XORWF r232ch,W CALL update_tx_checksums MOVFW t232xs XORWF t232cl,W XORWF t232ch,W CALL WriteMem INCF Txwl,F BTFSC STATUS,Z INCFSZ Txwh,F GOTO lprtw2 ;Now read back and verify the pseudo-random numbers and, at the same time, ;write back the one's compliments of those numbers DouseTx NOP DouseStatus CLRF r232xs CLRF r232cl CLRF r232ch CLRF t232xs CLRF t232cl CLRF t232ch ClrMemA16 lprtr1 CLRWDT MOVFW t232xs XORWF t232cl,W XORWF t232ch,W CALL update_rx_checksums MOVFW r232xs XORWF r232cl,W XORWF r232ch,W CALL update_tx_checksums CALL ReadMem MOVWF temp XORWF t232xs,W XORWF t232cl,W XORWF t232ch,W BTFSS STATUS,Z GOTO mem_test_failed MOVFW temp XORLW 0XFF CALL WriteMem INCF Txwl,F BTFSC STATUS,Z INCFSZ Txwh,F GOTO lprtr1 SetMemA16 lprtr2 CLRWDT MOVFW t232xs XORWF t232cl,W XORWF t232ch,W CALL update_rx_checksums MOVFW r232xs XORWF r232cl,W XORWF r232ch,W CALL update_tx_checksums CALL ReadMem MOVWF temp XORWF t232xs,W XORWF t232cl,W XORWF t232ch,W BTFSS STATUS,Z GOTO mem_test_failed MOVFW temp XORLW 0XFF CALL WriteMem INCF Txwl,F BTFSC STATUS,Z INCFSZ Txwh,F GOTO lprtr2 ;Now read back and verify the one's compliments of the pseudo-random numbers DouseError CLRF r232xs CLRF r232cl CLRF r232ch CLRF t232xs CLRF t232cl CLRF t232ch ClrMemA16 lprtr3 CLRWDT MOVFW t232xs XORWF t232cl,W XORWF t232ch,W CALL update_rx_checksums MOVFW r232xs XORWF r232cl,W XORWF r232ch,W CALL update_tx_checksums CALL ReadMem XORWF t232xs,W XORWF t232cl,W XORWF t232ch,W XORLW 0XFF BTFSS STATUS,Z GOTO mem_test_failed INCF Txwl,F BTFSC STATUS,Z INCFSZ Txwh,F GOTO lprtr3 SetMemA16 lprtr4 CLRWDT MOVFW t232xs XORWF t232cl,W XORWF t232ch,W CALL update_rx_checksums MOVFW r232xs XORWF r232cl,W XORWF r232ch,W CALL update_tx_checksums CALL ReadMem XORWF t232xs,W XORWF t232cl,W XORWF t232ch,W XORLW 0XFF BTFSS STATUS,Z GOTO mem_test_failed INCF Txwl,F BTFSC STATUS,Z INCFSZ Txwh,F GOTO lprtr4 GOTO mem_test_passed mem_test_failed Blink ERR_MEMORY_TEST mem_test_passed ;------------------------------------------------------------------------------------ ; Scan for slave processors -- the board may have up to 7 slave processors installed DouseRx CLRF temp ;slave address CLRF SlaveAddress Movlf temp2,1 ;SlaveFlags bitmask for addressed slave MOVWF SlaveMask CLRF SlaveFlags loop_scan_slaves Movlf SoftTimer1,2 ;give each processor 100-200ms to respond (should respond in a few microseconds but timer resolution is 100ms) MOVFW temp ;loop for each slave 0..6 MOVWF PORTB ;address the slave to "unlisten" wait_no_ack CLRWDT BTFSC PORTA,4 ;wait for prior cycle to finish if necessary GOTO ack_is_off MOVF SoftTimer1,F ;check for timeout ... this would indicate a serious bus malfunction BTFSS STATUS,Z GOTO wait_no_ack Blink ERR_BUS_FAILURE GOTO end_slave_scan ack_is_off MOVLW TRISB MOVWF FSR ;leaves FSR pointing to TRISB CLRF INDF ;drive data onto bus MOVFW PORTA ANDLW 0XE0 IORLW 0X08 MOVWF PORTA ;setup cycle ID and R/W bits for a "Chip Select" bus cycle IORLW 0X01 MOVWF PORTA ;assert E to start the cycle wait_ack CLRWDT BTFSS PORTA,4 ;wait for acknowledge GOTO ack_is_on MOVF SoftTimer1,F ;check for timeout ... this would indicate that the addressed slave is not present BTFSS STATUS,Z GOTO wait_ack GOTO ack_timed_out ack_is_on MOVFW temp2 ;slave is present -- set corresponding flag bit IORWF SlaveFlags,F ack_timed_out BCF PORTA,0 ;de-assert E ... end the write cycle DECF INDF,F ;tri-state the databus INCF temp,F ;index/address the next slave RLF temp2,F BTFSS temp2,7 GOTO loop_scan_slaves end_slave_scan MOVF SlaveFlags,F ;are any slave ports present? MOVLW ERR_NO_SLAVE_PORTS BTFSC STATUS,Z CALL set_blink_code ;********************************************************************************************************************* ;End Power-On Self Test ;********************************************************************************************************************* DouseError NOP DouseTx NOP DouseRx LightStatus MOVLW 2 SUBWF led_code,W BTFSC STATUS,C DouseStatus ;--------------------------------------------------------------------------------------------------------------------- ; 2nd phase of initializations... (Power-On Self Test has now finished) ;Initialize most software registers (variables) ;First set up default baud rate to 2400 ... this is the power-on value ;The baud rate can be changed later via software commands ;Entry point for BREAK command (break condition on serial port receive line) ;The break command is used to reset the baud rate to 2400 BREAK_RESET Bank1 MOVLW (1 << TXEN) ;enable serial port transmit, not BRGH, asynchronous mode, 8 bit MOVWF RegXfer0 ;this register is later transfered to TXSTA MOVLW 129 ;2400 baud MOVWF RegXfer1 ;this register is later transfered to SPBRG Bank0 ;Entry point for changing baud rate ... flushes all buffers and resets port at new baud rate ;Also resets all slave ports to their power-up (inactive) state ;TXSTA is copied from RegXfer0 ;SPBRG is copied from RegXfer1 RESET_BAUD ClrI MOVLW 1 << SPEN MOVWF RCSTA ;Disable serial port receiver but leave I/O pins configured as Rx and Tx ;Reset slaves by "unlistening" them while they are already in an "unlistened" state ;This will trigger them to reset to their power-on state (clear all buffers, disable serial port receiver, etc.) BTFSS Flags1,SlaveSelected ;is a slave addressed? GOTO no_unl_slave_rst MOVFW SlaveAddress BusChipSelect ;tell last slave to "unlisten" BCF Flags1,SlaveSelected no_unl_slave_rst MOVF SlaveFlags,F ;are any slaves connected? BTFSC STATUS,Z GOTO no_slaves_rst MOVLW 2 MOVWF temp ;loop counter for assurring that each slave is "unlistened" at least once next_slave_rst INCF SlaveAddress,F ;compute address of next connected slave BCF STATUS,C RLF SlaveMask,F BTFSS SlaveMask,7 GOTO no_lsm_roll_rst CLRF SlaveAddress Movlf SlaveMask,1 DECF temp,F BTFSC STATUS,Z GOTO no_slaves_rst ;done "unlistening" all the slaves no_lsm_roll_rst MOVFW SlaveMask ANDWF SlaveFlags,W BTFSC STATUS,Z GOTO next_slave_rst MOVF SlaveAddress,W ;"unlisten" next slave BusChipSelect GOTO next_slave_rst no_slaves_rst MOVLW (1 << RxPipeEmpty) MOVWF Flags1 MOVLW (1 << CTxPipeEmpty) MOVWF Flags2 MOVLW 10 MOVWF rc_break_counter CLRF CTxPipeCount CLRF ferr_count CLRF TxBufPCount0 CLRF TxBufPCount1 CLRF TxSerial0 CLRF TxSerial1 CLRF TxBufVCount0 CLRF TxBufVCount1 CLRF TxBufVCount2 CLRF TxBufVCount3 Movlf TxPipeRptr,TxPipe ;Read pointer for Transmit pipe Movlf TxPipeWptr,TxPipe ;Write pointer for Transmit pipe Movlf RxPipeRptr,RxPipe ;Read pointer for Receive pipe Movlf RxPipeWptr,RxPipe ;Write pointer for Receive pipe Movlf CTxPipeRptr,CTxPipe ;Read pointer for command-interpreter Tx pipe Movlf CTxPipeWptr,CTxPipe ;Write pointer for command-interpreter Tx pipe SetI CLRF rx_state ;Rx state starts at 0 CLRF tx_state ;Tx state starts at 0 CLRF ctx_state ;command Tx state CLRF crx_state ;command Rx state Movlf Txwl,LOW(Txbuf) ;low byte of write pointer for Transmit_Buffer (Enqueue pointer) Movlf Txwh,HIGH(Txbuf) ;high byte of write pointer for Transmit_Buffer Movlf Txrl,LOW(Txbuf) ;low byte of read pointer for Transmit_Buffer (Dequeue pointer) Movlf Txrh,HIGH(Txbuf) ;high byte of read pointer for Transmit_Buffer Movlf RxBufEmptyFlags,0XFF ;all buffers are empty Bank1 Movlf Rx0rl,LOW(Rx0buf) ;low byte of read pointer for Rx0buf Movlf Rx1rl,LOW(Rx1buf) ;low byte of read pointer for Rx1buf Movlf Rx2rl,LOW(Rx2buf) Movlf Rx3rl,LOW(Rx3buf) Movlf Rx4rl,LOW(Rx4buf) Movlf Rx5rl,LOW(Rx5buf) Movlf Rx6rl,LOW(Rx6buf) Movlf RxMrl,LOW(RxMbuf) ;low byte of read pointer for buffer for control channel Movlf Rx0rh,HIGH(Rx0buf) ;high byte of read pointer for Rx0buf Movlf Rx1rh,HIGH(Rx1buf) Movlf Rx2rh,HIGH(Rx2buf) Movlf Rx3rh,HIGH(Rx3buf) Movlf Rx4rh,HIGH(Rx4buf) Movlf Rx5rh,HIGH(Rx5buf) Movlf Rx6rh,HIGH(Rx6buf) Movlf RxMrh,HIGH(RxMbuf) Movlf Rx0wl,LOW(Rx0buf) ;low byte of write pointer for Rx0buf Movlf Rx1wl,LOW(Rx1buf) Movlf Rx2wl,LOW(Rx2buf) Movlf Rx3wl,LOW(Rx3buf) Movlf Rx4wl,LOW(Rx4buf) Movlf Rx5wl,LOW(Rx5buf) Movlf Rx6wl,LOW(Rx6buf) Movlf RxMwl,LOW(RxMbuf) Movlf Rx0wh,HIGH(Rx0buf) ;high byte of write pointer for Rx0buf Movlf Rx1wh,HIGH(Rx1buf) Movlf Rx2wh,HIGH(Rx2buf) Movlf Rx3wh,HIGH(Rx3buf) Movlf Rx4wh,HIGH(Rx4buf) Movlf Rx5wh,HIGH(Rx5buf) Movlf Rx6wh,HIGH(Rx6buf) Movlf RxMwh,HIGH(RxMbuf) ;Enable and initialize the serial port hardware MOVF RegXfer0,W MOVWF TXSTA MOVF RegXfer1,W MOVWF SPBRG Bank0 MOVLW (1 << SPEN) | (1 << CREN) ;enable serial port, enable receiver MOVWF RCSTA ;##################################################################################################################### ;********************************************************************************************************************* ; Main foreground loop ;********************************************************************************************************************* mainloop CLRWDT BTFSS Flags1,RxPipeEmpty GOTO process_rx ;------ poll_slaves ------ ;This code only gets executed once the RxPipe has been emptied ; ;Select next slave and ask it if it has any data ... read the data if so ;Also write data from its corresponding receive buffer if it is ready to accept it BTFSS Flags1,SlaveSelected ;is a slave addressed? GOTO no_unlisten_slave MOVFW SlaveAddress BusChipSelect ;tell last slave to "unlisten" BCF Flags1,SlaveSelected no_unlisten_slave MOVF SlaveFlags,F ;are any slaves connected? BTFSC STATUS,Z GOTO no_slaves next_slave_address INCF SlaveAddress,F ;compute address of next connected slave BCF STATUS,C RLF SlaveMask,F BTFSS SlaveMask,7 GOTO no_lsm_roll CLRF SlaveAddress Movlf SlaveMask,1 no_lsm_roll MOVFW SlaveMask ANDWF SlaveFlags,W BTFSC STATUS,Z GOTO next_slave_address MOVLW 0X80 ;Select next slave IORWF SlaveAddress,W BusChipSelect BSF Flags1,SlaveSelected BusPollRx ;see if slave has any data waiting BTFSC STATUS,Z GOTO no_slave_rxdata MOVWF temp SUBLW 31 ;is there at least 32 bytes bufferred? BTFSC STATUS,C GOTO check_txbuf_size ;no, don't encode a packet unless the transmit buffer is close to empty Movlf temp,32 ;encode a packet holding 32 bytes of data GOTO encode_packet check_txbuf_size MOVF TxBufPCount1,F ;If the Transmit buffer is close to empty then encode the packet regardless of its size BTFSS STATUS,Z GOTO no_slave_rxdata MOVLW 8 ;less than 8 packets buffered? SUBWF TxBufPCount0,W BTFSC STATUS,C GOTO no_slave_rxdata encode_packet MOVFW Txwl ;save transmit buffer enqueue pointer incase it overflows MOVWF temp3 ;(so the addition of this latest packet can be undone) MOVFW Txwh MOVWF temp4 SWAPF SlaveAddress,W MOVWF temp2 RLF temp2,F MOVLW 0XE0 ANDWF temp2,F ;slave address goes in bits 5..7 DECF temp,W IORWF temp2,W ;size encoded in packet header is (byte_count - 1) and it goes in bits 0..4 CALL WriteTxBuf MOVFW TxSerial0 CALL WriteTxBuf MOVFW TxSerial1 CALL WriteTxBuf move_packet_loop BusReadRx CALL WriteTxBuf DECFSZ temp,F GOTO move_packet_loop BTFSS Flags1,TxBufOverflow ;did the Transmit buffer overflow? GOTO no_txbuf_overflow BCF Flags1,TxBufOverflow ;consume the flag MOVFW temp3 ;reset write pointer to its value before this last packet was added MOVWF Txwl ;(discard the packet) MOVFW temp4 MOVWF Txwh INCF TxBufVCount0,F ;increment packet loss counter BTFSC STATUS,Z INCF TxBufVCount1,F BTFSC STATUS,Z INCF TxBufVCount2,F BTFSC STATUS,Z INCF TxBufVCount3,F GOTO no_slave_rxdata no_txbuf_overflow INCF TxSerial0,F ;increment transmit packet serial number BTFSC STATUS,Z INCF TxSerial1,F INCF TxBufPCount0,F ;increment transmit buffer packet counter BTFSC STATUS,Z INCF TxBufPCount1,F no_slave_rxdata ;Check if there is data to send to the slave... if so check if slave can accept any ... if so send it MOVFW SlaveMask ANDWF RxBufEmptyFlags,W BTFSS STATUS,Z GOTO no_slave_txdata BusPollTx ;see if slave can accept any more transmit data BTFSC STATUS,Z GOTO no_slave_txdata MOVWF temp Move RxBufIndex,SlaveAddress ADDWF RxBufIndex,F Move RxBufMask,SlaveMask fill_txpipe_loop CALL ReadRxBuf BusWriteTx MOVFW RxBufMask ANDWF RxBufEmptyFlags,W ;loop while there is data to send BTFSS STATUS,Z GOTO no_slave_txdata DECFSZ temp,F ; ... and the slave's Tx Pipe is not full GOTO fill_txpipe_loop no_slave_txdata no_slaves ;Encode a packet from the control channel if enough bytes are buffered or the transmit buffer is empty BTFSC Flags2,CTxPipeEmpty ;is there any data in the control channel's transmit pipe? GOTO no_control_packet ; no BTFSC Flags1,CTxPipeFull ;is control channel's transmit pipe full? GOTO encode_control_packet ; yes, encode a packet MOVF ctx_state,F ;is the control channel still generating output? BTFSS STATUS,Z GOTO no_control_packet ; yes, wait until it stops or its pipe fills encode_control_packet MOVFW Txwl ;save transmit buffer enqueue pointer incase it overflows MOVWF temp3 ;(so the addition of this latest packet can be undone) MOVFW Txwh MOVWF temp4 MOVFW CTxPipeCount ;also save CTx pipe position .. don't loose data from the control channel MOVWF temp5 ;since it can simply wait to encode more output MOVFW CTxPipeRptr MOVWF temp6 Move temp,CTxPipeCount MOVLW 33 SUBWF temp,W MOVLW 32 BTFSC STATUS,C MOVWF temp ;temp is packet size .. truncated to 32 bytes if necessary DECF temp,W ;size-1 goes in low 5 bits IORLW 0XE0 ;address (=7) goes in high 3 bits CALL WriteTxBuf MOVFW TxSerial0 CALL WriteTxBuf MOVFW TxSerial1 CALL WriteTxBuf ec_packet_loop Move FSR,CTxPipeRptr MOVFW INDF CALL WriteTxBuf INCF CTxPipeRptr,F MOVFW CTxPipeRptr XORLW CTxPipeEnd MOVLW CTxPipe BTFSC STATUS,Z MOVWF CTxPipeRptr DECF CTxPipeCount,F DECFSZ temp,F GOTO ec_packet_loop BTFSS Flags1,TxBufOverflow ;did the Transmit buffer overflow? GOTO no_txbuf_overflow2 BCF Flags1,TxBufOverflow ;consume the flag MOVFW temp3 ;reset write pointer to its value before this last packet was added MOVWF Txwl ;(discard the packet) MOVFW temp4 MOVWF Txwh MOVFW temp5 ;reset CTx pipe position to value before write attempt MOVWF CTxPipeCount MOVFW temp6 MOVWF CTxPipeRptr GOTO no_control_packet ;Note: packet loss counter not incremented since data was left in CTxPipe no_txbuf_overflow2 BCF Flags1,CTxPipeFull ;update CTx pipe status flags MOVF CTxPipeCount,F BTFSC STATUS,Z BSF Flags2,CTxPipeEmpty INCF TxSerial0,F ;increment transmit packet serial number BTFSC STATUS,Z INCF TxSerial1,F INCF TxBufPCount0,F ;increment transmit buffer packet counter BTFSC STATUS,Z INCF TxBufPCount1,F no_control_packet ;Drive the Tx state machine through another state if the Tx Pipe isn't full BTFSC Flags1,TxPipeFull ;can this processor's transmit pipe accept another character? GOTO tx_return MOVF tx_state,F ;is any transmit activity currently occurring? (state 0 = idle) BTFSS STATUS,Z GOTO process_tx ;Transmit state is currently idle... ;check if Transmit_Buffer contains any packets ... begin transmitting a packet if so MOVFW TxBufPCount1 IORWF TxBufPCount0,W BTFSS STATUS,Z INCF tx_state,F ;tx state 1 begins transmission of next packet in TxBuf tx_return CLRWDT ;If there is any data waiting in the Receive Buffer for the command-interpreter then process it BTFSS RxBufEmptyFlags,7 GOTO process_command_rx ;If the command-interpreter is sending anything then drive its tx state machine through another state ;Note that tx states are started by the command_rx state machine ... commands and responses are ping-ponged (not streamed) BTFSC Flags1,CTxPipeFull GOTO mainloop MOVF ctx_state,F BTFSS STATUS,Z GOTO process_command_tx BTFSS Flags2,RC_BREAK ;has a BREAK condition been detected? GOTO ml_nobreak CLRF RCSTA ; yes, shut off serial port so its logic resets BTFSS Flags1,SlaveSelected ;is a slave addressed? GOTO nus_brk MOVFW SlaveAddress BusChipSelect ; yes, tell last slave to "unlisten" BCF Flags1,SlaveSelected nus_brk GOTO BREAK_RESET ;Reset baud rate ml_nobreak GOTO mainloop ;-------------------------------------------------------------------------------------- ; Receive 1 character from RxPipe and process it via the Rx state machine process_rx MOVFW RxPipeRptr MOVWF FSR SUBWF RxPipeWptr,W BTFSC STATUS,Z GOTO rx_pipe_is_empty BSF Flags1,Receiving MOVFW INDF MOVWF r232 INCF RxPipeRptr,F ;Increment RxPipe read pointer MOVFW RxPipeRptr SUBLW RxPipeEnd MOVLW RxPipe BTFSC STATUS,Z MOVWF RxPipeRptr MOVLW HIGH RxJmpTbl MOVWF PCLATH MOVF rx_state,W ADDLW LOW RxJmpTbl BTFSC STATUS,C INCF PCLATH,F MOVWF PCL ;jump to handler for current rx_state ;State-machine state handler takes over from here ... ; ;The context for the state handlers is as follows: ; 1). The r232 register contains the next character from the input stream ; 2). Control should be returned to rx_done (jump to the label below when done) ; or, alternately, to rx_done_noxs if the checksums need to be preserved ; rather than updated rx_done MOVFW r232 CALL update_rx_checksums rx_done_noxs GOTO mainloop rx_pipe_is_empty BSF Flags1,RxPipeEmpty GOTO mainloop ;------------------------------------------------------------------------------------------------ ; TxPipe is not full (or has not yet been detected as being full) and tx_state is not idle -- ; drive the Tx state machine through 1 state/iteration (each state/iteration typically produces ; one output character which is written to the transmit pipe) process_tx INCF TxPipeWptr,W ;tentatively increment TxPipe write pointer (to detect a full pipe) MOVWF TentativeTxWptr SUBLW TxPipeEnd MOVLW TxPipe BTFSC STATUS,Z MOVWF TentativeTxWptr MOVFW TxPipeRptr ;check to see if pipe is full SUBWF TentativeTxWptr,W BTFSC STATUS,Z GOTO tx_pipe_is_full BSF Flags1,Transmitting MOVLW HIGH TxJmpTbl MOVWF PCLATH DECF tx_state,W ;tx_state = 1 is the first entry in the table ADDLW LOW TxJmpTbl BTFSC STATUS,C INCF PCLATH,F MOVWF PCL ;jump to handler for current tx_state ;State-machine state handler takes over from here ... ; ;The context for the state handlers is as follows: ; 1). The character produced should be written to t232 ... it will be written to the TxPipe below ; 2). Control should be returned to tx_done (below) if a character was written, to tx_null ; if a character was not written, or to tx_done_noxs if a character was written but ; should not be applied to the checksums tx_done MOVFW t232 call update_tx_checksums tx_done_noxs Move FSR,TxPipeWptr Move INDF,t232 Move TxPipeWptr,TentativeTxWptr Bank1 BSF PIE1,TXIE Bank0 tx_null GOTO tx_return tx_pipe_is_full BSF Flags1,TxPipeFull GOTO tx_return ;------------------------------------------------------------------------------------------ ; Read 1 character from Receive_Buffer[7] and process it via the command Rx state machine process_command_rx Movlf RxBufIndex,14 Movlf RxBufMask,0X80 CALL ReadRxBuf MOVWF rCmd MOVLW HIGH CRxJmpTbl MOVWF PCLATH MOVF crx_state,W ADDLW LOW CRxJmpTbl BTFSC STATUS,C INCF PCLATH,F MOVWF PCL ;jump to handler for current rx_state ;State-machine state handler takes over from here ... ; ;The context for the state handlers is as follows: ; 1). The rCmd register contains the next character from the input stream ; 2). Control should be returned to crx_done (jump to the label below when done) ; or, alternately, to crx_done_noxs if the checksums should not be updated crx_done MOVFW rCmd CALL update_crx_checksums crx_done_noxs ;------ Testing -- printf( "rxc %.2X s %.2X xs %.2X cl %.2X ch %.2X\n", rCmd, crx_state, rCmdxs, rCmdcl, rCmdch ); ; to the auxiliary port at address 6 ; ; MOVLW 'r' ; CALL WriteRxBuf6 ; MOVLW 'x' ; CALL WriteRxBuf6 ; MOVLW 'c' ; CALL WriteRxBuf6 ; MOVLW ' ' ; CALL WriteRxBuf6 ; SWAPF rCmd,W ; CALL WriteNybRB6 ; MOVF rCmd,W ; CALL WriteNybRB6 ; MOVLW ' ' ; CALL WriteRxBuf6 ; MOVLW ' ' ; CALL WriteRxBuf6 ; MOVLW ' ' ; CALL WriteRxBuf6 ; ; MOVLW 's' ; CALL WriteRxBuf6 ; MOVLW ' ' ; CALL WriteRxBuf6 ; SWAPF crx_state,W ; CALL WriteNybRB6 ; MOVF crx_state,W ; CALL WriteNybRB6 ; MOVLW ' ' ; CALL WriteRxBuf6 ; MOVLW ' ' ; CALL WriteRxBuf6 ; MOVLW ' ' ; CALL WriteRxBuf6 ; ; MOVLW 'x' ; CALL WriteRxBuf6 ; MOVLW 's' ; CALL WriteRxBuf6 ; MOVLW ' ' ; CALL WriteRxBuf6 ; SWAPF rCmdxs,W ; CALL WriteNybRB6 ; MOVF rCmdxs,W ; CALL WriteNybRB6 ; MOVLW ' ' ; CALL WriteRxBuf6 ; MOVLW ' ' ; CALL WriteRxBuf6 ; MOVLW ' ' ; CALL WriteRxBuf6 ; ; MOVLW 'c' ; CALL WriteRxBuf6 ; MOVLW 'l' ; CALL WriteRxBuf6 ; MOVLW ' ' ; CALL WriteRxBuf6 ; SWAPF rCmdcl,W ; CALL WriteNybRB6 ; MOVF rCmdcl,W ; CALL WriteNybRB6 ; MOVLW ' ' ; CALL WriteRxBuf6 ; MOVLW ' ' ; CALL WriteRxBuf6 ; MOVLW ' ' ; CALL WriteRxBuf6 ; ; MOVLW 'c' ; CALL WriteRxBuf6 ; MOVLW 'h' ; CALL WriteRxBuf6 ; MOVLW ' ' ; CALL WriteRxBuf6 ; SWAPF rCmdch,W ; CALL WriteNybRB6 ; MOVF rCmdch,W ; CALL WriteNybRB6 ; MOVLW 13 ; CALL WriteRxBuf6 ; MOVLW 10 ; CALL WriteRxBuf6 GOTO mainloop ;------ Testing -- write hexadecimal representation of nybble to channel 6's receive buffer ------ ;WriteNybRB6 andlw 0x0f ; addlw 0xf6 ; btfsc STATUS,C ; addlw 7 ; addlw 58 ; ;------ Testing -- write character to channel 6's receive buffer ------ ;WriteRxBuf6 MOVWF PORTB ; ClrMemA16 ; Bank1 ; MOVF Rx6wl,W ; Bank0 ; MOVWF PORTD ; ClrMemAClk ; Bank1 ; MOVF Rx6wh,W ; Bank0 ; SetMemAClk ; MOVWF PORTD ; Movlf FSR,TRISB ; CLRF INDF ; SetMemCE1 ; SetMemWr ;note -- CE1 and OE are on different ports so its ok to do read-modify-write back-to-back ; ; Bank1 ; INCF Rx6wl,F ;increment the enqueue pointer ; BTFSS STATUS,Z ;Carry out of low byte? ; GOTO wrxbuf6_noihbp ; no, no need to increment high byte ; ; INCF Rx6wh,F ; MOVLW 0X1F ;check for a carry from bit 4 to bit 5 (high 3 bits are buffer address ... low 5 are offset) ; ANDWF Rx6wh,W ; BTFSS STATUS,Z ;Carry from bit4 to bit5 ? ; GOTO wrxbuf6_noihbp ; no, done ; ; DECF Rx6wh,F ; yes, restore the high 3 bits and clear the low 5 bits ; MOVLW 0XE0 ; ANDWF Rx6wh,F ; ;wrxbuf6_noihbp Bank0 ; ClrMemWr ; ClrMemCE1 ; DECF INDF,F ; ; BCF RxBufEmptyFlags,6 ; RETURN ; ;------------------------------------------------------------------------------------------------ ; CTxPipe is not full (or has not yet been detected as being full) and ctx_state is not idle -- ; drive the command interpreter Tx state machine through 1 state/iteration (each state/iteration ; typically produces one output character) process_command_tx INCF CTxPipeWptr,W ;tentatively increment CTxPipe write pointer (to detect a full pipe) MOVWF TentativeCTxWptr SUBLW CTxPipeEnd MOVLW CTxPipe BTFSC STATUS,Z MOVWF TentativeCTxWptr MOVFW CTxPipeRptr SUBWF TentativeCTxWptr,W BTFSC STATUS,Z GOTO ctx_pipe_is_full MOVLW HIGH CTxJmpTbl MOVWF PCLATH DECF ctx_state,W ;ctx_state = 1 is the first entry in the table ADDLW LOW CTxJmpTbl BTFSC STATUS,C INCF PCLATH,F MOVWF PCL ;jump to handler for current tx_state ;State-machine state handler takes over from here ... ; ;The context for the state handlers is as follows: ; 1). The character produced should be written to tCmd ... tCmd will be written to the CTx Pipe below ; 2). Control should be returned to ctx_done (below) if a character was written, ctx_null if a ; character was not written, or ctx_done_noxs if the checksums should not be updated ctx_done MOVFW tCmd call update_ctx_checksums ctx_done_noxs BCF Flags2,CTxPipeEmpty Move FSR,CTxPipeWptr Move INDF,tCmd Move CTxPipeWptr,TentativeCTxWptr INCF CTxPipeCount,F ;------ Testing -- printf( "txc %.2X s %.2X xs %.2X cl %.2X ch %.2X\n", tCmd, ctx_state, tCmdxs, tCmdcl, tCmdch ); ; to the auxiliary port at address 6 ; ; MOVLW 't' ; CALL WriteRxBuf6 ; MOVLW 'x' ; CALL WriteRxBuf6 ; MOVLW 'c' ; CALL WriteRxBuf6 ; MOVLW ' ' ; CALL WriteRxBuf6 ; SWAPF tCmd,W ; CALL WriteNybRB6 ; MOVF tCmd,W ; CALL WriteNybRB6 ; MOVLW ' ' ; CALL WriteRxBuf6 ; MOVLW ' ' ; CALL WriteRxBuf6 ; MOVLW ' ' ; CALL WriteRxBuf6 ; ; MOVLW 's' ; CALL WriteRxBuf6 ; MOVLW ' ' ; CALL WriteRxBuf6 ; SWAPF ctx_state,W ; CALL WriteNybRB6 ; MOVF ctx_state,W ; CALL WriteNybRB6 ; MOVLW ' ' ; CALL WriteRxBuf6 ; MOVLW ' ' ; CALL WriteRxBuf6 ; MOVLW ' ' ; CALL WriteRxBuf6 ; ; MOVLW 'x' ; CALL WriteRxBuf6 ; MOVLW 's' ; CALL WriteRxBuf6 ; MOVLW ' ' ; CALL WriteRxBuf6 ; SWAPF tCmdxs,W ; CALL WriteNybRB6 ; MOVF tCmdxs,W ; CALL WriteNybRB6 ; MOVLW ' ' ; CALL WriteRxBuf6 ; MOVLW ' ' ; CALL WriteRxBuf6 ; MOVLW ' ' ; CALL WriteRxBuf6 ; ; MOVLW 'c' ; CALL WriteRxBuf6 ; MOVLW 'l' ; CALL WriteRxBuf6 ; MOVLW ' ' ; CALL WriteRxBuf6 ; SWAPF tCmdcl,W ; CALL WriteNybRB6 ; MOVF tCmdcl,W ; CALL WriteNybRB6 ; MOVLW ' ' ; CALL WriteRxBuf6 ; MOVLW ' ' ; CALL WriteRxBuf6 ; MOVLW ' ' ; CALL WriteRxBuf6 ; ; MOVLW 'c' ; CALL WriteRxBuf6 ; MOVLW 'h' ; CALL WriteRxBuf6 ; MOVLW ' ' ; CALL WriteRxBuf6 ; SWAPF tCmdch,W ; CALL WriteNybRB6 ; MOVF tCmdch,W ; CALL WriteNybRB6 ; MOVLW 13 ; CALL WriteRxBuf6 ; MOVLW 10 ; CALL WriteRxBuf6 ; ctx_null GOTO mainloop ctx_pipe_is_full BSF Flags1,CTxPipeFull GOTO mainloop ;----------------------------------------------------------------------------------- ; Set blink code ; Higher blink codes are assumed to be more severe errors than lower ; blink codes so a higher code will not be replaced by a lower code. set_blink_code DouseStatus MOVWF temp SUBWF led_code,W BTFSC STATUS,C RETURN MOVFW temp MOVWF led_code RETURN ;------------------------------------------------------------------------------------ ; Apply W to receive checksums update_rx_checksums xorwf r232xs,F ;update plain checksum xorwf r232cl,F ;update cyclic checksum addwf r232ch,F bcf STATUS,C rrf r232ch,F btfsc r232cl,0 bsf r232ch,7 rrf r232cl,F movlw 0x31 addwf r232cl,F btfsc STATUS,C incf r232ch,F movlw 0x03 addwf r232ch,F return ;------------------------------------------------------------------------------------ ; Apply W to transmit checksums update_tx_checksums xorwf t232xs,F ;update plain checksum xorwf t232cl,F ;update cyclic checksum xor w with low byte addwf t232ch,F ;add w to high byte bcf STATUS,C ;clear status carry rrf t232ch,F ;rotate high right through carry btfsc t232cl,0 ;t232.0 set? bsf t232ch,7 ; set t232.15 rrf t232cl,F ;rotate low byte right through carry movlw 0x31 addwf t232cl,F ;add 31h to low byte btfsc STATUS,C ;Carry? incf t232ch,F ; inc high byte movlw 0x03 addwf t232ch,F ;add 3h to high byte return ;------------------------------------------------------------------------------------ ; Apply W to command-interpreter receive checksums update_crx_checksums xorwf rCmdxs,F ;update plain checksum xorwf rCmdcl,F ;update cyclic checksum addwf rCmdch,F bcf STATUS,C rrf rCmdch,F btfsc rCmdcl,0 bsf rCmdch,7 rrf rCmdcl,F movlw 0x31 addwf rCmdcl,F btfsc STATUS,C incf rCmdch,F movlw 0x03 addwf rCmdch,F return ;------------------------------------------------------------------------------------ ; Apply W to command-interpreter transmit checksums update_ctx_checksums xorwf tCmdxs,F ;update plain checksum xorwf tCmdcl,F ;update cyclic checksum addwf tCmdch,F bcf STATUS,C rrf tCmdch,F btfsc tCmdcl,0 bsf tCmdch,7 rrf tCmdcl,F movlw 0x31 addwf tCmdcl,F btfsc STATUS,C incf tCmdch,F movlw 0x03 addwf tCmdch,F return ;################################################################################################ ; SRAM interface subroutines ;--------------------------------------------------------------------------- ; Read one byte from SRAM. Call with FSR pointing to the low byte of the ; address and FSR + 1 pointing to the high byte of the address. A16 should ; be set by the caller (it is not changed). ; ; The address pin assignment is arbitrary. All of the SRAM interface ; subroutines adhere to the convention that the latched address lines ; are A0..7 and the direct lines are A8..15. ; ; The byte read is returned in W. ; ; Time = 17 instruction cycles (3.4us) including call and return ; ReadMem Move PORTD,INDF ClrMemAClk INCF FSR,F SetMemAClk Move PORTD,INDF SetMemCE1 SetMemOE ;note -- CE1 and OE are on different ports so its ok to do read-modify-write back-to-back DECF FSR,F MOVFW PORTB ClrMemOE ClrMemCE1 RETURN ;--------------------------------------------------------------------------- ; Write one byte to SRAM. Call with FSR pointing to the low byte of the ; address and FSR + 1 pointing to the high byte of the address. A16 should ; be set by the caller (it is not changed). The byte to write should be ; in the W register. ; ; The W register is modified (contains high byte of address) upon return. ; ; Time = 23 instruction cycles (4.6us) including call and return ; WriteMem MOVWF PORTB Move PORTD,INDF ClrMemAClk INCF FSR,F SetMemAClk Move PORTD,INDF Bank1 CLRF TRISB Bank0 SetMemCE1 SetMemWr ;note -- CE1 and OE are on different ports so its ok to do read-modify-write back-to-back DECF FSR,F ClrMemWr ClrMemCE1 Bank1 DECF TRISB,F Bank0 RETURN ;--------------------------------------------------------------------------- ; Write one byte to the transmit buffer. ; ; Updates enqueue pointer. ; Sets Flags1:TxBufOverflow and doesn't move enqueue pointer if the enqueue ; pointer would lap the dequeue pointer when moved. ; ; Modifies FSR ; WriteTxBuf MOVWF PORTB SetMemA16 Move PORTD,Txwl ClrMemAClk MOVFW Txwh SetMemAClk MOVWF PORTD Movlf FSR,TRISB CLRF INDF SetMemCE1 SetMemWr ;note -- CE1 and OE are on different ports so its ok to do read-modify-write back-to-back INCF Txwl,F ;this pointer needs to be incremented sooner or later and here is a good place to do it BTFSC STATUS,Z ;since something needs to be inserted between the SetMemWr and the ClrMemWr INCF Txwh,F ClrMemWr ClrMemCE1 DECF INDF,F MOVFW Txwl ;Check for buffer overflow XORWF Txrl,W BTFSS STATUS,Z RETURN MOVFW Txwh XORWF Txrh,W BTFSS STATUS,Z RETURN BSF Flags1,TxBufOverflow ;The buffer did overflow MOVLW 1 SUBWF Txwl,F BTFSS STATUS,C SUBWF Txwh,F RETURN ;--------------------------------------------------------------------------- ; ReadTxBuf is located on high page ;--------------------------------------------------------------------------- ; WriteRxBuf is located on high page ;--------------------------------------------------------------------------- ; Read one byte from one of the receive buffers. ; ; Precondition: ; ; RxBufIndex contains SlaveAddress * 2. ; Note that SlaveAddress = 7 for the control channel. ; ; RxBufMask contains POW(2, SlaveAddress). ; ; The dequeue pointer for the buffer (pointer pair at Rx0rl + RxBufIndex) ; points to the next location within the buffer to be read. ; ; ; Postcondition: ; ; W contains the byte read from [Rx0rl + RxBufIndex]. ; ; The dequeue pointer for the indexed receive buffer has been incremented. ; ; RxBufEmptyFlags has been OR'd with RxBufMask (bit corresponding to the ; indexed slave has been set) if the dequeue ptr equals the enqueue ptr. ; ; ; Modifies: ; FSR ; mem_temp ; mem_temp2 ReadRxBuf ClrMemA16 MOVLW Rx0rl ADDWF RxBufIndex,W MOVWF FSR Move PORTD,INDF MOVWF mem_temp ;copy dequeue pointer to mem_temp2:mem_temp ClrMemAClk INCF FSR,F MOVFW INDF MOVWF mem_temp2 SetMemAClk MOVWF PORTD SetMemCE1 SetMemOE ;note -- CE1 and OE are on different ports so its ok to do read-modify-write back-to-back INCF mem_temp,F ;increment mem_temp2:mem_temp and copy back to dequeue pointer BTFSS STATUS,Z GOTO rrxbnboc INCF mem_temp2,F MOVLW 0X1F ANDWF mem_temp2,W BTFSS STATUS,Z ;was there a carry from bit 4 to bit 5 ? (high 3 bits are buffer address ... low 5 are offset) GOTO rrxbnboc DECF mem_temp2,F ; yes, clear low 5 bits but leave buffer address the same MOVLW 0XE0 ANDWF mem_temp2,F rrxbnboc Move INDF,mem_temp2 ;copy incremented pointer to dequeue pointer DECF FSR,F Move INDF,mem_temp MOVLW Rx0wl ;check for empty buffer ADDWF RxBufIndex,W MOVWF FSR MOVFW INDF XORWF mem_temp,W BTFSS STATUS,Z GOTO no_empty_readrxbuf INCF FSR,F MOVFW INDF XORWF mem_temp2,W BTFSS STATUS,Z GOTO no_empty_readrxbuf MOVFW RxBufMask IORWF RxBufEmptyFlags,F no_empty_readrxbuf MOVFW PORTB ;end the memory read cycle ClrMemOE ClrMemCE1 RETURN ;*********************************************************************************************** ; ROM page break ;*********************************************************************************************** org 0x800 ;high page of ROM ;--------------------------------------------------------------------------------------------------------------------- ; Rx state machine dispatch table and handlers ; ; This table is used to jump to the receive character state-machine state handler for a given rx_state. ; The first entry corresponds to rx_state = 0, the second entry to rx_state = 1, etc. ; ; ; The context for the state handlers is as follows: ; ; 1). The r232 register contains the next character from the input stream ; ; 2). Control should be returned to rx_done. Since rx_done is on a different ROM page, each handler ; should jump to return_from_rx ... this code fragment will set the page bits and jump to rx_done. ; ; 3). Additional labels are provided upstream from return_from_rx which perform commonly executed ; tasks prior to returning as a convenience and to save space in ROM ; ; 4). The normal return methods update the checksums. If the checksums need to be left alone then ; use the _noxs versions of the returns. clear_rx_state CLRF rx_state GOTO return_from_rx next_rx_state INCF rx_state,W set_rx_state MOVWF rx_state return_from_rx LowPage goto rx_done clear_rx_state_noxs CLRF rx_state ;no checksum update versions of returns GOTO return_from_rx_noxs next_rx_state_noxs INCF rx_state,W set_rx_state_noxs MOVWF rx_state return_from_rx_noxs LowPage goto rx_done_noxs ; ; This dispatch table must be located in same ROM page as the handlers RxJmpTbl GOTO RX_0 GOTO RX_1 GOTO RX_2 GOTO RX_3 GOTO RX_4 GOTO RX_5 GOTO RX_6 GOTO RX_7 GOTO RX_8 rxs0 CLRF rx_state ;jump here to reset Rx state and test r232 from state 0 RX_0 MOVFW r232 ;1st byte of packet header XORLW '@' BTFSS STATUS,Z GOTO return_from_rx GOTO next_rx_state RX_1 MOVFW r232 ;2nd byte of packet header XORLW 'P' BTFSS STATUS,Z GOTO rxs0 CLRF r232xs CLRF r232cl CLRF r232ch GOTO next_rx_state_noxs RX_2 SWAPF r232,W ;address/length descriptor MOVWF rx_substate RRF rx_substate,F MOVLW 0X07 ANDWF rx_substate,F ;rx_substate = address MOVLW 0X1F ANDWF r232,W ADDLW 1 MOVWF rx_substate2 ;rx_substate2 = length MOVLW Rx0wl ;RxTwh:RxTwl = buffer enqueue pointer ADDWF rx_substate,W ; used later by WriteRxBuf ADDWF rx_substate,W MOVWF FSR MOVFW INDF MOVWF RxTwl INCF FSR,F MOVFW INDF MOVWF RxTwh GOTO next_rx_state RX_3 MOVFW r232 MOVWF rx_substate3 ;rx_substate3 = low byte of serial number GOTO next_rx_state ;serial number ... high byte ;Lost packet count = (packet serial number - expected serial number) ;expected serial number = RxSerial1:RxSerial0 + 1 ;Stick lost packet count in rx_substate4:rx_substate_3 ... ;if checksum passes then add it to total lost packet count and add it (+1) to RxSerial1:RxSerial0 RX_4 Move rx_substate4,r232 ;high byte of serial number COMF RxSerial0,F ;take 1's compliment and add... same result as adding 1 then subtracting COMF RxSerial1,F ; or adding 1 then taking 2's compliment and adding MOVFW RxSerial0 ADDWF rx_substate3,F BTFSC STATUS,Z INCF rx_substate4,F MOVFW RxSerial1 ADDWF rx_substate4,F GOTO next_rx_state ;Receive one databyte ... note that data has been scrambled and must be unscrambled: ; ; The body contains the data (1 to 32 bytes, as specified in the header). ; ; The data bytes themselves are modified via the following algorithm: (using C syntax) ; output_byte = input_byte ^ (cyclic_checksum & 0xff); ; apply_to_checksums( input_byte ); ; ; To restore the data to its original value: ; input_byte = output_byte ^ (cyclic_checksum & 0xff); ; apply_to_checksums( input_byte ); ; ; This modification of the data is done to prevent packets being sent to cascaded ; multiplexers from being misinterpreted as packets destined for this multiplexer if the ; receive logic gets out of phase. RX_5 MOVFW r232cl ;restore original databyte from scrambled value XORWF r232,W MOVWF r232 ;r232 will be applied to the checksums upon return CALL WriteRxBuf DECFSZ rx_substate2,F GOTO return_from_rx GOTO next_rx_state ;Checksums RX_6 MOVFW r232xs ;plain checksum XORWF r232,W BTFSC STATUS,Z GOTO next_rx_state_noxs GOTO rxs0 RX_7 MOVFW r232cl ;low byte of cyclic checksum XORWF r232,W BTFSC STATUS,Z GOTO next_rx_state_noxs GOTO rxs0 RX_8 MOVFW r232ch ;high byte of cyclic checksum XORWF r232,W BTFSS STATUS,Z GOTO rxs0 ;All checksums passed ... the packet was good ;add current missing packet count to accumulator MOVFW rx_substate3 ADDWF RxBufMissCount0,F MOVLW 1 BTFSC STATUS,C ADDWF RxBufMissCount1,F BTFSC STATUS,C ADDWF RxBufMissCount2,F BTFSC STATUS,C ADDWF RxBufMissCount3,F MOVFW rx_substate4 ADDWF RxBufMissCount1,F MOVLW 1 BTFSC STATUS,C ADDWF RxBufMissCount2,F BTFSC STATUS,C ADDWF RxBufMissCount3,F ;add current missing packet count + 1 to the Rx serial number to bring RxSerial1:RxSerial0 up to date ;(missing packet count is the only information about the packet's serial number saved) MOVFW rx_substate3 ADDWF RxSerial0,F BTFSC STATUS,C INCF RxSerial1,F MOVFW rx_substate4 ADDWF RxSerial1,F ;Commit the packet to the buffer by copying the tentative write pointer to the actual enqueue pointer MOVLW Rx0wl ADDWF rx_substate,W ADDWF rx_substate,W MOVWF FSR Move INDF,RxTwl INCF FSR,F Move INDF,RxTwh ;Clear the "buffer empty" flag for the receive buffer to which the packet was addressed Movlf rx_substate2,0XFE MOVF rx_substate,F BTFSC STATUS,Z GOTO xtlprrbefmrxp lprrbefmrxp BSF STATUS,C RLF rx_substate2,F DECFSZ rx_substate,F GOTO lprrbefmrxp xtlprrbefmrxp MOVFW rx_substate2 ANDWF RxBufEmptyFlags,F GOTO clear_rx_state ;--------------------------------------------------------------------------------------------------------------------- ; Tx state machine dispatch table and handlers ; ; This table is used to jump to the transmit character state-machine state handler for a given tx_state. ; The first entry corresponds to tx_state = 1, the second entry to tx_state = 2, etc. ; Note that there is no entry for tx_state = 0 since that is the idle state. ; ; ;The context for the state handlers is as follows: ; ; 1). The character generated should be written to t232. ; ; 2). Control should be returned to tx_done if an output character was written. Since tx_done is on a ; different ROM page, each handler should jump to return_from_tx ... this code fragment will set the ; page bits and jump to tx_done. ; ; 3). Control should be returned to tx_null if a character was not written. Each handler should jump ; to null_tx_return when done if no output was produced. ; ; 4). Additional labels are provided upstream from return_from_tx which perform commonly executed ; tasks prior to returning as a convenience and to save space in ROM ; ; 5). The normal return methods update the checksums. If the checksums need to be left alone then ; use the _noxs versions of the returns. end_tx CLRF tx_state GOTO return_from_tx next_tx_state INCF tx_state,W set_tx_state MOVWF tx_state return_from_tx LowPage goto tx_done null_tx_return LowPage goto tx_null end_tx_noxs CLRF tx_state ;no checksums update versions of returns GOTO return_from_tx_noxs next_tx_state_noxs INCF tx_state,W set_tx_state_noxs MOVWF tx_state return_from_tx_noxs LowPage goto tx_done_noxs ; ; This dispatch table must be located in same ROM page as the handlers TxJmpTbl GOTO TX_1 GOTO TX_2 GOTO TX_3 GOTO TX_4 GOTO TX_5 GOTO TX_6 GOTO TX_7 GOTO TX_8 GOTO TX_9 TX_1 Movlf t232,'@' GOTO next_tx_state TX_2 Movlf t232,'P' GOTO next_tx_state TX_3 CALL ReadTxBuf ;address/length descriptor MOVWF t232 SWAPF t232,W MOVWF tx_substate RRF tx_substate,F MOVLW 0X07 ANDWF tx_substate,F ;tx_substate = address MOVLW 0X1F ANDWF t232,W ADDLW 1 MOVWF tx_substate2 ;tx_substate2 = length CLRF t232xs CLRF t232cl CLRF t232ch GOTO next_tx_state TX_4 CALL ReadTxBuf ;low byte of serial number MOVWF t232 GOTO next_tx_state TX_5 CALL ReadTxBuf ;high byte of serial number MOVWF t232 GOTO next_tx_state ;Write one databyte ... note that data needs to be scrambled: ; ; The body contains the data (1 to 32 bytes, as specified in the header). ; ; The data bytes themselves are modified via the following algorithm: (using C syntax) ; output_byte = input_byte ^ (cyclic_checksum & 0xff); ; apply_to_checksums( input_byte ); ; ; To restore the data to its original value: ; input_byte = output_byte ^ (cyclic_checksum & 0xff); ; apply_to_checksums( input_byte ); ; ; This modification of the data is done to prevent packets being sent to cascaded ; multiplexers from being misinterpreted as packets destined for this multiplexer if the ; receive logic gets out of phase. TX_6 CALL ReadTxBuf MOVWF temp ;save unmodified databyte for checksum computation XORWF t232cl,W MOVWF t232 MOVFW temp LowPage CALL update_tx_checksums ;compute checksum using unmodified databyte HighPage DECFSZ tx_substate2,F GOTO return_from_tx_noxs GOTO next_tx_state_noxs TX_7 Move t232,t232xs ;plain checksum GOTO next_tx_state_noxs TX_8 Move t232,t232cl ;cyclic checksum low byte GOTO next_tx_state_noxs TX_9 Move t232,t232ch ;cyclic checksum high byte MOVLW 1 SUBWF TxBufPCount0,F BTFSS STATUS,C SUBWF TxBufPCount1,F GOTO end_tx_noxs ;--------------------------------------------------------------------------------------------------------------------- ; Command-interpreter Rx state machine dispatch table and handlers ; ; This table is used to jump to the receive character state-machine state handler for a given crx_state. ; The first entry corresponds to crx_state = 0, the second entry to crx_state = 1, etc. ; ; ; The context for the state handlers is as follows: ; ; 1). The rCmd register contains the next character from the input stream ; ; 2). Control should be returned to crx_done. Since crx_done is on a different ROM page, each handler ; should jump to return_from_crx ... this code fragment will set the page bits and jump to crx_done. ; ; 3). Additional labels are provided upstream from return_from_crx which perform commonly executed ; tasks prior to returning as a convenience and to save space in ROM ; ; 4). The normal return methods update the checksums. If the checksums need to be left alone then ; use the _noxs versions of the returns. clear_crx_state CLRF crx_state GOTO return_from_crx next_crx_state INCF crx_state,W set_crx_state MOVWF crx_state return_from_crx LowPage goto crx_done clear_crx_state_noxs CLRF crx_state ;no checksum versions of returns GOTO return_from_crx_noxs next_crx_state_noxs INCF crx_state,W set_crx_state_noxs MOVWF crx_state return_from_crx_noxs LowPage goto crx_done_noxs ; ; This dispatch table must be located in same ROM page as the handlers CRxJmpTbl GOTO CRX_0 GOTO CRX_1 GOTO CRX_2 GOTO CRX_3 GOTO CRX_4 GOTO CRX_5 GOTO CRX_6 GOTO CRX_7 GOTO CRX_8 GOTO CRX_9 GOTO CRX_10 GOTO CRX_11 GOTO CRX_12 GOTO CRX_13 GOTO CRX_14 GOTO CRX_15 GOTO CRX_16 GOTO CRX_17 GOTO CRX_18 GOTO CRX_19 GOTO CRX_20 crxs0 CLRF crx_state ;jump here to reset CRX state and test rCmd from state 0 CRX_0 MOVFW rCmd XORLW '@' BTFSC STATUS,Z GOTO next_crx_state_noxs GOTO return_from_crx_noxs CRX_1 MOVFW rCmd XORLW 'R' BTFSC STATUS,Z GOTO next_crx_state_noxs ;"read" command XORLW 'W' ^ 'R' BTFSC STATUS,Z GOTO SET_CRX_7 XORLW 'B' ^ 'W' BTFSS STATUS,Z GOTO crxs0 MOVLW 16 GOTO set_crx_state_noxs ;"baud rate" command SET_CRX_7 MOVLW 7 ;"write" command GOTO set_crx_state_noxs ;------ Read command ------ CRX_2 MOVLW 0X1C ;Address/length byte -- verify that bits 2..4 are zeros ANDWF rCmd,W BTFSS STATUS,Z GOTO crxs0 SWAPF rCmd,W Bank1 MOVWF RegXferMCUAddr ;MCU address is in high 3 bits RRF RegXferMCUAddr,F MOVLW 7 ANDWF RegXferMCUAddr,F Bank0 MOVLW 3 ANDWF rCmd,W ADDLW 1 Bank1 MOVWF RegXferLen Bank0 CLRF rCmdxs ;clear checksums ... checksum will be taken over addr/len byte CLRF rCmdcl ; (this byte) and register start address (next byte) CLRF rCmdch GOTO next_crx_state CRX_3 MOVFW rCmd ;register start address Bank1 MOVWF RegXferAddr Bank0 GOTO next_crx_state ;Checksums of addr/len and register start address bytes CRX_4 MOVFW rCmdxs ;plain checksum XORWF rCmd,W BTFSC STATUS,Z GOTO next_crx_state_noxs GOTO crxs0 CRX_5 MOVFW rCmdcl ;low byte of cyclic checksum XORWF rCmd,W BTFSC STATUS,Z GOTO next_crx_state_noxs GOTO crxs0 CRX_6 MOVFW rCmdch ;high byte of cyclic checksum XORWF rCmd,W BTFSS STATUS,Z GOTO crxs0 ;All checksums passed ... the packet was good ... generate a response ;At this point ; RegXferMCUAddr holds the MCU address, ; RegXferAddr holds the register address, ; RegXferLen holds the length ; rCmdxs, rCmdcl, and rCmdch hold the checksums of the command packet (which must be echoed in the reply) Move tCmdxs,rCmdxs ;copy checksums for reply Move tCmdcl,rCmdcl Move tCmdch,rCmdch MOVLW 7 Bank1 XORWF RegXferMCUAddr,W Bank0 BTFSC STATUS,Z GOTO REGREAD_MASTER ;read from this MCU's register space ;Read from a slave MCU's register space ;Note that if a read command specifies an address for which no slave is installed then the ;firmware will crash. It is the host computer's responsibility to check the installed slaves ;status byte and not request any transfers to/from non-existent slave MCUs. BTFSS Flags1,SlaveSelected ;is a slave addressed? GOTO nus_rreg MOVFW SlaveAddress BusChipSelect ;tell last slave to "unlisten" BCF Flags1,SlaveSelected nus_rreg Bank1 MOVF RegXferMCUAddr,W Bank0 IORLW 0X80 BusChipSelect ;address the slave specified MOVLW WordBufAddr BusWriteIAddr Bank1 MOVF RegXferAddr,W Bank0 BusWriteIndirect Bank1 MOVF RegXferLen,W Bank0 BusWriteIndirect MOVLW 0 BusWriteIAddr ;command the slave MCU to perform a read transfer to the Word Buffer MOVLW WordBuf0 BusWriteIAddr BusReadIndirect Bank1 MOVWF RegXfer0 ;move data into RegXfer 0..3 Bank0 BusReadIndirect Bank1 MOVWF RegXfer1 Bank0 BusReadIndirect Bank1 MOVWF RegXfer2 Bank0 BusReadIndirect Bank1 MOVWF RegXfer3 MOVF RegXferMCUAddr,W Bank0 BusChipSelect ;"unlisten" the slave FINISH_CRX_READ MOVLW 1 ;CTx state for response to read command MOVWF ctx_state GOTO clear_crx_state_noxs ;Read a block of registers from this MCU's register space REGREAD_MASTER Bank1 MOVF RegXferAddr,W Bank0 MOVWF FSR MOVLW HIGH WBRTT ;use jump-table to read n bytes to buffer in right-justified fashion MOVWF PCLATH Bank1 MOVF RegXferLen,W Bank0 ADDLW LOW WBRTT BTFSC STATUS,C INCF PCLATH,F ClrI MOVWF PCL WBRTT GOTO WBR0 GOTO WBR1 GOTO WBR2 GOTO WBR3 ;WBR4 MOVF INDF,W Bank1 MOVWF RegXfer0 Bank0 INCF FSR,F WBR3 MOVF INDF,W Bank1 MOVWF RegXfer1 Bank0 INCF FSR,F WBR2 MOVF INDF,W Bank1 MOVWF RegXfer2 Bank0 INCF FSR,F WBR1 MOVF INDF,W Bank1 MOVWF RegXfer3 Bank0 WBR0 SetI GOTO FINISH_CRX_READ ;------ Write Command ------ CRX_7 MOVLW 0X1C ;Address/length byte -- verify that bits 2..4 are zeros ANDWF rCmd,W BTFSS STATUS,Z GOTO crxs0 SWAPF rCmd,W Bank1 MOVWF RegXferMCUAddr ;MCU address is in high 3 bits RRF RegXferMCUAddr,F MOVLW 7 ANDWF RegXferMCUAddr,F Bank0 MOVLW 3 ANDWF rCmd,W ADDLW 1 Bank1 MOVWF RegXferLen Bank0 CLRF rCmdxs ;clear checksums CLRF rCmdcl CLRF rCmdch GOTO next_crx_state CRX_8 MOVFW rCmd ;register start address Bank1 MOVWF RegXferAddr MOVF RegXferLen,W ;block length (1..4) Bank0 SUBLW 5 ADDWF crx_state,F ;State 9 for 4 bytes, state 10 for 3 bytes, etc. GOTO return_from_crx CRX_9 MOVF rCmd,W Bank1 MOVWF RegXfer0 Bank0 GOTO next_crx_state CRX_10 MOVF rCmd,W Bank1 MOVWF RegXfer1 Bank0 GOTO next_crx_state CRX_11 MOVF rCmd,W Bank1 MOVWF RegXfer2 Bank0 GOTO next_crx_state CRX_12 MOVF rCmd,W Bank1 MOVWF RegXfer3 Bank0 GOTO next_crx_state CRX_13 MOVFW rCmdxs ;plain checksum XORWF rCmd,W BTFSC STATUS,Z GOTO next_crx_state_noxs GOTO crxs0 CRX_14 MOVFW rCmdcl ;low byte of cyclic checksum XORWF rCmd,W BTFSC STATUS,Z GOTO next_crx_state_noxs GOTO crxs0 CRX_15 MOVFW rCmdch ;high byte of cyclic checksum XORWF rCmd,W BTFSS STATUS,Z GOTO crxs0 ;All checksums passed ... the packet was good ... write the data and then generate a response ;At this point ; RegXferMCUAddr holds the MCU address, ; RegXferAddr holds the register address, ; RegXferLen holds the length ; RegXfer0 .. RegXfer3 hold the right-justified block of registers ; rCmdxs, rCmdcl, and rCmdch hold the checksums of the command packet (which must be echoed in the reply) Move tCmdxs,rCmdxs ;copy checksums for reply Move tCmdcl,rCmdcl Move tCmdch,rCmdch MOVLW 7 Bank1 XORWF RegXferMCUAddr,W Bank0 BTFSC STATUS,Z GOTO REGWRITE_MASTER ;write to this MCU's register space ;Write to a slave MCU's register space ;Note that if a read command specifies an address for which no slave is installed then the ;firmware will crash. It is the host computer's responsibility to check the installed slaves ;status byte and not request any transfers to/from non-existent slave MCUs. BTFSS Flags1,SlaveSelected ;is a slave addressed? GOTO nus_wreg MOVFW SlaveAddress BusChipSelect ;tell last slave to "unlisten" BCF Flags1,SlaveSelected nus_wreg ;------ Testing -- printf( "Block Write %.2X addr %.2X len %.2X %.2X%.2X%.2X%.2X\n", RegXferMCUAddr, RegXferAddr, RegXferLen, RegXfer3, RegXfer2, RegXfer1, RegXfer0 ); ; to the auxiliary port at address 6 ; ; LowPage ; MOVLW 'B' ; CALL WriteRxBuf6 ; MOVLW 'l' ; CALL WriteRxBuf6 ; MOVLW 'o' ; CALL WriteRxBuf6 ; MOVLW 'c' ; CALL WriteRxBuf6 ; MOVLW 'k' ; CALL WriteRxBuf6 ; MOVLW ' ' ; CALL WriteRxBuf6 ; MOVLW 'W' ; CALL WriteRxBuf6 ; MOVLW 'r' ; CALL WriteRxBuf6 ; MOVLW 'i' ; CALL WriteRxBuf6 ; MOVLW 't' ; CALL WriteRxBuf6 ; MOVLW 'e' ; CALL WriteRxBuf6 ; MOVLW ' ' ; CALL WriteRxBuf6 ; Bank1 ; SWAPF RegXferMCUAddr,W ; Bank0 ; CALL WriteNybRB6 ; Bank1 ; MOVF RegXferMCUAddr,W ; Bank0 ; CALL WriteNybRB6 ; MOVLW ' ' ; CALL WriteRxBuf6 ; MOVLW ' ' ; CALL WriteRxBuf6 ; MOVLW ' ' ; CALL WriteRxBuf6 ; ; MOVLW 'a' ; CALL WriteRxBuf6 ; MOVLW 'd' ; CALL WriteRxBuf6 ; MOVLW 'd' ; CALL WriteRxBuf6 ; MOVLW 'r' ; CALL WriteRxBuf6 ; MOVLW ' ' ; CALL WriteRxBuf6 ; Bank1 ; SWAPF RegXferAddr,W ; Bank0 ; CALL WriteNybRB6 ; Bank1 ; MOVF RegXferAddr,W ; Bank0 ; CALL WriteNybRB6 ; MOVLW ' ' ; CALL WriteRxBuf6 ; MOVLW ' ' ; CALL WriteRxBuf6 ; MOVLW ' ' ; CALL WriteRxBuf6 ; ; MOVLW 'l' ; CALL WriteRxBuf6 ; MOVLW 'e' ; CALL WriteRxBuf6 ; MOVLW 'n' ; CALL WriteRxBuf6 ; MOVLW ' ' ; CALL WriteRxBuf6 ; Bank1 ; SWAPF RegXferLen,W ; Bank0 ; CALL WriteNybRB6 ; Bank1 ; MOVF RegXferLen,W ; Bank0 ; CALL WriteNybRB6 ; MOVLW ' ' ; CALL WriteRxBuf6 ; MOVLW ' ' ; CALL WriteRxBuf6 ; MOVLW ' ' ; CALL WriteRxBuf6 ; ; Bank1 ; SWAPF RegXfer3,W ; Bank0 ; CALL WriteNybRB6 ; Bank1 ; MOVF RegXfer3,W ; Bank0 ; CALL WriteNybRB6 ; Bank1 ; SWAPF RegXfer2,W ; Bank0 ; CALL WriteNybRB6 ; Bank1 ; MOVF RegXfer2,W ; Bank0 ; CALL WriteNybRB6 ; Bank1 ; SWAPF RegXfer1,W ; Bank0 ; CALL WriteNybRB6 ; Bank1 ; MOVF RegXfer1,W ; Bank0 ; CALL WriteNybRB6 ; Bank1 ; SWAPF RegXfer0,W ; Bank0 ; CALL WriteNybRB6 ; Bank1 ; MOVF RegXfer0,W ; Bank0 ; CALL WriteNybRB6 ; ; MOVLW 13 ; CALL WriteRxBuf6 ; MOVLW 10 ; CALL WriteRxBuf6 ; HighPage ; Bank1 MOVF RegXferMCUAddr,W Bank0 IORLW 0X80 BusChipSelect ;address the slave specified MOVLW WordBufAddr BusWriteIAddr Bank1 MOVF RegXferAddr,W Bank0 BusWriteIndirect Bank1 MOVF RegXferLen,W Bank0 BusWriteIndirect Bank1 MOVF RegXfer0,W ;move data from RegXfer 0..3 Bank0 BusWriteIndirect Bank1 MOVF RegXfer1,W Bank0 BusWriteIndirect Bank1 MOVF RegXfer2,W Bank0 BusWriteIndirect Bank1 MOVF RegXfer3,W Bank0 BusWriteIndirect MOVLW 0X80 BusWriteIAddr ;command the slave MCU to perform a write transfer from the Word Buffer Bank1 MOVF RegXferMCUAddr,W Bank0 BusChipSelect ;"unlisten" the slave FINISH_CRX_WRITE MOVLW 13 ;CTx state for response to write command MOVWF ctx_state GOTO clear_crx_state_noxs ;Write a block of registers to this MCU's register space REGWRITE_MASTER Bank1 MOVF RegXferAddr,W Bank0 MOVWF FSR MOVLW HIGH WBWTT ;use jump-table to write n bytes from buffer in right-justified fashion MOVWF PCLATH Bank1 MOVF RegXferLen,W Bank0 ADDLW LOW WBWTT BTFSC STATUS,C INCF PCLATH,F ClrI MOVWF PCL WBWTT GOTO WBW0 GOTO WBW1 GOTO WBW2 GOTO WBW3 ;WBW4 Bank1 MOVF RegXfer0,W Bank0 MOVWF INDF INCF FSR,F WBW3 Bank1 MOVF RegXfer1,W Bank0 MOVWF INDF INCF FSR,F WBW2 Bank1 MOVF RegXfer2,W Bank0 MOVWF INDF INCF FSR,F WBW1 Bank1 MOVF RegXfer3,W Bank0 MOVWF INDF INCF FSR,F WBW0 SetI GOTO FINISH_CRX_WRITE ;------ Baud rate change command ------ CRX_16 MOVLW 0X6C ;TXSTA value XOR'd with 0X6C XORWF rCmd,F MOVF rCmd,W Bank1 MOVWF RegXfer0 ;stash TXSTA value here for now Bank0 CLRF rCmdxs ;clear checksums CLRF rCmdcl CLRF rCmdch GOTO next_crx_state CRX_17 MOVLW 0X35 ;SPBRG value XOR'd with 0X35 XORWF rCmd,F MOVF rCmd,W Bank1 MOVWF RegXfer1 ;stash SPBRG value here for now Bank0 GOTO next_crx_state ;Checksums of TXSTA and SPBRG CRX_18 MOVFW rCmdxs ;plain checksum XORWF rCmd,W BTFSC STATUS,Z GOTO next_crx_state_noxs GOTO crxs0 CRX_19 MOVFW rCmdcl ;low byte of cyclic checksum XORWF rCmd,W BTFSC STATUS,Z GOTO next_crx_state_noxs GOTO crxs0 CRX_20 MOVFW rCmdch ;high byte of cyclic checksum XORWF rCmd,W BTFSS STATUS,Z GOTO crxs0 ;All checksums passed ... the packet was good ... reset baud rate CLRF RCSTA ;shut off serial port so its logic resets BTFSS Flags1,SlaveSelected ;is a slave addressed? GOTO nus_rbr MOVFW SlaveAddress BusChipSelect ;tell last slave to "unlisten" BCF Flags1,SlaveSelected nus_rbr LowPage GOTO RESET_BAUD ;--------------------------------------------------------------------------------------------------------------------- ; Command interpreter Tx state machine dispatch table and handlers ; ; This table is used to jump to the transmit character state-machine state handler for a given ctx_state. ; The first entry corresponds to ctx_state = 1, the second entry to ctx_state = 2, etc. ; Note that there is no entry for ctx_state = 0 since that is the idle state. ; ; ;The context for the state handlers is as follows: ; ; 1). The output character should be written to tCmd ; ; 2). Control should be returned to ctx_done if an output character was written. Since ctx_done is on a ; different ROM page, each handler should jump to return_from_ctx ... this code fragment will set the ; page bits and jump to ctx_done. ; ; 3). Control should be returned to ctx_null if a character was not written. Each handler should jump ; to null_ctx_return when done if no output was produced. ; ; 4). Additional labels are provided upstream from return_from_ctx which perform commonly executed ; tasks prior to returning as a convenience and to save space in ROM ; ; 5). The normal return methods update the checksums. If the checksums need to be left alone then ; use the _noxs versions of the returns. end_ctx CLRF ctx_state GOTO return_from_ctx next_ctx_state INCF ctx_state,W set_ctx_state MOVWF ctx_state return_from_ctx LowPage goto ctx_done null_ctx_return LowPage goto ctx_null end_ctx_noxs CLRF ctx_state ;no-checksums versions of returns GOTO return_from_ctx_noxs next_ctx_state_noxs INCF ctx_state,W set_ctx_state_noxs MOVWF ctx_state return_from_ctx_noxs LowPage goto ctx_done_noxs ; ; This dispatch table must be located in same ROM page as the handlers CTxJmpTbl GOTO CTX_1 GOTO CTX_2 GOTO CTX_3 GOTO CTX_4 GOTO CTX_5 GOTO CTX_6 GOTO CTX_7 GOTO CTX_8 GOTO CTX_9 GOTO CTX_10 GOTO CTX_11 GOTO CTX_12 GOTO CTX_13 GOTO CTX_14 ;----------- ;Send "read registers" response CTX_1 MOVLW '@' MOVWF tCmd GOTO next_ctx_state_noxs CTX_2 MOVLW 'r' MOVWF tCmd GOTO next_ctx_state_noxs CTX_3 MOVF tCmdxs,W ;Checksum previously saved from the command packet now being replied to MOVWF tCmd GOTO next_ctx_state_noxs CTX_4 MOVF tCmdcl,W MOVWF tCmd GOTO next_ctx_state_noxs CTX_5 MOVF tCmdch,W MOVWF tCmd CLRF tCmdxs CLRF tCmdcl CLRF tCmdch Bank1 MOVF RegXferLen,W Bank0 SUBLW 5 ADDWF ctx_state,F ;goto state 6 for 4-byte transfer, 5 for 3-byte transfer, etc. GOTO return_from_ctx_noxs CTX_6 Bank1 MOVF RegXfer0,W Bank0 MOVWF tCmd GOTO next_ctx_state CTX_7 Bank1 MOVF RegXfer1,W Bank0 MOVWF tCmd GOTO next_ctx_state CTX_8 Bank1 MOVF RegXfer2,W Bank0 MOVWF tCmd GOTO next_ctx_state CTX_9 Bank1 MOVF RegXfer3,W Bank0 MOVWF tCmd GOTO next_ctx_state CTX_10 MOVF tCmdxs,W ;Checksum of data MOVWF tCmd GOTO next_ctx_state_noxs CTX_11 MOVF tCmdcl,W MOVWF tCmd GOTO next_ctx_state_noxs CTX_12 MOVF tCmdch,W MOVWF tCmd GOTO end_ctx_noxs ;----------- ;Send "write registers" response CTX_13 MOVLW '@' MOVWF tCmd GOTO next_ctx_state_noxs CTX_14 MOVLW 'w' MOVWF tCmd MOVLW 10 GOTO set_ctx_state_noxs ;Send checksums previously saved from the command packet now being replied to ;--------------------------------------------------------------------------- ; Read one byte from the transmit buffer. ; ; Updates dequeue pointer. ; ReadTxBuf SetMemA16 Move PORTD,Txrl ClrMemAClk MOVFW Txrh SetMemAClk MOVWF PORTD SetMemCE1 SetMemOE ;note -- CE1 and OE are on different ports so its ok to do read-modify-write back-to-back INCF Txrl,F ;this pointer needs to be incremented sooner or later and here is a good place to do it BTFSC STATUS,Z ;since a delay has to come between asserting OE and reading the data INCF Txrh,F MOVFW PORTB ClrMemOE ClrMemCE1 RETURN ;--------------------------------------------------------------------------- ; Write one byte to one of the receive buffers. ; ; Precondition: ; ; The tentative enqueue pointer (RxTwl, RxTwh) points to the next location ; within the buffer to be written. ; ; W contains the databyte to be written to the buffer. ; ; ; Postcondition: ; ; W has been written to [RxTwh:RxTwl]. ; ; The tentative enqueue pointer has been incremented. ; ; ; Modifies: ; FSR (= TRISB) WriteRxBuf MOVWF PORTB ClrMemA16 Move PORTD,RxTwl ClrMemAClk MOVFW RxTwh SetMemAClk MOVWF PORTD Movlf FSR,TRISB CLRF INDF SetMemCE1 SetMemWr ;note -- CE1 and OE are on different ports so its ok to do read-modify-write back-to-back INCF RxTwl,F ;increment the tentative enqueue pointer BTFSS STATUS,Z ;Carry out of low byte? GOTO wrxbuf_noihbp ; no, no need to increment high byte INCF RxTwh,F MOVLW 0X1F ;check for a carry from bit 4 to bit 5 (high 3 bits are buffer address ... low 5 are offset) ANDWF RxTwh,W BTFSS STATUS,Z ;Carry from bit4 to bit5 ? GOTO wrxbuf_noihbp ; no, done DECF RxTwh,F ; yes, restore the high 3 bits and clear the low 5 bits MOVLW 0XE0 ANDWF RxTwh,F wrxbuf_noihbp ClrMemWr ClrMemCE1 DECF INDF,F RETURN END