Do you have a question? Post it now! No Registration Necessary
Subject
- Posted on
PIC 16F872 I2C problem
- 08-17-2005
- k.renaud
August 17, 2005, 7:43 am

Hi all,
I'm a newbie to Microchip PICs having used other 8-bit controllers and
I've run into a problem I don't quite understand.
I simply cannot get I2C communication to work. I want to use the 16F872
as an I2C slave device, but it just doesn't respond.
I've tried both Application Note 734 assembler program and the example
EX_SLAVE.C that comes with the CCS C Compiler but with the same result.
As a master I've tried both an ETX-format PC and a PC with I2C USB
adapter. When the master tries to address the PIC at address 0x22,
there is no response at all according to the oscilloscope.
As the other Philips chips on the bus work and the problem still
remains when I disconnect everything except the pull-up:s and the PIC I
guess the problem isn't electrical.
Is there any special considerations/settings when programming the PIC
from MPLAB to enable SSP or can you suggest any other possible error
sources?

Re: PIC 16F872 I2C problem
C Application example:
#if defined(__PCM__)
#include <16F872.h>
#fuses HS,NOWDT,NOPROTECT,NOLVP
#use delay(clock20%000000)
#use rs232(baud96%00, xmit=PIN_C6, rcv=PIN_C7) // Jumpers: 8 to 11, 7
to 12
9600, xmit=PIN_C6, rcv=PIN_C7) // Jumpers: 8 to 11, 7 to 12
#endif
#use i2c(SLAVE, SDA=PIN_C4, SCL=PIN_C3, address=0x22)
typedef enum {NOTHING, CONTROL_READ,
ADDRESS_READ, READ_COMMAND_READ} I2C_STATE;
I2C_STATE fState;
BYTE address, buffer[0x10];
#INT_SSP
void ssp_interupt ()
{
BYTE incoming;
if (i2c_poll() == FALSE) {
if (fState == ADDRESS_READ) { //i2c_poll() returns false on the
i2c_write (buffer[address]);//interupt receiving the second
fState = NOTHING; //command byte for random read
operation
}
}
else {
incoming = i2c_read();
if (fState == NOTHING){
fState = CONTROL_READ;
}
else if (fState == CONTROL_READ) {
address = incoming;
fState = ADDRESS_READ;
}
else if (fState == ADDRESS_READ) {
buffer[address] = incoming;
fState = NOTHING;
}
}
}
void main ()
{
int i;
fState = NOTHING;
address = 0x00;
for (i=0;i<0x10;i++)
buffer[i] = 0x00;
enable_interrupts(GLOBAL);
enable_interrupts(INT_SSP);
while (TRUE) {}
}
##########################################################3
Asm code
#include <p16f872.inc> ; Change to device that you are using.
;---------------------------------------------------------------------
;Constant Definitions
;---------------------------------------------------------------------
#define NODE_ADDR 0x22 ; I2C address of this node
; Change this value to address that
; you wish to use.
;---------------------------------------------------------------------
; Buffer Length Definition
;---------------------------------------------------------------------
#define RX_BUF_LEN .32 ; Length of receive buffer
;---------------------------------------------------------------------
; Variable declarations
;---------------------------------------------------------------------
VARs udata
WREGsave res 1
STATUSsave res 1
FSRsave res 1
PCLATHsave res 1
Index res 1 ; Index to receive buffer
Temp res 1 ;
VARs1 udata
RXBuffer res RX_BUF_LEN ; Holds rec'd bytes from master
; device.
;---------------------------------------------------------------------
; Vectors
;---------------------------------------------------------------------
STARTUP code
nop
goto Startup ;
nop ; 0x0002
nop ; 0x0003
goto ISR ; 0x0004
PROG code
;---------------------------------------------------------------------
; Macros
;---------------------------------------------------------------------
memset macro Buf_addr,Value,Length
movlw Length ; 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 FSR ; specified.
SetNext movlw Value
movwf INDF
incf FSR,F
decfsz Temp,F
goto SetNext
endm
LFSR macro Address,Offset ; This macro loads the correct value
movlw Address ; into the FSR given an initial data
movwf FSR ; memory address and offset value.
movf Offset,W
addwf FSR,F
endm
;---------------------------------------------------------------------
; Main Code
;---------------------------------------------------------------------
Startup
bcf STATUS,RP1
bsf STATUS,RP0
call Setup
Main clrwdt ; Clear the WDT
goto Main ; Loop forever.
;---------------------------------------------------------------------
; Interrupt Code
;---------------------------------------------------------------------
ISR
movwf WREGsave ; Save WREG
movf STATUS,W ; Get STATUS register
banksel STATUSsave ; Switch banks, if needed.
movwf STATUSsave ; Save the STATUS register
movf PCLATH,W ;
movwf PCLATHsave ; Save PCLATH
movf FSR,W ;
movwf FSRsave ; Save FSR
banksel PIR1
btfss PIR1,SSPIF ; Is this a SSP interrupt?
goto $ ; No, just trap here.
bcf PIR1,SSPIF
call SSP_Handler ; Yes, service SSP interrupt.
banksel FSRsave
movf FSRsave,W ;
movwf FSR ; Restore FSR
movf PCLATHsave,W ;
movwf PCLATH ; Restore PCLATH
movf STATUSsave,W ;
movwf STATUS ; Restore STATUS
swapf WREGsave,F ;
swapf WREGsave,W ; Restore WREG
retfie ; Return from interrupt.
;---------------------------------------------------------------------
Setup
;
; Initializes program variables and peripheral registers.
;---------------------------------------------------------------------
banksel PCON
bsf PCON,NOT_POR
bsf PCON,NOT_BOR
banksel Index ; Clear various program variables
clrf Index
clrf PORTB
clrf PIR1
banksel TRISB
clrf TRISB
movlw 0x36 ; Setup SSP module for 7-bit
banksel SSPCON
movwf SSPCON ; address, slave mode
movlw NODE_ADDR
banksel SSPADD
movwf SSPADD
clrf SSPSTAT
banksel PIE1 ; Enable interrupts
bsf PIE1,SSPIE
bsf INTCON,PEIE ; Enable all peripheral interrupts
bsf INTCON,GIE ; Enable global interrupts
bcf STATUS,RP0
return
;---------------------------------------------------------------------
SSP_Handler
;---------------------------------------------------------------------
; The I2C code below checks for 5 states:
;---------------------------------------------------------------------
; State 1: I2C write operation, last byte was an address byte.
;
; SSPSTAT bits: S = 1, D_A = 0, R_W = 0, BF = 1
;
; State 2: I2C write operation, last byte was a data byte.
;
; SSPSTAT bits: S = 1, D_A = 1, R_W = 0, BF = 1
;
; State 3: I2C read operation, last byte was an address byte.
;
; SSPSTAT bits: S = 1, D_A = 0, R_W = 1, BF = 0
;
; State 4: I2C read operation, last byte was a data byte.
;
; SSPSTAT bits: S = 1, D_A = 1, R_W = 1, BF = 0
;
; State 5: Slave I2C logic reset by NACK from master.
;
; SSPSTAT bits: S = 1, D_A = 1, R_W = 0, BF = 0
;
; For convenience, WriteI2C and ReadI2C functions have been used.
;----------------------------------------------------------------------
banksel SSPSTAT
movf SSPSTAT,W ; Get the value of SSPSTAT
andlw b'00101101' ; Mask out unimportant bits in SSPSTAT.
banksel Temp ; Put masked value in Temp
movwf Temp ; for comparision checking.
State1: ; Write operation, last byte was an
movlw b'00001001' ; address, buffer is full.
xorwf Temp,W ;
btfss STATUS,Z ; Are we in State1?
goto State2 ; No, check for next state.....
memset RXBuffer,0,RX_BUF_LEN ; Clear the receive buffer.
clrf Index ; Clear the buffer index.
call ReadI2C ; Do a dummy read of the SSPBUF.
return
State2: ; Write operation, last byte was data,
movlw b'00101001' ; buffer is full.
xorwf Temp,W
btfss STATUS,Z ; Are we in State2?
goto State3 ; No, check for next state.....
LFSR RXBuffer,Index ; Point to the buffer.
call ReadI2C ; Get the byte from the SSP.
movwf INDF ; 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 ; Yes, clear the buffer index.
return
State3: ; Read operation, last byte was an
movlw b'00001100' ; address, buffer is empty.
xorwf Temp,W
btfss STATUS,Z ; Are we in State3?
goto State4 ; No, check for next state.....
clrf Index ; Clear the buffer index.
LFSR RXBuffer,Index ; Point to the buffer
movf INDF,W ; Get the byte from buffer.
call WriteI2C ; Write the byte to SSPBUF
incf Index,F ; Increment the buffer index.
return
State4: ; Read operation, last byte was data,
movlw b'00101100' ; buffer is empty.
xorwf Temp,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.
LFSR RXBuffer,Index ; Point to the buffer
movf INDF,W ; Get the byte
call WriteI2C ; Write to SSPBUF
incf Index,F ; Increment the buffer index.
return
State5:
movlw b'00101000' ; A NACK was received when transmitting
xorwf Temp,W ; data back from the master. Slave logic
btfss STATUS,Z ; is reset in this case. R_W = 0, D_A = 1
goto I2CErr ; and BF = 0
return ; If we aren't in State5, then something is
; wrong.
I2CErr nop
banksel PORTB ; Something went wrong! Set LED
bsf PORTB,7 ; and loop forever. WDT will reset
goto $ ; device, if enabled.
return
;---------------------------------------------------------------------
; WriteI2C
;---------------------------------------------------------------------
WriteI2C
banksel SSPSTAT
btfsc SSPSTAT,BF ; Is the buffer full?
goto WriteI2C ; Yes, keep waiting.
banksel SSPCON ; No, continue.
DoI2CWrite
bcf SSPCON,WCOL ; Clear the WCOL flag.
movwf SSPBUF ; Write the byte in WREG
btfsc SSPCON,WCOL ; Was there a write collision?
goto DoI2CWrite
bsf SSPCON,CKP ; Release the clock.
return
;---------------------------------------------------------------------
ReadI2C
;---------------------------------------------------------------------
banksel SSPBUF
movf SSPBUF,W ; Get the byte and put in WREG
return
end ; End of file

