AnsweredAssumed Answered

STM32F1 two byte receive with subaddress

Question asked by Jackson.James.001 on Dec 17, 2016
Latest reply on Dec 19, 2016 by Jackson.James.001

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;

}

Outcomes