AnsweredHot!I2C fault recovery with MCC PIC24F

Page: 12 > Showing page 1 of 2
Author
paulnotpaul
Starting Member
  • Total Posts : 6
  • Reward points : 0
  • Joined: 2017/01/25 07:53:34
  • Location: 0
  • Status: offline
2017/02/27 20:56:45 (permalink)
0

I2C fault recovery with MCC PIC24F

Hello!
 
I have a PIC24FV16KM204 I2C master that communicates to an I2C slave via wires that go through a slip ring. The I2C is a lidar sensor that sits on a head and rotates on a shaft.
 
This system works for a while if I run the head under 200 RPMs. If i go faster there is a spike in the I2C lines causes the I2C communications to fail. If I hard reset the PIC24 and everything works fine again. 
 
I know that it is a bad choice to have the i2c go through a noisy slip ring, but I'm trying to see if I can recover from events like this that will happen occasionally. 
 
I am using the MCC autogenerated I2C functions . Here's a shot of of a failure event:
[See attached 1 - DS1Z_QuickPrint3.png]
 
I think what is going on is that the device is holding the SDA signal low after an EMF spike. 
[See attached 2 - DS1Z_QuickPrint7.png]
 
Above is a shot of it zoomed out. I detect this failure in my code, and I power cycle the slave device (I have access to a reset line, and the SDA line going high at the white cursor line is after a slave reset) and run MSSP2_Init(). This does not fix the communications, and on the scope i never see anything getting toggled on the SCL and SDA. 
 
Immediately after the failure, and before I reset the slave device, and run MSSP_Init(), I notice that the MSSP software is stuck with a full trb queue. 
// check if there is space in the queue
if (mssp2_i2c_object.trStatus.s.full != true)
This check fails and the queue never gets empty. So, after I detect this I power cycle the slave device, and run MSSP_Init() and try to communicate. The power cycle makes the slave let go of the SDA line so that's good, but no further communications happens. When i look at the debugger, I see that the messages are always pending.
 
When I hit the reset button on the PIC everything works. 
 
Any ideas on how to get the system to recover from this? Thanks! 
 
I'm also looking to redesign this whole thing by having a chip that relays the I2C data to CAN, so can is what is running through the slip ring and not I2C. 
 
-Paul
 
 
post edited by paulnotpaul - 2017/02/27 21:01:28

Attached Image(s)

#1
qhb
Superb Member
  • Total Posts : 8712
  • Reward points : 0
  • Joined: 2016/06/05 14:55:32
  • Location: One step ahead...
  • Status: online
Re: I2C fault recovery with MCC PIC24F 2017/02/27 21:03:04 (permalink) ☄ Helpfulby paulnotpaul 2017/03/02 07:54:51
3 (3)
To unlock the I2C bus, do this.
Disable the I2C stack.
Let the SDA pin float high, and manually toggle the SCL pin low then high nine times, at 100kHz or slower.
Then do this:
> 5us delay, SDA low, > 5us delay, SCL low (this is a START)
> 5us delay, SCL high, > 5us delay, SDA high (this is a STOP)
The bus is now idle. You can now reinitialise the MCC I2C driver.
 
#2
paulnotpaul
Starting Member
  • Total Posts : 6
  • Reward points : 0
  • Joined: 2017/01/25 07:53:34
  • Location: 0
  • Status: offline
Re: I2C fault recovery with MCC PIC24F 2017/03/02 08:19:58 (permalink)
0
qhb
To unlock the I2C bus, do this.
Disable the I2C stack.
Let the SDA pin float high, and manually toggle the SCL pin low then high nine times, at 100kHz or slower.
Then do this:
> 5us delay, SDA low, > 5us delay, SCL low (this is a START)
> 5us delay, SCL high, > 5us delay, SDA high (this is a STOP)
The bus is now idle. You can now reinitialise the MCC I2C driver.



Thanks for the tips! 
 
Ok so I tried implementing  your solution but I have a problem. I can't for the life of me seem to be able to take control of the SCL and SDA pins after disabling the I2C peripheral. 
 
Here is what I have so far. I have a function that gets called whenever an I2C error happens. In this function I do the following:
  •  disable SSPEN,
  • set the TRIS bits for the SDA, SCL pins 
  • try toggling trough LAT
For whatever reason, these lines don't get toggled after the SSPEN disable. Here is my code:
 
