AnsweredAssumed Answered

STM32F030F4P6 I2C Slave Transmitter Problem : TXI event triggered more times than requested by I2C master

Question asked by guwono.septijo on Jan 3, 2014
Latest reply on Jan 6, 2014 by guwono.septijo
Hi all..
i have a strange problem, on STM32F030F4P6 (20 pin TSSOP), using CooCox

I built an I2C Slave (LCD Shield). This unit must respon to other device act as I2C master.

This is what happened :
When the I2C Master request 2 bytes, the I2C_IT_TXIS in this I2C-slave was triggered/called/interrupted 3 times (only happened for the first time after MCU resetted). After the first request (and triggered 3x), it behaved normally (triggered twice) but the data received by I2C master is already screwed....


(Round 1 - After this I2C-Slave's MCU resetted)
1a) I2C-Master request 2 bytes
 b) I2C_IT_ADDR flag appear on this I2C-Slave, the variable jTX resetted to 0x00
 c) I2C_IT_TXI flag appear 3 times on this I2C-Slave.
 d) On each TXI flag, var jTX incremented and sent to I2C-Master (jTX=0x01, jTX=0x02 and jTX=0x03)
 e) however, only 2 bytes received by I2C-Master (jTX=0x01 and jTX=0x02)

(Round 2)
2a) I2C-Master request 2 bytes
 b) I2C_IT_ADDR flag appear on this I2C-Slave, the variable jTX resetted to 0x00
 c) I2C_IT_TXI flag appear 2 times on this I2C-Slave.
 d) On each TXI flag, var jTX incremented and sent to I2C-Master (jTX=0x1 and jTX=0x02)
 e) however, I2C-Master received 0x03 (third byte from event 1d) and received 0x01 (first byte from this event)

On the next 2 bytes Request-from-I2C-Master, I2C-Master will always receive 2 and 1

(Round 3,4,... etc)
3a) I2C-Master request 2 bytes
 b) I2C_IT_ADDR flag appear on this I2C-Slave, the variable jTX resetted to 0x00
 c) I2C_IT_TXI flag appear 2 times on this I2C-Slave.
 d) On each TXI flag, var jTX incremented and sent to I2C-Master (jTX=0x1 and jTX=0x02)
 e) however, I2C-Master received 0x02 (second byte from event 2d) and received 0x01 (first byte from this event)

if i reset this slave (STM32F030F4P6), the cycle back to round (1) (slave sent 0x01, 0x02 and 0x03 but master received 0x01 and 0x02), then round (2) (slave sent 0x01, 0x02 but master received 0x03 and 0x01), then round (3) (slave sent 0x01, 0x02 but master received 0x02 and 0x01) and so on ...

here is my Code.

#include <stm32f0xx.h>
#include <stm32f0xx_gpio.h>
#include <stm32f0xx_rcc.h>
#include <stm32f0xx_i2c.h>
#include <stm32f0xx_misc.h> // NVIC

int main(void) {
  // enable clock for used IO pins
  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);

  // I2C1 : SCL = 17:PA9, SDA = 18:PA10
  GPIO_InitTypeDef GPIO_InitStruct;
  GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
  GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStruct.GPIO_OType = GPIO_OType_OD; // Open Drain
  GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;
  GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
  GPIO_Init(GPIOA, &GPIO_InitStruct);

  GPIO_PinAFConfig(GPIOA, GPIO_PinSource9 , GPIO_AF_4); // 17:PA9  = AF4/I2C1_SCL
  GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_4); // 18:PA10 = AF4/I2C1_SDA

  RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);

  I2C_DeInit(I2C1);          //Deinit and reset the I2C to avoid it locking up
  I2C_SoftwareResetCmd(I2C1, ENABLE);
  I2C_SoftwareResetCmd(I2C1, DISABLE);

  // Init I2C1
  I2C_InitTypeDef  I2C_InitStructure;

  /* Configure the I2C clock source. The clock is derived from the HSI */
  RCC_I2CCLKConfig(RCC_I2C1CLK_HSI);

  I2C_Cmd(I2C1, ENABLE);

  /*!< I2C configuration */
  I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
  I2C_InitStructure.I2C_AnalogFilter = I2C_AnalogFilter_Enable;
  I2C_InitStructure.I2C_DigitalFilter = 0x00;
  I2C_InitStructure.I2C_OwnAddress1 = 121 << 1;
  I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
  I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
  I2C_InitStructure.I2C_Timing = 0x00200000;

  /* Apply I2C configuration after enabling it */
  I2C_Init(I2C1, &I2C_InitStructure);

  // Interrupted I2C1
  NVIC_InitTypeDef NVIC_InitStructure;
  NVIC_InitStructure.NVIC_IRQChannel = I2C1_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPriority = 1;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);

  I2C_ITConfig(I2C1, I2C_IT_ERRI | I2C_IT_TCI | I2C_IT_STOPI | I2C_IT_NACKI
       | I2C_IT_ADDRI | I2C_IT_RXI | I2C_IT_TXI, ENABLE);

  while(1) {
  }
}

