0
\$\begingroup\$

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.

Clock Stretching problem

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 relevent part of the interrupt.

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; 
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.