Hello All, Could anyone please help me by mailing a simple I2C Read/Write routine to control a 24C01 serial EEPROM, as I have been struggling trying to modify AN578 for over a month. Below is the source code for my latest attempt which receives data at 1200 baud from the serial port of a p.c. The program is almost working; it appears to be writing and reading to the EEPROM since 'gibberish' is printed on the LCD every time a character is sent from the p.c. I have done away with the interupt routine - is this okay ? is it possible to control the I2C features without using interupts in a similar manor to the USART features ?. Also do I still need to include the SSPADD address or is this only needed for multi-master operation ?. Many thanks: Lee Hewitt (Manchester ENGLAND) TITLE "UART I2C & LCD Routine: 1200 Baud USART operation" SUBTITLE "Almost works: I2C operation seems OK, but LCD displays rubbish !" ;Second attempt With no interupts, using both WRBYTE and RDBYTE routines - LIST P=16c65, R=HEX include "p16c65.inc" include "a:\defs\lcd_defs.h" ERRORLEVEL -302 __config _CP_OFF & _PWRTE_ON & _WDT_OFF & _XT_OSC ;Handshaking Equates SOH equ 0x01 STX equ 0x02 ETX equ 0x03 EOT equ 0x04 ENQ equ 0x05 ACK equ 0x06 ;Defines #define ZERO STATUS,Z #define CARRY STATUS,C #define RPAGE STATUS,5 #define E_LINE PORTB,4 #define RS_LINE PORTB,5 #define RBPU 0X01,7 ;Macro definitions pulse_e macro bsf E_LINE call short_delay bcf E_LINE endm ;variables cblock 0x20 RX_BYTE TX_BYTE dly_cnt_1 dly_cnt_2 dly_cnt_3 temp temp1 FLAG ; Common flag bits register EEPROM ; Bit buffer ERCODE ; Error code (to indicate bus status) ADDR ; Address register (Memory location inside EEPROM) DATAI ; Stored data Input register DATAO ; Stored data Output register SLAVE ; Device address (1010xxx0) TXBUF ; TX buffer RXBUF ; RX buffer COUNT ; Bit counter TEMP ; Temporary storage TEMP1 ; Scratchpad register endc ; FLAG Bits ERR_1 EQU 0 ; Error flag (i.e. FLAG reg bit 0) ; EEPROM Bits DI EQU 7 ; EEPROM input DO EQU 6 ; EEPROM output ; I2C Device Bits SDA EQU 4 ;data in/out (pin 24: RC4/SDA) SCL EQU 3 ;serial clock (pin 18: RC3/SCL) ;End of files/bits equate ;----------------------------------------------------------------------------- ; reset vector ;----------------------------------------------------------------------------- org 00h goto reset ;----------------------------------------------------------------------------- ; program entry point ;----------------------------------------------------------------------------- org 10h reset movlw 0x00 ;Initial set-up of I/O ports movwf PORTC movwf PORTD movwf PORTE ;Set-up of DDR's bsf STATUS,RP0 ;Select bank 1 movlw 0x00 movwf TRISA ;Port A - all pins unused, output 0 movwf TRISB ;Port B - all pins outputs (LCD Module) movwf TRISD ;Port D - all pins unused, output 0 movwf TRISE ;Port E - all pins unused, output 0 bsf RBPU movlw b'10011000' movwf TRISC ;set-up USART and I2C I/O pins bcf STATUS,RP0 ;Select bank 0 movlw b'00010000' ;Added from LCD code movwf PORTA ; movlw b'00001111' ; movwf PORTB ; bcf STATUS,RP1 ;Always maintain this bit clear (origional reset ) bsf STATUS,RP0 ; Select page 1 clrf PIE1 ;**** Disable all perhipheral interupts **** movlw b'10100010' ;Slave address for PIC during RX (A2 Hex) movwf SSPADD bcf STATUS,RP0 ; Select page 0 bcf PIR1,3 ; Clear SSP interrupt flag clrf INTCON ;**** Dissable ALL interupts **** clrf FLAG ; Clear error register movlw b'00110110' ;**** Enable I2C pins, 7-bit address with no INT **** movwf SSPCON ; mode enabled movlw b'10000000' ;Disable weak pull-ups on Port B option ;set-up LCD Module call init_lcd ;Initialise LCD module ?? call long_delay ;Wait for LCD to reliably initialise call long_delay ;Wait for LCD to reliably initialise ;Initialise USART movlw b'10010000' ;Load RCSTA with 0x90 movwf RCSTA bsf STATUS,RP0 ;Select bank 1 movlw .46 ;46 for 1200 Baud movwf SPBRG ;Baud rate is now set at 1200 bps movlw b'10100000' ;Load TXSTA with 0xA0 movwf TXSTA bcf STATUS,RP0 ;Select bank 0 ;< Serial Transmit/Receive Routine > clrf TXREG clrf TX_BYTE clrf RX_BYTE clrf ADDR poll_rx btfss PIR1,RCIF ;Wait to receive a byte from host goto poll_rx movf RCREG,w ;Load received data into w reg movwf RX_BYTE ;Store received data in file reg REC_BYTE poll_tx btfss PIR1,TXIF ;TXIF is set when TXREG is empty or clear goto poll_tx movlw 0x06 ;Load data for transmission directly ! movwf TXREG ;Transmit the data when TXREG is loaded ;< I2C Transmit/Receive Routine > main clrf TEMP clrf SSPBUF ;Is This Correct ???? bcf STATUS,RP0 ;?? movf RX_BYTE,w ;Get RX byte from USART in W reg movwf DATAO ;Load DATAO reg with RX data from USART movlw B'10100110' ; Address of device being addressed (WRITE) movwf SLAVE bsf STATUS,RP0 ;Select Page 1 CheckAgain ;Wait here for I2C STOP condition btfsc SSPSTAT,4 ;If STOP bit received last... goto Goxmit ;goto Goxmit when P bit set in SSPSTAT btfsc SSPSTAT,3 ;If START bit recieved last... goto CheckAgain ; wait for STOP bit (wait_stop) Goxmit ;Stop condition been detected bcf STATUS,RP0 ;Select Page 0 call WRBYTE ; Output byte (in W reg) pl_IWR btfss PIR1,3 ;Wait here until I2C TX sequence is complete goto pl_IWR bcf PIR1,3 ;I2C TX sequence is complete, thus clear flag ;(Error check for WRBYTE routine put below) ;Come here upon return from WRBYTE routine btfss FLAG,ERR_1 ; Check for error (upon return from subroutine) goto p1_IRD ;(Checkout) EEPROM error flag is clear, thus con tinue movf ERCODE,W ; Get error code call send_lcd_char ;Display I2C error condition on LCD bcf FLAG,ERR_1 ;Clear error flag (FLAG reg bit 0) goto CheckAgain ;An I2C error has occurred ;Call the RDBYTE routine now I2C transmission is complete movlw B'10100110' ; Address of device being addressed (WRITE) movwf SLAVE bsf STATUS,RP0 ;Select Page 1 Check_2 ;Wait here for I2C STOP condition btfsc SSPSTAT,4 ;If STOP bit received last... goto Go_Rx ;goto Go_rx when P bit set in SSPSTAT btfsc SSPSTAT,3 ;If START bit recieved last... goto Check_2 ; wait for STOP bit (wait_stop) Go_Rx bcf STATUS,RP0 ;Select page 0 clrf ADDR ;Select address 0 call RDBYTE p1_IRD btfss PIR1,3 ;Wait here until I2C TX or RX is complete goto p1_IRD bcf PIR1,3 ;Either TX or RX I2C data has been detected bsf STATUS,RP0 ;Select page 1 for examinination of SSPSTAT btfss SSPSTAT,0 ;Is the SSPBUFF full (I2C receive complete) ? goto p1_IRD ;No, thus **** ??????? Correct ???????? ***** btfss SSPSTAT,5 ;Yes, has an I2C ADDRESS been received ? goto main ;Yes, thus ignore and loop back to main bcf STATUS,RP0 ;No, thus I2C DATA must have been received movf DATAI,w ;Thus load W reg with I2C data received ;Come here upon return from RDBYTE routine btfss FLAG,ERR_1 ; Check for error (upon return from subroutine) goto no_RD_error ;(Ckeckout)EEPROM error flag is clear, thus cont inue movf ERCODE,W ; Get error code call send_lcd_char ;Display I2C error condition on LCD bcf FLAG,ERR_1 ;Clear error flag (FLAG reg bit 0) goto Check_2 ;An I2C error has occurred no_RD_error ;No EEPROM errors have been detected movf DATAI,w ;Load W reg with received I2C data call send_lcd_char ;Display received I2C data READ from EEPROM goto poll_rx ;Go back and wait for UART serial receive ;----------------------------------------------------------------------------- ; BYTE-WRITE, write one byte to I2C (Master Mode) ;----------------------------------------------------------------------------- ; Input : DATAO= data to be written ; ADDR = destination address ; SLAVE = device address (1010xxx0) ; Output : Data written to EEPROM device ;----------------------------------------------------------------------------- ; WRBYTE bcf STATUS,RP0 ;Select Page 0 movf SLAVE,W ; Send SLAVE address movwf TXBUF ; to TX buffer call BSTART ; Generate START bit call TX ; Output SLAVE data address bcf STATUS,RP0 ;Select Page 0 ?? movf DATAO,W ; Move DATA movwf TXBUF ; into transmit buffer call TX ; Output DATA and detect acknowledgement call BSTOP ; Generate STOP bit return ;----------------------------------------------------------------------------- ; BYTE-READ, read one byte from I2C (Master Mode) ;----------------------------------------------------------------------------- ; Input : ADDR = source address ; SLAVE = device address (1010xxx0) ; Output : DATAI = data read from serial EEPROM ;----------------------------------------------------------------------------- RDBYTE bcf STATUS,RP0 ;Select Page 0 movf SLAVE,W ; Move SLAVE address movwf TXBUF ; into buffer (R/W = 0) call BSTART ; Generate START bit call TX ; Output SLAVE address. Check ACK. bcf STATUS,RP0 ;Select Page 0 ?? movf ADDR,W ; Put slave data address into movwf TXBUF ; Xmit buffer call TX ; Output WORD address. Check ACK. call BSTART ; START READ << ???? >> bcf STATUS,RP0 ;Select Page 0 ?? movf SLAVE,W ; movwf TXBUF bsf TXBUF,0 ; Specify READ mode (R/W = 1) call TX ; Output SLAVE address call RX ; READ in data and acknowledge call BSTOP ; Generate STOP bit bcf STATUS,RP0 ;Select Page 0 ?? movf RXBUF,W ; Save data from buffer movwf DATAI ; to DATAI file. return ;----------------------------------------------------------------------------- ; RECEIVE eight data bits subroutine ;----------------------------------------------------------------------------- ; Input : None ; Output : RXBUF = 8-bit data received ;----------------------------------------------------------------------------- RX bcf STATUS,RP0 movlw .8 ; 8 bits of data movwf COUNT clrf RXBUF ; RXLP rlf RXBUF, F ; Shift data to buffer btfss 3,0 bcf RXBUF,0 ; carry ---> f(0) btfsc 3,0 bsf RXBUF,0 call BITIN bcf STATUS,RP0 btfsc EEPROM,DI bsf RXBUF,0 ; Input bit =1 decfsz COUNT, F ; 8 bits? goto RXLP bsf EEPROM,DO ; Set acknowledge bit = 1 call BITOUT ; to STOP further input retlw 0 ;----------------------------------------------------------------------------- ; TRANSMIT 8 data bits subroutine ;----------------------------------------------------------------------------- ; Input : TXBUF ; Output : Data X'mitted to EEPROM device ;----------------------------------------------------------------------------- TX bcf STATUS,RP0 movlw .8 movwf COUNT ; TXLP bcf EEPROM,DO ; Shift data bit out. btfsc TXBUF,7 ; If shifted bit = 0, data bit = 0 bsf EEPROM,DO ; Otherwise data bit = 1 call BITOUT ; Serial data out bcf STATUS,RP0 rlf TXBUF, F ; Rotate TXBUF left btfss 3,0 ; f(6) ---> f(7) bcf TXBUF,0 ; f(7) ---> carry btfsc 3,0 ; carry ---> f(0) bsf TXBUF,0 decfsz COUNT, F ; 8 bits done? goto TXLP ; No. call BITIN ; Read acknowledge bit bcf STATUS,RP0 movlw A'3' ;EEPROM error status 3 ?? btfsc EEPROM,DI ; Check for acknowledgement call ERR ; No acknowledge from device bcf STATUS,RP0 retlw 0 ;----------------------------------------------------------------------------- ; Single bit receive from I2C to PIC ;----------------------------------------------------------------------------- ; Input : None ; Output : Data bit received ;----------------------------------------------------------------------------- BITIN bsf STATUS,RP0 bsf TRISC,SDA ; Set SDA for input bcf STATUS,RP0 bcf EEPROM,DI bsf STATUS,RP0 bsf TRISC,SCL ; Clock high movlw A'1' ;EEPROM error status 1 ?? bcf STATUS,RP0 btfsc PORTC,SCL ; Skip if SCL is high goto BIT1 bcf STATUS,RP0 btfss FLAG,ERR_1 ; Remain as first error encountered movwf ERCODE ; Save error code bsf FLAG,ERR_1 ; Set error flag BIT1 bcf STATUS,RP0 btfss PORTC,SDA ; Read SDA pin, for ACK low goto ACKOK bcf STATUS,RP0 bsf EEPROM,DI ; DI = 1 ACKOK bsf STATUS,RP0 nop ; Delay bcf TRISC,SCL ; Return SCL to low retlw 0 ;----------------------------------------------------------------------------- ; Single bit data transmit from PIC to I2C ;----------------------------------------------------------------------------- ; Input : EEPROM register, bit DO ; Output : Bit transmitted over I2C ; Error bits set as necessary ;----------------------------------------------------------------------------- BITOUT bcf STATUS,RP0 btfss EEPROM,DO goto BIT0 bsf STATUS,RP0 bsf TRISC,SDA ; Output bit 0 movlw A'2' ;EEPROM error status 2 bcf STATUS,RP0 btfsc PORTC,SDA ; Check for error code 2 goto CLK1 btfss FLAG,ERR_1 ; Remain as first error encountered movwf ERCODE ; Save error code bsf FLAG,ERR_1 ; Set error flag goto CLK1 ; SDA locked low by device ; BIT0 bsf STATUS,RP0 bcf TRISC,SDA ; Output bit 0 nop ; Delay nop nop CLK1 bsf STATUS,RP0 bsf TRISC,SCL movlw A'1' ;EEPROM Error code 1 ?? bcf STATUS,RP0 btfsc PORTC,SCL ; SCL locked low? goto BIT2 ; No. bcf STATUS,RP0 btfss FLAG,ERR_1 ; Yes. movwf ERCODE ; Save error code bsf FLAG,ERR_1 ; Set error flag BIT2 nop nop bsf STATUS,RP0 bcf TRISC,SCL ; Return SCL to low retlw 0 ;----------------------------------------------------------------------------- ; START bit generation routine ;----------------------------------------------------------------------------- ; input : none ; output : initialize bus communication ;----------------------------------------------------------------------------- ;Generate START bit (SCL is high while SDA goes from high to low transition) ;and check status of the serial clock. BSTART bsf STATUS,RP0 bsf TRISC,SDA ; Make sure SDA is high bsf TRISC,SCL ; Set clock high movlw A'1' ;EEPROM Ready error status code 1 ?? bcf STATUS,RP0 btfss PORTC,SCL ; Locked? call ERR ; SCL locked low by device, flag error bsf STATUS,RP0 bcf TRISC,SDA ; SDA goes low during SCL high nop ; Timing adjustment, 1.5uS @2MHz nop nop bcf TRISC,SCL ; Start clock train retlw 0 ;----------------------------------------------------------------------------- ; STOP bit generation routine ;----------------------------------------------------------------------------- ; Input : None ; Output : Bus communication, STOP condition ;----------------------------------------------------------------------------- ;Generate STOP bit (SDA goes from low to high during SCL high state) ;and check bus conditions. BSTOP bsf STATUS,RP0 bcf TRISC,SDA ; Return SDA to low bsf TRISC,SCL ; Set SCL high nop nop nop movlw A'1' ;EEPROM Ready error code 1 ?? bcf STATUS,RP0 btfss PORTC,SCL ; High? call ERR ; No, SCL locked low by device bsf STATUS,RP0 bsf TRISC,SDA ; SDA goes from low to high during SCL high movlw A'4' ;EEPROM Ready error code 4 ?? btfss TRISC,SDA ; High? call ERR ; No, SDA bus not release for STOP bcf STATUS,RP0 retlw 0 ;----------------------------------------------------------------------------- ; Two wire/I2C - CPU communication error status table and subroutine ;----------------------------------------------------------------------------- ; input : W-reg = error code ; output : ERCODE = error code ; FLAG(ERR_1) = 1 ; ; code error status mode ; ------- ------------------------------------------------------ ; 1 : SCL locked low by device (bus is still busy) ; 2 : SDA locked low by device (bus is still busy) ; 3 : No acknowledge from device (no handshake) ; 4 : SDA bus not released for master to generate STOP bit ;----------------------------------------------------------------------------- ; ;Subroutine to identify the status of the serial clock (SCL) and serial data ;(SDA) condition according to the error status table. Codes generated are ;useful for bus/device diagnosis. ERR bcf STATUS,RP0 btfss FLAG,ERR_1 ; Keep first error reported movwf ERCODE ; Save error code bsf FLAG,ERR_1 ; Set error flag retlw 0 ; ********* LCD Drive Subroutines ********* init_lcd movlw MODE_8 ;MUST BE INCLUDED call send_lcd_cmd ;MUST BE INCLUDED movlw 0x32 ;MUST BE INCLUDED call send_lcd_cmd ;MUST BE INCLUDED movlw MODE_4 ;function mode set-up call send_lcd_cmd ;4-bit mode, 2 lines, 5x7 dot format movlw INC_NSFT ;entry mode set-up* call send_lcd_cmd ;cursor incriment on write* movlw DISP_ON_FLASH call send_lcd_cmd movlw CLR_DISP call send_lcd_cmd ;clear diaply, cursor home call long_delay return send_lcd_cmd movwf temp swapf temp,w andlw 0X0F bcf RS_LINE bcf E_LINE movwf PORTB call short_delay pulse_e ;call macro call short_delay movf temp,w andlw 0F movwf PORTB call short_delay pulse_e ;call macro call short_delay return send_lcd_char movwf temp swapf temp,w andlw 0X0F bsf RS_LINE bcf E_LINE iorlw 0X20 movwf PORTB call short_delay pulse_e ;call macro call short_delay movf temp,w andlw 0F iorlw 0X20 movwf PORTB call short_delay pulse_e ;call macro call short_delay return ;Delay routines short_delay movlw 0X08 movwf dly_cnt_1 dly_loop_1 decfsz dly_cnt_1,f goto dly_loop_1 return med_delay movlw 0X80 movwf dly_cnt_2 dly_loop_2 call short_delay decfsz dly_cnt_2,f goto dly_loop_2 return long_delay movlw 0X40 movwf dly_cnt_3 dly_loop_3 call med_delay decfsz dly_cnt_3,f goto dly_loop_3 return end