0
\$\begingroup\$

I am implementing drivers for I2C interrupts for STM32F401RE and looks like the API for transferring the data seems to work. But for receiving, I see that I'm only able to receive one byte.

logic analyzer trace of I2C bus

I am using the MCP9808 temperature sensor and can read 2 bytes in one transaction.

Here's the snapshot of the sequence of steps for reading 2 bytes according to STM32's reference manual.

screenshot from STM32 reference manual

Code snippet for IRQ handler (edit: ignore the TX parts since that's not the concern here):

void I2C1_EV_IRQHandler (void)
{
    uint8_t eventInterrupt = (I2C_handle_p->pI2Cx->CR2 & I2C_CR2_ITEVTEN) >> I2C_CR2_ITEVTEN_Pos;
    uint8_t bufferInterrupt = (I2C_handle_p->pI2Cx->CR2 & I2C_CR2_ITBUFEN) >> I2C_CR2_ITBUFEN_Pos;
    uint8_t temp;

    if (eventInterrupt)
    {
        //  validate the completion of START condition
        temp =  (I2C_handle_p->pI2Cx->SR1 & I2C_SR1_SB) >> I2C_SR1_SB_Pos;
        if (temp)
        {
            if (I2C_handle_p->I2C_State == I2C_TX_BUSY)
            {
                I2C_WriteSlaveAddress(I2C_handle_p, WRITE);     // write slave address along with write bit
            }
            else if (I2C_handle_p->I2C_State == I2C_RX_BUSY)
            {
                I2C_WriteSlaveAddress(I2C_handle_p, READ);      // write slave address along with read bit
            }
        }

        // ADDR
        temp = (I2C_handle_p->pI2Cx->SR1 & I2C_SR1_ADDR) >> I2C_SR1_ADDR_Pos;
        if (temp)
        {
            I2C_ClearADDRFlag(I2C_handle_p->pI2Cx);             // clear address flag
        }

        // TXE, RXNE
        if (bufferInterrupt)
        {
            // TXing
            temp = (I2C_handle_p->pI2Cx->SR1 & I2C_SR1_TXE) >> I2C_SR1_TXE_Pos;

            if (temp && I2C_handle_p->I2C_State == I2C_TX_BUSY)
            {
                I2C_TXE_Interrupt();
            }

            // RXing
            temp = (I2C_handle_p->pI2Cx->SR1 & I2C_SR1_RXNE) >> I2C_SR1_RXNE_Pos;

            if (temp && I2C_handle_p->I2C_State == I2C_RX_BUSY)
            {
                 if (I2C_handle_p->rxBufferLength == 2) {
                    // send NACK 
                    I2C_ControlAcking(I2C_handle_p->pI2Cx, RESET);
                    I2C_handle_p->pI2Cx->CR1 |= 1 << I2C_CR1_POS_Pos;
                 }

        }

        //BTF
        temp = (I2C_handle_p->pI2Cx->SR1 & I2C_SR1_BTF) >> I2C_SR1_BTF_Pos;
        if (temp)
        {
            if (I2C_handle_p->I2C_State == I2C_TX_BUSY)
            {
                if (!I2C_handle_p->txBufferLength)
                {
                    GenerateStopCondition(I2C_handle_p);
                    I2C_StopTransmission();
                }
            }
            else if (I2C_handle_p->I2C_State == I2C_RX_BUSY)            
            {

                if (I2C_handle_p->rxBufferLength == 2)
                {
                    GenerateStopCondition(I2C_handle_p);

                    I2C_handle_p->pRxBuffer[I2C_handle_p->rxStartIndex++] = (uint8_t) I2C_handle_p->pI2Cx->DR;
                    I2C_handle_p->rxBufferLength--;

                    I2C_handle_p->pRxBuffer[I2C_handle_p->rxStartIndex++] = (uint8_t) I2C_handle_p->pI2Cx->DR;
                    I2C_handle_p->rxBufferLength--;

                    I2C_StopTransmission();

                }
                if (!I2C_handle_p->rxBufferLength)                      // no more bytes to read
                {
                    GenerateStopCondition(I2C_handle_p);
                    I2C_StopTransmission();
                }
            }
        }

    }

}
\$\endgroup\$
3
  • \$\begingroup\$ Well then you have an error in your code. The sensor is not the one sending NAK, it is your master that sends the NAK to indicate to sensor that no more data should be sent. Are you using the STM32 I2C HAL or writing your own driver? \$\endgroup\$
    – Justme
    Commented Apr 5, 2020 at 19:48
  • \$\begingroup\$ @Justme - well I do send the NACK first as mentioned in the reference manual -> imgur.com/a/PGnW9yj but then BTF doesn't get set after \$\endgroup\$
    – xyf
    Commented Apr 5, 2020 at 19:52
  • \$\begingroup\$ Key point: In a read transaction, the slave is responsible for generating the ACK on the address byte, but the master is responsible for generating the ACK on the data bytes. You shouldn't have been expecting to receive an ACK, you should have been generating one to tell the slave you're going to continue requesting more data. \$\endgroup\$
    – The Photon
    Commented Apr 5, 2020 at 23:56

1 Answer 1

1
\$\begingroup\$

You violate the sequence by clearing the ADDR bit first, and then setting ACK and POS bits. So the sequence in your code is not according to what the manual says. Clearing the ADDR bit already starts the transmission of next byte, so by the time the code goes to setting ACK and POS bits, it is too late to set them anymore, and it uses the settings where NAK is sent right after the first data byte instead of the second.

\$\endgroup\$
2
  • \$\begingroup\$ Oh right. But what's prompting the transmission of the NACK after master receives the first byte and who sends it? the master itself considering the incorrect sequence? also, there's no need for me to check for RXNE bit then? I did the following and seems to work but it seems more of a "hard-coded" approach rather: imgur.com/a/QCtoc6e \$\endgroup\$
    – xyf
    Commented Apr 5, 2020 at 21:04
  • \$\begingroup\$ Like I said, the STM32 sends NACK after the first data byte, because it was configured to do so with the ACK and POS bits, as they were not set up properly. In this specific case told by the manual, there is no need to check for RXNE because when BTF is set, the transfer of both bytes has finished and they can be read from the DR. You can check for yourself, but most likely you can check for RXNE being set, read first data byte, and check again for RXNE set to indicate the the last data byte. The sequence in the manual just takes a shortcut. \$\endgroup\$
    – Justme
    Commented Apr 5, 2020 at 21:18

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.