;i2c-to-rs232 bridge ; ;modification history: ;2008-09-08: created #include __config (_INTRC_OSC_NOCLKOUT & _WDT_OFF & _PWRTE_OFF & _MCLRE_OFF & _CP_OFF & _BOR_OFF & _IESO_OFF & _FCMEN_OFF) #define NODE_ADDR d'38' ;i2c address #define BUF_SIZE d'89' ;size of ringbuffer (limited by size of register ;bank 0 (96 bytes) and the number of variables ;allocated on register bank 0, that is, 7, see ;cblock 0x20 below). #define START_BUF d'128' ;code for start buffering rs232 input #define GET_CHAR d'129' ;code for getting next byte from buffer #define BUFFER 0 ;buffering flag (see variable state, below) #define READ 1 ;read flag (see variable state, below) cblock 0x20 ;variables i2c_rx ;buffer for one byte received from i2c i2c_tx ;buffer for one byte to transmit over i2c rs232_rx ;buffer for one byte received from rs232 rs232_tx ;buffer for one byte to transmit over r232 state ;state variable: ;no bit set: bytes received from i2c are ; forwared to rs232 and back-looped to i2c, ; bytes received from rs232 are ignored ;bit 0 (BUFFER): set when rs232 input should be ; stored in ringbuffer, this is enabled by ; sending START_BUF on the i2c, the START_BUF ; byte itself is back-looped to i2c but not ; forwarded to rs232 ;bit 1 (READ): set when the next byte of the ; ringbuffer should be replied, this is enabled ; by sending GET_CHAR on the i2c, the GET_CHAR ; byte itself is not back-looped (but the byte ; of the ringbuffer, instead) and not forwarded ; to rs232 sspstat_masked ;help variable for masking the SSPSTAT register buf_ptr ;current position in the ringbuffer buf ;ringbuffer (extends BUF_SIZE bytes towards the ;end of register bank 0) endc ;set up all ports banksel PORTA clrf PORTA clrf PORTB clrf PORTC ;port settings (1 is input, 0 is output) ;PORTA (8 pins) is unused except for pin 2 movlw b'00000000' banksel TRISA movwf TRISA ;ports B (4 pins) are shared with ic2 and uart pins ;RB4 (pin 13): SDA (i2c) ;RB5 (pin 12): RX (rs232) ;RB6 (pin 11): SCL (i2c) ;RB7 (pin 10): TX (rs232) movlw b'01110000' banksel TRISB movwf TRISB ;PORTC (8 pins) is (partly) connected to LEDs movlw b'00000000' banksel TRISC movwf TRISC ;digital pins only banksel ANSEL movlw h'0' movwf ANSEL movlw h'0' movwf ANSELH clrf CM1CON0 clrf CM2CON0 banksel ADCON0 clrf ADCON0 banksel ADCON1 clrf ADCON1 ;disable interrupts banksel PIE1 clrf PIE1 clrf PIE2 banksel INTCON clrf INTCON clrf PIR1 clrf PIR2 ;uart settings: 19200 bits/s, 8 bit, no parity on transmission banksel SPBRGH movlw d'0' movwf SPBRGH movlw d'12' movwf SPBRG movlw b'00100110' movwf TXSTA movlw b'10010000' ;enable rs232, 8-bit continuous reception banksel RCSTA movwf RCSTA ;i2c settings: slave mode, 7-bit address (the two assignments are ;necessary due to a silicon error on the PIC16F690, see PIC16F690 ;data sheet errata) banksel SSPCON movlw b'00111001' movwf SSPCON movlw b'00110110' movwf SSPCON banksel SSPADD movlw NODE_ADDR ;set i2c address movwf SSPADD ;configuration completed banksel PORTA clrf PORTA clrf PORTB clrf PORTC ;wait for received byte on i2c i2c_receive: bsf PORTC,0 bcf PORTC,1 bcf PORTC,2 btfss PIR1,SSPIF goto rs232_receive bcf PIR1,SSPIF bcf PORTC,0 bsf PORTC,2 i2c_rx_addr: banksel SSPSTAT movf SSPSTAT,W bcf STATUS,RP0 andlw b'00101101' movwf sspstat_masked movlw b'00001001' xorwf sspstat_masked,W btfss STATUS,Z goto i2c_rx_data movf SSPBUF,W ;throw the address away (dummy read) goto i2c_receive i2c_rx_data: movlw b'00101001' xorwf sspstat_masked,W btfss STATUS,Z goto i2c_tx_addr movf SSPBUF,W ;put the data to i2c_rx, i2c_tx, and rs232_tx movwf i2c_rx movwf i2c_tx movwf rs232_tx bcf state,READ ;clear reading flag check_start_buf: movlw START_BUF ;check whether i2c_rx == START_BUF subwf i2c_rx,W btfss STATUS,Z goto check_get_char call initBuffer ;clear buffer bsf state,BUFFER ;set buffering flag bsf PORTA,2 goto i2c_receive check_get_char: movlw GET_CHAR ;check whether i2c_rx == GET_CHAR subwf i2c_rx,W btfss STATUS,Z ;(i2c_rx != START_BUF && i2c_rx != GET_CHAR), so forward i2c_rx to rs232 goto rs232_transmit bcf state,BUFFER ;clear buffering flag bcf PORTA,2 bsf state,READ ;set reading flag goto i2c_receive i2c_tx_addr: banksel SSPSTAT movf SSPSTAT,W bcf STATUS,RP0 andlw b'00101100' movwf sspstat_masked movlw b'00001100' xorwf sspstat_masked,W btfss STATUS,Z goto rs232_receive i2c_transmit: banksel SSPSTAT btfsc SSPSTAT,BF goto i2c_transmit bcf STATUS,RP0 btfsc state,READ call readBuffer write_byte: bcf SSPCON,WCOL movfw i2c_tx movwf SSPBUF btfsc SSPCON,WCOL goto write_byte bsf SSPCON,CKP ;release the clock rs232_receive: bcf STATUS,RP0 bcf PORTC,0 bsf PORTC,1 bcf PORTC,2 btfss PIR1,RCIF goto i2c_receive bcf PORTC,1 bsf PORTC,2 btfsc RCSTA,OERR ;catch overflow error goto overflowerror btfsc RCSTA,FERR ;catch framing error goto frameerror movfw RCREG movwf rs232_tx movwf rs232_rx btfss state,BUFFER ;replace the statement by 'goto rs232_transmit' to implement a ;rs232-backloop goto i2c_receive call writeBuffer goto i2c_receive overflowerror: bcf RCSTA,CREN ;pulse cren off movfw RCREG ;flush fifo movfw RCREG ;all three elements movfw RCREG bsf RCSTA,CREN ;turn CREN back on, this clears OERR goto i2c_receive frameerror: movfw RCREG ;reading RCREG clears FERR goto i2c_receive rs232_transmit: btfss PIR1,TXIF goto rs232_transmit movfw rs232_tx movwf TXREG goto i2c_receive ;initialize ringbuffer initBuffer clrf buf_ptr clear_byte: movlw buf ;load address of buf into FSR addwf buf_ptr,W movwf FSR clrf INDF movlw d'1' addwf buf_ptr,F movlw BUF_SIZE subwf buf_ptr,W ;check whether (buf_ptr - BUF_SIZE) == 0 btfss STATUS,Z goto clear_byte clrf buf_ptr return ;write rs232_rx to current pointer position in ringbuffer writeBuffer movlw buf addwf buf_ptr,W movwf FSR movfw rs232_rx movwf INDF movlw d'1' ;increase pointer (wrap if it exceeds BUF_SIZE) addwf buf_ptr,F movlw BUF_SIZE ;check whether buf_ptr < BUF_SIZE subwf buf_ptr,W btfsc STATUS,Z clrf buf_ptr return ;read data at current pointer position from ringbuffer and put to i2c_tx readBuffer movlw d'1' ;decrease pointer (wrap if it exceeds BUF_SIZE) subwf buf_ptr,F movfw buf_ptr xorlw b'11111111' btfss STATUS,Z goto read_byte movlw BUF_SIZE movwf buf_ptr movlw d'1' subwf buf_ptr read_byte: movlw buf addwf buf_ptr,W movwf FSR movfw INDF movwf i2c_tx return end