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

Slave I2C code (no interrupts) showing read/respond.
Goto page Previous  1, 2, 3, 4  Next
 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
Ttelmah



Joined: 11 Mar 2010
Posts: 19195

View user's profile Send private message

PostPosted: Mon Nov 18, 2019 12:34 pm     Reply with quote

No, the clock is held on every transaction.
When you read data this immediately releases the clock.
The point about a master read, is you read data and don't release the
line (this is the '2'), then release it once you have loaded the reply.
Every individual transfer is synchronised by the clock hold.
allenhuffman



Joined: 17 Jun 2019
Posts: 537
Location: Des Moines, Iowa, USA

View user's profile Send private message Visit poster's website

PostPosted: Mon Nov 18, 2019 5:17 pm     Reply with quote

I have things working "perfectly" on two boards, but then I started testing on a PIC24FJ64GA002 and the behavior changes.

On this board, it initializes I2C with P and S both 0 (good). When the first message comes in, it doesn't seem to toggle S or the IRQ, but I can look in the debugger and see P is 1 (so it saw the stop bit, but not the stat and data). After that, messages process as expected.

It worked on 24FJ256GA106, and a 24EP256GP202, but I'm still working on the 24FJ64GA002 to figure out what is different.
_________________
Allen C. Huffman, Sub-Etha Software (est. 1990) http://www.subethasoftware.com
Embedded C, Arduino, MSP430, ESP8266/32, BASIC Stamp and PIC24 programmer.
http://www.whywouldyouwanttodothat.com ?
jeremiah



Joined: 20 Jul 2010
Posts: 1314

View user's profile Send private message

PostPosted: Tue Nov 19, 2019 11:58 am     Reply with quote

I would check the errata on that chip. We had some troubles with the 44 pin version (24FJ64GA004) that we had to do a lot of workarounds for.
allenhuffman



Joined: 17 Jun 2019
Posts: 537
Location: Des Moines, Iowa, USA

View user's profile Send private message Visit poster's website

PostPosted: Tue Nov 19, 2019 12:26 pm     Reply with quote

jeremiah wrote:
I would check the errata on that chip. We had some troubles with the 44 pin version (24FJ64GA004) that we had to do a lot of workarounds for.


Will do. I found some other posts in Microchip (one from 2008) about I2C quirks.

The end result for me was much more robust I2C polled code:

Now, if I am waiting on a Receive (Master Write) and I get a Master Read, I send it back some blank data (so the Master does not hang) and go back to Receive for the next proper message. (Thus, doing a Receive will stay there until it receives something.)

If I am doing a Response (waiting on Master Read) and I get a Master Write, I consume the unexpected data (to clear the receive buffer, etc.) and then return a 0 indicating the response was not sent. (Calling code can handle.)

I need to add some timeouts and make some things a bit more bulletproof (it can still hang if transmission is disrupted, since I rely on seeing a Stop bit which might not come).

But it's great progress.
_________________
Allen C. Huffman, Sub-Etha Software (est. 1990) http://www.subethasoftware.com
Embedded C, Arduino, MSP430, ESP8266/32, BASIC Stamp and PIC24 programmer.
http://www.whywouldyouwanttodothat.com ?
allenhuffman



Joined: 17 Jun 2019
Posts: 537
Location: Des Moines, Iowa, USA

View user's profile Send private message Visit poster's website

PostPosted: Tue Nov 19, 2019 12:30 pm     Reply with quote

In case anyone wants to eyeball this, here is the work-in-progress polled code I have been working on.

(EDIT: Minor update. A few more still to be posted after I get them tested.)

