Author Topic: Problems trying to Read using IIC  (Read 14375 times)

Offline mhoneywill

  • Full Member
  • ***
  • Posts: 173
    • View Profile
Problems trying to Read using IIC
« on: June 04, 2009, 01:23:20 PM »
Hi Mark,

I'm trying to debug some IIC problems. My Hardware has two IIC devices a TC665 Fan controller and a PCF8575 16 bit IO expander, I've added these chips to the Simulator and it all seems to work fine. On the real hardware I have been testing the integrity of the IIC by changing the FAN speed in response to a button push. Using the following Commands

static const unsigned char IIC_WrTC665_FanSlow[] = {0x36, 0x06, 0x00};            // {address, register, data}
static const unsigned char IIC_WrTC665_FanFast[] = {0x36, 0x06, 0x0f};            // {address, register, data}
static const unsigned char IIC_WrTC665_FanSpeed[] = {0x36, 0x07};            // {address, register, data}
static const unsigned char IIC_RdTC665_FanSpeed[] = {2, 0x37, OWN_TASK};            // {address, register, data}
static const unsigned char IIC_Rd8575[] = {2, 0x41, OWN_TASK};            // {bytes, address, TASK_TO_WAKE}
static const unsigned char IIC_Rd8575_1[] = {1, 0x41, OWN_TASK};            // {bytes, address, TASK_TO_WAKE}
static const unsigned char IIC_Rd8575_2[] = {2, 0x41, OWN_TASK};            // {bytes, address, TASK_TO_WAKE}
static const unsigned char IIC_Rd8575_3[] = {3, 0x41, OWN_TASK};            // {bytes, address, TASK_TO_WAKE}
      

I start the IIC interface using the following

static void fnConfigIIC_Interface(void)
{
    IICTABLE tIICParameters;

    tIICParameters.Channel = 0;
    tIICParameters.usSpeed = 100;                                        // 100k
    tIICParameters.Rx_tx_sizes.TxQueueSize = 64;                         // transmit queue size
    tIICParameters.Rx_tx_sizes.RxQueueSize = 64;                         // receive queue size
    tIICParameters.Task_to_wake = 0;                                     // no wake on transmission

    if ((IICPortID = fnOpen( TYPE_IIC, FOR_I_O, &tIICParameters)) !=0) { // open the channel with defined configurations
   fnWrite(IICPortID, (unsigned char *)&IIC_WrTC665_FanSlow, sizeof(IIC_WrTC665_FanSlow)); // set the fan speed
        fnRead(IICPortID, (unsigned char *)&IIC_Rd8575, 0);            // start the read process of 16 bytes
    }
}

I get a write to the Fan as expected, followed by a read from the 8575 chip, but a logic analyser shows 3 bytes being read not 2 as expected.

I can write as much as I want to the Fan chip, but as soon as I try another read of the 8575 chip using fnRead(IICPortID, (unsigned char *)&IIC_Rd8575, 0); only one byte is returned and the IIC bus seems to lock up.

I've documented my findings in the attached document, which has the logic analyser traces.

It seems that uTasker is trying to read 1 more byte than requested, and that only reading 1 byte seems to lockup the IIC driver.

Do you have any thoughts?

Cheers

Martin


Offline mark

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 3234
    • View Profile
    • uTasker
Re: Problems trying to Read using IIC
« Reply #1 on: June 04, 2009, 02:51:49 PM »
Hi Martin

The attachment worked well but the logic analyser traces are not that clear.

I just took a look and compared a standard write/read sequence with two types of chips (one from Luminary and the other a Coldfire - just for comparison).

I think that the simulator wouldn't notice an error in the controller use and so this suggests that the problem is there. My first feeling is that it is indeed a Luminary driver problem and not general. In fact most of the I2C driver is in the processor specific part - IIC_drv.c is really only putting the messages into the output buffer, which is hardware independent and looks normal.

The luminary controller is a bit special since it more or less takes over the details of transmitting the address and also the first byte of data. This means that there is a basic difference in how telegrams with one byte content are sent in comparison to telegrams with more bytes. It looks as though the transmission side is working well but the reception side not. Very likely a HW driver bug which hasn't been detected due to little use of this interface in the Luminary project.

Basically I suspect something in this area in fnTxIIC() in the reading case:

        if (ptIICQue->ucPresentLen == 1) {
            iic->I2CMCS = (I2C_RUN | I2C_START | I2C_STOP);              // start transfer of single byte followed by a stop condition
        }
        else {
            iic->I2CMCS = (I2C_RUN | I2C_START | I2C_DATACK);            // start transfer of first of multiple bytes with acks
        }


Here the controller setup is different for single byte reads (they seem to hang...).
In multiple byte reads they don't seem to hang but seem to read one byte more than expected (sound like the internal address + first byte queue in the chip not being respected(?)).

My conclusion is therefore that the HW driver must be corrected. Not sure how just yet and it may require a bit of experimentation and 'reading-between-the-lines' in the data sheet. Sorry about this but I 'll get back once I have a patch.

Regards

Mark




Offline mhoneywill

  • Full Member
  • ***
  • Posts: 173
    • View Profile
