• PIC as I2C Master/Slaves

    Here is a fairly full-featured interrupt-driven I2C slave. ( The master part is easy).
    This slave relies on the work of many others, but I have added some bug fixes and extra features. For example, you can write
    0xAA,0X55,0x00,**your new address here**,0x00
    and you can change the address of the slave.

    Like my other stuff, it ONLY works on 18F chips. It should be "plug and play" in virtually any 18F chip.

    Code:
    ASM
        ifndef __18F2321
            error "18F2321 not selected"
        endif
    ENDASM 
           
    ASM
           movlw 0x62                              ; %0110 0010  = 4 Mhz, internal osc block
           movwf OSCCON
           movlw 0x80                              ; %1000 0000  = PLL disabled
           movwf OSCTUNE
    ENDASM       
    
            DATA @0,$38      ; Default I2C address if not determined in HW
                          
            DEFINE OSC 4                                                          
            Define USE_LFSR 1
    
    ;---------------- Alias the bits ------------------------------------
        
        SSPIF               VAR PIR1.3    ' SSP (I2C) interrupt flag
        SSPIE               VAR PIE1.3    ' Int Enable
       
        SSPUA               VAR SSPSTAT.1
        SSPS                VAR SSPSTAT.3
        SSPP                VAR SSPSTAT.4
        SSPSMP              VAR SSPSTAT.7
        
        BF                  VAR SSPSTAT.0 ' SSP (I2C) Buffer Full
        R_W                 VAR SSPSTAT.2 ' SSP (I2C) Read/Write
        D_A                 VAR SSPSTAT.5 ' SSP (I2C) Data/Address
        CKP                 VAR SSPCON1.4  ' SSP (I2C) SCK Release Control
        SSPEN               VAR SSPCON1.5  ' SSP (I2C) Enable
        SSPOV               VAR SSPCON1.6  ' SSP (I2C) Receive Overflow Indicator
        WCOL                VAR SSPCON1.7  ' SSP (I2C) Write Collision Detect
       
        
        
         MasterClock VAR WORD BANKA SYSTEM  
        
     
    '------------------- Buffer defintion --------------------
        RxBufferLEN         con 10         
        RxBuffer            var BYTE[Rxbufferlen + 1]    
        TxBufferLEN         CON 10
        TXBuffer            VAR BYTE[TxBufferLEN + 1]
       
        GeneralCall         VAR BIT
        RXBufferOverFlow    VAR BIT
        TxBufferUnderFlow   VAR BIT
        HWAddress           VAR BIT
        RXBufferPointer     VAR Byte
        TXBufferPointer     VAR Byte
        address             VAR BYTE
        x                   VAR BYTE
        dummy               var byte
        I2CAddress          VAR BYTE
        WCOLCounter         VAR BYTE
        I2CDeviceAddress     VAR BYTE
        Holdoff             VAR WORD ; retry timer
    
        HWAddress = 0       ; Get address from EEPROM, not pins
        
        TestArray   VAR BYTE [200]
    
        IF !HWAddress THEN
    ;----------- Get our Address from EEPROM --------------    
             READ 0,I2CAddress  
    ;-----------------------------------------------------     
        ELSE
    ;----------------OR Get our address from PORT B0..2-----------
            I2CDeviceAddress = PORTB & %00000111
            I2CMajorAddress     CON $3     ; upper 4 bits
            I2CAddress = (I2CMajorAddress <<4) | I2CDeviceAddress <<1 ; bit 0 is R/W
    ;---------------------------------------------------------------       
        Endif
        
        SSPADD = I2Caddress ' Move our address into the proper reg
        SSPCON2.0 = 1      ; Allow clock stretching
        SSPCON2.1 = 0      ; Set to 1 to enable masking of address 1 ; 0 is R/W bit
        SSPCON2.2 = 0      ; Set to 1 to enable masking of address bit 2
        SSPCON2.3 = 0      ; Set to 1 to enable masking of address bit 3
        SSPCON2.4 = 0      ; Set to 1 to enable masking of address bit 4
        SSPCON2.5 = 0      ; Set to 1 to enable masking of address bit 5
        SSPCON2.6 = 0      ; Not used in slave
        SSPCON2.7 = 1      ; General Call enabled
        SSPSTAT = 0
        SSPEN = 1             
        SSPIE = 1 
        SSPIF = 0 
        SSPCON1 = $36         ' Set to I2C slave with 7-bit address    
           
    ;---------------------------------------------------------------
        TRISA = %11111111
        TRISB = %11111111
        TRISC = %11111111
           
    ' -------------- Turn on Ints  ------------------------
        INTCON.7 = 1  ; global ints
        INTCON.6 = 1  ; peripheral ints
        IPR1.3 = 1    ; high priority
     
    ;------------------  Clear parameters --------------
        RXBufferPointer = 0
        TxBufferPointer = 0
        GeneralCall = 0 
        RxBufferOverFlow = 0
        TXBufferUnderFlow = 0
        WCOLCounter = 0
    ;----------------Timer Setup-----------------------------------  
         T0CON = %10000011 ; /16 prescaler
    ;-------- -------------------------------------------     
         WDTCON = 1  ; Turn on the WDT
    ;--------------------------------------------------------------   
       
    
    INCLUDE "DT_INTS-18.bas"    '  Thanks Darrel!
    INCLUDE "ReEnterPBP-18.bas"     ' 
    
    ASM
    INT_LIST  macro    ; IntSource,         Label,  Type, ResetFlag?
            INT_Handler    SSP_INT,  _I2C_Int,   PBP,  yes
            INT_Handler    TMR0_INT,  MainTimer, ASM, yes
        endm
        INT_CREATE               ; Creates the interrupt processor
    endasm
    
    ''----------------- Initialization Done! -----------------------------
    
        Goto OverInt ; jump over ISR, don't execute it.
    ;----------------------- Timer INt ------------------------------------    
    ASM
    MainTimer
            movlw   0x00         ;  10 mSec
            movwf   TMR0H
            movlw   0x63
            movwf   TMR0L
            
            infsnz  MasterClock
            incf    MasterClock + 1
            
    ;    This is a good place to pick up pin changes        
          
     INT_ENABLE TMR0_INT
     INT_RETURN
    
    ENDASM
    ;---------------------------------------------------------------------
    '------------------------- I2C subroutine  -------------------------------- 
    I2C_Int:
      ; We don't get here uless someone has matched our address register.
    
    If R_W = 0 then             'This is a WRITE by the master to us
        IF BF = 0 Then goto i2CEXIT   ; Nothing for us!
        if D_A = 0 then         'We have an address
          address = SSPBUF     'Need to READ Address from buffer to clear it.
           
           IF Address = 0 THEN
               GeneralCall = 1   ; Set flag if we got a general call address 
           ELSE
               GeneralCall = 0     
           ENDIF
         
              
        else            
            IF WCOL OR SSPOV THEN I2CExit
             IF RXBufferPointer < RxBufferLen - 1 THEN
                 RXBuffer [RxBufferPointer] = SSPBuf   ; 
                 RxBufferPointer = RXBufferPointer + 1
             ELSE
                 Dummy = SSPBuf  ; Grab the data but don't overwrite old
                 RXBufferOverFlow = 1
             ENDIF 
                  
            ENDIF
              
    
        else   ' R_W = 1 Master wants to read FROM us
    
              IF !D_A Then
                    TxBufferPointer = 0        ' Mark as first read
                    RXBufferPointer = 0
              EndIF
            
              IF CKP THEN I2CExit
    
                  dummy = SSPBUF  ;NECESSARY!
     
    SENDI2C:
                  WCOL = 0
                      SSPBUF = TXBuffer[TXBufferPointer]    ' Get data from array
                       IF WCOL THEN 
                           WCOLCounter = WCOLCounter + 1            ; Don't hang the bus with infinite WRITES!
                           IF WColCounter >= 7 Then goto I2CExit     ; 7
                           RANDOM Holdoff
                           Pauseus Holdoff >> 3    ; wait 0-8 msec 
                           Goto SendI2C       ; Retry
                        ELSE
                           WColCounter = 0
                        ENDIF   
                       TXBufferPointer = TXBufferPointer + 1
                       
                 If TXBufferPointer = TXBufferLen THEN
                      TXBufferUnderflow = 1
                      TXBufferPointer = TXBufferLen - 1 ; Repeat last if req made again, but set flag now
                 ENDIF
        ENDIF
    
    I2CEXIT:
        SSPOV = 0
        BF = 0 
        sspif = 0
        CKP = 1 
    @ INT_ENABLE SSP_INT      ; probably don't need this, but I like to be certain it gets restarted
    @ INT_RETURN
    
    
    ' -------------------------end I2C subroutine  -------------------------------- 
    '--------------------[End ISRs]---------------------------------------
    OverInt:
    
    @ INT_ENABLE  SSP_INT     
    @ INT_ENABLE TMR0_INT
    '--------------------[ main program loop]---------------------------------------
    Main:
       IF RXBuffer[0] = $AA THEN
          IF RXBuffer[1] = $55 and RXBuffer[2] = 0  And RxBuffer[4] = 0 THEN
            Dummy = RXBuffer[3]
            WRITE 0,Dummy   ; Write a new address
            Pause 10
    @ RESET  ; and pick up the new address      
          ENDIF 
       ENDIF
    ;-------------------  Load the input buffer to the output buffer for test -----------   
       For X = 0 to TXBufferLen - 1  
         TxBuffer[X] = RXBuffer[X]
        next x
    ;----------------------------------------------------------------------
    goto main 
    
    end
    
    ASM
      ORG 0x0FF0  ; Keep track of pgm size in case we want to use it on a smaller chip
      NOP
    ENDASM
    This article was originally published in forum thread: PIC as I2C Master/Slaves started by Adamey View original post