//For i2c recovery
#define SCL_TRIS TRISBbits.TRISB3
#define SDA_TRIS TRISBbits.TRISB2
#define SCL LATBbits.LATB3
#define SDA LATBbits.LATB2

void LTP_recover(void)
{
     DP1 = 1; // This is a debug pin output connect to purple channel of scope
     SSP2CON1 &= 0xFFEF; //disable SSPEN
      //__delay_ms(10);
     SCL_TRIS = 0; //output
     SDA_TRIS = 1; //input
     
 
     // toggle the SCL pin 9 times
     int i;
     for(i = 0; i < 9; i++)     {
         SCL = 0;
         DP1=0;
         __delay_us(10);
         SCL = 1;
         DP1=1;
         __delay_us(10);
     }
     
     //START CONDITION
     SDA_TRIS = 0; //output
     
     __delay_us(5);
     SDA = 0;
     __delay_us(5);
     SCL = 0;
     __delay_us(5);
     
     //STOP CONDITION
     SCL = 1;
     __delay_us(5);
     SDA = 1;
     __delay_us(5);
     
     //Renable the I2C Stack
     SCL_TRIS = 1;
     SDA_TRIS = 1;
     //enable the SSPEN bit
     SSP2CON1 |= 0x0010;
     MSSP2_I2C_Initialize();
     DP1 = 0;
}

 
For whatever reason the pins aren't getting toggled! See the attached scope shot. The purple channel is the debug pin output to make sure this function is getting called. I don't think it is a hardware issue because I made an empty project where I just toggle the SDA and SCL pins with the same hardware, and they get toggled fine.
 
Is there something else I have to do to take control of the SDL SCL pins after disabling SSPEN?
 
Thank you!! 
post edited by paulnotpaul - 2017/03/02 08:21:38

Attached Image(s)

#3
CinziaG
mind power
  • Total Posts : 3144
  • Reward points : 0
  • Joined: 2016/12/07 14:20:36
  • Location: Wien
  • Status: offline
Re: I2C fault recovery with MCC PIC24F 2017/03/02 12:25:22 (permalink) ☄ Helpfulby paulnotpaul 2017/03/02 13:47:59
3 (2)
Ok,
try putting SSP2CON1 register and the others related to power-on values, usually 0x0000 ...

in 2018 you signed for your annihilation. in 2019 it will come ;) I promise
#4
NKurzman
A Guy on the Net
  • Total Posts : 16886
  • Reward points : 0
  • Joined: 2008/01/16 19:33:48
  • Location: 0
  • Status: online
Re: I2C fault recovery with MCC PIC24F 2017/03/02 12:44:41 (permalink) ☄ Helpfulby paulnotpaul 2017/03/02 13:47:58
3 (2)
  • SSP2CON1 &= 0xFFEF; //disable SSPEN Why and
  • SSP2CON1 just disable the enable bit.
  • set the TRIS bits for the SDA, SCL pins  to Inputs
  • Set the Lat for the SDA, SCL pins  to 0
  • try toggling through TRIS 
#5
Mysil
Super Member
  • Total Posts : 3082
  • Reward points : 0
  • Joined: 2012/07/01 04:19:50
  • Location: Norway
  • Status: offline
Re: I2C fault recovery with MCC PIC24F 2017/03/02 13:20:36 (permalink) ☄ Helpfulby paulnotpaul 2017/03/02 13:47:53
4 (4)
Hi,
SSP2CON1 &= 0xFFEF;   This statement clear the Wrong bit.
 
SSPEN is bit 5, when counting from 0, not bit 4.  Mask would have to be 0xFFDF; to hit just the SSPEN bit.
Setting and Clearing is more reliably done using named structures or constants defined in the Device support file:
SSP2CON1bits.SSPEN = 0;
and
SSP2CON1bits.SSPEN = 1;
 
Regards,
   Mysil
#6
paulnotpaul
Starting Member
  • Total Posts : 6
  • Reward points : 0
  • Joined: 2017/01/25 07:53:34
  • Location: 0
  • Status: offline
Re: I2C fault recovery with MCC PIC24F 2017/03/02 13:54:50 (permalink)
5 (1)
You guys and girls are the best!! 
 
Mysil, I will never set bits with bitflags like that again when I can use their defined names. 
 
Ok, so my recovery routine successfully fixes the I2C bus and brings it back to an idle state. See attached.
 
Here's the new problem. 
 
