• I2C Slave with a PIC

    This code was developed from Aratti's example located here: http://www.picbasic.co.uk/forum/show...0366#post70366

    A short video:

    The master on the left, a 16F877 is telling the first 16F88 (on the top) to turn on/off the LED, and then reads 24 from it. After that, the master tells the second 16F88 (on the bottom) to turn on/off the LED, and then reads 12 from it.

    It's in slow motion because I was testing everything, but I discovered that the I2C protocol is very fast!

    Al, thanks a lot one more time!

    Daniel.

    Master 16f877 - External 4Mhz Crystal:
    Code:
    '-------------------------------------------------------------------------------
    '-------------------------------------------------------------------------------
    ' Master
    '-------------------------------------------------------------------------------
    '-------------------------------------------------------------------------------
    
    DEFINE OSC 4
    
    'OPTION_REG.7 = 0
    
    ADCON1 = 7
    
    '------------------------------------------------------------------------------
    
    DEFINE I2C_SLOW 1
    
    TRISB.5 = 1
    TRISB.7 = 1
    
    '------------------------------------------------------------------------------
    
    scl VAR PORTB.5        ' i2c clock input
    sda VAR PORTB.7     ' i2c data input
    
    '------------------------------------------------------------------------------
    
    I2Caddress VAR BYTE
    
    valor VAR BYTE
    
    '------------------------------------------------------------------------------
    
    ' Set LCD Data port
    DEFINE LCD_DREG PORTD
    ' Set starting Data BIT (0 OR 4) IF 4-BIT bus
    DEFINE LCD_DBIT 4
    ' Set LCD Register Select port
    DEFINE LCD_RSREG PORTC
    ' Set LCD Register Select BIT
    DEFINE LCD_RSBIT 6
    ' Set LCD Enable port
    DEFINE LCD_EREG PORTC
    ' Set LCD Enable BIT
    DEFINE LCD_EBIT 7
    ' Set LCD bus size (4 OR 8 bits)
    DEFINE LCD_BITS 4
    ' Set number of lines ON LCD
    DEFINE LCD_LINES 2
    ' Set command delay time in us
    DEFINE LCD_COMMANDUS 2000
    ' Set Data delay time in us
    DEFINE LCD_DATAUS 50
    
    '------------------------------------------------------------------------------
    
    Pause 500       ' Wait for LCD to startup
    LCDOut $fe, 1
    LCDOut $fe, "Daniel"
    Pause 500       ' Wait for LCD to startup
    
    valor = 0
    
    '------------------------------------------------------------------------------
    
    main:
          I2Caddress = $3
    
          LCDOut $fe, 1
          LCDOut "Writting 1 - LED ON..."
          Pause 500
    
          I2CWrite SDA, SCL, I2Caddress, [6], bogus ' Write offset to slave
          Pause 500
    
          LCDOut $fe, 1
          LCDOut "Writting 1 - LED OFF..."
          Pause 500
    
          I2CWrite SDA, SCL, I2Caddress, [8], bogus ' Write to slave
          Pause 500
    
          LCDOut $fe, 1
          LCDOut "Reading 1..."
          Pause 500
    
          I2CRead SDA, SCL, I2Caddress, [valor], bogus ' Read from slave
          LCDOut $fe, 1
          LCDOut "Value: ", DEC valor
          Pause 500
    
          '--------
    
          I2Caddress = $5
    
          LCDOut $fe, 1
          LCDOut "Writting 2 - LED ON..."
          Pause 500
    
          I2CWrite SDA, SCL, I2Caddress, [4], bogus ' Write to slave
          Pause 500
    
          LCDOut $fe, 1
          LCDOut "Writting 2 - LED OFF..."
          Pause 500
    
          I2CWrite SDA, SCL, I2Caddress, [10], bogus ' Write to slave
          Pause 500
    
          LCDOut $fe, 1
          LCDOut "Reading 2..."
          Pause 500
    
          I2CRead SDA, SCL, I2Caddress, [valor], bogus ' Read from slave
          LCDOut $fe, 1
          LCDOut "Value: ", DEC valor
          Pause 500
    
          '--------
    
          GoTo main       ' Do it forever
    
    '-------------------------------------------------------------------------------
    
    bogus:
            LCDOut $fe,1, "Time out"    ' I2C command timed out
            Pause 500
    
            GoTo main
    
    '-------------------------------------------------------------------------------
    
    End
    
    '-------------------------------------------------------------------------------
    '-------------------------------------------------------------------------------
    First Slave - 16F88 - Internal 8Mhz
    Code:
    '------------------------------------------------------------------------------
    '------------------------------------------------------------------------------
    ' Slave
    '------------------------------------------------------------------------------
    '------------------------------------------------------------------------------
    
    @ device pic16f88, intrc_osc_noclkout    ' system clock options
    @ device pic16f88, wdt_on            ' watchdog timer
    @ device pic16f88, pwrt_on            ' power-on timer
    @ device pic16f88, mclr_off            ' master clear options (internal)
    @ device pic16f88, protect_off        ' code protect off
    
    '--- set device  ---------------------------------------------------------------
    
    DEFINE OSC 8
    OSCCON = %01110001 '8mhz
    
    '------------------------------------------------------------------------------
    
    CMCON = 7
    ADCON1 = 0 ' Disable A/D converter
    ADCON0 = 0
    ANSEL = 0 ' all analog pins to digital
    
    '--- tris port config here (don't use SCL & SDA pins other than I2C)  ----------
    
    DEFINE I2C_HOLD 1
    
    TRISB.1 = 1
    TRISB.4 = 1
    
    '--- Alias pins  ---------------------------------------------------------------
    
    SDADIR VAR TRISB.1
    SCLDIR VAR TRISB.4
    
    '--- Define here your variables and alias  -------------------------------------
    
    LEDT VAR PORTA.2
    LEDL VAR PORTB.7
    LEDM VAR PORTB.6
    LEDR VAR PORTB.5
    readcnt VAR BYTE
    datain VAR BYTE
    WrData VAR BYTE
    
    '--- Define used register flags  ------------------------------------------------
    
    SSPIF               VAR PIR1.3    ' SSP (I2C) interrupt flag
    SSPIE               VAR PIE1.3 
    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 SSPCON.4  ' SSP (I2C) SCK Release Control
    SSPEN               VAR SSPCON.5  ' SSP (I2C) Enable
    SSPOV               VAR SSPCON.6  ' SSP (I2C) Receive Overflow Indicator
    WCOL                VAR SSPCON.7  ' SSP (I2C) Write Collision Detect
    STAT_BF             VAR SSPSTAT.0 ' SSP (I2C) Buffer Full
    STAT_RW             VAR SSPSTAT.2 ' SSP (I2C) Read/Write
    STAT_DA             VAR SSPSTAT.5 ' SSP (I2C) Data/Address
    CKE                 VAR SSPSTAT.6 ' SSP (I2C) Data/Address
    
    '--- Rx Buffer defintion  ------------------------------------------------------
    
    RxBufferLEN         CON 1       
    RxBuffer            VAR BYTE[Rxbufferlen]
    RxBufferIndex       VAR BYTE
    
    '--- Tx Buffer defintion  ------------------------------------------------------
    
    TxBufferLEN         CON 1
    TxBuffer            VAR BYTE[txbufferlen]
    TxBufferIndex       VAR BYTE
    
    '--- Define constants  ---------------------------------------------------------
    
    I2Caddress          CON $3 ' Make address = 3
    
    '--- Initialize I2C slave mode  ------------------------------------------------
    
    SCLDIR = 1          ' SCL must be an input before enabling interrupts
    SDADIR = 1   
    SSPADD = I2Caddress ' Set our address
    SSPCON = $36        ' Set to I2C slave with 7-bit address
    SSPSTAT = 0
    SSPIE = 1 
    SSPIF = 0 
    RxBufferIndex = 0
    TxBufferIndex = 0
    
    '--- Initialization Done!  -----------------------------------------------------
    
    GoTo main
    
    '--- I2C subroutine   ----------------------------------------------------------
    
    i2cslave:                                      ' I2C slave subroutine
              SSPIF = 0                            ' Clear interrupt flag
              IF R_W = 1 Then i2crd                ' Read data from us
              IF BF = 0 Then i2cexit               ' Nothing in buffer so  exit
              IF D_A = 1 Then i2cwr                ' Data for us (not  address)
              IF SSPBUF != I2Caddress Then i2cexit ' Clear the address from  the buffer
              readcnt = 0                          ' Mark as first read
              GoTo i2cexit
    
    i2cwr:                                ' I2C write data to us
           datain = SSPBUF                ' Put buffer data into array
           Rxbuffer[Rxbufferindex]=datain
           Rxbufferindex=rxbufferindex+1
    
           IF rxbufferindex=RxBufferLEN Then           ' end of buffer  transfer 
              WrData=1
              rxbufferindex=0
           EndIF
    
           GoTo i2cexit
    
    i2crd:                                      ' I2C read data from us
           IF D_A = 0 Then
              TxBufferIndex = 0
           EndIF
    
           While STAT_BF : Wend                        ' loop while buffer  is full
    
           wcol = 0                                    ' clear collision  flag
           SSPBUF = TxBuffer[TxBufferIndex]
    
           While wcol 
                 wcol = 0
                 SSPBUF = TxBuffer[TxBufferIndex]
           Wend
    
           CKP = 1                                     ' release clock,  allowing read by master
           TxBufferIndex = TxBufferIndex + 1           ' increment index
           IF TxBufferIndex = TxBufferlen Then         ' all bytes have been  tx
              TxBufferIndex = 0                           ' reset index             
           EndIF
                 
    i2cexit:
             Return
    
    '--- End I2C subroutine   ------------------------------------------------------
    
    Main:
          txbuffer = 12
    
          IF SSPIF = 1 Then
             GoSub i2cslave
          EndIF
    
          SSPOV = 0
          WCOL = 0
    
          Select Case RxBuffer[0]
                 Case 6
                         High LEDT
                 Case 8
                         Low LEDT
          End Select
    
          WrData=0  
          GoTo Main
    
    
    '------------------------------------------------------------------------------
    
    End
    
    '------------------------------------------------------------------------------
    '------------------------------------------------------------------------------
    Second Slave - 16F88 - Internal 8Mhz
    Code:
    '------------------------------------------------------------------------------
    '------------------------------------------------------------------------------
    ' Slave
    '------------------------------------------------------------------------------
    '------------------------------------------------------------------------------
    
    @ device pic16f88, intrc_osc_noclkout    ' system clock options
    @ device pic16f88, wdt_on            ' watchdog timer
    @ device pic16f88, pwrt_on            ' power-on timer
    @ device pic16f88, mclr_off            ' master clear options (internal)
    @ device pic16f88, protect_off        ' code protect off
    
    '--- set device  ---------------------------------------------------------------
    
    DEFINE OSC 8
    OSCCON = %01110001 '8mhz
    
    '------------------------------------------------------------------------------
    
    CMCON = 7
    ADCON1 = 0 ' Disable A/D converter
    ADCON0 = 0
    ANSEL = 0 ' all analog pins to digital
    
    '--- tris port config here (don't use SCL & SDA pins other than I2C)  ----------
    
    DEFINE I2C_HOLD 1
    
    TRISB.1 = 1
    TRISB.4 = 1
    
    '--- Alias pins  ---------------------------------------------------------------
    
    SDADIR VAR TRISB.1
    SCLDIR VAR TRISB.4
    
    '--- Define here your variables and alias  -------------------------------------
    
    LEDT VAR PORTA.2
    LEDL VAR PORTB.7
    LEDM VAR PORTB.6
    LEDR VAR PORTB.5
    readcnt VAR BYTE
    datain VAR BYTE
    WrData VAR BYTE
    
    '--- Define used register flags  ------------------------------------------------
    
    SSPIF               VAR PIR1.3    ' SSP (I2C) interrupt flag
    SSPIE               VAR PIE1.3 
    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 SSPCON.4  ' SSP (I2C) SCK Release Control
    SSPEN               VAR SSPCON.5  ' SSP (I2C) Enable
    SSPOV               VAR SSPCON.6  ' SSP (I2C) Receive Overflow Indicator
    WCOL                VAR SSPCON.7  ' SSP (I2C) Write Collision Detect
    STAT_BF             VAR SSPSTAT.0 ' SSP (I2C) Buffer Full
    STAT_RW             VAR SSPSTAT.2 ' SSP (I2C) Read/Write
    STAT_DA             VAR SSPSTAT.5 ' SSP (I2C) Data/Address
    CKE                 VAR SSPSTAT.6 ' SSP (I2C) Data/Address
    
    '--- Rx Buffer defintion  ------------------------------------------------------
    
    RxBufferLEN         CON 1       
    RxBuffer            VAR BYTE[Rxbufferlen]
    RxBufferIndex       VAR BYTE
    
    '--- Tx Buffer defintion  ------------------------------------------------------
    
    TxBufferLEN         CON 1
    TxBuffer            VAR BYTE[txbufferlen]
    TxBufferIndex       VAR BYTE
    
    '--- Define constants  ---------------------------------------------------------
    
    I2Caddress          CON $5 ' Make address = 5
    
    '--- Initialize I2C slave mode  ------------------------------------------------
    
    SCLDIR = 1          ' SCL must be an input before enabling interrupts
    SDADIR = 1   
    SSPADD = I2Caddress ' Set our address
    SSPCON = $36        ' Set to I2C slave with 7-bit address
    SSPSTAT = 0
    SSPIE = 1 
    SSPIF = 0 
    RxBufferIndex = 0
    TxBufferIndex = 0
    
    '--- Initialization Done!  -----------------------------------------------------
    
    GoTo main
    
    '--- I2C subroutine   ----------------------------------------------------------
    
    i2cslave:                                      ' I2C slave subroutine
              SSPIF = 0                            ' Clear interrupt flag
              IF R_W = 1 Then i2crd                ' Read data from us
              IF BF = 0 Then i2cexit               ' Nothing in buffer so  exit
              IF D_A = 1 Then i2cwr                ' Data for us (not  address)
              IF SSPBUF != I2Caddress Then i2cexit ' Clear the address from  the buffer
              readcnt = 0                          ' Mark as first read
              GoTo i2cexit
    
    i2cwr:                                ' I2C write data to us
           datain = SSPBUF                ' Put buffer data into array
           Rxbuffer[Rxbufferindex]=datain
           Rxbufferindex=rxbufferindex+1
    
           IF rxbufferindex=RxBufferLEN Then           ' end of buffer  transfer 
              WrData=1
              rxbufferindex=0
           EndIF
    
           GoTo i2cexit
    
    i2crd:                                      ' I2C read data from us
           IF D_A = 0 Then
              TxBufferIndex = 0
           EndIF
    
           While STAT_BF : Wend                        ' loop while buffer  is full
    
           wcol = 0                                    ' clear collision  flag
           SSPBUF = TxBuffer[TxBufferIndex]
    
           While wcol 
                 wcol = 0
                 SSPBUF = TxBuffer[TxBufferIndex]
           Wend
    
           CKP = 1                                     ' release clock,  allowing read by master
           TxBufferIndex = TxBufferIndex + 1           ' increment index
           IF TxBufferIndex = TxBufferlen Then         ' all bytes have been  tx
              TxBufferIndex = 0                           ' reset index             
           EndIF
                 
    i2cexit:
             Return
    
    '--- End I2C subroutine   ------------------------------------------------------
    
    Main:
          txbuffer = 24
    
          IF SSPIF = 1 Then
             GoSub i2cslave
          EndIF
    
          SSPOV = 0
          WCOL = 0
    
          Select Case RxBuffer[0]
                 Case 4
                         High LEDT
                 Case 10
                         Low LEDT
          End Select
    
          WrData=0  
          GoTo Main
    
    
    '------------------------------------------------------------------------------
    
    End
    
    '------------------------------------------------------------------------------
    '------------------------------------------------------------------------------
    This article was originally published in forum thread: I2C Salve with a PIC started by ralfmayr View original post