I2C Slave with a PIC


Closed Thread
Results 1 to 40 of 130

Hybrid View

  1. #1
    Join Date
    Nov 2008
    Posts
    48

    Default I2C Slave with a PIC

    Hi there,
    i tried, as mentined in an other thread before, that i try to implement
    the i2c-slave code from Robert Soubie in a PIC18F2431, but there are still
    problems in that code: Writing to the slave wokrs fine, but reading from
    the slave results in errors and sometimes senseless values on the master side.
    Did someone here use this code from Robert and could help on this problem?
    Regards,
    Ralf

  2. #2
    Join Date
    Sep 2004
    Location
    montreal, canada
    Posts
    6,898


    Did you find this post helpful? Yes | No

    Default

    How about posting what you've done so far, this way we could check with your code?
    Steve

    It's not a bug, it's a random feature.
    There's no problem, only learning opportunities.

  3. #3
    Join Date
    Nov 2008
    Posts
    48


    Did you find this post helpful? Yes | No

    Default

    Hi mister_e,
    good idea:

    What i have done is using th original code from Robert Soubie posted on his website.
    I made some more remarks and checked all gaainst the MCHIP AppNote AN734, i hope i made no mistake.

    1. I initialize the i2c:

    'Initialize I2C slave mode
    SCLDIR = 1 ' SCL must be made an input before enabling interrupts
    SDADIR = 1 ' SDA must be made an input before enabling interrupts
    SSPCON = $36 ' configure for i2c 7Bit
    gosub read_12c_address ' get address from B0 - B3
    SSPSTAT = 0 ' setting some regs...
    SSPIE = 1
    SSPIF = 0
    ' PIE1 = 1 ' ?????????
    PEIE = 1
    GIE = 1
    RxBufferIndex = 0
    TxBufferIndex = 0

    2. In the main program i include the int-handler:

    On Interrupt goto int_hand
    .... main program parts ....

    '***************** INTERRUPT HANDLER ************
    Disable
    int_hand:
    include "i2c_slave_int2.inc"
    'INTCON.1 = 0
    Resume
    Enable
    '*********************************************

    3. The "i2c_slave_int.inc" you can see here (derived from Robert Soubie)

    '*********************************************
    '* Name : i2c_slave_int.inc *
    '* Author : Ralf Mayr *
    '* : Derived from Robert Soubie *
    '* Notes : Original Code by Robert Soubie *
    '*********************************************


    goto i2c_interrupt_handler

    '*********************************************
    'write TXBuffer to I2C
    '*********************************************
    write_i2c:

    while STAT_BF = 1
    i = 0
    wend ' loop while buffer is full
    wcol = 0 ' clear collision flag
    SSPBUF = TxBuffer(TxBufferIndex)
    while wcol = 1
    wcol = 0
    SSPBUF = TxBuffer(TxBufferIndex)
    wend
    CKP = 1 ' release clock, allowing read by master
    TxBufferIndex = TxBufferIndex + 1 ' increment index
    if TxBufferIndex = 2 then ' all bytes have been transmitted
    TxBufferIndex = 0
    int_ready = 1 ' so reset index
    endif
    goto i2c_interrupt_handler_exit

    '***********************************************
    'Interrupt handling starts here
    '***********************************************
    i2c_interrupt_handler:

    'mask Status Register first
    i2c_Save_Status = SSPSTAT & i2c_slave_mask

    '***********************************************
    ' Write operation, last byte was an address, buffer is full
    ' BF = 1
    ' UA = NA
    ' RW = 0 (we get data)
    ' S = 1 (Start bit was set)
    ' P = NA
    ' DA = 0 (last byte was address)
    '***********************************************
    State_1:
    if i2c_Save_Status <> %00001001 then State_2
    i2cTempVAr = SSPBUF ' empty buffer
    if sspov then ' overflow occured?
    sspov = 0
    i2cTempVAr = SSPBUF ' empty buffer
    endif
    for RxBufferIndex = 0 to 2 ' empty receive buffer
    RxBuffer(RxbufferIndex) = 0
    next RxBufferIndex
    RxBufferIndex = 0 ' reset buffer index
    goto i2c_interrupt_handler_exit

    '***********************************************
    ' Write operation, last byte was data, buffer is full
    ' BF = 1
    ' UA = NA
    ' RW = 0 (we get data)
    ' S = 1 (Start bit was set)
    ' P = NA
    ' DA = 1 (last byte was data)
    '***********************************************
    State_2:
    if i2c_Save_Status <> %00101001 then State_3
    RxBuffer(RxBufferIndex) = SSPBUF ' read received byte and empty buffer
    if sspov then ' overflow occured?
    sspov = 0
    i2cTempVAr = SSPBUF ' empty buffer
    endif
    RxBufferIndex = RxBufferIndex + 1 ' point at next empty slot in buffer
    goto i2c_interrupt_handler_exit

    '***********************************************
    ' Read operation, last byte was an address, buffer is empty
    ' BF = unknown ?
    ' UA = NA
    ' RW = 1 (we send data)
    ' S = 1
    ' P = NA
    ' DA = 0 (last byte was address)
    '***********************************************
    State_3:
    if i2c_Save_Status <> %00001100 then State_4
    goto write_i2c ' write next byte

    '***********************************************
    ' Read operation, last byte was data, buffer is empty
    ' BF = 0
    ' UA = NA
    ' RW = 1 (we send data)
    ' S = 1 (Start bit was set)
    ' P = NA
    ' DA = 1 (last byte send was data)
    '***********************************************
    State_4:
    if i2c_Save_Status <> %00101100 then State_5
    goto write_i2c ' write next byte

    '***********************************************
    ' Unknown operation
    ' BF = 0
    ' UA = NA
    ' RW = 0
    ' S = 1 (Start bit was set)
    ' P = NA
    ' DA = 1 (last byte send was data)
    '***********************************************
    State_5:
    if i2c_Save_Status <> %00101000 then State_i2CErr
    CKP = 1
    goto i2c_interrupt_handler_exit


    '***********************************************
    ' We should never get here... BUT WE GET HERE *g*
    '***********************************************
    State_i2cErr:
    i2c_error = 1

    '***********************************************
    ' Did we receive one command byte and two parameter bytes?
    ' if so, RxBufferIndex equals 3
    '***********************************************
    i2c_interrupt_handler_exit:

    if rxbufferindex = 3 then
    CKP = 0 ' Disable clock
    include "Proc_i2c.inc"
    rxBufferIndex = 0 ' Reset Rx Buffer index for next time we get a command
    TxBufferindex = 0 ' Reset Tx Buffer index for sending two result bytes
    int_ready = 1
    CKP = 1 ' Enable clock
    endif

    if sspov then ' overflow occured?
    i2cTempVAr = SSPBUF ' empty buffer
    sspov = 0
    endif

    SSPIF = 0 ' reset interrupt flag that took us here

    4. The include file "Proc_I2C.inc resolves the vars and their values:

    i2c_OutL = RxBuffer(0)
    i2c_OutH = $ff
    if RxBuffer(0) = "M" then
    i2c_OutL = var1
    i2c_OutH = "M"
    else
    if RxBuffer(0) = "I" then
    i2c_OutL = var2
    i2c_OutH = "I"
    else
    if RxBuffer(0) = "G" then
    i2c_OutL = var3
    i2c_OutH = "G"
    else
    i2c_OutH = $FF 'error unknown command
    endif
    endif
    endif

    TxBuffer(0) = i2c_outh
    TxBuffer(1) = i2c_outL

    5. The masster accesses the slave with I2CWrite and I2CRead, but sometimes the master gets errors and also the slave sets the i2c_eror bit (a variable from me in State_i2cErr).
    - The master always sends three bytes (the "id" and the two databytes).
    - The master always reads two bytes (two values form the "id "in the write operation before)
    Writing to the slave seems to work fine, but reading from the slave results in errors and sometimes senseless values on the master side.

  4. #4


    Did you find this post helpful? Yes | No

    Default

    Hey Ralf,

    Where you able to solve the problem? I'm still working on the slave code, but I'm not able to read from the slave. I'm getting frustrated with this.

    Thanks!

    Daniel.

  5. #5
    Join Date
    Nov 2008
    Posts
    48


    Did you find this post helpful? Yes | No

    Default

    Hi Daniel,
    no, i stopped working on it, it is not possible to get it work... :-(

    I solved my problem using serin/serout.
    But it would be great when we exchange our know-how, so we can solve this together!

    -,Ralf

  6. #6
    Join Date
    May 2008
    Location
    Italy
    Posts
    825


    Did you find this post helpful? Yes | No

    Default

    Here a working snipet for I2C slave communication.

    I2C write command will be received into variable array RxBuffer and RxBuffer[0] is used for the case select routine.
    I2C read command will return content of variable array TxBuffer. So before reading you have to update the array in someways. The easy way is to write a command that will update the array TxBuffer with the values you need than you read them.

    Array lenght can be changed, changing the constants "RxBufferLEN" & "TxBufferLEN"

    Slave address can be changed changing the constant "I2Caddress".

    A 20 MHz quartz is recommended.

    Add your code in the correct place and you should not have problem.


    Code:
    '****************************************************************
    '*  Name    : I2C_Slave                                         *
    '*  Author  :                                                   *
    '*  Notice  :                                                   *
    '*          : All Rights Reserved                               *
    '*  Date    : 03/01/2008                                        *
    '*  Version : 1.0                                               *
    '*  Notes   : I2C Slave with Pic16F876                          *
    '*          :                                                   *
    '****************************************************************
    
    '-- set device & tris port config here (don't use SCL & SDA pins other than I2C) ----
    
    DEFINE I2C_HOLD 1
    
    '---------------- Alias pins -----------------------------------
    
    SCLDIR              var TRISC.3 
    SDADIR              var TRISC.4
    
    '--------------- 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 3           
    RxBuffer            var byte[Rxbufferlen]
    RxBufferIndex       var byte
    
    '------------------ Tx Buffer defintion ----------------------
    TxBufferLEN         con 3
    TxBuffer            var byte[txbufferlen]
    TxBufferIndex       var byte
    
    
    '------------------ Define constants ------------------------
    I2Caddress          CON $2                  ' Make address = 2
    
    
    
    
    ' --------- Define here your variables and alias ------------------
    
    
    
    
    ' -------------- Initialize I2C slave mode ------------------------
    
    SCLDIR = 1                                  ' SCL must be an input before enabling interrupts
    SDADIR = 1   
    SSPADD = I2Caddress                         ' Set our address
    SSPCON2.7 = 0                               ' General call address disabled
    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:
    IF SSPIF = 1 Then
    GoSub i2cslave
    EndIF
    
    SSPOV = 0
    WCOL = 0
    
    Select case RBuffer[0]
    '--------------------- Place here your case select code
    end select
    
    WrData=0  
    GoTo Main
    
    
    ' ----------------- This space for your program code
    
    End
    Enjoy

    Al.
    All progress began with an idea

  7. #7
    Pedro Pinto's Avatar
    Pedro Pinto Guest


    Did you find this post helpful? Yes | No

    Default

    Hello Danielhr77

    I look in this moment for a slave I2C solution for my project

    If i understand right if i need to receive or tramsmit more than one byte, it need to increase this value, like:
    '--- Rx Buffer defintion ----------------------------------------------------
    RxBufferLEN CON 4 'for receive up to 4 bytes
    RxBuffer VAR BYTE[Rxbufferlen]
    RxBufferIndex VAR BYTE
    '
    '--- Tx Buffer defintion ---------------------------------------------------
    TxBufferLEN CON 4 'for send up to 4 bytes
    TxBuffer VAR BYTE[txbufferlen]
    TxBufferIndex VAR BYTE


    'Master send 4 bytes
    I2CWrite SDA, SCL, I2Caddress, [4,10,56,21], bogus ' Write offset to slave
    Pause 500


    On the Slave side, how can i read and show the received 4 bytes?
    Is this correct? Sorry for asking but i have not very experience.
    I want sent back the received 4 bytes

    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
    Case 56
    do anything
    Case 21
    do anything
    End Select
    '
    WrData=0
    GoTo Main
    End

    Thank You
    Best regards
    Pedro

  8. #8
    Join Date
    May 2008
    Location
    Italy
    Posts
    825


    Did you find this post helpful? Yes | No

    Default

    Pedro , to read back the bytes sent just tranfer the content from RxBuffer int TxBuffer in the slave.

    Code:
    Select Case RxBuffer[0]
    TxBuffer[0]=RxBuffer[0]
    TxBuffer[1]=RxBuffer[1]
    TxBuffer[2]=RxBuffer[2]
    TxBuffer[3]=RxBuffer[3]
    Case 4
    High LEDT
    Case 10
    Low LEDT
    Case 56
    do anything
    Case 21
    do anything
    End Select
    From master just use this code:

    Code:
    I2CRead SDA,SCL,I2Caddress,[str YourBuffer\4], bogus
    You will find the four bytes into the array YourBuffer

    Al.
    All progress began with an idea

  9. #9
    Pedro Pinto's Avatar
    Pedro Pinto Guest


    Did you find this post helpful? Yes | No

    Default

    Hello Al

    Thank you

    Best regards
    Pedro

  10. #10
    Pedro Pinto's Avatar
    Pedro Pinto Guest


    Did you find this post helpful? Yes | No

    Default

    Hi Al

    If the master send 4 bytes to the slave and the slave send the received bytes back to the master, must in that moment the master not change to slave mode to receive this 4 bytes?
    If yes, i can't see that in yours and danielhr77 code

    Regards
    Pedro

  11. #11
    Join Date
    May 2008
    Location
    Italy
    Posts
    825


    Did you find this post helpful? Yes | No

    Default

    Pedro, I am not sure to understand your question correctly, but I try to answer

    Master remain master. Slave will not return your 4 bytes without a proper request. When you want the 4 bytes back to the master then you make the request with:
    Code:
    I2CRead SDA,SCL,I2Caddress,[str YourBuffer\4], bogus
    and you will find the four bytes into the array YourBuffer that you have properly declared in your MASTER.

    Al.
    All progress began with an idea

  12. #12
    Join Date
    Jan 2014
    Posts
    2


    Did you find this post helpful? Yes | No

    Default Re: I2C Salve with a PIC

    Excuse me for my English.
    How can I send data from a PIC 16F876 to four slaves
    using the I2C bus protocol, with the same address?
    Could you help me, a piece of code would be welcome!
    mimie64.

  13. #13
    Join Date
    Jun 2009
    Location
    Sc*nthorpe, UK
    Posts
    333


    Did you find this post helpful? Yes | No

    Default Re: I2C Salve with a PIC

    I2C bus protocol requires unique addresses for each slave. Just code the address into each slave if you are building the slaves yourself of course.

  14. #14
    Join Date
    Feb 2007
    Posts
    37


    Did you find this post helpful? Yes | No

    Default Re: I2C Salve with a PIC

    Thanks to all of you for your greath wok!

    ... just to avoid to have trouble in future and have a clear view of a new project, I have in plan to use the slave routine on almost 12 PIC of 18F2550 and one Master with 18F4550.

    The idea is that:
    I shall have the Master far from the first slaves at about two to three meters and the second slave is far from the first slave around 50 cm, and all other slaves are distanced 50 cm between them.

    At the end the total length shall be more than 10 meters. but less that 12 meters.

    Also, I would use the RJ45 connectors to make interconnection between the PCBs and use the preassembled CAT5 Ethernet patch cable to carry the supply voltage from the Master to the slaves.

    I have thinked to put on pin 1, 2 and 3 the VCC, on pin 4 the Clk, on pin 5 the Dat and on pin 6. 7 and 8 the GND?

    The lenght of the cables can be a problem?

    The idea to use the RJ45 with the CAT5 patch cable is good?

    Leo
    Last edited by Wirecut; - 27th March 2016 at 13:19. Reason: typo error
    Leo

  15. #15
    Join Date
    May 2013
    Location
    australia
    Posts
    2,383


    Did you find this post helpful? Yes | No

    Default Re: I2C Slave with a PIC

    WIKI I2C
    The maximum number of nodes is limited by the address space, and also by the total bus capacitance of 400 pF, which restricts practical communication distances to a few meters.
    CAT5 CAPACITANCE
    52pF / metre

    cat5 balanced cable / i2c unbalanced signal.
    cat5 characteristic impedence of 100 ohms is probably a big load for the i2c pins also.
    its not a good match , and won't go far

  16. #16
    Join Date
    Aug 2011
    Posts
    408


    Did you find this post helpful? Yes | No

    Default Re: I2C Slave with a PIC

    A regular CAT5 RJ45 has 4 twisted pairs, but they're not "in order"

    1,2 orange,orange/wht
    3,6 green,green/wht
    4,5 blue,blue/wht
    7,8 brown,brown/wht

    I'd pair the SDA and SCL signals each with a GND (ie 1=GND, 2=SDA, and 7=SCL, 8=GND) and use the others for PWR/GND.

    10 meters is a long way for a single-ended TTL level signal. They make I2C bus extenders for this sort of thing.

  17. #17
    Join Date
    Feb 2007
    Posts
    37


    Did you find this post helpful? Yes | No

    Default Re: I2C Slave with a PIC

    If I have correctly understand, the maximum length from the capacitance point of view is around 8 meters, but what shall be the maximum distance for the single ended TTL?

    Leo
    Leo

  18. #18
    Join Date
    Sep 2009
    Posts
    737


    Did you find this post helpful? Yes | No

    Default Re: I2C Slave with a PIC

    SDA and SCL are not good combination in one pair. Because of cross talk.
    Much better is to combine GND and SDA on ona pair, GND and SCL on second pair. But capacitance play big roll. Probably CLK must be driven, not with pull up. SDA should have strong pull up.
    But do not connect multiple GND on both sides, as that will create long loop antenna, probably...
    I thing, but maybe i'm wrong, best chanse to get it to work is:
    1. Pair GND and signal on 2 pairs for SDA and SCL, connect that ground only on output port of module.
    2. Use third pair for power
    3. Ground unused pair master, as GND for signals.
    Generally not good idea to run TTL signals out of boards.
    If you design master and slave's why not to go with RS485, half duplex or duplex?

  19. #19
    Join Date
    Sep 2005
    Location
    Campbell, CA
    Posts
    1,107


    Did you find this post helpful? Yes | No

    Default Re: I2C Slave with a PIC

    It will work fine - as long as the PICs are the only I2C devices on the bus. The I2C specification was written to deal with low-power devices that have no more than 2 mA drive. As a result, all the pull ups (in parallel) could be no smaller than 2.5K. Since a PIC pin will sink more than 20mA, you can use much smaller pull ups. If there are only two PICs on the bus, I regularly use a 1K pull up at each end, with 10 ohms in series (one at each end) to reduce ringing. Since your CAT-5 will be twisted pairs, use one wire of each pair as GND (and the other as SDA or SCL). You should have no trouble at distance of at least 50'. But if you do get errors, you can slow the I2C bus down to lower data rates.

    Be aware, however that any other non-PIC devices on the bus will probably not be able to drive such small pullups properly.
    Charles Linquist

Similar Threads

  1. Problem with PICto PIC I2C MASTER-SLAVE
    By juanen19 in forum mel PIC BASIC Pro
    Replies: 2
    Last Post: - 11th June 2013, 03:58
  2. PIC as I2C Slave
    By Mainul in forum General
    Replies: 4
    Last Post: - 5th January 2013, 14:23
  3. I2C Slave, config Vref - + in pic with ADC
    By sebapostigo in forum PBP Wish List
    Replies: 4
    Last Post: - 5th March 2007, 04:21
  4. Pic as an i2c slave
    By Robert Soubie in forum mel PIC BASIC Pro
    Replies: 1
    Last Post: - 25th January 2007, 22:11
  5. Use pic as slave in I2C
    By robert0 in forum General
    Replies: 2
    Last Post: - 3rd February 2006, 20:26

Members who have read this thread : 6

You do not have permission to view the list of names.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts