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
allenhuffman



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

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

PostPosted: Wed Nov 13, 2019 9:30 am     Reply with quote

gaugeguy wrote:
It actually says "sets or clears", not "cleared"


I found a lot of answers in the Microchip forums (people asking the same thing I am looking for, as far back as 2003) so I have much reading to do. (Funny enough, one of the folks answering me there today was asking the question over a decade ago. Pay it forward!)

I also found this excellent I2C overview from Microchip:

https://www.youtube.com/watch?v=yM9OpRPMEAE

It focuses on the Master, but gave me things to look at. For instance, Page 18 of that I2C document explains all the things that set the SI2CxIF bit, so when I was checking it ideally I'd need to do more before going off to an i2c_read().

The Microchip presentation also has a bunch of extra checks he does to make sure things get an ACK before moving forward. I do not know how much of this the i2c_xxx() calls handle. I know they return a byte, which could be 0x00, so I don't see how they could return a status saying "was not ACKed" so I could give up like the Microchip examples do.

I am getting closer to understanding how to implement our own Microchip I2C API so we I can add some of the functionality I want.
_________________
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: Wed Nov 13, 2019 9:53 am     Reply with quote

If you look at PCM programmer's I2C bus scanner, the way this works is
by detecting the ACK from the slave.
allenhuffman



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

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

PostPosted: Wed Nov 13, 2019 9:58 am     Reply with quote

Ttelmah wrote:
If you look at PCM programmer's I2C bus scanner, the way this works is
by detecting the ACK from the slave.


There seems to be plenty of Master examples (the Microchip presentation only covers Master code). I am writing Slave code, with a PC being the Master.

My ultimate goal is just to get the Stop bit detection working reliably in my polled code, then port it to five different boards we have.

I did find one bothersome note:

Quote:
What I found is that the stop bit does not generate an interrupt. What I noticed is that the stop flag did not appear to set immediately after the stop condition. So it became necessary after each received character to poll the status register for a status change within a fixed time period.


If true, that would explain a lot, since I am not doing any polling. Now that I've had time to look at how Microchip does it, I'm going to alter my approach today and see if it works. I'll post my results.
_________________
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: Wed Nov 13, 2019 1:40 pm     Reply with quote

It is exactly the same on the slave.
For 90% of transaction, the slave generates the ACK.
Only in the special case of a slave write, does the master generate the ACK.
Then, just as for a master, the ACKSTAT bit reflects this, _but_ this only
exists after the byte is transferred. On the master the master is generating
the clock, and the master transmit waits for the byte to send. On the slave
the write returns as soon as the buffer register is loaded, so you have
to wait for TBF to go to 0 before testing the bit.
allenhuffman



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

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

PostPosted: Wed Nov 13, 2019 3:06 pm     Reply with quote

Ttelmah wrote:
On the slave
the write returns as soon as the buffer register is loaded, so you have
to wait for TBF to go to 0 before testing the bit.


This is good to know.

I assume it would actually need to be enough time for that byte to be written out, and the Master to send the stop bit? There's a small amount of time between the last clocked Slave byte out and the stop condition being seen in the Saleae scope I am using.

This makes sense, based on what someone said in the Microchip forum about having to wait for the P to appear. I had code that worked with an extra "if" but not if that removed, and I tracked it down to timing - delay_ms(1), etc. trying to see what was going on. I was doing a hard-coded delay before the P check.

I've tried to keep the code the same as ISR code, except for polling/resetting the IRQ bit at the start, plus the addition of the P bit test (which I plan to add to the ISR code we use as well). I'll check out the buffer status.

QUESTION: When the slave starts writing a response, I am doing the same thing. If the slave tries to write a 10 byte response, and the master only reads 5 bytes (bad code, go to your room) it detects the P stop from the master and aborts the write (only 5 written). Is the P fast when writing back to the master?

I got my code working on five different boards today, using three different PIC24 parts. We now have multiple confirmations of having two types of PICs on the same line (with FTDI driving it from a PC master) can cause issues, unless we slow the speed down and change the FTDI clock rate. We've switched out the PIC part we use on two boards and the problem goes away. That chewed up a bunch of time figuring out.
_________________
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: Wed Nov 13, 2019 4:19 pm     Reply with quote

What is a good way to prevent a SLAVE from locking by doing an i2c_write() when the Master isn't ready (or already wrote) ?

What's the best thing to check before an i2c_read() to make sure it's in a good state?
_________________
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: Thu Nov 14, 2019 1:47 am     Reply with quote

The slave won't lock. All it does is load the buffer, and return.
It is then waiting for the master to clock the byte. The code can loop
waiting for this. The bus though will then be 'locked' with SDA held
by the slave.
The key is that if something like this happens, it is the master that has to
clear it. It does this by generating extra clock pulses. If the bus is not
running, the solution is to send a string of clocks (simplest way is to turn
off the I2C, and operate the clock line nine times). This then
'releases' the slave from it's waiting to send. This is in the I2C specs.
Physically test that SDA & SCL are high before starting operations
with the master.
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 14, 2019 9:27 am     Reply with quote

We have had so many issues with I2C lockups from various reasons, I am trying to do what I can on the Slave side to avoid that. If the Slave can detect it should not do an i2c_read () before doing that, I'd really like to add that code.

On the PIC24 I am using at the moment, the first thing i2c_read does is loop forever unless Start bit is 1:

Code:
* Check Start bit
007A4:  BTSS.B  278.3   // S - start bit (skip next instruction if set)
007A6:  BRA     7A4


It locks forever unless the Start bit is set. I expect I can just check that and hope between the check and the i2c_read() code that the stoP bit doesn't toggle to 1 indicating no more data (i.e. my code loop just loaded a byte, and went back to the top and thought there was more, then tries to read but the Master is done and then Stop bit is sent, leaving i2c_read() hanging.)

I'm trying to make my end of this code more bulletproof, since we have had so many issues with i2c lockups. The FTDI drivers do not give us control of the pins to do the "release" that is mentioned in the I2C specification.
_________________
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: Thu Nov 14, 2019 2:24 pm     Reply with quote

In I2C, it is the master's responsibility to deal with this not the slave.
Add the fix to the master.

If you are using interrupt driven handling, then you don't perform a
read, until the byte has already been received.
If you are polling the interrupt flag, then you are still using this hardware.
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 14, 2019 4:18 pm     Reply with quote

Ttelmah wrote:
In I2C, it is the master's responsibility to deal with this not the slave.
Add the fix to the master.

If you are using interrupt driven handling, then you don't perform a
read, until the byte has already been received.
If you are polling the interrupt flag, then you are still using this hardware.


Yes, this is for a polled I2C implementation.

The FTDI library also does not have timeouts, so the Master will hang on the read, deadlocking the system on both ends :-)

The transactions are like this:

Quote:
Master: write 100 bytes

Slave: read 100 bytes

...slave should turn around and write 5 byte response...

Master: read 5 bytes (hangs)

Slave: only now ready, attempts to write. (hangs)


I am looking at clock stretching to prevent the master from doing the read.

I have also created a commented listing of all the slave i2c_xxx() CCS library calls, showing what bits of each register they read/write and such. This will come in handy since we can implement our own and add timeouts for when doing this without IRQs.

I've learned more about I2C in the past 48 hours than I have in the past few months.
_________________
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: Fri Nov 15, 2019 11:13 am     Reply with quote

The latest problem I solved was better understanding how the FTDI Windows I2C driver works. I wrote a C# wrapper so I could use their DLL:

Code:
FT4222_I2CMaster_Write(FTDIHandle, slaveAddress, ref writeBuf[0], sizeToWrite, ref sizeTransferred);


This Master write call returns quickly, and can return while the I2C driver is still sending data. We use Master code that is like this pseudo code:

write (command);
sleep (50); // 50 ms
read (response);

At the bus speed we are operating, writing 10 bytes of data was taking around 10ms, and 100 bytes was around 100ms.

Thus, doing a write of 10 bytes, sleeping 50ms, then read only gave a 40ms gap between write and read.

But, when I tested with 100 bytes of data, a sleep of 50ms, and a read, there was basically no gap. The sleep(50) was happening while the FTDI driver was still outputting data.

I solved this by using their FT4222_I2CMaster_GetStatus() call, which returns an 8-bit value. Checking the IDLE bit and not sleeping until it was set gave me a 50ms gap between 10 and 100 byte packets.

The FTDI header file has a macro:

Code:
uint8_t status;
FT4222_I2CMaster_GetStatus(handle, &status);
#define I2CM_IDLE(status)            (((status) & 0x20) != 0)


...so I just did that in C#.

That keeps timing consistent, and now I need to add some extra code to my PIC24 *polled* routine so it does not try to read if the bus is not set up for that. That way, if timing is off, the Slave won't hang.

After the Master writes data, I see SDA and SCL are both high, then after my sleep delay, I see the Master Start bit and things continue as expected.

In the bad case, where the Slave isn't ready yet (Master did not wait long enough before trying to read), I see the initial Master Write go through, then a Stop bit, and SDA and SCL go high. Then, the Master tries to read and sends the Start/Address and both SDA and SCL go low...

At that point, if the Slave then tries to do a read, it hangs polling the SI2CxIF interrupt flag status bit.

I plan to add some code in my polled Slave read routine to check for that first. If the bus lines are not where we expect, I'll give up with an error that the user code can detect so we know there is a timing problem (much better than just hanging).
_________________
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: Mon Nov 18, 2019 9:21 am     Reply with quote