After the error is detected and I try to fix it and run MSSP2_Initialize(), and reset the slave the device, the MSSP2 engine that runs on an interrupt seems to be broken. Running it in debug mode I realized that the interrupt function that does the actually bit pushing through the wires never gets called after my recovery function runs.
 
Is there some interrupt flag that needs clearing? Here's the autogenerated MCC interrupt block for i2c.  
 
 
void __attribute__ ( ( interrupt, no_auto_psv ) ) _MSSP2Interrupt ( void )
{
  
    static uint8_t *pi2c_buf_ptr;
    static uint16_t i2c_address = 0;
    static uint8_t i2c_bytes_left = 0;
    static uint8_t i2c_10bit_address_restart = 0;

    IFS3bits.SSP2IF = 0;

    // Check first if there was a collision.
    // If we have a Write Collision, reset and go to idle state */
    if(MSSP2_I2C_WRITE_COLLISION_STATUS_BIT)
    {
        // clear the Write collision
        MSSP2_I2C_WRITE_COLLISION_STATUS_BIT = 0;
        mssp2_i2c_state = S_MASTER_IDLE;
        *(p_mssp2_i2c_current->pTrFlag) = MSSP2_I2C_MESSAGE_FAIL;

        // reset the buffer pointer
        p_mssp2_i2c_current = NULL;

        return;
    }

    /* Handle the correct i2c state */
    switch(mssp2_i2c_state)
    {
        case S_MASTER_IDLE: /* In reset state, waiting for data to send */

            if(mssp2_i2c_object.trStatus.s.empty != true)
            {
                // grab the item pointed by the head
                p_mssp2_i2c_current = mssp2_i2c_object.pTrHead;
                mssp2_i2c_trb_count = mssp2_i2c_object.pTrHead->count;
                p_mssp2_i2c_trb_current = mssp2_i2c_object.pTrHead->ptrb_list;

                mssp2_i2c_object.pTrHead++;

                // check if the end of the array is reached
                if(mssp2_i2c_object.pTrHead == (mssp2_i2c_tr_queue + MSSP2_I2C_CONFIG_TR_QUEUE_LENGTH))
                {
                    // adjust to restart at the beginning of the array
                    mssp2_i2c_object.pTrHead = mssp2_i2c_tr_queue;
                }

                // since we moved one item to be processed, we know
                // it is not full, so set the full status to false
                mssp2_i2c_object.trStatus.s.full = false;

                // check if the queue is empty
                if(mssp2_i2c_object.pTrHead == mssp2_i2c_object.pTrTail)
                {
                    // it is empty so set the empty status to true
                    mssp2_i2c_object.trStatus.s.empty = true;
                }

                // send the start condition
                MSSP2_I2C_START_CONDITION_ENABLE_BIT = 1;
                
                // start the i2c request
                mssp2_i2c_state = S_MASTER_SEND_ADDR;
            }

            break;

        case S_MASTER_RESTART:

            /* check for pending i2c Request */

            // ... trigger a REPEATED START
            MSSP2_I2C_REPEAT_START_CONDITION_ENABLE_BIT = 1;

            // start the i2c request
            mssp2_i2c_state = S_MASTER_SEND_ADDR;

            break;

        case S_MASTER_SEND_ADDR_10BIT_LSB:

            if(MSSP2_I2C_ACKNOWLEDGE_STATUS_BIT)
            {
                mssp2_i2c_object.i2cErrors++;
                MSSP2_I2C_Stop(MSSP2_I2C_MESSAGE_ADDRESS_NO_ACK);
            }
            else
            {
                // Remove bit 0 as R/W is never sent here
                MSSP2_I2C_TRANSMIT_REG = (i2c_address >> 1) & 0x00FF;

                // determine the next state, check R/W
                if(i2c_address & 0x01)
                {
                    // if this is a read we must repeat start
                    // the bus to perform a read
                    mssp2_i2c_state = S_MASTER_10BIT_RESTART;
                }
                else
                {
                    // this is a write continue writing data
                    mssp2_i2c_state = S_MASTER_SEND_DATA;
                }
            }

            break;

        case S_MASTER_10BIT_RESTART:

            if(MSSP2_I2C_ACKNOWLEDGE_STATUS_BIT)
            {
                mssp2_i2c_object.i2cErrors++;
                MSSP2_I2C_Stop(MSSP2_I2C_MESSAGE_ADDRESS_NO_ACK);
            }
            else
            {
                // ACK Status is good
                // restart the bus
                MSSP2_I2C_REPEAT_START_CONDITION_ENABLE_BIT = 1;

                // fudge the address so S_MASTER_SEND_ADDR works correctly
                // we only do this on a 10-bit address resend
                i2c_address = 0x00F0 | ((i2c_address >> 8) & 0x0006);

                // set the R/W flag
                i2c_address |= 0x0001;

                // set the address restart flag so we do not change the address
                i2c_10bit_address_restart = 1;

                // Resend the address as a read
                mssp2_i2c_state = S_MASTER_SEND_ADDR;
            }

            break;

        case S_MASTER_SEND_ADDR:

            /* Start has been sent, send the address byte */

            /* Note:
                On a 10-bit address resend (done only during a 10-bit
                device read), the original i2c_address was modified in
                S_MASTER_10BIT_RESTART state. So the check if this is
                a 10-bit address will fail and a normal 7-bit address
                is sent with the R/W bit set to read. The flag
                i2c_10bit_address_restart prevents the address to
                be re-written.
             */
            if(i2c_10bit_address_restart != 1)
            {
                // extract the information for this message
                i2c_address = p_mssp2_i2c_trb_current->address;
                pi2c_buf_ptr = p_mssp2_i2c_trb_current->pbuffer;
                i2c_bytes_left = p_mssp2_i2c_trb_current->length;
            }
            else
            {
                // reset the flag so the next access is ok
                i2c_10bit_address_restart = 0;
            }

            // check for 10-bit address
            if(i2c_address > 0x00FF)
            {
                // we have a 10 bit address
                // send bits<9:8>
                // mask bit 0 as this is always a write
                MSSP2_I2C_TRANSMIT_REG = 0xF0 | ((i2c_address >> 8) & 0x0006);
                mssp2_i2c_state = S_MASTER_SEND_ADDR_10BIT_LSB;
            }
            else
            {
                // Transmit the address
                MSSP2_I2C_TRANSMIT_REG = i2c_address;
                if(i2c_address & 0x01)
                {
                    // Next state is to wait for address to be acked
                    mssp2_i2c_state = S_MASTER_ACK_ADDR;
                }
                else
                {
                    // Next state is transmit
                    mssp2_i2c_state = S_MASTER_SEND_DATA;
                }
            }
            break;

        case S_MASTER_SEND_DATA:

            // Make sure the previous byte was acknowledged
            if(MSSP2_I2C_ACKNOWLEDGE_STATUS_BIT)
            {
                // Transmission was not acknowledged
                mssp2_i2c_object.i2cErrors++;

                // Reset the Ack flag
                MSSP2_I2C_ACKNOWLEDGE_STATUS_BIT = 0;

                // Send a stop flag and go back to idle
                MSSP2_I2C_Stop(MSSP2_I2C_DATA_NO_ACK);

            }
            else
            {
                // Did we send them all ?
                if(i2c_bytes_left-- == 0U)
                {
                    // yup sent them all!

                    // update the trb pointer
                    p_mssp2_i2c_trb_current++;

                    // are we done with this string of requests?
                    if(--mssp2_i2c_trb_count == 0)
                    {
                        MSSP2_I2C_Stop(MSSP2_I2C_MESSAGE_COMPLETE);
                    }
                    else
                    {
                        // no!, there are more TRB to be sent.
                        //MSSP2_I2C_START_CONDITION_ENABLE_BIT = 1;

                        // In some cases, the slave may require
                        // a restart instead of a start. So use this one
                        // instead.
                        MSSP2_I2C_REPEAT_START_CONDITION_ENABLE_BIT = 1;

                        // start the i2c request
                        mssp2_i2c_state = S_MASTER_SEND_ADDR;

                    }
                }
                else
                {
                    // Grab the next data to transmit
                    MSSP2_I2C_TRANSMIT_REG = *pi2c_buf_ptr++;
                }
            }
            break;

        case S_MASTER_ACK_ADDR:

            /* Make sure the previous byte was acknowledged */
            if(MSSP2_I2C_ACKNOWLEDGE_STATUS_BIT)
            {

                // Transmission was not acknowledged
                mssp2_i2c_object.i2cErrors++;

                // Send a stop flag and go back to idle
                MSSP2_I2C_Stop(MSSP2_I2C_MESSAGE_ADDRESS_NO_ACK);

                // Reset the Ack flag
                MSSP2_I2C_ACKNOWLEDGE_STATUS_BIT = 0;
            }
            else
            {
                MSSP2_I2C_RECEIVE_ENABLE_BIT = 1;
                mssp2_i2c_state = S_MASTER_ACK_RCV_DATA;
            }
            break;

        case S_MASTER_RCV_DATA:

            /* Acknowledge is completed. Time for more data */

            // Next thing is to ack the data
            mssp2_i2c_state = S_MASTER_ACK_RCV_DATA;

            // Set up to receive a byte of data
            MSSP2_I2C_RECEIVE_ENABLE_BIT = 1;

            break;

        case S_MASTER_ACK_RCV_DATA:

            // Grab the byte of data received and acknowledge it
            *pi2c_buf_ptr++ = MSSP2_I2C_RECEIVE_REG;

            // Check if we received them all?
            if(--i2c_bytes_left)
            {

                /* No, there's more to receive */

                // No, bit 7 is clear. Data is ok
                // Set the flag to acknowledge the data
                MSSP2_I2C_ACKNOWLEDGE_DATA_BIT = 0;

                // Wait for the acknowledge to complete, then get more
                mssp2_i2c_state = S_MASTER_RCV_DATA;
            }
            else
            {

                // Yes, it's the last byte. Don't ack it
                // Flag that we will nak the data
                MSSP2_I2C_ACKNOWLEDGE_DATA_BIT = 1;

                MSSP2_I2C_FunctionComplete();
            }

            // Initiate the acknowledge
            MSSP2_I2C_ACKNOWLEDGE_ENABLE_BIT = 1;
            break;

        case S_MASTER_RCV_STOP:
        case S_MASTER_SEND_STOP:

            // Send the stop flag
            MSSP2_I2C_Stop(MSSP2_I2C_MESSAGE_COMPLETE);
            break;

        default:

            // This case should not happen, if it does then
            // terminate the transfer
            mssp2_i2c_object.i2cErrors++;
            MSSP2_I2C_Stop(MSSP2_I2C_LOST_STATE);
            break;

    }
}

 
Attached is a scope trace showing no i2c comms attempts after a fault. 
post edited by paulnotpaul - 2017/03/02 14:12:05