Read message from Master:
Code:
unsigned int i2cSlaveReceive (void *buffer, size_t size)
{
    unsigned int state = 0;
    unsigned int value = 0;
    unsigned int bytesTransferred = 0;

    // NULL check.
    if ((buffer == NULL) || (size == 0)) return 0;
   
    // There is a bug in the 24FJ64GA002 which misses the
    // first Start bit and Event Interrupt Flag Status.

    do
    {
        // Wait for START bit
        while (S == 0);

        // Wait for first address byte so we can tell if this is a Read or Write.
        while (I2C_SYSTEM_BUS_IRQ_PENDING_BIT == 0);
        // Do not clear. We may need it pending for main loop below.

        // If this is a Write to Slave, we are done here.
        if (RW == 0) break;

        // Else, it was unexpected.
        i2c_read (SYSTEM_BUS, 2); // Address
        i2c_write (SYSTEM_BUS, 0x42);
        while (P == 0)
        {
            if (TBF == 0)
            {
                i2c_write (SYSTEM_BUS, 0x42);
            }
        }
        I2C_SYSTEM_BUS_IRQ_PENDING_BIT = 0;

    } while (1);
 
    // Process INCOMING address and INCOMING data bytes.
    while (1)
    {
        // Wait for I2C interrupt pending.
        while (I2C_SYSTEM_BUS_IRQ_PENDING_BIT == 0);
     
        I2C_SYSTEM_BUS_IRQ_PENDING_BIT = 0; // Clear IRQ flag

        state = i2c_isr_state(SYSTEM_BUS);

        value = (state & ~BIT(7)); // Mask off high bit.

        // High bit (1xxxxxxx) indicates READ, else WRITE
        if ((state & BIT(7)) == 0) // High bit CLEAR (Read from Master)
        {
            unsigned int data;
           
            if (value == 0x00)
            {
                // 0 - Address match received with R/W bit clear, perform
                i2c_read (SYSTEM_BUS);
            }
            else
            {
                // 1-0x7F - Master has written data; i2c_read() will immediately
                // return the data
                data = i2c_read (SYSTEM_BUS);

                if (bytesTransferred < size)
                {
                    buffer[bytesTransferred] = data;
                    bytesTransferred++;
                }
            }
        }
        else // High bit SET (Slave writing to Master)
        {
            // Write? We were trying to read!
            break;
        }

        // If Reading, we read however many bytes the Master wrote,
        //      or up to 'size' bytes.
        // If Writing, we send however many bytes the Master reads,
        //      or up to 'size' bytes.
       
        // Wait for either a stop bit, or another incoming data byte.
        while ((P == 0) && (I2C_SYSTEM_BUS_IRQ_PENDING_BIT == 0));
       
        if (P != 0)
        {
            break;
        }
    } // end of while (1)
   
    return bytesTransferred;
} // end of i2cSlaveReceive ()


Write Message to Master:
Code:

unsigned int i2cSlaveRespond (void *buffer, size_t size)
{
    unsigned int state = 0;
    unsigned int value = 0;
    unsigned int bytesTransferred = 0;

    // NULL check.
    if ((buffer == NULL) || (size == 0)) return 0;

    do
    {
        // Wait for START bit
        while (S == 0);

        // Wait for first address byte so we can tell if this is a Read or Write.
        while (I2C_SYSTEM_BUS_IRQ_PENDING_BIT == 0);
        // Do not clear. We may need it pending for main loop below.

        // If this is a Read from Master, we are done here.
        if (RW == 1) break;

        // Else, it was unexpected. Master was Writing, not reading.
       
        // Consume incoming message.
        while (P == 0)
        {
            if (RBF == 0)
            {
                i2c_read (SYSTEM_BUS);
            }
        }
        I2C_SYSTEM_BUS_IRQ_PENDING_BIT = 0;
       
        return 0;

    } while (1);
   

    // Process INCOMING address and OUTGOING data bytes.
    while (1)
    {
        // Wait for I2C interrupt pending.
        while (I2C_SYSTEM_BUS_IRQ_PENDING_BIT == 0);

        I2C_SYSTEM_BUS_IRQ_PENDING_BIT = 0; // Clear IRQ flag

        state = i2c_isr_state(SYSTEM_BUS);

        value = (state & ~BIT(7)); // Mask off high bit.

        // High bit (1xxxxxxx) indicates READ, else WRITE
        if ((state & BIT(7)) == 0) // High bit CLEAR (Read from Master)
        {
            // Read? We were trying to write!
            break;
        }
        else // High bit SET (Slave writing to Master)
        {
            if (value == 0x00)
            {
                // 0x80 - Address match received with R/W bit set; perform
                // i2c_read( ) to read the I2C address, and use i2c_write( ) to
                // pre-load the transmit buffer for the next transaction (next
                // I2C read performed by master will read this byte).
                i2c_read (SYSTEM_BUS, 2);
                i2c_write (SYSTEM_BUS, buffer[0]);
            }
            else
            {
                // 0x81-0xFF - Transmission completed and acknowledged; respond
                // with i2c_write() to pre-load the transmit buffer for the next
                // transition (the next I2C read performed by master will read
                // this byte).
                i2c_write (SYSTEM_BUS, buffer[bytesTransferred]);
                //i2c_write (SYSTEM_BUS, buffer[value]);
            }
        }

        // Done when STOP bit is seen.

        // If Reading, we read however many bytes the Master wrote,
        //      or up to 'size' bytes.
        // If Writing, we send however many bytes the Master reads,
        //      or up to 'size' bytes.
       
        // Wait for either a stop bit, or another incoming data byte.
        while ((P == 0) && (I2C_SYSTEM_BUS_IRQ_PENDING_BIT == 0));

        if (P != 0)
        {
            break;
        }

        // To avoid returining +1, we increment after the Stop bit check.
        bytesTransferred++;

    } // end of while (1)
   
    return bytesTransferred;
} // end of i2cSlaveRespond ()

