PIC18F as I2C slave

Status
Not open for further replies.

Lourens

Member
I am busy developing software for using a pic18f as a I2C slave. So any of you boffins that have done this before please share your ideas. I looked at the datasheets and also the very good application note (AN734) from Microchip. There are a few things in the datasheet that is not so clear to me and looking at AN734 i have a suspision the author also had the problem around handling the situation when a no-acknowledge is generated by the master after the slave tx'ed a data byte. Take note I am an old timer using assembler.
 
Nigel has shown somewhere in his site how it could be done.

Worked for me. Maybe you could adapt it to the 18F family.
 
Hi atferrari
Under normal circumstances (no faults, blips, comms failures etc.) the I2C slave hardware in PIC16 and PIC18 behave the same. It is when things go wrong or out of synch with the I2C comms that the slave hardware for the 2 types differ, and i need to identify the condition and take the appropriate action so as to make the communication robust. I want to implement a slave function block in my visual programming system VPS_P18 (a copy was posted on this forum some 2 months ago). Anyway i will continue with my development and also have a look at what Nigel did.
 
I just did a similar thing. The differences are explaned in the apendix to the AN. Aaron
 
Hi Aaron
Thanks for the reply. As mentioned the AN gave me some good ideas but also raised some questions. Look at the State4 code - this part of the ISR will be entered into after the slave transmitted a data byte. Notice how the SSPCON1,CKP bit is tested at the start By doing this the author is making use of information that is not clear when looking at the PIC18F data sheet (DS39631E 2008) On Page 177, Figure 17-9 it shows the CKP bit remaining high on the falling edge of the 9th clock pulse when the master device generates the NO ACK condition. In 17.4.4.3 on page 180 is described the behaviour of CKP in slave transmit mode with no mention what happens if a NO ACK is encountered. Anyhow my code will also flush the Tx-buffer if it encounters this situation. Did you also use assembler and pic18 to implement the slave?
Regards
Lourens
 
Lourens
I do use assembler and usually work with 18F but this time I used a 16F690. Because I usually use 18F I read the appendix and figured there would be no problem with the changes mentioned.

I'll be anxious to hear if you find out differently. Aaron
 
i will first do the code for the slave receiving and then tackle the slave transmitting part. Because of work commitments it is going to take a week or two.Will keep you updated on progress.
 
Well I'm not sure if this might be helpful, but MikroC library keeps it real simple. This is the code I use to get a 16-bit data from an ADC chip (ADS1100) for my PIC18F4620:

C:
  Soft_I2C_Init();               //  Software SPI initializing.
  Soft_I2C_Start();            //  a. Initiate a start 
  Soft_I2C_Write(0x90);   //  b. Sends address to ADS1100 at 0x90. Refer datasheet at address section. 
  Soft_I2C_Write(0x8c);   //  c. Default value (0x8c) to be written to the chip. Refer datasheet for more.
  Soft_I2C_Stop();            //  d: Stop signal


  Soft_I2C_Start();                    // Start software I2C
  Soft_I2C_Write(0x91);           // command ADC send the data 
  highbyte=Soft_I2C_Read(1);  // Read the seven most significant bits (D14-D08), send an ACK    
  owbyte=Soft_I2C_Read(0);   // Read the remaining D07-D00 data bits, and send a NACK.    
  Soft_I2C_Stop();                    // Stop

Cheers.
Vizier87
 
Last edited:
Vizier87
I for one only program in assembler. I wish I knew something about C so I could at least understand C examples that I find, but so far it has just scared me! But that is for another thread. So thanks anyway. Aaron
 
Well I find that assembly language is very crude. But it's up to you bro. Good luck with your project.
 
