Skip to main content
Visitor II
December 31, 2019
Question

Fix I2C ACK delay

  • December 31, 2019
  • 5 replies
  • 3340 views

How can I fix the super long delay and weird ACK glitch with I2C on STM8S?

I'm having some troubles with I2C communications between a raspberry pi and STM8S. The STM8S is a slave transmitter and I2C is running at 100 KHz. It works much the time, but I get a bad reading about 5% of the time. It seems to be due to way rpi implements clock stretching, and what seems like an excessive delay after being addressed.

0690X00000BvV8iQAF.jpg

In this case the logic analyzer is showing what I expected the rpi to read, but it received 0x90E1 instead of 0x10E1. It seems to be due to the super stretched and compressed clock, and the unexpected high signal during during the ACK after being addressed.

I have the STM8S running at 16 MHz and have tried 4.7k and 1k pull ups with no change in behavior.

I've also tried turning off all interrupts other than I2C and still see the same behavior. Same thing with setting the other interrupts to lower priorities.

I've also tried disabling clock stretching, using different clock speeds, and changing interrupt priorities. The only thing that's been effective is lowering the clock speed to 40 KHz, but that's not a viable long term solution.

Full source code is at https://github.com/Ranthalion/ph/tree/master/firmware/src.

Here are some snippets of the set up code.

void init()
{
 clock_setup();
 GPIO_setup();
 ADC_setup();
 TIM2_setup();
 I2C_setup(0x45);
 
 ITC_SetSoftwarePriority(ITC_IRQ_TIM2_OVF, ITC_PRIORITYLEVEL_2);
 ITC_SetSoftwarePriority(ITC_IRQ_ADC1, ITC_PRIORITYLEVEL_2);
 
 /* Enable general interrupts */
 enableInterrupts();
}
 
 
 
void ADC_setup()
 {
 ADC1_DeInit();
 
 ADC1_Init(ADC1_CONVERSIONMODE_CONTINUOUS, 
 ADC1_CHANNEL_6,
 ADC1_PRESSEL_FCPU_D12, 
 ADC1_EXTTRIG_GPIO, 
 DISABLE, 
 ADC1_ALIGN_RIGHT, 
 ADC1_SCHMITTTRIG_CHANNEL6, 
 DISABLE);
 ADC1_DataBufferCmd(ENABLE);
 ADC1_ITConfig(ADC1_IT_EOCIE, ENABLE);
 ADC1_Cmd(ENABLE);
 }
 
 void I2C_setup(uint16_t address)
 {
 I2C_DeInit();
 I2C_Init(100000, 
 address << 1, 
 I2C_DUTYCYCLE_2, 
 I2C_ACK_CURR, 
 I2C_ADDMODE_7BIT, 
 (CLK_GetClockFreq() / 1000000));
 //I2C_StretchClockCmd(DISABLE);
 
 /* Enable Error Interrupt*/
 I2C_ITConfig((I2C_IT_TypeDef)(I2C_IT_ERR | I2C_IT_EVT | I2C_IT_BUF), ENABLE);
 
 I2C_Cmd(ENABLE);
 }

Here's the relevant part of the interrupt handler.

INTERRUPT_HANDLER(I2C_IRQHandler, 19)
{
 static u8 sr1; 
 static u8 sr2;
 static u8 sr3;
 
 // save the I2C registers configuration
 sr1 = I2C->SR1;
 sr2 = I2C->SR2;
 sr3 = I2C->SR3;
 
 /* Communication error? */
 if (sr2 & (I2C_SR2_WUFH | I2C_SR2_OVR |I2C_SR2_ARLO |I2C_SR2_BERR))
 { 
 I2C->CR2|= I2C_CR2_STOP; // stop communication - release the lines
 I2C->SR2= 0; // clear all error flags
 SetLED(LED_PANIC, 1);
 }
 
 /* More bytes received ? */
 if ((sr1 & (I2C_SR1_RXNE | I2C_SR1_BTF)) == (I2C_SR1_RXNE | I2C_SR1_BTF))
 {
 //I2C_byte_received(I2C->DR);
 Slave_Buffer_Rx[Rx_Idx++] = I2C->DR;
 }
 
 /* Byte received ? */
 if (sr1 & I2C_SR1_RXNE)
 {
 //I2C_byte_received(I2C->DR);
 Slave_Buffer_Rx[Rx_Idx++] = I2C->DR;
 }
 
 /* NAK? (=end of slave transmit comm) */
 if (sr2 & I2C_SR2_AF)
 { 
 I2C->SR2 &= ~I2C_SR2_AF; // clear AF
 if (led.pattern == LED_OFF)
 {
 LEDOff();
 }
 }
 
 /* Stop bit from Master (= end of slave receive comm) */
 if (sr1 & I2C_SR1_STOPF) 
 {
 I2C->CR2 |= I2C_CR2_ACK; // CR2 write to clear STOPF
 if (led.pattern == LED_OFF)
 {
 LEDOff();
 }
 }
 
 /* Slave address matched (= Start Comm) */
 if (sr1 & I2C_SR1_ADDR)
 {
 Rx_Idx = 0;
 }
 
 /* More bytes to transmit ? */
 if ((sr1 & (I2C_SR1_TXE | I2C_SR1_BTF)) == (I2C_SR1_TXE | I2C_SR1_BTF))
 {
 I2C_SendData(0x00);
 }
 
 /* Byte to transmit ? */
 if (sr1 & I2C_SR1_TXE)
 {
 if (led.pattern == LED_OFF)
 {
 LEDToggle();
 }
 
 if (Rx_Idx == 0)
 {
 I2C->DR = (phRaw >> 8);
 }
 else if (Rx_Idx ==1)
 { 
 I2C->DR = phRaw;
 }
 else
 {
 I2C->DR = 0xFF;
 }
 Rx_Idx++; 
 } 
 
 return; 
}

    This topic has been closed for replies.

    5 replies

    Visitor II
    November 30, 2020

    Hello MLane.1

    Have you found a solution for you problem? I am struggling with exactly the same problem: Clock stretching at the STM8 slave. The RPi seems not to know the clock stretching and produces short glitches on SDA. I get different results with the osciloscope I2c decoder and with the i2cget command on the RPi.

    Explorer
    November 13, 2021

    Rasperry Pi (actually Broadcom) has a bug that breaks clock stretching. Broadcom has refused to admit it but it is well documented. Google it.

    MLane.1Author
    Visitor II
    November 13, 2021

    Thanks for the response. That is the same root cause I came to find. I was not able to identify any other solution, so I had to hyper-optimize my ST code to ensure that clock stretching was not necessary.

    Explorer
    November 13, 2021

    > I had to hyper-optimize my ST code to ensure that clock stretching was not necessary.

    I had to use a bit-bang i2c on my raspi.

    Visitor II
    November 14, 2021

    As I recall, there is a very short interrupt latency present at the beginning of slave transmit event. Either boost reactivity or reduce i2c bit rate....

    Graduate II
    November 14, 2021

    Look to be ways to optimize decision trees and code paths.​