cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F1 two byte receive with subaddress

James Jackson
Associate II
Posted on December 17, 2016 at 16:48

I have an STM32F103 that I would like to use for I2C communication. I have been using the 'Optimized examples for I2C' with the STM32F1, but I have modified it to send a subaddress before performing the read. With my modifications to the optimized examples, I can only receive one byte. Trying to receive two or more bytes always results in a NACK on the first byte, instead of the second.

I can't find anything in the documentation describing this behavior, or if there is a difference in receiving two bytes with or without a subaddress. I'm probably just not looking in the right places.

I hate how I can't just post a couple lines of microcontroller code to fully describe my problem, but I don't know what else to do. Here are the relevant lines of my function.

 uint32_t timeout = I2C_DEFAULT_TIMEOUT;
uint16_t temp;
// send subaddress if we need to
if(reg_ != 0xFF)
{
 /* Send START condition */
 I2Cx_->CR1 |= I2C_CR1_START;
 /* Wait until SB flag is set: EV5 */
 while ((I2Cx_->SR1 & I2C_SR1_SB) != I2C_SR1_SB)
 {
 if (timeout-- == 0)
 return hardware_failure();
 }
 /* Send slave address */
 I2C_Send7bitAddress(I2Cx_, addr_, I2C_Direction_Transmitter);
 timeout = I2C_DEFAULT_TIMEOUT;
 /* Wait until ADDR is set: EV6 */
 while ((I2Cx_->SR1 & I2C_SR1_ADDR) != I2C_SR1_ADDR)
 {
 if (timeout-- == 0)
 return hardware_failure();
 }
 /* Clear ADDR flag by reading SR2 register */
 temp = I2Cx_->SR2;
 // Send subaddress
 I2Cx_->DR = reg_;
 // Make sure byte transfer finished
 while ((I2Cx_->SR1 & I2C_SR1_BTF) != I2C_SR1_BTF);
}
if (len_ == 2)
{
 /* Set POS bit */
 I2Cx_->CR1 |= I2C_CR1_POS;
 uint32_t timeout = I2C_DEFAULT_TIMEOUT;
 /* Send START condition */
 I2Cx_->CR1 |= I2C_CR1_START;
 /* Wait until SB flag is set: EV5 */
 while ((I2Cx_->SR1 & I2C_SR1_SB) != I2C_SR1_SB)
 {
 if (timeout-- == 0)
 return hardware_failure();
 }
 /* Send slave address */
 /* Set the address bit0 for read */
 I2C_Send7bitAddress(I2Cx_, addr_, I2C_Direction_Receiver);
 /* Wait until ADDR is set: EV6 */
 timeout = I2C_DEFAULT_TIMEOUT;
 while ((I2Cx_->SR1 & I2C_SR1_ADDR) != I2C_SR1_ADDR)
 {
 if (timeout-- == 0)
 return hardware_failure();
 }
 /* EV6_1: The acknowledge disable should be done just after EV6,
 that is after ADDR is cleared, so disable all active IRQs around ADDR clearing and
 ACK clearing */
 __disable_irq();
 /* Clear ADDR by reading SR2 register */
 temp = I2Cx_->SR2;
 /* Clear ACK */
 I2C_AcknowledgeConfig(I2Cx_, DISABLE);
 I2Cx_->CR1 &= ~I2C_CR1_ACK;
 /*Re-enable IRQs */
 __enable_irq();
 /* Wait until BTF is set */
 timeout = I2C_DEFAULT_TIMEOUT;
 while (!(I2Cx_->SR1 & I2C_SR1_BTF))
 {
 if(timeout-- == 0)
 {
 return hardware_failure();
 }
 }
 /* Disable IRQs around STOP programming and data reading because of the limitation ?*/
 __disable_irq();
 /* Program the STOP */
 I2C_GenerateSTOP(I2Cx_, ENABLE);
 /* Read first data */
 data_buffer_[index_] = I2Cx_->DR;
 /* Re-enable IRQs */
 __enable_irq();
 /**/
 index_++;
 /* Read second data */
 data_buffer_[index_] = I2Cx_->DR;
 /* Make sure that the STOP bit is cleared by Hardware before CR1 write access */
 timeout = I2C_DEFAULT_TIMEOUT;
 while (!(I2Cx_->CR1 & I2C_CR1_STOP))
 {
 if(timeout-- == 0)
 return hardware_failure();
 }
 /* Enable Acknowledgement to be ready for another reception */
 I2C_AcknowledgeConfig(I2Cx_, ENABLE);
 /* Clear POS bit */
 I2Cx_->CR1 &= (uint16_t) ~I2C_CR1_POS;
}�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?

#i2c #polling #subaddress #stm32
1 ACCEPTED SOLUTION

Accepted Solutions
Posted on December 18, 2016 at 13:18

I don't see where do you set I2Cx_CR1.ACK before reading.

JW

View solution in original post

4 REPLIES 4
Seb
ST Employee
Posted on December 17, 2016 at 17:12

If trying to address an I2C EEPROM which memory content requires 2 byte subaddress, as an alternative until the issue is solved could be to use bit banging by GPIO. Search for 'I2C Checklist' on the community to grab a pseudo code inside the tip and tricks.

As long as speed is not critical (which would then favour SPI or QSPI memories), it will do the interim job.

Hope this helps in the short term,

Posted on December 17, 2016 at 22:31

Thanks Seb,  I'll look into the I2C Checklist.

Unfortunately, GPIO bit-banging is definitely going to be too slow for my purposes.  I only need a single byte subaddress, and I'm certain that it is possible to do this with the peripheral, since it's a very common paradigm to have a subaddress on I2C reads.

Posted on December 18, 2016 at 13:18

I don't see where do you set I2Cx_CR1.ACK before reading.

JW

Posted on December 19, 2016 at 17:59

Haha!  Thanks JW.

I was trusting the optimized examples a bit too much.  It turns out, the optimized I2C examples are set up to perform byte transfer via a NACK-NACK-ACK.  Every I2C sensor I've ever used is a ACK-ACK-NACK and I didn't realize that the examples were backwards on purpose.