CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to support@ccsinfo.com

I2C interrupt hangs PIC

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
subhan.mohammed



Joined: 21 Nov 2014
Posts: 8

View user's profile Send private message

I2C interrupt hangs PIC
PostPosted: Mon Jul 11, 2016 5:43 am     Reply with quote

Hi All,

I am using PIC24F32KA304 as an I2C slave device to communicate with iMX6 ARM device. This PIC is acting as a watchdog to the iMX6. So iMX6 sends a kick command to PIC every one second. I have noticed that after few commands(500 to 2000) the PIC I2C fails and the code hangs up in i2c_read function in i2c interrupt routine. Even the PIC watchdog does not reset the PIC.

Any help regarding the above behavior will be very grateful.

Thanks
Ttelmah



Joined: 11 Mar 2010
Posts: 19255

View user's profile Send private message

PostPosted: Mon Jul 11, 2016 6:40 am     Reply with quote

You do realise that you can get interrupts that don't want a read?.
Depending how your I2C is setup, I2C_start can generate an interrupt, as can I2C_stop.

Also on a address write, that signals a read, you need to both read and write in the one interrupt.

I'd expect the watchdog is resetting the PIC (if it is configured correctly), but the I2C hardware may well be in a partially hung state.

Have you looked to see if either SDA, or SCL is low?.

A 'classic error', that can cause problems of this sort, is having a master that does not NACK the last read transaction. The slave will not then have the status bits set correctly. Effectively the master both indicates the transaction is finished (i2C_stop), and says it is going on (I2C_read). However the watchdog does reset the PIC I2C from this.
The I2C will be frozen, if the master holds the clock line low. This will hold a read. This is I2C 'hold', and if it continues, is a master error. Also if there is a byte in the I2C output buffer, and then too few clocks are generated by the master. This will then leave the SDA frozen.
It can also get frozen, if you use I2C_read(2), and then do not load the output register.
On my code using watchdogs and I2C, I always have the PIC verify that the clock input is _high_ before enabling the I2C interrupt, and if not display/signal an error.
The master device if it gets into an unexpected state, implements an I2C reset (typically sensing the SDA line, and sending extra clocks).

Tell us the sequence of transactions sent by your master, and post your PIC slave code.
subhan.mohammed



Joined: 21 Nov 2014
Posts: 8

View user's profile Send private message

PostPosted: Mon Jul 11, 2016 7:05 am     Reply with quote

Thank you for quick response.

PIC slave code is:
Code:

#USE I2C(slave, sda = PIN_B9, scl = PIN_B8, SLOW, ADDRESS=0xA0, STREAM = SOM_I2C)   // Configure Device as Slave

#INT_SI2C
void I2CSlaveISR()
{
   
    tuint8 vState = 0;
    tuint8 vDeviceAddress;
    tuint8 vData;
   
    vState = i2c_isr_state();

    if(vState == 0x00)
    {
        // Master had send a write request
        vDeviceAddress = i2c_read(SOM_I2C, TRUE);
        if(vSomCommandRcvFlag == FALSE)
        {
            vSomCommandInfo.CmdLength = 0;
        }
    }
    else if(vState > 0x00 && vState < 0x80)
    {
        // Master is sending data
        vData = i2c_read(SOM_I2C, TRUE);
        if((vData == '\r') && (vSomCommandRcvFlag == FALSE))
        {
            vSomCommandRcvFlag = TRUE;
            // If we have received the carriage return at location greater than buffer size
            if(vSomCommandInfo.CmdLength >= Coms_dCOMMAND_BUFFER_LENGTH )
            {
                vSomCommandInfo.CmdLength = Coms_dCOMMAND_BUFFER_LENGTH - 1;
            }
        }
        if((vSomCommandInfo.CmdLength < Coms_dCOMMAND_BUFFER_LENGTH ) && (vSomCommandRcvFlag == FALSE))
        {
            vSomRXBuffer[vSomCommandInfo.CmdLength] = vData;
            vSomCommandInfo.CmdLength++;
        }
    }
    else if(vState == 0x80)
    {
        // Master had send a read request
        vDeviceAddress = i2c_read(SOM_I2C, 2);

        if(vTxBuffer.Len > 0)
        {
            // Send data to master
            i2c_write(SOM_I2C, vTxBuffer.Buffer[vTxBuffer.Start]);
            vTxBuffer.Len--;

            if(vTxBuffer.Start == dI2C_TRANSMIT_BUFFER_SIZE - 1)
            {
                vTxBuffer.Start = 0;
            }
            else
            {
                vTxBuffer.Start++;
            }
        }
        if(vTxBuffer.Len == 0)
        {
            output_low(GPIO_dPIC_IRQ_PIN);
        }
    }
    else    //vState > 0x80)
    {
        // Send data to master
        // This will be used when more than one byte is requested by the I2C master.
        // In this application we are only bound to one byte at a time in I2C read
    }
}
Ttelmah