Attached Image(s)

#7
Mysil
Super Member
  • Total Posts : 3082
  • Reward points : 0
  • Joined: 2012/07/01 04:19:50
  • Location: Norway
  • Status: offline
Re: I2C fault recovery with MCC PIC24F 2017/03/02 16:27:48 (permalink) ☼ Best Answerby paulnotpaul 2017/03/03 17:37:47
3.67 (3)
Hi,
There might be 2 different ways to get this going again.
 
You may try to set the interrupt flag to tickle the interrupt handler to continue where it got stuck:
    IFS3bits.SSP2IF = 1; 
 
That require you only disable the SSPEN bit, perform the recovery sequence, and reenable SSPEN,
without calling  I2C2_Initialize();
 
The other possibility is to fix I2C2_Initialize() function, such that is reset everything that need to be initialized correctly. The I2C driver work from a queue of transfer requests, and as far as I remember, the initialize function do not clear each queue entry slot, it just set the start and end pointers. 
Also, the initialize function is initially performed with Global interrupts disabled. 
When you call I2C2_Initialize(); in the running program, Global interrupts will be enabled, unless you do something about that also. 
Then, inside I2C2_Initialize(); the first thing MCC do, is to activate SSPEN before doing other settings.
 
In my opinion, the procedure should rather be:
Disable SSP interrupts:   IEC3bits.SSP2IE = 0;
Disable BCL interrupts:   IEC3bits.BCL2IE = 0;
Clear I2C control register:  SSP2CON1 = 0; 
Clear the SSP driver State variable: i2c2_state = S_MASTER_IDLE;
Clear other static variables keeping state:   p_i2c2_current = NULL;
                                                     p_i2c2_trb_current = NULL;
                                                     i2c2_trb_count = 0;