_________________
Allen C. Huffman, Sub-Etha Software (est. 1990) http://www.subethasoftware.com
Embedded C, Arduino, MSP430, ESP8266/32, BASIC Stamp and PIC24 programmer.
http://www.whywouldyouwanttodothat.com ?


Last edited by allenhuffman on Tue Nov 19, 2019 2:44 pm; edited 1 time in total
allenhuffman



Joined: 17 Jun 2019
Posts: 537
Location: Des Moines, Iowa, USA

View user's profile Send private message Visit poster's website

PostPosted: Tue Nov 19, 2019 12:34 pm     Reply with quote

Yep, I2C issue is known and there is a workaround:
http://ww1.microchip.com/downloads/en/DeviceDoc/80000470j.pdf
_________________
Allen C. Huffman, Sub-Etha Software (est. 1990) http://www.subethasoftware.com
Embedded C, Arduino, MSP430, ESP8266/32, BASIC Stamp and PIC24 programmer.
http://www.whywouldyouwanttodothat.com ?
Ttelmah



Joined: 11 Mar 2010
Posts: 19195

View user's profile Send private message

PostPosted: Tue Nov 19, 2019 2:11 pm     Reply with quote

That issue only applies to a six year old revision of the chip.
Assuming you are working with something reasonably recent, this
should not apply to you.
allenhuffman



Joined: 17 Jun 2019
Posts: 537
Location: Des Moines, Iowa, USA

View user's profile Send private message Visit poster's website

PostPosted: Tue Nov 19, 2019 2:13 pm     Reply with quote

Ttelmah wrote:
That issue only applies to a six year old revision of the chip.
Assuming you are working with something reasonably recent, this
should not apply to you.


Sadly, it's on a brand new board we just made (PIC from Digi-Key). We (software guys) aren't sure how to check for the revision of the chip.
_________________
Allen C. Huffman, Sub-Etha Software (est. 1990) http://www.subethasoftware.com
Embedded C, Arduino, MSP430, ESP8266/32, BASIC Stamp and PIC24 programmer.
http://www.whywouldyouwanttodothat.com ?
Ttelmah



Joined: 11 Mar 2010
Posts: 19195

View user's profile Send private message

PostPosted: Tue Nov 19, 2019 2:43 pm     Reply with quote

The device programmer can read the revision ID. Depends what software
you are using.
If you want to write a program just read the configuration data space.
The revision is in the last word of this.
allenhuffman



Joined: 17 Jun 2019
Posts: 537
Location: Des Moines, Iowa, USA

View user's profile Send private message Visit poster's website

PostPosted: Tue Nov 19, 2019 2:51 pm     Reply with quote

Ttelmah wrote:
The device programmer can read the revision ID. Depends what software
you are using.
If you want to write a program just read the configuration data space.
The revision is in the last word of this.


I have the ICD-U80 with the CCS Load program, but I think we have a PICkit-3 around here somewhere. I see Target Device ID, but I'm not sure I am finding revision for the chip here (just for the hardware debugger itself, it looks like).
_________________
Allen C. Huffman, Sub-Etha Software (est. 1990) http://www.subethasoftware.com
Embedded C, Arduino, MSP430, ESP8266/32, BASIC Stamp and PIC24 programmer.
http://www.whywouldyouwanttodothat.com ?
allenhuffman



Joined: 17 Jun 2019
Posts: 537
Location: Des Moines, Iowa, USA

View user's profile Send private message Visit poster's website