Re: Problems trying to Read using IIC
« Reply #2 on: June 04, 2009, 06:03:35 PM »
Thanks, Mark for the quick reply.

I'll do some more investigation tomorrow to see if I can help find the problem

Cheers

Martin

Offline mark

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 3234
    • View Profile
    • uTasker
Re: Problems trying to Read using IIC
« Reply #3 on: June 05, 2009, 04:09:46 PM »
Hi Martin

I think that I could identify the problem and have a patch.
Basically the Luminar I2C controller is a bit different in the way that it handles the slave address transmission (this is sent on both reads and writes).

Problem when reading 1 bytes:
In this case the device sends a start condition, the slave address, reads one byte and sends a stop condition - all with one command. There is then an interrupt which identifies that the read has completed and saves this to the input buffer. The interrupt handler was then sending commanding a stop condition and expecting a further interrupt to be received. Thsi was never arriving and so subsequent operation was blocked. The single byte read was in fact correct on the I2C bus though.

Problem with one byte too many being read:
When 2 or more bytes are read the case is a bit different since the stop condition is not commanded immediately. Although it was respecting the fact that the first interrupt (usually it informs that the slave address has been sent) contained a read, the byte counter was one off - it this read one byte too many. I am not sure whether it actually returned one byte too many in the read buffer though because I didn't look at that part (the error was before it).

The correction is as follows:

1) In fnTxIIC() in LM3Sxxxx.c