Hi Vizier87
Thanks for your code example. I am also not to familiar with C but i can follow the general idea of your code, but do note i am attempting the code for a I2C slave. There is a fair amount of difference to that of a I2C master. I have well tested code for a master (in assembler) which is in the form of a function block in a visual programming system. So my aim is to extend the system with a function block for the slave.To give you an idea i include an app note showing how to use the master function block to interface to an RTC from Microchip. The (assembler) code for this function block is available should anyone require it.
Greetings
 

Attachments

  • AN1202A.pdf
    356.6 KB · Views: 593
I got a PIC 18f24k20 to work as a slave transmitter this morning. Assembly code. Only uses states 1,3 & 5. In this simple example, I do not use the FSR but send a preloaded byte (info) in state 3.
There are LEDs turned on in the program to act as debug progress checks. The code may be a little cluttered but it works according to tests so far. The master is also a 24K20 and sends the byte to another slave(F690) to display on 3
7-segs. I got help from this site http://www.astrosurf.com/soubie/pic_as_an_i2c_slave.htm.
Aaron

Code:
;---------------------------------------------------------------------
;18F slave as transmitter	(only uses states 1,3 & 5) see I2C PIC slave f690 receiver
; File: an734_PIC18.asm		CORRECTED so it WORKS
;in this simple example, I do not use the FSR but send a preloaded byte (info) in state 3
;there are LEDs turned on in the program to act as debug progress checks
	list		p=18f24K20			
	#include	<p18f24K20.inc>			
;---------------------------------------------------------------------
#define RX_BUF_LEN 2	;one address byte and one data byte
#define	info	.199	;this is the byte to be sent to the master
#define	ADDRESS	0x34
#define SLAV_ADDR 0x20 ; I2C address of this node

	cblock   
	FSRsave     
	PCLATHsave 
	Index       
	Temp       
	RXBuffer
	info
	TEMP_STAT	;in the AN this was a duplicate use of the temp file (temp)
	endc    
;---------------------------------------------------------------------
; Include Files
;---------------------------------------------------------------------
;config options at end of program
	CONFIG		fosc = intio67, fcmen = off, ieso = off	;1h
	CONFIG		pwrt = on, boren = OFF, borv = 18	;2L
	CONFIG		wdtps = 1, wdten = off		;2h, 
			;default  PBADEN = ON ;RB0-4 analog input on reset ;mclr enabled  ;3h
	CONFIG		lvp = off, debug = off			;4L
			;default code protection off		;5L, 5h
			;default write protection off		;6L, 6h
			;default table read protection off 	;7L, 7h
memset macro Buf_addr,Value,RX_BUF_LEN
    movlw   RX_BUF_LEN      ; This macro loads a range of data memory
    movwf   Temp        ; with a specified value. The starting
    movlw   Buf_addr    ; address and number of bytes are also
    movwf   FSR0L       ; specified.
SetNext
    movlw   Value
    movwf   INDF0
    incf    FSR0L,F
    decfsz  Temp,F
    goto    SetNext
    endm
load macro Address,Offset   ; This macro loads the correct value
    movlw   Address         ; into the FSR given an initial data
    movwf   FSR0L           ; memory address and offset value.
    movf    Offset,W
    addwf   FSR0L,F
    endm

	org	0x0000
	goto	begin		;comment out if no interrupts
	org	0x0008		;     (add enable/disable interrupts
	goto	isr_high	;       to delays if there are interrupts)
;	org	0x0018		;
;	goto	isr_low		;
   

begin
	movlw	0xD0		;internal osc 4.0 MHz,  b'11010000'  pg 29
	movwf	osccon
	
	bcf	adcon0,ADON	;turn off AD
	clrf	ansel		;digital I/O on RA0-5  & RE0-2 of 43K  pg 136

	clrf	anselh		;RB0-4 are NOT analog inputs    pg 137

	bcf	cm1con0,c1on	;comparators off         pg 284
	bcf	cm2con0,c2on	;                        pg 285
	clrf	trisa
	clrf	trisb
	clrf	portb
	clrf	porta
    clrf    Index       
    clrf    Temp       
    clrf    RXBuffer
    movlw	.199
    movwf	info    
    call    Setup
