1
\$\begingroup\$

I have a custom board with an STM32L151RB. I want to use I2C in master mode with standard peripheral library. I've started by copying chunks of code from demo project.

The problem is that the start condition is sent (confirmed with a logic analyzer), but the SB (start bit) in I2C_SR (status register) is never set.

I have 4k7 pullups. I2C lines change state separately (so they are not shorted to anything, GPIOs seem to be configured okay and I2C peripheral has clock enabled): enter image description here

I dump I2C registers in my busy waiting loop and always get (hex):

I2C1->CR1=00000501
I2C1->CR2=00000002
I2C1->SR1=00000000

This is my init code:

void i2c_init(void){
    //enable clock to I2C peripheral
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);

    //configure pins in alternate mode (GPIO clock is already enabled in pins_init)
    GPIO_PinAFConfig(GPIOB, GPIO_AF_I2C1, GPIO_Pin_6 | GPIO_Pin_7);

    GPIO_InitTypeDef g;

    //PB6, PB7 - output, open drain, fast
    g.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
    g.GPIO_Mode = GPIO_Mode_AF;
    g.GPIO_Speed = GPIO_Speed_2MHz;
    g.GPIO_OType = GPIO_OType_OD;
    g.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOB, &g);

    I2C_InitTypeDef init;

    init.I2C_ClockSpeed = 100000;
    init.I2C_Mode = I2C_Mode_I2C;
    init.I2C_DutyCycle = I2C_DutyCycle_2;
    init.I2C_OwnAddress1 = 0x00;
    init.I2C_Ack = I2C_Ack_Enable;
    init.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;

    I2C_Init(I2C1, &init);
    I2C_Cmd(I2C1, ENABLE);
}

This is my master code:

  I2C_ClearFlag(I2C1, I2C_FLAG_AF);
  I2C_AcknowledgeConfig(I2C1, ENABLE);

  /*!< Send I2C1 START condition */
  I2C_GenerateSTART(I2C1, ENABLE);

  /*!< Test on I2C1 EV5 and clear it */
  while ((!I2C_GetFlagStatus(I2C1,I2C_FLAG_SB)))  /*!< EV5 */
  {
    debugf("----busy wait----");
    debugf("I2C1->CR1=%08X", (unsigned int)I2C1->CR1);
    debugf("I2C1->CR2=%08X", (unsigned int)I2C1->CR2);
    debugf("I2C1->SR1=%08X", (unsigned int)I2C1->SR1);
    delay_ms(150); //this delay does not change anything (just limits debug terminal speed)
  } //this loop never exits

Why can the start bit in status register not be set after the GPIOs have successfully transmitted a start condition?

\$\endgroup\$
1
\$\begingroup\$

I have found out that it is a GPIO configuration issue. Using this code it turned out that my line:

GPIO_PinAFConfig(GPIOB, GPIO_AF_I2C1, GPIO_Pin_6 | GPIO_Pin_7);

set registers incorrectly. The correct way to setup I2C on PB6/PB7 is:

GPIOB->AFR[0] |= ( 1 << 30 ) | ( 1 << 26); // P6/P7 => AF4

Now I2C works fine.

UPDATE

Ahh.... the joys of C type safety. The proper invocation of ST library function is:

GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_I2C1);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_I2C1);
  1. GPIO_Pin_6 and GPIO_PinSource6 are different defines
  2. Pin argument comes second
  3. Alternate functions argument comes third
  4. Pins have to be configured one-by-one

Lesson learned: use enums for your own code arguments instead of magic numbers!

\$\endgroup\$

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.