PostPosted: Thu Nov 21, 2019 10:49 am     Reply with quote

I have now moved on to implementing messaging code to make use of my I2C polled receive/respond routines. Once I get to testing those, I am sure I will find more cases where the code can hang.

There are places in my code, and in the CCS i2c_xxx() routines, where they poll for a bit to change. Under normal circumstances, things work fine, but if a signal is disrupted and a state change is missed, we get a hang.

I'll try to post "final" code when I get to that point.
_________________
Allen C. Huffman, Sub-Etha Software (est. 1990) http://www.subethasoftware.com
Embedded C, Arduino, MSP430, ESP8266/32, BASIC Stamp and PIC24 programmer.
http://www.whywouldyouwanttodothat.com ?
newguy



Joined: 24 Jun 2004
Posts: 1899

View user's profile Send private message

PostPosted: Thu Nov 21, 2019 11:28 am     Reply with quote

Have you considered launching a timer which is configured to expire in 2x or 3x your maximum anticipated I2C timeout? Then poll for your anticipated I2C state OR the timer expiring. If the timer expires, at least you can break out of your I2C routine and take some sort of remedial action instead of blocking/hanging in a test which will never become true.
allenhuffman



Joined: 17 Jun 2019
Posts: 537
Location: Des Moines, Iowa, USA

View user's profile Send private message Visit poster's website

PostPosted: Thu Nov 21, 2019 4:03 pm     Reply with quote

newguy wrote:
Have you considered launching a timer which is configured to expire in 2x or 3x your maximum anticipated I2C timeout? Then poll for your anticipated I2C state OR the timer expiring. If the timer expires, at least you can break out of your I2C routine and take some sort of remedial action instead of blocking/hanging in a test which will never become true.


I hadn't -- and that seems like a good solution. I could read the timer value even without using any interrupts. In earlier test code, I was just doing a loop countdown, which varies in time on different speed processors.
_________________
Allen C. Huffman, Sub-Etha Software (est. 1990) http://www.subethasoftware.com
Embedded C, Arduino, MSP430, ESP8266/32, BASIC Stamp and PIC24 programmer.
http://www.whywouldyouwanttodothat.com ?
jaka



Joined: 04 May 2014
Posts: 35
Location: Finland

View user's profile Send private message

PostPosted: Fri Nov 22, 2019 8:17 am     Reply with quote

Just a quick comment. You said that you are using FTDI as a master. At least the MPSSE in FT232H devices doesn't support I2C clock stretching. So it's pretty useless if trying to communicate with a microcontroller based slave device.

We had lots of problems with the FT232H and eventually had to change to another USB-I2C chip which does support clock stretching.
allenhuffman



Joined: 17 Jun 2019
Posts: 537
Location: Des Moines, Iowa, USA

View user's profile Send private message Visit poster's website

PostPosted: Fri Nov 22, 2019 8:57 am     Reply with quote

jaka wrote:
Just a quick comment. You said that you are using FTDI as a master. At least the MPSSE in FT232H devices doesn't support I2C clock stretching. So it's pretty useless if trying to communicate with a microcontroller based slave device.

We had lots of problems with the FT232H and eventually had to change to another USB-I2C chip which does support clock stretching.


Wow, thanks! Good to know! Our main PIC24 board has an FTDI chip on it (USB to the PC). We use the FTD2XX dll and LIBFT4222 dll.

These do seem to honor clock stretching. Our production system doesn't do it, but when I went to write there I2C routines, I found it did work. My PIC24 code can do this:

Code:
i2cSlaveReceive( … );
...master writes data...
...slave receives data...
i2cSlaveRespond( … ); // slave will wait.
...wait for seconds at the Master side, before doing a read...
...master does a read...
...slave writes data...


I was pleased to see it working, since our PC host code does a bunch of sleeps between Writes and Reads to give the PIC24 chain time to respond. It looks like I can update this so the master can just Write/Read and it will wait (in a thread) until the slave responds. Awesome. That will really speed things up.
_________________
Allen C. Huffman, Sub-Etha Software (est. 1990) http://www.subethasoftware.com
Embedded C, Arduino, MSP430, ESP8266/32, BASIC Stamp and PIC24 programmer.
http://www.whywouldyouwanttodothat.com ?
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Goto page Previous  1, 2, 3, 4  Next
Page 3 of 4

 
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