Clear all entries in transfer queue:    i2c2_tr_queue[...]
Initialize the I2C2_object head and tail pointers...   set .empty and clear .full
Initialize the hardware as it is done, but SSP2CON1 register with SSPEN bit should be the last one.
Clear  interrupt flags:            IFS3bits.SSP2IF = 0;
                                           IFS3bits.BCL2IF = 0;
Enable interrupts:                 IEC3bits.SSP2IE = 1;
                                           IEC3bits.BCL2IE = 1;
There also are some static variables inside the interrupt handler,
I have not studied the details if any of those have to be cleared also.
 
Regards,
   Mysil
 
#8
paulnotpaul
Starting Member
  • Total Posts : 6
  • Reward points : 0
  • Joined: 2017/01/25 07:53:34
  • Location: 0
  • Status: offline
Re: I2C fault recovery with MCC PIC24F 2017/03/03 17:37:35 (permalink)
4.75 (4)
You are all awesome, thanks so much for your help. 
 
Mysil, following your advice I was able to fix it! I now have an I2C bus that is tolerant to me unplugging the wire and it recovers in under 10ms! 
 
When I just tried to tickle the interrupt flag to get the I2C isr going I kept getting TRAPS address errors. This is because there are a lot of pointers used by the MSSP I2C state machine that the MCC generates and I had to reset each one right. Also, as you hinted at Mysil, the timing of when to enable the I2C peripheral was crucial.
 
