View previous topic :: View next topic |
Author |
Message |
reesesm2000
Joined: 18 Sep 2022 Posts: 12
|
I2C Slave clock stretching |
Posted: Sun Oct 23, 2022 10:54 am |
|
|
I have been trying for a while now to figure out why my slave device is stretching the clock in I2C. I have taken the sample code from ex_i2c_slave_k42.c and put it into a PIC16F15225. Only things I changed in the example were the device, the I2C pins and the #use to
#use I2C(SLAVE, scl=SCL_PIN, sda=SDA_PIN, address=0xA0, no_stretch, force_hw)
Even though I have NO_STRETCH in the #use, the clock is still being stretched by the device. (Not sure how to add images to this forum but I do have one showing the stretching.)
I have also tried this with a 16F18426 with the same result.
Anyone have any idea why the device is still stretching the clock? |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19495
|
|
Posted: Mon Oct 24, 2022 1:30 am |
|
|
It will.
For your PIC the hardware does this.
Look at the timing diagram in the datasheet for your chip. Say 25-17.
Look at the SCL line. Note that it is held low until CKP is set. All automatic.
The 'NO_STRETCH' option is dependant on what the chip supports. On
devices like many of the PIC24's, there is a 'STREN' bit (stretch enable),
and on these this can be turned off.
So the data sheet entry really needs to say:
NO_STRETCH - Disables clock stretching (if this is supported by the chip).
As a comment, clock stretching is _mandatory_ in I2C. The only reason
these later chips allow it to be disabled is to support other bus standards. |
|
|
reesesm2000
Joined: 18 Sep 2022 Posts: 12
|
|
Posted: Mon Oct 24, 2022 5:02 am |
|
|
Thanks for the reply.
That is sad. I was hoping to use a PIC with a RPi using HW I2C on both sides but due to the bug in the Broadcom chipset in the RPi, it seems I need to find another device other than a PIC.
I find it weird that a PIC running at 32MHz needs to clock stretch an I2C bus running at 100KHz or even 10KHz. Must be a strange HW implementation to need to stretch the clock. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19495
|
|
Posted: Mon Oct 24, 2022 5:17 am |
|
|
If your slave reads the byte immediately, there shouldn't be any visible
stretching at all.
At 10KHz, you'd have time to do a little dance, even on a slave transmit
routine.
If there is visible stretching, then it says there is something wrong with
your slave handler code.
The Broadcom chipset, has an issue, when the clock is released less than
5uSec after the end of the I2C bit time. Otherwise it accepts it OK. So
you may have to slow down your read code. |
|
|
reesesm2000
Joined: 18 Sep 2022 Posts: 12
|
|
Posted: Mon Oct 24, 2022 3:11 pm |
|
|
This is the entirety of the code. It is really just the example program that comes with the compiler.
Code: | #include <16F15225.h>
#device icd=true
#use delay(internal=32MHz)
#fuses LVP
#define SCL_PIN PIN_C0
#define SDA_PIN PIN_C1
#pin_select SCL1OUT = SCL_PIN
#pin_select SDA1OUT = SDA_PIN
#use I2C(SLAVE, scl=SCL_PIN, sda=SDA_PIN, address=0xA0, no_stretch, force_hw)
unsigned int8 address, buffer[256];
#INT_SSP
void i2c_interrupt(void)
{
unsigned int8 state, incoming;
state = i2c_isr_state();
if(state <= 0x80) //Master is sending data
{
if(state == 0x80)
incoming = i2c_read(2); //Passing 2 as parameter, causes the function to read the SSPBUF without releasing the clock
else
incoming = i2c_read();
if(state == 1) //First received byte is address
address = incoming;
else if(state >= 2 && state != 0x80) //Received byte is data
buffer[address++] = incoming;
}
if(state >= 0x80) //Master is requesting data
{
i2c_write_slave(buffer[address++]);
}
}
void main(void)
{
unsigned int16 i;
for(i=0;i<256;i++)
buffer[i] = i;
enable_interrupts(GLOBAL);
enable_interrupts(INT_SSP);
while(TRUE)
{
}
} |
This stretches the clock cycle by ~3/8ths (or a bit more) of a cycle resulting in a tiny high pulse due to the bug in the RPi. This causes another device to misbehave on the bus pulling the SDA line low and messing up everything.
The misbehaving device is also a PIC (provided by a third party) and interestingly, it does not cause the clock to be stretched. It is a PIC18F device so presumably not supporting disabling stretching either. I am about to try and port the code to MPLAB and use the xc8 compiler and see if it makes any difference. Another project I have also using a 16F18426 does not exhibit this behaviour but it is compiled using xc8. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19495
|
|
Posted: Tue Oct 25, 2022 1:35 am |
|
|
No.
Some PIC18's do allow stretch to be disabled. So (for example), the
PIC18F2525, has a SEN bit (start/stretch enable), which allows this to be
disabled in slave mode. The same bit exists on your chip, but only affects
master mode.
Problem is you have to search each individual datasheet for whether
this ability exists.
So on the 2525:
Quote: |
bit 0 SEN: Start Condition Enable/Stretch Enable bit(2)
In Master mode:
1 = Initiate Start condition on SDA and SCL pins. Automatically cleared by hardware.
0 = Start condition Idle
In Slave mode:
1 = Clock stretching is enabled for both slave transmit and slave receive (stretch enabled)
0 = Clock stretching is disabled
|
While on your chip:
Quote: |
Bit 0 – SEN
Start Condition Enable bit (Master mode only)(2)
Value Description
1 Initiates Start condition on SDAx and SCLx pins; automatically cleared by hardware
0 Start condition is Idle
|
Note the 'master mode only' bit of this on your chip.
There are PIC16's that support this. For instance the 16F183x6.
You just have happened to have 'shot yourself in the foot', by requiring
this, and choosing a chip that does not support it. |
|
|
reesesm2000
Joined: 18 Sep 2022 Posts: 12
|
|
Posted: Tue Oct 25, 2022 5:48 pm |
|
|
Moved over to MPLABX and compiling with xc8 and the clock stretching is gone.
Slightly different code because of the MCC generated bits but also a 24xx simulation returning exactly the same data. (16 bytes read from address 16)
Will have to decode the hex next to see what is different from PIC-C to xc8 is. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19495
|
|
Posted: Wed Oct 26, 2022 1:04 am |
|
|
Try a simple thing.
Try setting the SEN bit yourself (just do a #bit SEN=getenv("BIT:SEN"),
and then at the start of the code set this to '1'.
One possibility, is that the SEN bit _does_ actually work (though the data
sheet says it shouldn't), and CCS have followed the data sheet, while
MicroChip have set the bit anyway.... |
|
|
reesesm2000
Joined: 18 Sep 2022 Posts: 12
|
|
Posted: Wed Oct 26, 2022 5:11 pm |
|
|
Not sure what that does under the skin but it breaks comms totally. The master sends a write to the address, the slave ACKs it and then pulls both the SDA and SCL lines low and never releases them. Removing SEN=1 and we are back to the way it was before. I verified it was the slave pulling them low by pulling it out of the bus. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19495
|
|
Posted: Thu Oct 27, 2022 12:58 am |
|
|
OK. So the bit doesn't work on this slave, which is what the data sheet
says. |
|
|
reesesm2000
Joined: 18 Sep 2022 Posts: 12
|
|
Posted: Sat Oct 29, 2022 1:41 pm |
|
|
So after much testing and trying different things, unfortunately there is something broken in the PIC-C I2C Slave implementation. No matter what I change, I cannot get it to not misbehave on the bus. It took a while but I converted the whole code base to compile in MPLAB X and everything runs just fine. No messing with the bus, no stretching clocks. It just works. The waveforms are really clean too.
I much prefer PIC-C but there is some bug in there that I just cannot get past. Pity because constant crashes of MPLAB X 6 is a pain but at least it works. |
|
|
newguy
Joined: 24 Jun 2004 Posts: 1907
|
|
Posted: Sat Oct 29, 2022 4:22 pm |
|
|
Take a look at the disassembly from MPLAB/XC8 and compare it to the CCS disassembly. Start with the setup of the I2C and see if there are any differences. If there are, use the MPLAB setup in the CCS version and see if it makes a difference. If not, move on to the implementation (i.e. how MPLAB uses the I2C), and repeat.
It's probably just a bug with the compiler. Once you figure out what's wrong, fixing it will be easy. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19495
|
|
Posted: Mon Oct 31, 2022 12:26 am |
|
|
Key thing that has not been said, is which actual i2C transaction shows the
stretch?. The obvious one that will is where the master requests data from
the slave. Here the CCS code deliberately holds the clock, till the new data
is loaded. This is done by the line:
incoming = i2c_read(2);
Now if this is the line causing the problem (try just removing the '2').
You have a problem working out how to handle this, since the data needs
to be loaded before the master read is performed. One thing that can
speed the PIC slave I2C handling a lot is to use I2C_write_slave throughout
instead of I2C_write. This is not shown in the example, but is the better
way of handling the slave write transactions. Also don't take the write data
from an array (slow to access). |
|
|
|