Main
    goto    Main	;wait for interrupt
Setup
    bsf     TRISC,3
    bsf     TRISC,4
    clrf    FSR0L
    clrf    FSR0H
    movlw   SLAV_ADDR      ;Load Address of Slave node
    movwf   SSPADD
    movlw   0x36
    movwf   SSPCON1
    clrf    SSPSTAT
    clrf    SSPCON2
    bsf     SSPCON2,SEN ;Enable Clock Stretching for both transmit and slave
    bcf     PIR1,SSPIF  ;Clear MSSP interrupt flag
    bsf     PIE1,SSPIE  ;Enable MSSP interrupt enable bit
    bsf		intcon,peie
    bsf		intcon,gie
    return
;---------------------------------------------------------------------
; Interrupt Code
;---------------------------------------------------------------------
ISR_HIGH
	bsf	portb,2		;this is a debug progress check
    movf    FSR0L,W     ;
    movwf   FSRsave     ; Save FSR
    btfss   PIR1,SSPIF  ; Is this a SSP interrupt?
    goto    $           ; No, just trap here. change if there are other typs of interrupts
    bcf     PIR1,SSPIF	;yes
    call    SSP_Handler ; Yes, service SSP interrupt.
    
    
    movf    FSRsave,W   ;
    movwf   FSR0L       ; Restore FSR
    bsf     SSPCON1,CKP ; Release clock( for transmit and receive)
    retfie  FAST        ; Return from interrupt

;----------------------------------------------------------------
; State 1: I2C write operation, last byte was an address byte
; SSPSTAT bits: S = 1, D_A = 0, R_W = 0, BF = 1		b'0000 1001'  9h	.9
;
; State 2: I2C write operation, last byte was a data byte	b'0010 1001'	29h	.41
; SSPSTAT bits: S = 1, D_A = 1, R_W = 0, BF = 1
;
; State 3: I2C read operation, last byte was an address byte	b'0000 1100'	Ch	.12	
; SSPSTAT bits: S = 1, D_A = 0, R_W = 1 (see Appendix C for more information)
;
; State 4: I2C read operation, last byte was a data byte	b'0010 1100'	2Ch	.44
; SSPSTAT bits: S = 1, D_A = 1, R_W = 1, BF = 0
;
; State 5: Slave I2C logic reset by NACK from master		b'0010 1000	28h	.40
; SSPSTAT bits: S = 1, D_A = 1, BF = 0, CKP = 1 (see Appendix C for more information)
; For convenience, WriteI2C and ReadI2C functions have been used.
;-----------------------------------------------------------------
SSP_Handler
    movf    SSPSTAT,W       ; Get the value of SSPSTAT
    andlw   b'00101101'     ; Mask out unimportant bits(7,6,4,1) in SSPSTAT.
    movwf   Temp_STAT            ; for comparision checking.
State1                     ; Write operation, last byte was an
    movlw   b'00001001'     ;checking to see if it's an address,and if buffer is full.
    xorwf   Temp_STAT,W          ;
    btfss   STATUS,Z        ; Are we in State1? Yes if ^^^^, D/A=0, BF=1
    goto    State2          ; No, check for next state.....
    memset RXBuffer,0,RX_BUF_LEN ; Clear the receive buffer.
    clrf    Index           ; Clear the buffer index.
    movf    SSPBUF,W        ; Do a dummy read of the SSPBUF.
   bsf	portb,0		;this is a debug progress check
    return