In the end I was able to just modify the MSSP2_Initialize() function and added some resets that actually reset the system properly. Added below is the new MSSP2_Initialize() function. 
 
 
void MSSP2_I2C_Initialize(void)
{
    //Disable the interrupts
    IEC3bits.SSP2IE = 0;
    //
    mssp2_i2c_state = S_MASTER_IDLE;
    mssp2_i2c_trb_count = 0;
    p_mssp2_i2c_trb_current = NULL;
    p_mssp2_i2c_current = NULL;

    mssp2_i2c_object.pTrHead = mssp2_i2c_tr_queue;
    mssp2_i2c_object.pTrTail = mssp2_i2c_tr_queue;
    mssp2_i2c_object.trStatus.s.empty = true;
    mssp2_i2c_object.trStatus.s.full = false;

    mssp2_i2c_object.i2cErrors = 0;

    // SMP High Speed; CKE Idle to Active;
    SSP2STAT = 0x0000;

    // SBCDE disabled; BOEN disabled; SCIE disabled; PCIE disabled; DHEN disabled; SDAHT 100ns; AHEN disabled;
    SSP2CON3 = 0x0000;
    // Baud Rate Generator Value: SSPADD 25;
    // Calculated Frequency: 307692.30769230769230769230769230769230769230769230769230769230769230769230769230769230769230769230769230769230769230769230769230769231
    SSP2ADD = 0x0019;

    // SSPEN enabled; WCOL no_collision; CKP Clock Stretch; SSPM FOSC/(2 * (BRG_Value_I2C + 1)); SSPOV no_overflow;
    SSP2CON1 = 0x0028;

    // enable the master interrupt
    IEC3bits.SSP2IE = 1;

    // clear the master interrupt flag
    IFS3bits.SSP2IF = 0;
    IFS3bits.BCL2IF = 0;

}

 
Thanks so much everyone!!!!!

Attached Image(s)

#9
tread
New Member
  • Total Posts : 15
  • Reward points : 0
  • Joined: 2017/06/08 11:10:12
  • Location: 0
  • Status: offline
Re: I2C fault recovery with MCC PIC24F 2017/07/13 07:55:43 (permalink)
4 (1)
paulnotpaul
In the end I was able to just modify the MSSP2_Initialize() function and added some resets that actually reset the system properly. Added below is the new MSSP2_Initialize() function. 
 
 [code]void MSSP2_I2C_Initialize(void)
{
    mssp2_i2c_state = S_MASTER_IDLE;
    mssp2_i2c_trb_count = 0;
    p_mssp2_i2c_trb_current = NULL;
    p_mssp2_i2c_current = NULL;



*update: I edited my post after realizing how confusing the grammar was. 
 
Thanks guys for the help with this one, I was beating my head against the desk for a while not understanding why clearing the SCL line did not fix the issue. In the end, the fix for myself was a combination of: 
 
1) Disable I2CEN 
2) Toggle SCL until SDA goes High
3) Clear the state of the bus from the initialize function as shown from @paulnotpaul.
  • BTW I did not need to clear the interrupt flags as the initialize function from MCC clears the interrupt flag by default. Additionally I did not need to disable the interrupts. However I did have to set the i2c enable bit to 0 before calling the i2c initialize() function. The i2c Intialize() function then sets the i2c enable bit to 1 by default.
  • Tthe output latch on the SCL line did not adhere to control until I disabled the module and of course SCL tris bit to output. After the recovery is complete you will want to reset the ddr setting.
