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: