PIC 16F872 I2C problem

Do you have a question? Post it now! No Registration Necessary

Translate This Thread From English to

Threaded View


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



Quoted text here. Click to load it

Lack of usefull information, please enter your code!



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.


Re: PIC 16F872 I2C problem


Quoted text here. Click to load it

Where I have read this I don't know.
But somewhere I heard that the Microchip code was not very well suited or
maybe buggy for some (or all) chips.



Re: PIC 16F872 I2C problem


Try posting the code so otheres can test it.

Quoted text here. Click to load it



Site Timeline