AnsweredAssumed Answered

STM32F4Discovery I2C in Slave mode

Question asked by yosypenko.vasyl on Dec 24, 2014
Hello!

I need to connect two STM32F407Discovery board via i2c for my task. First board is master which send data packet to slave board and receive data back. Slave board just receive data and save it for master.

Next part works correct, slave send ack to master; led is blinking.

uint8_t received_data[2];
 
    while(1)
    {
        I2C_start(I2C1, SLAVE_ADDRESS, I2C_Direction_Transmitter);
        I2C_write(I2C1, 0x20);
        I2C_write(I2C1, 0x40);
        I2C_write(I2C1, 0x80);
        I2C_stop(I2C1);
 
        LED_GREEN_TOGGLE;
        _delay_ms(50);
    }
}

When I try to receive data from slave:
uint8_t received_data[2];
 
    while(1)
    {

        I2C_start(I2C1, SLAVE_ADDRESS, I2C_Direction_Transmitter);
        I2C_write(I2C1, 0x20);
        I2C_write(I2C1, 0x40);
        I2C_write(I2C1, 0x80);
        I2C_stop(I2C1);
 
        I2C_start(I2C1, SLAVE_ADDRESS, I2C_Direction_Receiver);
        received_data[0] = I2C_read_ack(I2C1);
        received_data[1] = I2C_read_nack(I2C1);
        I2C_stop(I2C1);
 
        LED_GREEN_TOGGLE;
        _delay_ms(50);
 
    }
}
 
my program is freezing on line: I2C_start(I2C1, SLAVE_ADDRESS, I2C_Direction_Receiver); ,Led isn't blinking.

Code for slave board:
void I2C_Initialization(void)
{
    GPIO_InitTypeDef gpio_init;
    I2C_InitTypeDef i2c_init;
 
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
 
    gpio_init.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
    gpio_init.GPIO_Mode = GPIO_Mode_AF;
    gpio_init.GPIO_Speed = GPIO_Speed_50MHz;
    gpio_init.GPIO_PuPd = GPIO_PuPd_UP;  //Pull up resistor
    gpio_init.GPIO_OType = GPIO_OType_OD;  //Open Drain
    GPIO_Init(GPIOB, &gpio_init);
 
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_I2C1 ); // SCL
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_I2C1 ); // SDA
 
 
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
 
    I2C_DeInit(I2C1);
    I2C_SoftwareResetCmd(I2C1, ENABLE);
    I2C_SoftwareResetCmd(I2C1, DISABLE);
 
    i2c_init.I2C_ClockSpeed = 100000;
    i2c_init.I2C_Mode = I2C_Mode_I2C;
    i2c_init.I2C_DutyCycle = I2C_DutyCycle_2;;
    i2c_init.I2C_OwnAddress1 = SLAVE_ADDRESS;
    i2c_init.I2C_Ack = I2C_Ack_Enable;
    i2c_init.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
    I2C_Init(I2C1, &i2c_init);
 
    I2C_ITConfig(I2C1, I2C_IT_ERR, ENABLE);
    I2C_ITConfig(I2C1, I2C_IT_EVT, ENABLE);
    I2C_ITConfig(I2C1, I2C_IT_BUF, ENABLE);
 
    NVIC_InitTypeDef NVIC_InitStructure;
    // Configure the I2C event priority
    NVIC_InitStructure.NVIC_IRQChannel                   = I2C1_EV_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority        = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd                = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
 
    I2C_StretchClockCmd(I2C1, ENABLE);
    I2C_Cmd(I2C1, ENABLE);
}
 
//Clear ADDR by reading SR1, then SR2
void I2C_clear_ADDR(I2C_TypeDef* I2Cx)
{
    I2C_GetFlagStatus(I2Cx, I2C_FLAG_ADDR);
    ((void)(I2Cx->SR2));
}
 
//Clear STOPF by reading SR1, then writing CR1
void I2C_clear_STOPF(I2C_TypeDef* I2Cx)
{
    I2C_GetFlagStatus(I2Cx, I2C_FLAG_STOPF);
    I2C_Cmd(I2Cx, ENABLE);
}
 
 
 
 
 
