STM32F4 I2C Slave Receiver

Bala picture Bala · Aug 19, 2014 · Viewed 10.5k times · Source

I am using STM32F4 board as slave receiver and nordic board as master transmitter. I am able to send the slave address as 0x30 which is acknowledged by the slave and I send the device register address as 0x10 and I then send some data using

i2c_write(0x30, 0x10, data, 4);

I am able to get the events in my interrupt service routine. I received 0x00020002 for "I2C_EVENT_SLAVE_RECEIVER_ADDRESS_MATCHED". I then received 0x00020044 event and the clock stopped running. Can anyone please help me with this. By scoping, I saw Slave address, device register address and my first data with clock on the oscilloscope. But after that clock stopped.

I'm able to use STM32F4 as Master transmitter and read some sensors but I find it difficult to use STM32F4 as Slave receiver with nordic board as Master Transmitter

void i2c_init2()
{
    GPIO_InitTypeDef gpio_init;
    I2C_InitTypeDef i2c_init;
    NVIC_InitTypeDef NVIC_InitStructure, NVIC_InitStructure2;

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

        /*!< I2C Periph clock enable */
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE);
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);

        /* setup SCL and SDA pins
         * SCL on PB10 and SDA on PB11
         */
    gpio_init.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;     // we are going to use PB10 and PB11
    gpio_init.GPIO_Mode = GPIO_Mode_AF;                                 // set pins to alternate function
    gpio_init.GPIO_Speed = GPIO_Speed_50MHz;                        // set GPIO speed
    gpio_init.GPIO_PuPd = GPIO_PuPd_UP;                                 //Pull up resistor
    gpio_init.GPIO_OType = GPIO_OType_OD;                               //Open Drain
    GPIO_Init(GPIOB, &gpio_init);

        // Connect I2C2 pins to AF  
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_I2C2 ); // SCL
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource11, GPIO_AF_I2C2 ); // SDA

        /* Configure the Priority Group to 1 bit */
        NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

    NVIC_InitStructure.NVIC_IRQChannel = I2C2_EV_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    NVIC_InitStructure2.NVIC_IRQChannel = I2C2_ER_IRQn;
    NVIC_InitStructure2.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure2.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure2.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure2);

    I2C_ITConfig(I2C2, I2C_IT_EVT, ENABLE);
    I2C_ITConfig(I2C2, I2C_IT_ERR, ENABLE);
    I2C_ITConfig(I2C2, I2C_IT_BUF, ENABLE);


    i2c_init.I2C_ClockSpeed = 100000;
    i2c_init.I2C_Mode = I2C_Mode_I2C;
    i2c_init.I2C_DutyCycle = I2C_DutyCycle_2;
    i2c_init.I2C_OwnAddress1 = 0x30;
    i2c_init.I2C_Ack = I2C_Ack_Enable;
    i2c_init.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
    I2C_Init(I2C2, &i2c_init);

    I2C_StretchClockCmd(I2C2, ENABLE);
    I2C_Cmd(I2C2, ENABLE);
}

void I2C2_ER_IRQHandler(void)
{
        /* Read SR1 register to get I2C error */
    if ((I2C_ReadRegister(I2C2, I2C_Register_SR1 ) & 0xFF00) != 0x00)
    {
            STM_EVAL_LEDOn(LED6);
        /* Clears error flags */
        I2C2 ->SR1 &= 0x00FF;
    }
}

void I2C2_EV_IRQHandler(void)
{
    uint8_t dataRX;
    Event = I2C_GetLastEvent(I2C2 );
    printf("Event: 0x%x\n", Event);
    switch (Event)
    {

            case I2C_EVENT_SLAVE_RECEIVER_ADDRESS_MATCHED :
            {
                    printf("Slave Address Matched\n");
                    STM_EVAL_LEDOn(LED4);
                    I2C2 ->SR1;
                    I2C2 ->SR2;
                    break;
            }
            case I2C_EVENT_SLAVE_BYTE_RECEIVED :
            {
                    printf("Slave Byte Received\n");
                    dataRX = I2C_ReceiveData(I2C2 );
                    break;
            }
            case I2C_EVENT_SLAVE_ACK_FAILURE :
            {
                    STM_EVAL_LEDOn(LED3);
                    I2C2 ->SR1 &= 0x00FF;
                    break;
            }
            case I2C_EVENT_SLAVE_STOP_DETECTED :
            {
                    I2C2 ->SR1;
                    I2C2 ->CR1 |= 0x1;
                    break;
            }
    }
}

Answer

Chris Stratton picture Chris Stratton · Aug 19, 2014

I then received 0x00020044 event and the clock stopped running.

The term "event" is being used a bit loosely. What ST's header files do is define events as certain combinations of flags. You have a slightly different combination. Breaking it down, the following bits are set:

#define I2C_FLAG_BUSY                   ((uint32_t)0x00020000)
#define I2C_FLAG_RXNE                   ((uint32_t)0x10000040)
#define I2C_FLAG_BTF                    ((uint32_t)0x10000004)

(There are actually two sets in different registers - by the definition of your known events it looks like that leading "1" from the latter group gets dropped when they are combined, but I'm not 100% sure of that)

Looking in the reference manual, there is the following:

If RxNE is set and the data in the DR register is not read before the end of the next data reception, the BTF bit is set and the interface waits until BTF is cleared by a read from the I2C_DR register, stretching SCL low

These conditions would seem to be met, explaining why the STM32F4 I2C slave is stretching (stalling) the clock. It would appear that you need to read from the data register to allow it to continue - in effect, match this as an event and do that.

I further suspect you get in this condition when you have actually received two words - the one in the receive buffer signified by RXNE, and another in the receiver itself signified by BTF. At that point it is stuck and cannot accept any more - you might consider catching RXNE by itself by adding an interrupt enable for that, possibly improving efficiency by claiming the first word earlier before the second has finished receiving.

If you manage to get it to completely work, feel free to write your own exactly-what-you-did answer and accept that.