1
\$\begingroup\$

I am programming an I2C master on a STM32F401 nucleo board. I program it without HAL, just using registers.

I am interfacing a peripheral, that has to be read like the following:

  1. Start
  2. Write Address with R/W 0
  3. Write register address
  4. Send repeated start condition
  5. Write Address with R/W 1
  6. Read data from peripheral

The first 3 steps work totally fine, but I can't send the address with R/W 1 after the restart condition.

enter image description here Here you can see the timing diagram using a logic analyser. The restart condition is sent, but afterwards SCL and SDA are just low.

Here is my code

 i2c_start();
 if (i2c_sendAddr(TLC2991_ADDR)) { //returns != 0 if NACK
 fault = true;
 i2c_end();
 I2C1->SR1 &= ~I2C_SR1_AF;
 return -1;
 }
 i2c_sendByte(VOLTAGE_REG_OFFS + 2 * ch);
 i2c_wfw();
 i2c_start();
 i2c_sendByte(TLC2991_ADDR | 1); //tried i2c_sendAddr too
 u8 msb = i2c_recvByte(1);
 u8 lsb = i2c_recvByte(0);

And the functions called

void i2c_start() {
 I2C1->CR1 |= I2C_CR1_START;
 while (!I2C1->SR1 & I2C_SR1_SB);
}
int i2c_sendAddr(u8 addr) {
 I2C1->DR = addr;
 while (!(I2C1->SR1 & I2C_SR1_ADDR))
 if (I2C1->SR1 & I2C_SR1_AF)
 return -1;
 I2C1->SR2; //generates a read, clears ADDR
 return 0;
}
int i2c_sendByte(u8 data) {
 while (!(I2C1->SR1 & (I2C_SR1_TXE | I2C_SR1_AF)));
 I2C1->DR = data;
 return I2C1->SR1 & I2C_SR1_AF ? -1 : 0;
}
//wait for byte transfer
void i2c_wfw() {
 while (!(I2C1->SR1 & (I2C_SR1_BTF | I2C_SR1_AF)));
}

The TXE bit is high before setting the DR after the restart, afterwards it is low, but the I2C does not transmit anything. Please point out what I missed.

I have also checked the SDA line with an oscilloscope and there are no glitches or similar.

asked Aug 6, 2018 at 15:28
\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

I suggest trying the following:

i2c_start: while (!I2C1->SR1 & I2C_SR1_SB); -> while (!(I2C1->SR1 & I2C_SR1_SB));

i2c_sendByte: Move I2C1->DR = data; to first line. Replace check for TXE with check for BTF.

don't call i2c_wfw after i2c_sendByte(VOLTAGE_REG_OFFS + 2 * ch);, it will be done in i2c_sendByte as above.

use i2c_sendAddr instead of i2c_sendByte after repeated start.

Also, you check for AF in i2c_sendByte and i2c_wfw() but do not handle errors properly. It is better not to check it at all than have unpredictable code flow. I'd recommend looking at flowcharts in AN2824 and quite nice code example at GitHub.

answered Aug 6, 2018 at 20:37
\$\endgroup\$
1
  • \$\begingroup\$ Thanks, it looks like the check for SB really was the issue, I didn't even see that i missed the () there. \$\endgroup\$ Commented Aug 7, 2018 at 9:50

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.