2023-06-28 02:46 AM - edited 2023-06-28 02:47 AM
I have the following setup: Master<---------I2C------------>STM slave
The master communicates like this:
1) Master I2C write: [Start bit, I2C address (from master)], [Req Data1 (from master)]
2) Master I2C read: [Start bit, I2C address (from master)], [Resp Data1 (from slave)],......,[Resp DataN (from slave)]
With a "vanilla" STM slave implementation there is a problem because the STM slave requires time in between (1) and (2) to decode the "request type" byte and prepare the response data. However the master has no delay between (1) and (2), so the result is that the initial response bytes are invalid.
Normally this sort of issue is handled on slave I2C devices by clock stretching. In the STM32F401RE user manual the description of the clock stretching implementation seems to suggest that it operates on a single I2C transfer, not between I2C transfers. Is that correct? For example from the diagram below is it possible to hold clock stretching beyond EV4?
I've had trouble finding STM I2C slave example code (I guess normally it is the master) and the examples that I have found don't have custom clock stretching.
Can someone explain to me how to implement clock stretching between I2C transfers? Or perhaps there is a better way to prevent the master clocking the slave data out before it is ready?
Solved! Go to Solution.
2023-06-29 03:49 AM
Thanks for your responses. I've implemented like this which seems to work.
void I2C1_EV_IRQHandler(void)
{
uint32_t SR1_register = I2C1->SR1;
uint32_t SR2_register = I2C1->SR2;
if (SR1_register & I2C_SR1_ADDR) // EV1
{
// Only clear ADDR flag for master write operations.
// Otherwise leave it set to hold the SCL line low for clock stretching.
if (SR2_register & I2C_SR2_TRA)
{
// Matching I2C address received from master READ
if (waiting_for_response_data_ready)
{
// DON'T clear the ADDR flag so clock stretching is started
// Instead disable the I2C1_EV interrupt until the response data is ready
DISABLE_I2C1_EVENT_INTERRUPT();
}
else
{
// Response data already prepared, clear the ADDR flag as usual
__I2C_CLEAR_ADDRFLAG(I2C1);
}
}
else
{
// Matching I2C address received from master WRITE
// Clear ADDR flag
__I2C_CLEAR_ADDRFLAG(I2C1);
}
}
if (SR1_register & I2C_SR1_RXNE) // EV2
{
// Data byte received from master
request_data_byte = I2C1->DR;
waiting_for_response_data_ready = true;
}
if (SR1_register & I2C_SR1_STOPF) // EV4
{
// Stop received from master
// Clear STOPF flag
__I2C_CLEAR_STOPFLAG(I2C1);
}
}
2023-06-28 04:30 AM
The slave can simply not respond while it is not ready. Many devices behave this way, such as eeproms.
2023-06-28 05:04 AM
What do you mean by "not respond"? If the STM slave doesn't drive the SDA line when the master is clocking the SCL line, then the master will get invalid data won't it?
2023-06-28 09:47 AM
> Can someone explain to me how to implement clock stretching between I2C transfers? Or perhaps there is a better way to prevent the master clocking the slave data out before it is ready?
This is not possible since the slave does not have control of the clock between transfers. It only controls it after the ACK, and that is where clock stretching is implemented in the STM32. This is the proper place to implement it when the slave needs time to process data before responding.
2023-06-28 10:09 AM
You have two opportunities to slow down the transfer. The first is to delay ACK-ing the data byte in the 1st sequence (i.e. read the data byte, process it, gather the data for your response, then tell the STM32 to ACK). Not sure this is possible with the HAL library. The second place is to clock stretch the ACK to the address byte in the 2nd (read) sequence. You can use the HAL_I2C_AddressCallback() for this.
2023-06-28 03:56 PM
> What do you mean by "not respond"?
Not ACKing the device address. Pretending not being there.
2023-06-29 03:49 AM
Thanks for your responses. I've implemented like this which seems to work.
void I2C1_EV_IRQHandler(void)
{
uint32_t SR1_register = I2C1->SR1;
uint32_t SR2_register = I2C1->SR2;
if (SR1_register & I2C_SR1_ADDR) // EV1
{
// Only clear ADDR flag for master write operations.
// Otherwise leave it set to hold the SCL line low for clock stretching.
if (SR2_register & I2C_SR2_TRA)
{
// Matching I2C address received from master READ
if (waiting_for_response_data_ready)
{
// DON'T clear the ADDR flag so clock stretching is started
// Instead disable the I2C1_EV interrupt until the response data is ready
DISABLE_I2C1_EVENT_INTERRUPT();
}
else
{
// Response data already prepared, clear the ADDR flag as usual
__I2C_CLEAR_ADDRFLAG(I2C1);
}
}
else
{
// Matching I2C address received from master WRITE
// Clear ADDR flag
__I2C_CLEAR_ADDRFLAG(I2C1);
}
}
if (SR1_register & I2C_SR1_RXNE) // EV2
{
// Data byte received from master
request_data_byte = I2C1->DR;
waiting_for_response_data_ready = true;
}
if (SR1_register & I2C_SR1_STOPF) // EV4
{
// Stop received from master
// Clear STOPF flag
__I2C_CLEAR_STOPFLAG(I2C1);
}
}