Original:
    if (iic->I2CMSA & 0x01) {                                            // reading
        if (ptIICQue->ucPresentLen == 1) {
            iic->I2CMCS = (I2C_RUN | I2C_START | I2C_STOP);              // start transfer of single byte followed by a stop condition
        }
        else {
            iic->I2CMCS = (I2C_RUN | I2C_START | I2C_DATACK);            // start transfer of first of multiple bytes with acks
        }

New:
    if (iic->I2CMSA & 0x01) {                                            // reading
        if (--ptIICQue->ucPresentLen == 0) { // DECREMENTED AND COMPARED WITH ZERO
            iic->I2CMCS = (I2C_RUN | I2C_START | I2C_STOP);              // start transfer of single byte followed by a stop condition
        }
        else {
            iic->I2CMCS = (I2C_RUN | I2C_START | I2C_DATACK);            // start transfer of first of multiple bytes with acks
        }


This corrects the single read blocking problem and the read byte count but not yet the input buffer contents.

2) In fnI2CInt() in LM3Sxxxx.c

Original:

    if (ptIICQue->ucState & RX_ACTIVE) {                                 // receiving
        if (ptIICQue->ucPresentLen) {                                    // more to be received
            ptIICQue->ucPresentLen--;
            *IIC_rx_control[Channel]->IIC_queue.put++ = (unsigned char)iic->I2CMDR; // read the received byte
            if (ptIICQue->ucPresentLen == 0) {                           // last read
                iic->I2CMCS = (I2C_RUN | I2C_STOP);                      // start transfer of last byte with no ack, followed by a stop condition
            }
            else {
                iic->I2CMCS = (I2C_RUN | I2C_DATACK);                    // start transfer of further acked byte
            }
            IIC_rx_control[Channel]->IIC_queue.chars++;                  // and put it into the rx buffer
            if (IIC_rx_control[Channel]->IIC_queue.put >= IIC_rx_control[Channel]->IIC_queue.buffer_end) {
                IIC_rx_control[Channel]->IIC_queue.put = IIC_rx_control[Channel]->IIC_queue.QUEbuffer;
            }
        }



New:

    if (ptIICQue->ucState & RX_ACTIVE) {                                 // receiving

  // THIS BLOCK NO LONGER DEPENDENT ON ptIICQue->ucPresentLen BEING NON-ZERO
        *IIC_rx_control[Channel]->IIC_queue.put++ = (unsigned char)iic->I2CMDR; // read the received byte
        IIC_rx_control[Channel]->IIC_queue.chars++;                      // and put it into the rx buffer
        if (IIC_rx_control[Channel]->IIC_queue.put >= IIC_rx_control[Channel]->IIC_queue.buffer_end) {
            IIC_rx_control[Channel]->IIC_queue.put = IIC_rx_control[Channel]->IIC_queue.QUEbuffer;
        }

        if (ptIICQue->ucPresentLen) {                                    // more to be received
            ptIICQue->ucPresentLen--;
            if (ptIICQue->ucPresentLen == 0) {                           // last read
                iic->I2CMCS = (I2C_RUN | I2C_STOP);                      // start transfer of last byte with no ack, followed by a stop condition
            }
            else {
                iic->I2CMCS = (I2C_RUN | I2C_DATACK);                    // start transfer of further acked byte
            }
        }


Modified, also when the reception is complete the read byte is saved to the buffer. Otherwise, with the counter adjustment, it would have missed one byte.

Note that I have commented out a simulator #ifdef _WINDOWS block of code which is also moved up. I corrected on the target and expect that the simulator will also need a slight adjustment too. I will add this later, once investigated and tested, but for now this has given correct operation in my tests when reading and writing memory in an I2C RTC.

Tell me how you get on.

Cheers

Mark




Offline mark

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 3234
    • View Profile
    • uTasker
Re: Problems trying to Read using IIC
« Reply #4 on: June 05, 2009, 08:07:33 PM »
Martin

I tested the simulator with the simulation code also moved to just before
if (ptIICQue->ucPresentLen) {
and it seemed to work correctly.

Regards

Mark

Offline mhoneywill

  • Full Member
  • ***
  • Posts: 173
    • View Profile
Re: Problems trying to Read using IIC
« Reply #5 on: June 05, 2009, 09:34:57 PM »
Hi Mark,

I applied your modifications and it has improved things, I seem to be reading the correct number of bytes now, but I’m still got the following problems.

1.   Single byte reads are still broken.
2.   Reads of the PCF8575 chip, triggered from another task cause the IIC bus to lock up.

I've attached a document describing my findings in more detail. I'll also email you my current project and the logic trace taken with DigiView (I've sent you traces before so I assume you can still view them)

Cheers

Martin

Offline mark

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 3234
    • View Profile
    • uTasker
Re: Problems trying to Read using IIC
« Reply #6 on: June 05, 2009, 10:25:24 PM »
Hi Martin

When testing single byte reads I didn't have further reads waiting in the queue afterwards. It may be that a single byte read will work for you if it is not immediately followed by another activity - I will therefore add this test case to my tests to see what is going on.

In the initialisation sequence you are commanding several reads. Would it not be better to only set a task to be woken on the final read - this will avoid the owner task being woken when the first read has been performed and then again when each following one has completed. The risk is that you don't know how many bytes may be already waiting from follow on reads. If you wake only at the end then you known that all read results are waiting in that order, and that the messages are really complete (you could also check fnMsgs() for the total expected byte count to be absolutely sure. This probably has nothing to to with the follow on problem.

When I tested I could start a read of x bytes from a particular address and so the read was started at any point in time. I always commanded a write to first set the address, followed by the read. The logic analyser recording looks like a successful single byte read (with no previous write). I will add tests of reads without an initial write too.

Regards

Mark

Offline mhoneywill

  • Full Member
  • ***
  • Posts: 173
    • View Profile
Re: Problems trying to Read using IIC
« Reply #7 on: June 05, 2009, 10:51:01 PM »
Hi Mark,

I'm only commanding several reads in the initialisation task for testing purposes, normally I wouldn't have any there. It was only to prove the point that single byte reads seemed to fail.

My I/O chips don't need a write beforehand as they only have two data registers.

Cheers

Martin


Offline mark

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 3234
    • View Profile
    • uTasker
Re: Problems trying to Read using IIC
« Reply #8 on: June 06, 2009, 01:16:13 AM »
Hi Martin

Here comes the next patch (I could also get the SDA line to stay low when trying to read more that one byte).

In fnTxIIC()

    if (iic->I2CMSA & 0x01) {                                            // reading
        if (--ptIICQue->ucPresentLen == 0) {                             // {12}
            iic->I2CMCS = (I2C_RUN | I2C_START | I2C_STOP);              // start transfer of single byte followed by a stop condition
        }
        else {
            iic->I2CMCS = (I2C_RUN | I2C_START | I2C_DATACK);            // start transfer of first of multiple bytes with acks
        }
        iic->I2CMIMR = IIC_INTERRUPT_BIT;                                // {12} enable interrupt on completion


The last line needs to be added to enable the interrupt.

A read without a write before it wasn't enabling the I2C interrupt and this was causing it to hang. Since my chip generally needs a write beforehand, and this enables the interrupt and doesn't disable it until the queue is emptied, I obviously never noticed it.

In fact this was quite nasty. I had to remove power to get the RTC to recover! Resets were not enough...

This will almost certainly cure the second problem that you had (the most important).
I still couldn't find a problem with reading single bytes - perhaps it was somehow related in your sequence?

Regards

Mark


Offline mhoneywill

  • Full Member
  • ***
  • Posts: 173
    • View Profile
Re: Problems trying to Read using IIC
« Reply #9 on: June 06, 2009, 01:47:50 AM »
Brilliant, that seems to have fixed it, Reads now work without a problem.

Also I'm pretty sure the problem with single byte reads has gone away too (a typo on my part with your previous fix).

So in conclusion. It looks like IIC is now working again with no known faults.

Thanks again for the fast response Mark.

Cheers

Martin

Offline mark

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 3234
    • View Profile
    • uTasker
Re: Problems trying to Read using IIC
« Reply #10 on: June 06, 2009, 11:30:28 PM »
Hi Martin

Thanks for the feedback. My original tests certainly proved to be incomplete for the Luminary I2C (the single byte and multi-byte "Burst" modes really have to be tested independently).

Positive is that it does seem to be cured now and that I now have the extended test suite ready for use when necessary - this should avoid this being able to happen again. Another problem was that the simulator was allowing interrupts to be dispatched even when the I2C interrupt mask was not set, so I have made this more strict and will try to avoid allowing missing this again.

Regards

Mark