Re: PIC 16F872 I2C problem
You have several infinite loops in your code (I checked the ASM).
This may only work if you know exactly what you are doing, in this case you
need to have set at least the watchdog.
The best way to handle the infinite loop in the ISR is to check every
intterupt used, and after that if non of this caused the jump to the ISR,
reset all interrupts that were also enabled with another. Disable all unused
intterrupts if possible.
Why use an infinite loop for an I2C error???

Re: PIC 16F872 I2C problem
The assembler code is supplied on Microchip's website in Application
Note AN734 so I can't say why they chose to loop infinitely causing a
WDT reset in case of an I2C error. But this surely doesn't interfere
with normal I2C communication without errors.
The C code is supplied by CCS as an example with their compiler.
Because I have used supplied example code I strongly suspect that the
code isn't the problem but rather some kind of error during programming
although every other program that has been put into the PIC works ok.
The latest version of MPLAB is used and ICD2 as a programmer.
Site Timeline
- » RFO: Ethernet solution for use with PIC 16f877
- — Next thread in » Microcontroller Discussions
-
- » JDM PIC programmer question
- — Previous thread in » Microcontroller Discussions
-
- » New(ish) assembler for PIC16 microcontrollers (asm1825)
- — Newest thread in » Microcontroller Discussions
-
- » (PDF) Essentials of Anatomy & Physiology 2nd Ed by Kenneth Saladin
- — The site's Newest Thread. Posted in » Electronics (Polish)
-
- » Cortex-Mx MCUs with SWD access locked
- — The site's Last Updated Thread. Posted in » Embedded Programming
-