State2                     ; Write operation, last byte was data,
    movlw   b'00101001'     ; buffer is full.
    xorwf   Temp_STAT,W
    btfss   STATUS,Z        ; Are we in State2?
    goto    State3          ; No, check for next state.....
    load RXBuffer,Index     ; Point to the buffer.
    
    movf    SSPBUF,W        ; Dummy read to clear sspbuf so we can put number in FSR
    
    movlw	info	    ;this is the info we want stored in FSR
    
    movwf   INDF0           ; Put it in the buffer.
    incf    Index,F         ; Increment the buffer pointer.
    movf    Index,W         ; Get the current buffer index.
    sublw   RX_BUF_LEN      ; Subtract the buffer length.
    btfsc   STATUS,Z        ; Has the index exceeded the buffer length?
    clrf    Index
    return
State3			    ;read operation, last byte was address byte
    movf    Temp_STAT,W ;
    andlw   b'00101100'     ; Mask BF bit in SSPSTAT
    xorlw   b'00001100'							;!!!!!!!!!!!!!
    btfss   STATUS,Z        ; Are we in State3?
    goto    State4          ; No, check for next state.....
    movf    SSPBUF,W	; ??? THIS MUST BE A DUMMY READ TO CLEAR SSPBUF???
    
    bcf	    sspstat,bf		;clear bf bit   ????????????????????????
    
;    clrf    Index           ; Clear the buffer index.
;    load    RXBuffer,Index  ; Point to the buffer
;    movf    INDF0,W         ; Get the byte from buffer.
    movlw   info	     ;writes 199 to w to be transmitted to master and displayed
    call    WriteI2C         ; Write the byte to SSPBUF
;    incf    Index,F          ; Increment the buffer index.
    bsf     sspcon1,ckp
    clrf	sspstat
    bsf		portb,1		;this is a debug progress check
    return

State4				;read operation, last byte was a data byte
    btfsc   SSPCON1,CKP ;
    goto    State5
    movlw   b'00101100'     ; buffer is empty.
    xorwf   Temp_STAT,W
    btfss   STATUS,Z        ; Are we in State4?
    goto    State5          ; No, check for next state....
    movf    Index,W         ; Get the current buffer index.
    sublw   RX_BUF_LEN      ; Subtract the buffer length.
    btfsc   STATUS,Z        ; Has the index exceeded the buffer length?
    clrf    Index           ; Yes, clear the buffer index.
    load    RXBuffer,Index  ; Point to the buffer
    movf    INDF0,W         ; Get the byte
    call    WriteI2C        ; Write to SSPBUF
    incf    Index,F         ; Increment the buffer index.
    return
State5			; State 5: Slave I2C logic reset by NACK from master
			; SSPSTAT bits: S = 1, D_A = 1, BF = 0, CKP = 1 
			;R/W remains set. Instead of testing
			;this bit, the state machine tests the CKP bit,
			;expecting it to be set.
    movf    Temp_STAT,W ;
    andlw   b'00101000'     ; Mask RW bit in SSPSTAT
    xorlw   b'00101000'
    btfss   STATUS,Z        ; Are we in State5?
    
    goto    I2CErr          ; No, must be err
    clrf	index
	clrf	rxbuffer
	clrf	sspstat
	bsf	sspcon1,ckp
    
    return 	;to ISR 
I2CErr			;we should not get here
    nop                     ; Something went wrong! Set LED
    bsf     PORTB,7         ;turn on led 7 and loop forever. WDT will reset
    goto    $               ; device, if enabled.
;---------------------------------------------------------------------
; WriteI2C
;---------------------------------------------------------------------
WriteI2C
    btfsc   SSPSTAT,BF      ; Is the buffer full?
    goto    WriteI2C        ; Yes, keep waiting.
DoI2CWrite
    bcf     SSPCON1,WCOL    ; Clear the WCOL flag.
    movwf   SSPBUF          ; Write the byte in WREG
    btfsc   SSPCON1,WCOL    ; Was there a write collision?
    goto    DoI2CWrite
    return
    end
 
Hi Aaron
Thanks for sharing. Will certainly look at your code, specially because its assembler! I am steamed over at work so progress on this side is slow.
 
Status
Not open for further replies.
Cookies are required to use this site. You must accept them to continue using the site. Learn more…