4) Call Initialize() function with added values above from @paulnotpaul. 
 
Clearing the buffer and resetting the states was key to my success. After further looking at the code provided by MCC, I noticed that it keeps looping infinitely because it would mark the buffer as full but not send anything out after re-syncing the clocks. This behavior happens probably because it is waiting for an acknowledgment from the slave device.
 
If anyone can think of a way to find another solution that does not require modifying the i2c driver API, I would greatly appreciate it. In my current project my goal is to prohibit changes to the auto generated MCC API drivers(outside of the MCC application) to make the program code more reusable and stable. 
 
 
post edited by tread - 2017/08/23 09:48:07
#10
MisterHemi
Starting Member
  • Total Posts : 83
  • Reward points : 0
  • Joined: 2017/11/02 12:24:21
  • Location: 0
  • Status: online
Re: I2C fault recovery with MCC PIC24F 2019/02/13 10:27:02 (permalink)
0
qhb
To unlock the I2C bus, do this.
Disable the I2C stack.
Let the SDA pin float high, and manually toggle the SCL pin low then high nine times, at 100kHz or slower.
Then do this:
> 5us delay, SDA low, > 5us delay, SCL low (this is a START)
> 5us delay, SCL high, > 5us delay, SDA high (this is a STOP)
The bus is now idle. You can now reinitialise the MCC I2C driver.
 




Questions.... I know we set SCL and SDA as inputs.
 
1) Do we toggle the SCL pin using TRIS or LAT? 
 
2) Also do we toggle the pins using TRIS or LAT when generating a START and STOP?

My configuration:
MacBook Pro (Retina, 15-inch, Mid 2015) with MacOS High Sierra (10.13.3) and MPLAB X IDE v4.15
 
Curiosity PIC MZ EF, PIC24F Curiosity, and XPRESS EVAL BOARD (PIC16F18855).
#11
tread
New Member
  • Total Posts : 15
  • Reward points : 0
  • Joined: 2017/06/08 11:10:12
  • Location: 0
  • Status: offline
Re: I2C fault recovery with MCC PIC24F 2019/02/13 10:42:53 (permalink)
0
MisterHemi
qhb
To unlock the I2C bus, do this.
Disable the I2C stack.
Let the SDA pin float high, and manually toggle the SCL pin low then high nine times, at 100kHz or slower.
Then do this:
> 5us delay, SDA low, > 5us delay, SCL low (this is a START)
> 5us delay, SCL high, > 5us delay, SDA high (this is a STOP)
The bus is now idle. You can now reinitialise the MCC I2C driver.
 




Questions.... I know we set SCL and SDA as inputs.
 
1) Do we toggle the SCL pin using TRIS or LAT? 
 
2) Also do we toggle the pins using TRIS or LAT when generating a START and STOP?




 
1) Lat controls pin high/low (ie voltage = 0, voltage = 3.3/5v ), TRIS controls input or output direction of ping (Read or Write pin)
2) It would LAT pin per answer 1. 
 
#12
MisterHemi
Starting Member
  • Total Posts : 83
  • Reward points : 0
  • Joined: 2017/11/02 12:24:21
  • Location: 0
  • Status: online
Re: I2C fault recovery with MCC PIC24F 2019/02/13 10:53:34 (permalink)
0
tread
MisterHemi
qhb
To unlock the I2C bus, do this.
Disable the I2C stack.
Let the SDA pin float high, and manually toggle the SCL pin low then high nine times, at 100kHz or slower.
Then do this:
> 5us delay, SDA low, > 5us delay, SCL low (this is a START)
> 5us delay, SCL high, > 5us delay, SDA high (this is a STOP)
The bus is now idle. You can now reinitialise the MCC I2C driver.
 




Questions.... I know we set SCL and SDA as inputs.
 
1) Do we toggle the SCL pin using TRIS or LAT? 
 
2) Also do we toggle the pins using TRIS or LAT when generating a START and STOP?




 
1) Lat controls pin high/low (ie voltage = 0, voltage = 3.3/5v ), TRIS controls input or output direction of ping (Read or Write pin)
2) It would LAT pin per answer 1. 
 




Thanks, I was wondering because i've seen some post on the internet where people said to toggle TRIS.

My configuration:
MacBook Pro (Retina, 15-inch, Mid 2015) with MacOS High Sierra (10.13.3) and MPLAB X IDE v4.15
 
