Hello All, Could anyone possibly help me understand I2C operation, with particular reference to AN587 and the PIC16C65 ?. Basically I need to receive approximately 16 bytes via the serial USART port and sequentially write the received data to successive data RAM files inside the PIC file space, after which the data from the RAM must then be written into successive memory locations inside a 24C01 EEPROM via the I2C SSP port of the PIC16C65. Up to now I have managed to successfully configure the USART module to both receive and transmit data at 1200 Baud to and from a p.c. I have also attempted to modify AN578 in order to write the data received from the USART into a 24C01 EEPROM, but I am really struggling with this part !. Why is there no call to the RDBYTE routine in AN578 ? - is this routine not used in the application ? and if so then why is it included ?. Are interrupts essential when using the I2C module of the PIC16C65 ?, or can I disable the interrupts and just examine the flag bits in a similar manor to the USART routine ? Does AN578 only read and write to ONE memory location inside the EEPROM ?, since there is no incf ADDR instruction etc. in the code ?. Also in the WRBYTE routine there is no instruction which manipulates ADDR, although ADDR is specified as an input to the routine in the header comments above ?. - (ADDR is used in the RDBYTE routine though ?) Below is a listing of the code which I have managed to write so far, thanks to members of this list such as Andy Kunz and Gerry Cox etc. At present this code will receive a byte from the USART port at 1200 Baud and transmit ASCII ACK (0x06) back to the p.c. via the USART. As illustrated in the code, I am attempting to write the received byte to a 24C01 EEPROM via the modified I2C routine of AN578. When executed, the code initializes the LCD and waits for reception of a serial byte; then upon reception the code writes an endless string 'E's to the LCD and just locks up !. - I inserted the movlw A'E' and call send_lcd_char instructions in an attempt to de-bug the source of the problem. It seems as if the I2C bus is locked or something and the data is never being written to the EEPROM. - I have also monitored the SDA and SCL lines with a oscilloscope and I have found that both lines are idle (no activity) until the data is received by the USART, (i.e. until RX_BYTE is moved to DATAO). Is this correct only the data sheet implies that the SCL line becomes active directly after configuring the SSP module ?? Finally, what exactly does Multi-Master mode mean ? is this something to do with having more than one MCU ?; will AN578 also work for one MCU and one EEPROM (single master mode ?) as I have, or do I have to make more modifications to the code of AN578 ?. Why does the MCU need an address ? is this feature only required in Multi Master mode ? (i.e. where A2 is written to the SSPADD register ?) -and which is the slave now the MCU or the EEPROM ? Can anyone please help me to get the code working so I can progress to successively read and write data from the USART to the I2C SSP module. Many many thanks and kind regards: Lee Hewitt (Manchester ENGLAND) TITLE "UART I2C & LCD Routine: 1200 Baud USART operation" SUBTITLE "Version 1.4" LIST P=16c65, R=HEX include "p16c65.inc" include "a:\defs\lcd_defs.h" ERRORLEVEL -302 ;errorlevel -224 ;errorlevel -305 __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 ;ddr_setup macro ; bsf STATUS,RP0 ;Select page 1 for TRIS set-up ; movlw b'00000000' ; movwf TRISA ; movlw b'00000000' ; movwf TRISB ; bsf RBPU ; bcf STATUS,RP0 ;Select page 0 after TRIS set-up ; endm 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 TEMP_W ; Storage for W register TEMP_STAT ; Storage for STATUS 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 (24:RC4/SDA) SCL EQU 3 ;serial clock (18:RC3/SCL) ;End of files/bits equate ;----------------------------------------------------------------------------- ; reset vector ;----------------------------------------------------------------------------- org 00h goto reset ORG 04h ; Interrupt Vector goto service_int ;----------------------------------------------------------------------------- ; 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 movlw B'00001000' ;Enable SSP interrupt only, (not USART INT) movwf PIE1 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 movlw B'11000000' ; Enable interrupts (GIE and Perhip) movwf INTCON clrf FLAG ; Clear error register movlw B'00111110' ; I2C 7 bit slave mode with master 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 RX/TX routines 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 ;<1> main clrf TEMP ;call SetupDelay 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 (A6 Hex) 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 bcf INTCON,7 ;Disable all interrupts (Clear GIE) call WRBYTE ; Output byte (in W reg) btfss FLAG,ERR_1 ; Check for error (upon return from subroutine) goto Checkout ; No error, go on ;movf ERCODE,W ; Get error code movlw A'E' ;Added by me for de-bugging purposes !! 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 Checkout ;No errors have occurred bsf INTCON,7 ; Enable interrupts (Set GIE) goto poll_rx ;(GO BACK TO POLL RX) ;<1> ;-------------------------------------------------------------------- ; Interrupt service routine ; Only the SSP interrupt is enabled. This routine will read the I2C ; data and output it on the LEDs. ;-------------------------------------------------------------------- service_int bcf PIR1,3 ; Clear SSP interrupt movwf TEMP_W ; Save W register swapf STATUS,W ; Get STATUS register movwf TEMP_STAT ; Save STATUS register bsf STATUS,RP0 ;Select page 1 btfss SSPSTAT,0 ; Check Buffer Full Flag goto IntOut ; No data received, so exit bcf STATUS,RP0 ;Select page 0 movf SSPBUF,W ; Get I2C data from EEPROM bsf STATUS,RP0 ;Select page 1 btfss SSPSTAT,5 ; If Address received last... goto IntOut ; exit without saving it bcf STATUS,RP0 ;select page 0 call send_lcd_char ;Display data READ from EEPROM IntOut bsf STATUS,RP0 ;Select page 1 bsf PIE1,3 ; Re-enable SSP interrupt bcf STATUS,RP0 ;Select page 0 swapf TEMP_STAT,W ; Restore STATUS register movwf STATUS swapf TEMP_W,1 swapf TEMP_W,W ; Restore W register retfie ;Return from Interupt routine ;----------------------------------------------------------------------------- ; 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 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 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 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 1 ; 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 1 ; 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 1 ; 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 4 ; 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 ;----------------------------------------------------------------------------- ; DELAY, Provide a 1.54mS delay ;----------------------------------------------------------------------------- ; Input : None ; Output : None ;----------------------------------------------------------------------------- delay bcf STATUS,RP0 clrf TEMP ;clear last location dly1 nop nop nop decfsz TEMP, F ;reduce count goto dly1 ;loop retlw 0 ;Interupt Service Routine moved from below ;Keyboard scan routine cut from below !!!! ;***************************************************************** ;*This routine is a software delay. * ;*At 4Mhz clock, the loop takes 3uS, so initialize TEMP with * ;*a value of 3 to give 9uS, plus the move etc should result in * ;*a total time of > 10uS. * ;***************************************************************** ;SetupDelay ; decfsz TEMP, F ; goto SetupDelay ; return ; ********* 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