AnsweredAssumed Answered

Proper Initialization of the I2C Peripheral

Question asked by naphtali on Feb 9, 2014
Latest reply on Oct 5, 2016 by little.andy
I am starting this post to hopefully determine the proper initialization procedure for the I2C peripheral. I am specifically using an STM32F407, but I am assuming that this will be helping in a broader sense.

The background is that I wrote an I2C driver that, at first past, was extremely unreliable. It had the problem that it would sometimes start up fine, sometimes start up with a bus error, and sometimes start up every or every other time with the BUSY flag never clearing.

In my case, I was testing my code with a dev kit connected to an external LCD screen which hosted the pull up resistors for the bus. I noticed that as I moved around my initialization code, the start up behavior would change. The confusing part was that scoping the SDA and SCL lines generally showed no difference, electrically, on the bus. In order to solve the problem, I disconnected the display, enabled the internal pull ups, and scoped the lines. This showed me the culprit:

    GPIO_InitStructure.GPIO_Pin   = LCD_SCL_GPIO_Pin_x | LCD_SDA_GPIO_Pin_x;
    GPIO_Init(LCD_GPIOx, &GPIO_InitStructure);

What I could then see, was SDA going hi and then SCL going hi. In my interpretation, this action was interpreted by the processor as an external I2C master trying to communicate with it. It may have been waiting for a STOP condition before it would respond to any commands.

The real problem was that I had tried issuing START and STOP commands, waiting it out, and various other things, but the peripheral would just not respond when it was stuck in BUSY mode.

I will post my init code below, but any thoughts on how to be sure that one can avoid this issue? The primary issue here is that I should not be able to lose control of the bus. If this kind of thing can happen, what would I do to reset at start up and then anytime afterwards.

void init_lcd(void) {

    GPIO_InitTypeDef GPIO_InitStructure;
    I2C_InitTypeDef  I2C_InitStructure;
    DMA_InitTypeDef  DMA_InitStructure;

    // I2C Clock Enable
    RCC_APB1PeriphClockCmd(LCD_APB1Periph_I2Cx, ENABLE);

    // GPIOx clock enable
    RCC_AHB1PeriphClockCmd(LCD_AHB1Periph_GPIOx, ENABLE);

    // Reset I2C peripheral
    RCC_APB1PeriphResetCmd(LCD_APB1Periph_I2Cx, ENABLE);
    RCC_APB1PeriphResetCmd(LCD_APB1Periph_I2Cx, DISABLE);

    // Connect I2C pins to AF

    // I2C GPIO pin configuration
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
    GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_NOPULL;

    GPIO_InitStructure.GPIO_Pin   = LCD_SCL_GPIO_Pin_x;
    GPIO_InitStructure.GPIO_Pin   = LCD_SCL_GPIO_Pin_x | LCD_SDA_GPIO_Pin_x;
    GPIO_Init(LCD_GPIOx, &GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin   = LCD_SDA_GPIO_Pin_x;
    GPIO_Init(LCD_GPIOx, &GPIO_InitStructure);

    // I2C struct init
    I2C_InitStructure.I2C_ClockSpeed          = I2C_SPEED;
    I2C_InitStructure.I2C_Mode                = I2C_Mode_I2C;
    I2C_InitStructure.I2C_DutyCycle           = I2C_DUTYCYCLE;
    //I2C_InitStructure.I2C_Ack                 = I2C_Ack_Disable;
    I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;


    //Enable Clock Stretching

    // Clear any set flags on the DMA TX stream

    // Disable the I2C Tx DMA stream

    //Deinit I2C TX DMA

    DMA_InitStructure.DMA_Channel             = LCD_DMA_CHANNEL;
    DMA_InitStructure.DMA_PeripheralBaseAddr  = (uint32_t)(&(LCD_I2Cx->DR));
    DMA_InitStructure.DMA_PeripheralInc       = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc           = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_PeripheralDataSize  = DMA_PeripheralDataSize_Byte;
    DMA_InitStructure.DMA_MemoryDataSize      = DMA_MemoryDataSize_Byte;
    DMA_InitStructure.DMA_Mode                = DMA_Mode_Normal;
    DMA_InitStructure.DMA_Priority            = DMA_Priority_VeryHigh;
    DMA_InitStructure.DMA_FIFOMode            = DMA_FIFOMode_Disable;
    DMA_InitStructure.DMA_FIFOThreshold       = DMA_FIFOThreshold_Full;
    DMA_InitStructure.DMA_MemoryBurst         = DMA_MemoryBurst_Single;
    DMA_InitStructure.DMA_PeripheralBurst     = DMA_PeripheralBurst_Single;
    DMA_InitStructure.DMA_DIR                 = DMA_DIR_MemoryToPeripheral;
    DMA_InitStructure.DMA_Memory0BaseAddr     = (uint32_t)txBuff;
    DMA_InitStructure.DMA_BufferSize          = sizeof(txBuff);

    DMA_Init(LCD_DMA_STREAM_TX, &DMA_InitStructure);
} // end - void init_lcd(void) {