Menu

I2C timing and clock stretching

Help
2017-11-19
2017-11-19
  • Giuseppe D'Elia

    Giuseppe D'Elia - 2017-11-19

    First, I’m new to I2C and USART programming so I apologize if the question is an obvious one.

    I have GCBASIC v0.98.00, PICKIT3 programmer and MPLAB IPE.

    I use an PIC16F886 (Say PIC n.1) receiving data as an I2C Slave and a second PIC (say PIC n.2) transmitting data as an I2C Master.

    PIC n.2, on a button press, send two bytes data on I2C to the address of PIC n.1.

    PIC n.1 receives the data and send them to a terminal (CoolTerm) on a laptop trough a USART RS232 connection.

    I assumed that the data received by PIC n.2 are made by a Start condition, the bits related to the two data byte and a Stop bit.
    I.e., I assume that the stop bit occurs only at the end of the flux of data (two bytes in my test).

    Accordingly, I hold a loop until the stop bit is received, i.e., SSPSTAT.P = 1.

    Within the loop I wait for the condition SSPSTAT.BF = 1 AND SSPIF = 1, i.e., the SSPBUF is full and the interrupt due to the receive condition is set.

    when this condition is satisfied I first clear the interrupt bit SSPIF, then load SSPBUF on an array element (the current one) then I increase the index of the current array element and I set SSPCON.CKP on with the aim of release the clock.

    As a matter of fact I assume that the clock has been previously stretched by I2C hardware.

    For timing purpose I add 3us delay then I return to the wait. This is repeated until the stop bit is received.

    I attach the main part defining the I2C and USART parameters:

    #chip 16F886, 16
    #option explicit
    #config OSC= HS 'HS=Higest gain setting of internal inverter-amplifier
    '___________
    '   I2C
    ' Define I2C settings
    #define HI2C_BAUD_RATE 100
    #define HI2C_DATA PORTC.4
    #define HI2C_CLOCK PORTC.3
    'Set pin I2C directions
    Dir HI2C_DATA in
    Dir HI2C_CLOCK in
    'Now set up the I2C slave
    HI2CMode Slave
    HI2CSetAddress 0xA0 '=esadecimale 'in binario= b'10100000'
    '______________
    '   USART
    #define USART_BAUD_RATE 9600
    #define USART_DELAY 5 ms
    #define USART_BLOCKING
    'Set pin USART directions
    Dir portC.7 IN
    Dir portC.6 OUT
    #define SerInPort PORTc.7
    #define SerOutPort PORTc.6
    

    and here is the subroutine reading the I2c received data

     Dim flag as Byte
        Dim flag1 as Byte
        n_elm=0
    
        SET SSPSTAT.R_NOT_W ON 'Enable receive
    
        PIE1.SSPIE=1 'enable MSSP interrupt
    
       'Clear Collisions
        SET SSPCON1.WCOL OFF
        SET SSPCON1.SSPOV Off
    
       'Wait for receive
    do until SSPSTAT.P = 1  'do until StopBit=1
        Wait Until SSPSTAT.BF = 1 AND SSPIF = 1 'wait until full buffer and interrpt is on
        set PIR1.SSPIF  off
        Var_Array(n_elm) = SSPBUF
        n_elm=n_elm+1
        set SSPCON.CKP on
        wait 3 us
    Loop
         n_elm=n_elm-1
    

    The problem:
    Usually the data are correctly received. However, frequently they are not immediatly received when I press the button on PIC n.2 and at a new button press I receive the previous data, the address slave as a data and the new data.
    It seems like the clock stretching does not work as I assumed.

    Can someone help?

    Thank you

     
  • Giuseppe D'Elia

    Giuseppe D'Elia - 2017-11-19

    Hi Anobium,
    thank you for your quick answer.
    I know that there are libraries on the subject. I also downloaded them and read a little. I also read the AN734 Microchip note wherein the step by step process usefull to accomplish I2C communication is reported and I have seen the flowchart for slave unit of my interest.

    However, my main aim is to deeply understand how the communication work, not only to communicate.
    And then, the satisfaction to implement a routine following my understanding is the core of my aim.
    I'm sorry, this is an hobby not a work.

    In the case of my interest, I suspect that my doubts are essentially related to the clock stetching procedure. Also reading the AN734 note I did not gained a clear picture of the problem.

    As an example, when the step by step procedure is described, at point 7 (page 4) is reported that if SEN=1 slave software sets the CKP bit to release the SCL line.
    Accordingly, it seems that it is possible to control the clock stretching delay.

    On the other side on the PIC16F886 datasheet, the bit SEN of the SSPCON2 register is described in "I2C Master mode only".
    And so, in slave mode I do not understand how SEN can be set.

    Anyway I will study better the available libraries as well as the Master_Demo_16F886 which refers to master mode.

    Many thanks for your attention

     
    • Anobium

      Anobium - 2017-11-19

      Study the demos - it would take some time to deeply understand the APPNOTE. The Master and Slave IS based on the APPNOTE but with the context of making 16f and 18f work.

      I cannot remember the specific of clock stretching but look at State 06 in the ISR Handler - there is a lot of discussion on this. But, these libraries are tested and I know that folks have created working solutions - the simple code in State 06 in the ISR should give you the insight.

       
  • kent_twt4

    kent_twt4 - 2017-11-19

    I went thru a similar journey as this with the AN976. So from a long time back, here are the subs I used for the reading the PIC slave. This assumed a single Master, single Slave relationship, and therefore would probably not be as robust as the GCB library.

    If anything useful can be gleaned, then you are welcome. I think I used a 16f877a as Master, and 16f88 as Slave for my tests at the time. Also the CKE bit is set during initialization.

    '###############PIC SLAVE###############
    sub StartSlave
    SET SEN ON     'set start condition
      waitSlaveSEN:
      If SEN ON Then goto waitSlaveSEN 
    end sub
    
    sub TxI2CSlave(SendByte)
    SSPBUF = SendByte
    busidle
      slaveACK:
      If ACKSTAT ON Then goto SlaveACK  'Check if slave acknowleged
    'Print("tx")
    end sub
    
    sub RxI2CSlave
    Set RCEN ON
      slaveRCEN:
      If RCEN ON Then goto slaveRCEN
    ReadByte = SSPBUF
    end sub
    
    sub busidle
    notidle:
    If SSPCON2 AND b'00000000' NOT 1 Then goto notidle
    waitbusidle:
    If R_W On Then goto waitbusidle
    end sub
    
    sub MasterRcvNACK  
      Set ACKDT On  'Select master not acknowledge
      Set ACKEN On  
     waitMRcvNAck:
      If ACKEN ON Then goto waitMRcvNAck  'Check if operation complete
    end sub
    
    sub StopSlave
      SET PEN ON  'Set Stop condition
     waitSlaveStop:
      IF PEN ON Then goto waitSlaveStop  'Check if operation complete
    end sub
    
    sub WritePicSlave(writeID,I2CData) #NR  
      Start
      TxI2C(writeID)  'Device ID write byte
      TxI2CSerial(I2CData)  'Send out data byte to slave
      MasterNACK 
      Stop
    end sub 
    
    Function ReadPicSlave(readID)
      StartSlave
      TxI2CSlave(readID)  'Device ID read byte
      RxI2CSlave
      MasterRcvNACK
      StopSlave
      ReadPicSlave = ReadByte
    end function
    
     
  • Giuseppe D'Elia

    Giuseppe D'Elia - 2017-11-19

    I used SSPCON2.SEN and SSPCON.CKP to attempt controlling the clock stretching. It is shown in the following as Bold Characters

    Sub MyHI2CReceive ( n_elm )
    
        Dim flag as Byte
        Dim flag1 as Byte
        n_elm=0
    
        SET SSPSTAT.R_NOT_W ON 'Enable receive
    
        PIE1.SSPIE=1 'enable MSSP interrupt
    
       'Clear Collisions
        SET SSPCON1.WCOL OFF
        SET SSPCON1.SSPOV Off
    
       'Wait for receive
    do until SSPSTAT.P = 1  'do until StopBit=1
        Wait Until SSPSTAT.BF = 1 AND SSPIF = 1 'wait until full buffer and interrpt is on
        **set SSPCON2.SEN=0
        set SSPCON.CKP=0**
        set PIR1.SSPIF  off
        Var_Array(n_elm) = SSPBUF
        n_elm=n_elm+1
       ** set SSPCON2.SEN=1
        set SSPCON.CKP on**
        wait 3 us
    Loop
         n_elm=n_elm-1
    
    End Sub
    

    As I said in my first post, PIC n.2, the trasmitting one, trasmit upon pressing a button.

    Now data are read regularly when the button is regularly pressed.

    On the contrary, when the button is continuosly pressed the reception fails and PIC n.1, the receiving one, freeze.
    There are collisions, I suppose.

    My attempts to manage collisions by using SSPCON1.WCOL and SSPCON1.SSPOV have been unsuccessfull till now.
    On the other side this also means that still I'm not able to fully control the clock stretching on the receiver.
    Thank you all

     

Log in to post a comment.

Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.