cancel
Showing results for 
Search instead for 
Did you mean: 

Fix I2C ACK delay

MLane.1
Associate

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; 
}

6 REPLIES 6
PSedl.2
Associate

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.

mchahn
Senior

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.1
Associate

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.

mchahn
Senior

> 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.

S.Ma
Principal

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....

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

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..