Electrical – STM32: I2C Master does not generate START bit

programmingstm32

I am programming Blue Pill stm32f103c8t6 mcu, and I am having troubles implementing I2C driver for it. I have tried one I2C example from one site, then tried to implement it on my own by datasheet, yet it does not work – it does not generate START.

I know this I2C and I2C device work, because I have tried using STM32Duino software and the device with address 72 replied, but I have troubles reimplementing it on my own. I use CMSIS, no HAL and the minimal code is as follows:

void i2c_init() {

  I2C_InitTypeDef i2c_initstruct;
  I2C_StructInit(&i2c_initstruct);

  // Initialize I2C
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE);
  i2c_initstruct.I2C_ClockSpeed = 100e3;
  i2c_initstruct.I2C_Mode = I2C_Mode_I2C;
  i2c_initstruct.I2C_DutyCycle = I2C_DutyCycle_2;
  i2c_initstruct.I2C_OwnAddress1 = 0;
  i2c_initstruct.I2C_Ack = I2C_Ack_Disable;
  i2c_initstruct.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
  I2C_Init(I2C2, &i2c_initstruct);
  I2C_Cmd(I2C2, ENABLE);

  // Initialize GPIO as open drain alternate function
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
  GPIO_InitTypeDef GPIO_InitStruct;
  GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
  GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_OD;
  GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
  GPIO_Init(GPIOB, &GPIO_InitStruct);

}

void i2c_start2(uint8_t addr_with_mode) {
nlio_print("(0");

  // Wait until no longer busy
  while((I2C2->SR2 & I2C_SR2_BUSY) != 0);
  // Send start bit
  I2C2->CR1 |= I2C_CR1_START;

  // Wait until no longer busy
  while((I2C2->SR2 & I2C_SR2_BUSY) != 0);

  //while((I2C2->SR2 & I2C_SR2_MSL) == 0);

nlio_print("(1");
  // Wait until START bit is actually sent
  while((I2C2->SR1 & I2C_SR1_SB) == 0);

nlio_print("(2");
  // Write address
  I2C2->DR = addr_with_mode;

nlio_print("(3");
  // Wait for ACK (address match)
  while((I2C2->SR1 & I2C_SR1_ADDR) == 0);

nlio_print("(4");
  // Read SR1 followerd by SR2 to clear (I2C->SR1 & ADDR)
  (void) I2C2->SR2;
}

void i2c_send(uint8_t byte) {
  // Wait until Data register is empty
  while((I2C2->SR1 & I2C_SR1_TXE) == 0);

  // Send byte
  I2C2->DR = byte;
}

void i2c_end() {
  // Wait until Data register is empty
  while((I2C2->SR1 & I2C_SR1_TXE) == 0);

  // Wait until Byte transfer finished
  while((I2C2->SR1 & I2C_SR1_BTF) == 0);

  // Send stop
  I2C2->CR1 |= I2C_CR1_STOP;
}

#define WRITE 0

void i2c_test() {
  i2c_init();

  nlio_print("Hello\r\n");
  i2c_start2((72 << 1) | WRITE);
  nlio_println("Chp1");
  i2c_send(0);
  nlio_println("Chp2");
  i2c_end();
  // i2c_write(72, 0);

  nlio_println("Chp4");
  uint8_t rec;
  i2c_read(72, &rec);
  nlio_println("Chp5");

  // uart_println("Wait busy");
  // i2c_wait_busy();
  // uart_println("Wait done");

  // info();
  while(1);


  // uart_println("Sending STOP");
  // i2c_read(72);

  // uart_println("DONE\n");

  // while(1) {
  //   info();
  // }
}

int main() {
  // ...
  nlio_print("NLIO\r\n");
  i2c_test();
  while(1);
}

and it hangs when waiting for START condition, so the output is as follows:

NLIO
Hello
(0(1

I have no clue what I am doing wrong. Any help is appreciated.

EDIT: This problem happens also with pull-up 5k1 resistors each connected to SDA or SCL and 3.3V.

EDIT: I have printed the values of the I2C registers immediately after "(0" is printed:

 CR1 0001
 CR2 0024
OAR1 4000
OAR2 0000
  DR 0000
 SR1 0000
 SR2 0000
TRISE 0025

and immediately after printing "(1":

 CR1 0101
 CR2 0024
OAR1 4000
OAR2 0000
  DR 0000
 SR1 0000
 SR2 0002
TRISE 0025

and they remain that way until the program ends.

EDIT: I have compared the register values of I2C2 registers and GPIOB registers to the working example of STM32Duino, and there is no difference. I just don't understand why this code stops working.

Best Answer

The problem was in the initialization. I have used libopencm3 APIs for initialization and the code didn't hang after generating START bit. It's possible I have not initialized RCC propertly (for GPIOB), and the I2C therefore got stuck waiting for SB which was never going to be generated. Just for a reference, I am posting my new init code, which is mostly copied from libopencm3's STM32 I2C example:

void i2c_init(void) {
    /* Enable clocks for I2C2 and AFIO. */
    rcc_periph_clock_enable(RCC_I2C2);
    rcc_periph_clock_enable(RCC_GPIOB);
    rcc_periph_clock_enable(RCC_AFIO);

    /* Set alternate functions for the SCL and SDA pins of I2C2. */
    gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_50_MHZ,
              GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN,
              GPIO_I2C2_SCL | GPIO_I2C2_SDA);

    /* Disable the I2C before changing any configuration. */
    i2c_peripheral_disable(I2C2);

    /* APB1 is running at 36MHz. */
    i2c_set_clock_frequency(I2C2, I2C_CR2_FREQ_36MHZ);

    /* 400KHz - I2C Fast Mode */
    i2c_set_fast_mode(I2C2);

    /*
     * fclock for I2C is 36MHz APB2 _ cycle time 28ns, low time at 400kHz
     * incl trise _ Thigh = 1600ns; CCR = tlow/tcycle = 0x1C,9;
     * Datasheet suggests 0x1e.
     */
    i2c_set_ccr(I2C2, 0x1e);

    /*
     * fclock for I2C is 36MHz _ cycle time 28ns, rise time for
     * 400kHz => 300ns and 100kHz => 1000ns; 300ns/28ns = 10;
     * Incremented by 1 _ 11.
     */
    i2c_set_trise(I2C2, 0x0b);

    /*
     * This is our slave address - needed only if we want to receive from
     * other masters.
     */
    i2c_set_own_7bit_slave_address(I2C2, 0x32);

    /* If everything is configured _ enable the peripheral. */
    i2c_peripheral_enable(I2C2);
}