Joined: 11 Mar 2010
Posts: 19255

View user's profile Send private message

PostPosted: Mon Jul 11, 2016 7:27 am     Reply with quote

and what is being sent?.
subhan.mohammed



Joined: 21 Nov 2014
Posts: 8

View user's profile Send private message

PostPosted: Mon Jul 11, 2016 7:31 am     Reply with quote

The commands are

iMX6 sends "RREQ",0,0\r
On receiving the above PIC replies "ACOM"\r

Thanks
Ttelmah



Joined: 11 Mar 2010
Posts: 19255

View user's profile Send private message

PostPosted: Mon Jul 11, 2016 7:48 am     Reply with quote

Some problems.

Comments inline:
Code:

#USE I2C(slave, I2C1, ADDRESS=0xA0, STREAM = SOM_I2C)   // Configure Device as Slave
//A slave does not have a speed. Since you are using I2C1, use the
//shorthand for this.


#INT_SI2C
void I2CSlaveISR()
{
   
    tuint8 vState = 0;
    tuint8 vDeviceAddress;
    tuint8 vData;
    //Since you are using streams, you need to use these for state as well
    vState = i2c_isr_state(SOM_I2C);
    //This may be one problem....

    if(vState == 0x00)
    {
        // Master had send a write request
        vDeviceAddress = i2c_read(SOM_I2C, TRUE);
        if(vSomCommandRcvFlag == FALSE)
        {
            vSomCommandInfo.CmdLength = 0;
        }
    }
    else if(vState > 0x00 && vState < 0x80)
    {
        // Master is sending data
        vData = i2c_read(SOM_I2C, TRUE);
        if((vData == '\r') && (vSomCommandRcvFlag == FALSE))
        {
            vSomCommandRcvFlag = TRUE;
            // If we have received the carriage return at location greater than buffer size
            if(vSomCommandInfo.CmdLength >= Coms_dCOMMAND_BUFFER_LENGTH )
            {
                vSomCommandInfo.CmdLength = Coms_dCOMMAND_BUFFER_LENGTH - 1;
            }
        }
        if((vSomCommandInfo.CmdLength < Coms_dCOMMAND_BUFFER_LENGTH ) && (vSomCommandRcvFlag == FALSE))
        {
            vSomRXBuffer[vSomCommandInfo.CmdLength] = vData;
            vSomCommandInfo.CmdLength++;
        }
    }
    else if(vState == 0x80)
    {
        // Master had send a read request
        vDeviceAddress = i2c_read(SOM_I2C, 2);

        if(vTxBuffer.Len > 0)
        {
            // Send data to master
            i2c_write(SOM_I2C, vTxBuffer.Buffer[vTxBuffer.Start]);
            vTxBuffer.Len--;

            if(vTxBuffer.Start == dI2C_TRANSMIT_BUFFER_SIZE - 1)
            {
                vTxBuffer.Start = 0;
            }
            else
            {
                vTxBuffer.Start++;
            }
        }
        if(vTxBuffer.Len == 0)
        {
            //There needs to be a write here. Otherwise the bus will be hung
            //The master has requested a read, and expects to be able
            //to clock a byte back. The clock will not be released by the
            //slave until a byte is loaded into the buffer.....
            I2c_write(SOM_I2C, 0); //dummy byte
            output_low(GPIO_dPIC_IRQ_PIN);
        }
    }
    else    //vState > 0x80)
    {
        //Similarly to above, there needs to be a write at this point,
        //even if of  garbage.....
        I2c_write(SOM_I2C, 0);

        // Send data to master
        // This will be used when more than one byte is requested by the I2C master.
        // In this application we are only bound to one byte at a time in I2C read
    }
}


There are two potential problems. First I2C_ISR_state needs to be using the stream name (otherwise it may not give the data for the right I2C port), and then the master read transactions _must_ always be satisfied, otherwise the SCL will be held low waiting for the data.....
subhan.mohammed



Joined: 21 Nov 2014
Posts: 8

View user's profile Send private message

PostPosted: Mon Jul 11, 2016 7:56 am     Reply with quote