uint16_t jTX = 0;
uint32_t I2C1_IRQHandler(void) {

  // on each request-from-I2C-Master, I2C_ADDR was triggered/called, thus the jTX variable was resetted
  if (I2C_GetITStatus(I2C1, I2C_IT_ADDR)) {
     I2C_ClearITPendingBit(I2C1, I2C_IT_ADDR);
     I2C_ClearFlag(I2C1,I2C_FLAG_ADDR);
     jTX = 0;
     return 0;
  }

  if (I2C_GetITStatus(I2C1, I2C_IT_RXI)) {
     I2C_ClearITPendingBit(I2C1, I2C_IT_RXI);
     uint8_t Data = I2C_ReceiveData(I2C1);
     return 0;
  }

  // on the first (2 byte) request, the I2C_IT_TXIS below was triggered/called 3 times !!
  //   so this I2C-slave-unit sent 1,2,3 (only 1 and 2 received by the I2C-master)
  // on the next 2 byte request from I2C-Master, it only triggered/called twice (sending 1 and 2)
  // and the I2C-Master received 3 (from last batch) and 1 from this batch
  if (I2C_GetITStatus(I2C1, I2C_IT_TXIS)) {
      jTX++;
      I2C_SendData(I2C1,jTX);
     return 0;
  }

  if (I2C_GetITStatus(I2C1, I2C_IT_STOPI)) {
     I2C_ClearITPendingBit(I2C1, I2C_IT_STOPI);
     I2C_ClearFlag(I2C1,I2C_FLAG_STOPF);
     return 0;
  }

  if (I2C_GetITStatus(I2C1, I2C_IT_ALERT)) {
    I2C_ClearITPendingBit(I2C1, I2C_IT_ALERT);
    return 0;
  }

  /* Check on I2C1 Time out flag and clear it */
  if (I2C_GetITStatus(I2C1, I2C_IT_TIMEOUT)) {
    I2C_ClearITPendingBit(I2C1, I2C_IT_TIMEOUT);
    return 0;
  }

  /* Check on I2C1 Arbitration Lost flag and clear it */
  if (I2C_GetITStatus(I2C1, I2C_IT_ARLO)) {
    I2C_ClearITPendingBit(I2C1, I2C_IT_ARLO);
    return 0;
  }

  /* Check on I2C1 PEC error flag and clear it */
  if (I2C_GetITStatus(I2C1, I2C_IT_PECERR)) {
    I2C_ClearITPendingBit(I2C1, I2C_IT_PECERR);
    return 0;
  }

  /* Check on I2C1 Overrun/Underrun error flag and clear it */
  if (I2C_GetITStatus(I2C1, I2C_IT_OVR)) {
    I2C_ClearITPendingBit(I2C1, I2C_IT_OVR);
    return 0;
  }

  /* Check on I2C1 Acknowledge failure error flag and clear it */
  if (I2C_GetITStatus(I2C1, I2C_IT_NACKF)) {
    I2C_ClearITPendingBit(I2C1, I2C_IT_NACKF);
    return 0;
  }

  /* Check on I2C1 Bus error flag and clear it */
  if (I2C_GetITStatus(I2C1, I2C_IT_BERR)) {
    I2C_ClearITPendingBit(I2C1, I2C_IT_BERR);
    return 0;
  }
  return 0;
}

anyone have any idea ? maybe my I2C initialization ? maybe my way on handling interrupt ? clive1 maybe ? :) i see so many times here that clive1 respond and solved lots of problem here.

i've tried everything i know..
a) Googling ... "STM32F0 I2C Slave Transmitter Interrupt TXI I2C_SendData" etc etc
b) adding/remove pull-up (4K7 to 3.3V.. to 5V)
c) change I2C-Timing (using "I2C_Timing_Configuration_V1.0.1.xls|" from ST)
d) change GPIO Speed to 2Mhz, 10Mhz, 50Mhz
e) change I2C Filter (enable/disable the I2C's Analog / Digital filter)
f) set the Master to add STOP and release the bus after Request (i use Arduino + IDE 1.5.2 for the I2C Master)
g) trying other Arduino Board .. (i have one Arduino Duemilanove and hundreds of Arduino Nano) (i'm really mean it.. i do have hundreds of Arduino Nano Boards and hundreds of ATMega328p SMD ready to be assembled)
h) reading dozen STM32 's documentation / errata PDFs ..
i) enabling before initializing.
   I2C_Cmd(I2C1,ENABLE);
   I2C_Init(I2C1,&I2C_InitStruct);

.... with no luck...

maybe FLUSHING TXDR Register / TX Buffer (like flush your "unused thingy" in the toilet) after I2C_Send (if TXI appear more times than it has to), will do the trick (flush  the third-sent-ghost-byte to prevent it received by I2C-Master).. but, sadly, i dunno how to do it ....

maybe CLEAR the TXI FLAG without SEND something can do the trick too.. but i dunno how to achieved that too.. (no one have ever shared his/her working code.. or maybe no one has ever succeeded to create a working / bug free STM32-I2C-Slave-Transmitter ????)

i'm afraid that it is a bug from the STM32 .. after googling for dozen of hours, i noticed that lots of people stumble in this STM32's I2C wall too (so they said that STM32's I2C was scrappy, ugly, unreliable... and so on and so forth)

example in STM32F0xx_StdPeriph_Lib_V1.0.0 wasn't help much because :
a) it uses CPAL -  i can't find step-by-step tutorial on how to use CPAL for Slave Transmitter
b) too complex to trace the Code
c) there are no Slave Transmitter Mode in the I2C section of the example
d) the example needed STM320518 eval board (lots of "commenting-work" to do before it can be compiled and flashed to mcu)

thanks in advance.

Outcomes