Curiosity PIC MZ EF, PIC24F Curiosity, and XPRESS EVAL BOARD (PIC16F18855).
#13
qhb
Superb Member
  • Total Posts : 8712
  • Reward points : 0
  • Joined: 2016/06/05 14:55:32
  • Location: One step ahead...
  • Status: online
Re: I2C fault recovery with MCC PIC24F 2019/02/13 12:34:39 (permalink) ☄ Helpfulby Jerry Messina 2019/02/14 07:03:56
3.75 (4)
I don't think "tread" is used to simulating an open collector signal.
The correct answer is to set the pin's LAT bit to zero, and toggle the TRIS bit to drive the signal low or let it float high. You should never drive an I2C signal high, because something else might be driving it low.
 
#14
Mysil
Super Member
  • Total Posts : 3082
  • Reward points : 0
  • Joined: 2012/07/01 04:19:50
  • Location: Norway
  • Status: offline
Re: I2C fault recovery with MCC PIC24F 2019/02/13 12:40:44 (permalink)
3.75 (4)
Hi,
Driving a I2C signal line High, will break the Open Drain design of I2C hardware signaling specification.
While this will usually Not cause hardware damage, it is the wrong thing to do. It may cause a short circuit contention on the signal line, especially if there is more than one Master device connected to the bus.
 
So, in my opinion, answer in message #12 is Wrong.
Clearing LATx  register bit for the line, and then toggling TRISx bit,
will have the effect of correctly pulling the output Low when TRISx bit is 0, 
and letting I2C pull-up resistor do the work when TRISx bit is 1.
 
Some PIC devices have separate Open Drain Control registers: ODCx registers,
that may be used to achieve the same effect.
 
    Mysil
#15
tread
New Member
  • Total Posts : 15
  • Reward points : 0
  • Joined: 2017/06/08 11:10:12
  • Location: 0
  • Status: offline
Re: I2C fault recovery with MCC PIC24F 2019/02/14 05:34:51 (permalink)
1 (1)
Here are multiple links that suggest toggling the clock line if stuck in a stuck i2c error state. None of these implementations suggest setting the directional TRIS pins or manufacturer alternative. If you do a quick "Google" search, it is the most common solution. I don't think "qhb" has worked on many sensors or peripheral devices in the last 20 years. 
https://www.i2c-bus.org/i2c-primer/analysing-obscure-problems/blocked-bus/
https://community.nxp.com/thread/394582
 
#16
qhb
Superb Member
  • Total Posts : 8712
  • Reward points : 0
  • Joined: 2016/06/05 14:55:32
  • Location: One step ahead...
  • Status: online
Re: I2C fault recovery with MCC PIC24F 2019/02/14 05:44:58 (permalink)
3 (2)
I think there are many designers who are happy to violate datasheet specifications when things seem to work regardless.
(and I've been working with them for more than 20 years ;) )
 
#17
tread
New Member
  • Total Posts : 15
  • Reward points : 0
  • Joined: 2017/06/08 11:10:12
  • Location: 0
  • Status: offline
Re: I2C fault recovery with MCC PIC24F 2019/02/14 05:51:42 (permalink)
2 (1)
I think old engineers have forgotten to read data sheets because they think they know everything. This particular pic24 family states nothing with regards to which pins to toggle or not toggle for error recovery(DS70000195) or specific data sheet. I am not sure where you "invented" these rules, how long has it been since you have made a relevant contribution to a code base @qhb? 
#18
tread
New Member
  • Total Posts : 15
  • Reward points : 0
  • Joined: 2017/06/08 11:10:12
  • Location: 0
  • Status: offline
Re: I2C fault recovery with MCC PIC24F 2019/02/14 05:58:30 (permalink)
1 (1)
Class to teach middle schoolers how to use an hc08 is down the hall gramps (aka "qhb"). Technology has evolved since then, we no longer use windows xp and outdated and techniques that slow production down because of theories created before 1.8v logic levels. 
#19
Jerry Messina
Super Member
  • Total Posts : 357
  • Reward points : 0
  • Joined: 2003/11/07 12:35:12
  • Status: offline
Re: I2C fault recovery with MCC PIC24F 2019/02/14 07:03:13 (permalink)
3.67 (3)
If you do a quick "Google" search, it is the most common solution

Then let Google write your code.
 
qhb gave you the correct answer.
#20
Page: 12 > Showing page 1 of 2
Jump to:
© 2019 APG vNext Commercial Version 4.5