Thank you very much for your suggestions. I will modify the code and re-do my test. Will update you about the result.

But I am still not sure why the watchdog did not reset the PIC if it hangs up at i2c_read function?

These are the fuse I am using to set the watchdog
#FUSES WDT //!< Enable watchdog
#FUSES WPOSTS9 //!< Set the watchdog timer to 2.048 seconds

Thank you
Ttelmah



Joined: 11 Mar 2010
Posts: 19255

View user's profile Send private message

PostPosted: Mon Jul 11, 2016 7:57 am     Reply with quote

When I asked 'what is being sent', I wanted the I2C transactions. So:

START
write (0xA0)
etc..

Your format is very 'non standard' for I2C. I2C is not RS232.....

For I2C, normally the master sends start, then the device write address, followed by a register address, and then sequentially the bytes to send to the registers, followed by a stop.

When reading, the master sends a start, device write address, register address , restart, device read address, then read, and repeats for as many bytes as needed, sending a _NACK_ on the last byte read, then stop.

Not nacking the last byte can leave the slave in an incorrect state. Do you nack the last read, before your stop?.
subhan.mohammed



Joined: 21 Nov 2014
Posts: 8

View user's profile Send private message

PostPosted: Mon Jul 11, 2016 8:28 am     Reply with quote

Hi,

Sorry for not being clear with the sent commands.

At iMX6, I am using linux code to read/write I2C commands to PIC.

Our application running on iMX6 sends a kick command to PIC every one second.

iMX6 sends start, PIC address with write, data("RREQ",0,0,\r), and then stop.
After receiving the above data from iMX6, PIC verifies the data and then update the global buffer with the reply data("ACOM"\r) and then pull one of its gpio high.(This gpio is connected to iMX6 gpio).

On detecting this gpio high, iMX6 sends an i2c read command to read just one byte. While the gpio is high, iMX6 keep sending i2c read command to read one byte at a time.
Code:

while(gpio_is_high)
{
    read(vI2CFileD, (char *) &vRcvChar, 1)
}


When the PIC buffer is empty, PIC will pull the gpio low.
Code:

if(vTxBuffer.Len == 0)
{
    output_low(GPIO_dPIC_IRQ_PIN);
}
Ttelmah



Joined: 11 Mar 2010
Posts: 19255

View user's profile Send private message

PostPosted: Mon Jul 11, 2016 8:33 am     Reply with quote

In the ARM end, on the fifth read (so when expecting the carriage return), you need to send a NACK. This is standard for I2C (normally the master 'knows' how many bytes it is reading, and sends the NACK on the last byte).
This resets a couple of flags in the PIC.
subhan.mohammed



Joined: 21 Nov 2014
Posts: 8

View user's profile Send private message

PostPosted: Mon Jul 11, 2016 8:43 am     Reply with quote

Hi,

I am reading only one byte at a time, I am sending NACK at every i2c read command.

i2c read packet will be start, PIC address with read, read one byte from PIC, NACK, stop

Thanks
Ttelmah



Joined: 11 Mar 2010
Posts: 19255

View user's profile Send private message

PostPosted: Mon Jul 11, 2016 9:05 am     Reply with quote

The sequence should be:

start
write read address
read
read
read
read
read(nack)
stop

Otherwise how is the PIC to know where it is meant to be?.

I really would suggest you 'rethink', and switch to using standard I2C. So you write a register address after the device address, and no line feed.
subhan.mohammed



Joined: 21 Nov 2014
Posts: 8

View user's profile Send private message

PostPosted: Mon Jul 11, 2016 9:28 am     Reply with quote

Hi,

Yes we aware we are using the I2C in a non-standard way, but for various reasons, important to our application, we need to do this. However, this should not be an issue as we are simply sending a buffer of data to the device whilst observing the framing rules. For the vast majority of the time this has worked perfectly well and only sometimes see an error.

Do you have any clue on why the watchdog would fail to reset, when the context of an interrupt?

The PIC got hangs up on the following lines under #USE I2C
0732: BTSS.B 208.1
0734: BRA 732

Thanks
Ttelmah



Joined: 11 Mar 2010
Posts: 19255

View user's profile Send private message

PostPosted: Mon Jul 11, 2016 12:09 pm     Reply with quote

The watchdog will reset, if it is running properly. Are you sure it is not?. Read my comments in the first post. If it is actually hung because the I2C is expecting clocks from the master, it is the master that has to clear this. The chip would reset, and still be hung.

Have you added the fixes I've already pointed out?.
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group