Writing polled I2C code (no interrupts) using the stock CCS functions has provided a variety of places where a bus hang is possible. While the code I am using seems rock solid when timing is correct, I have been doing failure tests -- sending things too soon, too late, etc. -- to try to see what can be done on the Slave side to avoid hangs when in production.

For example, the i2c_read() call polls for the Start bit flag at the beginning:

Code:
* START
007A4:  BTSS.B  278.3   // S - skip next if 0 (no start bit)
                  //     if 0, go to START.
                  //     if 1, go to GCSTAT.
007A6:  BRA     7A4      // go to START


When using IRQs, the interrupt will always catch this (assuming some higher priority interrupt doesn't hog things too long). But when polling, if the Slave is busy processing a previous message, and the Master does a complete Write before Slave code can detect, by the time the Slave code runs it will see the IRQ bit pending, but the Start, Data, and Stop has already gone through. The Start bit was missed, and we get a bus hang.


i2c_read() will also poll the receive buffer status (RBF) bit (when not using the ,2 option) and can hang there if timing is badly off.

Because of this, trying to use these calls outside of an interrupt service routine can cause a bus hang if timing is not correct. (See previous post about the Windows side sending a read request way sooner than what the Slave could get to.)

I have been re-writing my own wrappers to these calls that add timeouts, with the intent that if this does happen, the code and break out and release the bus. TO the calling code, it will just be a "bad message" type error and it can go back to looking for a good one (and the Master would, hopefully, retry).

I now need to go back and revisit that Microchip I2C presentation and see if some of the clock stretching stuff makes more sense to me. There's some stuff handled by the hardware (STREN configuration bit) that will cause the clock to be held ("Holds CSLx clock low") at the beginning (start bit?) and end (stop bit?) of slave reception, which is why the i2c_read() calls only do a release at the end (other than the ,2 option which leaves it alone).

Happy Monday!
_________________
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: Mon Nov 18, 2019 9:40 am     Reply with quote

The clock hold is vital to I2C.

Remember the lines are 'open collector', so either device can drive a line
low. The CLK line acts as an NOR gate only high when neither master nor slave
is driving it. The slave holds the line low until it's transaction is finished,
delaying the master from being able to continue. This is essential to
maintaining the timing. Without this you have to 'guarantee' that the slave has
processed every transaction before another operation can begin....
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 9:58 am     Reply with quote

Ttelmah wrote:
The clock hold is vital to I2C.

Remember the lines are 'open collector', so either device can drive a line
low. The CLK line acts as an NOR gate only high when neither master nor slave
is driving it. The slave holds the line low until it's transaction is finished,
delaying the master from being able to continue. This is essential to
maintaining the timing. Without this you have to 'guarantee' that the slave has
processed every transaction before another operation can begin...


My current understanding is that the Slave PIC24 is holding that clock line as soon as it sees a Master "READ" request (byte with that R/W bit set). At the end of a Slave i2c_read(), that would be released.

I am going to try checking the Overflow bit in my non-ISR routine. If I see an interrupt flag, then check OV and see it is hit, I think I can assume that the receive buffer has the address from the Master's "read" request, but lost some data after it (FTDI driver does not check ACK/NACK and just keeps blindly writing). Thus, if I am in that condition, I can do an i2c_read() to get the address (?) but I want to bail since I've missed data. I would manually release the clock.

Code:
slaveReceive(); // ... waits for start bit, reads data up until stop bit, returns
processMessage();
slaveRespond(); // … send reply back to Master


slaveRespond() may do something like this to catch various ways the Slave can get out of sync if the Master didn't wait long enough.

Code:
slaveRespond()
{
   // wait for IRQ pending bit
   // clear IRQ pending bit
   // if OVerflow is set, release clock and bail?
   // if here, I was going to check for stoP bit - if it's set, we missed the whole message.
   // OVerflow may be enough.
}


Something like that.
_________________
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: Mon Nov 18, 2019 11:35 am     Reply with quote

Well, that didn't get me anywhere. I may be battling another PIC issue like we found with another processor, in regards to I2C.

After processing a Slave read from the Master:

Start bit, data bytes, stop bit

...I then turn around to send a response. At the start of my polled code, I see that the Start bit is set, but there is no interrupt flag pending and the receive buffer is empty.

Capturing this on the Saleae scope show the initial Master write, then it shows the Master sent a start bit followed by the address with the bit set indicating a read.

The PIC24 I2C3STAT register reflects that the Start bit was last seen (S==1, P==0) but there is no IRQ flag set, and RBF is 0 as well.

I power cycled the PIC24 board and repeated this.

This sends me back to the datasheet to see if there are other things that clear that interrupt flag.
_________________
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 2 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