void I2C1_EV_IRQHandler()
{
 
    //Clear AF from slave-transmission end
    if(I2C_GetITStatus(I2C1, I2C_IT_AF)) I2C_ClearITPendingBit(I2C1, I2C_IT_AF);
 
    switch (I2C_GetLastEvent(I2C1))
    {
        //SLAVE
        //Receive
        case I2C_EVENT_SLAVE_RECEIVER_ADDRESS_MATCHED: //EV1
                    I2C_clear_ADDR(I2C1);
                    break;
 
        case I2C_EVENT_SLAVE_BYTE_RECEIVED: //EV2
                    //Read it, so no one is waiting, clears BTF if necessary
 
                    data[i++] = I2C_ReceiveData(I2C1);
 
                    if (i == 3) i = 0;
 
                    break;
/*
                    //Do something with it
                    if(I2C_GetFlagStatus(I2C1, I2C_FLAG_DUALF))
                    {//Secondary Receive
                    }
                    else if(I2C_GetFlagStatus(I2C1, I2C_FLAG_GENCALL))
                    {//General Receive
                    }
                    else
                    {//Normal
                    }
                    break;
*/
        case I2C_EVENT_SLAVE_STOP_DETECTED: //End of receive, EV4
                    I2C_clear_STOPF(I2C1);
                    break;
 
 
        //Transmit
        case I2C_EVENT_SLAVE_TRANSMITTER_ADDRESS_MATCHED: //EV1
                    I2C_clear_ADDR(I2C1);
                    //Send first byte
                    //I2C_SendData(I2C1, 0x25);
                    break;
 
        case I2C_EVENT_SLAVE_BYTE_TRANSMITTED: //EV3
                    //Determine what you want to send
                    //data = 5;
                    if(I2C_GetFlagStatus(I2C1, I2C_FLAG_DUALF))
                    {//Secondary Transmit
                        //I2C_SendData(I2C1, 0x33);
                    }
                    else if(I2C_GetFlagStatus(I2C1, I2C_FLAG_GENCALL))
                    {//General Transmit
                        //I2C_SendData(I2C1, 0x11);
                    }
                    else
                    {//Normal
                        //I2C_SendData(I2C1, 0x44);
                    }
                    //Read flag and write next byte to clear BTF if present
                    I2C_GetFlagStatus(I2C1, I2C_FLAG_BTF);
                    I2C_SendData(I2C1, 0x44);
                    break;
 
        case I2C_EVENT_SLAVE_ACK_FAILURE://End of transmission EV3_2
                    LED_ORANGE_ENABLE;
                    //TODO: Doesn't seem to be getting reached, so just
                    //check at top-level
                    I2C_ClearITPendingBit(I2C1, I2C_IT_AF);
                    break;
 
        default:
                    break;
 
    }
 
}
 
 
void I2C1_ER_IRQHandler(void)
{
        //GPIO_SetBits(GPIOD, RED);
        //Can't use nice switch statement, because no fxn available
        if(I2C_GetITStatus(I2C1,        I2C_IT_SMBALERT)) {
        } else if(I2C_GetITStatus(I2C1, I2C_IT_TIMEOUT)) {
        } else if(I2C_GetITStatus(I2C1, I2C_IT_PECERR)) {
        } else if(I2C_GetITStatus(I2C1, I2C_IT_OVR)) {
            //Overrun
            //CLK stretch disabled and receiving
            //DR has not been read, b4 next byte comes in
            //effect: lose byte
            //should:clear RxNE and transmitter should retransmit
 
            //Underrun
            //CLK stretch disabled and I2C transmitting
            //haven't updated DR since new clock
            //effect: same byte resent
            //should: make sure discarded, and write next
        } else if(I2C_GetITStatus(I2C1, I2C_IT_AF)) {
            //Detected NACK
            //Transmitter must reset com
                //Slave: lines released
                //Master: Stop or repeated Start must must be generated
                //Master = MSL bit
            //Fixup
            I2C_ClearITPendingBit(I2C1, I2C_IT_AF);
        } else if(I2C_GetITStatus(I2C1, I2C_IT_ARLO)) {
            //Arbitration Lost
            //Goes to slave mode, but can't ack slave address in same transfer
            //Can after repeat Start though
        } else if(I2C_GetITStatus(I2C1, I2C_IT_BERR)) {
            //Bus Error
            //In slave mode: data discarded, lines released, acts like restart
            //In master mode: current transmission continues
        }
}

Image from logic analyzer :
I2C_Master_Slave.jpg

Also are attached source projects in CooCox IDE 1.7.6

Can anyone share working code for I2C slave (it doesn't matter with interrupt or without it)


Thanks.

Outcomes