This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

MSP430F5438A: I2C master receive one byte

Part Number: MSP430F5438A

Hi,

According to the user guide, for master receive, if I am expecting to receive only one byte, I need to set the start bit, wait for it to be cleared, then set the stop bit.

        else
        {
            ucb1_i2c.rx_mode = MASTER_RECEIVE;
            xSemaphoreGiveFromISR(ucb1_i2c.tx_semaphore,pdFALSE);
            if(NULL != i2ca_tx_callback)
            {
                (*i2ca_tx_callback)();
            }
            i2c_set_receive_mode(USCI_B1_BASE);
            uint8_t UCB1STT_timeout = 0;
            UCB1CTL1 |= UCTXSTT;
            if(1u == ucb1_i2c.master_rx->dat_len)
            {
                // UCB1STT_timeout usually around 57
                while((UCB1CTL1 & UCTXSTT) && (UCB1STT_timeout < 200))
                {
                    UCB1STT_timeout++;
                }
                UCB1CTL1 |= UCTXSTP;             // Generate I2C stop condition
            }
        }

currently, inside my isr, after the last byte is sent in master transmit mode, I will change to master receive mode. I will then set the start bit, poll for the UCTXSTT flag, then set the stop bit for the case when 1 byte is expected to be received.

However, I did not want my code to be stuck in the isr in case there was some error on the bus and the UCTXSTT flag was not cleared. Hence, I added a timeout counter to limit the number of times the while loop will occur.
Previously I checked that the loop usually happens around 57 times hence I used a timeout count of 200. However, recently it exceeded the 200 count and caused the bus to hang when I set the stop bit before the start bit was cleared.

Firstly, I would like to ask, other than polling for the UCTXSTT flag in the isr, is there other alternative way to handle the case of receiving one byte in master receive mode?
If the UCTXSTT flag was not cleared after polling it a certain number of times, how can I safely stop the transaction without causing the bus to hang?

Thanks
Kelvin

  • Hi Kelvin, 

    Can you send the full code?

    regards, 

    Henok

  • Hi Henok,

    #if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
    #pragma vector = USCI_B1_VECTOR
    __interrupt void USCI_B1_ISR(void)
    #elif defined(__GNUC__)
    void __attribute__ ((interrupt(USCI_B1_VECTOR))) USCI_B1_ISR (void)
    #else
    #error Compiler not supported!
    #endif
    {
      switch(__even_in_range(UCB1IV,12u))
      {
      case  0u: break;                           // Vector  0: No interrupts
      case  2u:
          UCB1IFG &= ~UCALIFG;
          ucb1_i2c.arbitration_loss = 1u;
          UCB1IE &= ~UCTXIE;
          xSemaphoreGiveFromISR(ucb1_i2c.tx_semaphore,pdFALSE);
          break;                           // Vector  2: ALIFG
      case  4u:
          UCB1CTL1 |= UCTXSTP;                  // I2C stop condition
          xSemaphoreGiveFromISR(ucb1_i2c.tx_semaphore,pdFALSE);
          break;                           // Vector  4: NACKIFG
      case  6u: break;                           // Vector  6: STTIFG
      case  8u:                              // Vector  8: STPIFG
          if (UCB1IFG & UCRXIFG)
          {
              if ((ucb1_i2c.rx_curr_len < ucb1_i2c.slave_rx->dat_len) &&
                      (ucb1_i2c.slave_rx != NULL))
              {
                  *ucb1_i2c.slave_rx->data++ = UCB1RXBUF;             // Get RX'd byte into buffer
                  ucb1_i2c.rx_curr_len++;
              }
              else
              {
                  ucb1_i2c.rx_succeed = 0u;
                  uint8_t dummyClear = UCB1RXBUF;
              }
          }
          if (ucb1_i2c.rx_succeed && (NULL != ucb1_i2c.stp_cb))
          {
              (*ucb1_i2c.stp_cb)();
    //          vTaskNotifyGiveFromISR(decode_i2c_handler, pdFALSE);
          }
          ucb1_i2c.rx_succeed = 1u;
          ucb1_i2c.rx_curr_len = 0;
          break;
      case 10u:                                  // Vector 10: RXIFG
      {
        if (ucb1_i2c.rx_mode == SLAVE_RECEIVE)
        {
            if ((ucb1_i2c.rx_curr_len < ucb1_i2c.slave_rx->dat_len)
                    && (ucb1_i2c.slave_rx != NULL))
            {
                *ucb1_i2c.slave_rx->data++ = UCB1RXBUF;                 // Get RX'd byte into buffer
                ucb1_i2c.rx_curr_len++;
            }
            else
            {
                ucb1_i2c.rx_succeed = 0u;
                uint8_t dummyClear = UCB1RXBUF;
            }
        }
        else if (ucb1_i2c.rx_mode == MASTER_RECEIVE)
        {
            if ((ucb1_i2c.rx_curr_len < ucb1_i2c.master_rx->dat_len)
                    && (ucb1_i2c.master_rx != NULL))
            {
                ucb1_i2c.rx_curr_len++;
                if (ucb1_i2c.rx_curr_len == (ucb1_i2c.master_rx->dat_len - 1))
                {
                    UCB1CTL1 |= UCTXSTP;
                }
                else if(ucb1_i2c.rx_curr_len == ucb1_i2c.master_rx->dat_len)
                {
                    ucb1_i2c.rx_mode = SLAVE_RECEIVE;
                    ucb1_i2c.rx_curr_len = 0;
                }
                *ucb1_i2c.master_rx->data++ = UCB1RXBUF;                 // Get RX'd byte into buffer
            }
            else
            {
                UCB1CTL1 |= UCTXSTP;
                ucb1_i2c.rx_mode = SLAVE_RECEIVE;
                ucb1_i2c.rx_curr_len = 0;
                uint8_t dummyClear = UCB1RXBUF;
            }
        }
        break;
      }
      case 12u:                                  // Vector 12: TXIFG
        if (ucb1_i2c.tx->dat_len)                          // Check TX byte counter
        {
          UCB1TXBUF = *ucb1_i2c.tx->data++;               // Load TX buffer
          ucb1_i2c.tx->dat_len--;                          // Decrement TX byte counter
        }
        else
        {
            if(1u == ucb1_i2c.with_stop)
            {
    //          UCB1IFG &= ~UCTXIFG;                  // Clear USCI_B1 TX int flag
              UCB1CTL1 |= UCTXSTP;                  // I2C stop condition
              UCB1CTL1 &= ~UCTR;
              UCB1IE &= ~UCTXIE;
              if(NULL != i2ca_tx_callback)
              {
                  (*i2ca_tx_callback)();
              }
              xSemaphoreGiveFromISR(ucb1_i2c.tx_semaphore,pdFALSE);
            }
            else
            {
                ucb1_i2c.rx_mode = MASTER_RECEIVE;
                xSemaphoreGiveFromISR(ucb1_i2c.tx_semaphore,pdFALSE);
                if(NULL != i2ca_tx_callback)
                {
                    (*i2ca_tx_callback)();
                }
                i2c_set_receive_mode(USCI_B1_BASE);
                uint8_t UCB1STT_timeout = 0;
                UCB1CTL1 |= UCTXSTT;
                if(1u == ucb1_i2c.master_rx->dat_len)
                {
                    // UCB1STT_timeout usually around 57
                    // todo: is 1000 timeout enough? what should be done when timeout occurs?
                    while((UCB1CTL1 & UCTXSTT) && (UCB1STT_timeout < 1000))
                    {
                        UCB1STT_timeout++;
                    }
                    UCB1CTL1 |= UCTXSTP;             // Generate I2C stop condition
                }
            }
        }
      default: break;
      }
    
    }

    This is the ISR code portion

    BaseType_t i2c_master_mode(uint16_t base, uint8_t slave_addr,
                               i2c_buffer_s *tx, i2c_buffer_s *rx,
                               i2c_mode_e mode)
    {
        BaseType_t ret = pdTRUE;
        i2c_ctrl_s *ctrl = get_i2c_ctrl(base);
        uint16_t i = 0;
        uint8_t timeout = 0;
        if (ctrl == NULL)
        {
            ret = pdFALSE;
            goto EXIT;
        }
        if (mode == MASTER_TRANSMIT_RECEIVE)
        {
            ctrl->with_stop = 0;
        }
        else
        {
            ctrl->with_stop = 1;
        }
        ctrl->tx = tx;
        ctrl->master_rx = rx;
        HWREG8(base + OFS_UCBxI2CSA) = slave_addr;
    
        for (i=0u; i<1000u; i++)
        {
            // check that bus is not busy before switching to master
            // and send the start condition
            if (HWREG8(base + OFS_UCBxSTAT) & UCBBUSY)
            {
                vTaskDelay(pdMS_TO_TICKS(1));
            }
            else
            {
                xSemaphoreTake(ctrl->tx_semaphore, 0u);
                i2c_set_master(base);
                i2c_set_transmit_mode(base);
                HWREG8(base + OFS_UCBxIE) |= UCTXIE;
                if (ctrl->arbitration_loss)
                {
                    ctrl->arbitration_loss = 0u;
                }
    
                UCB1CTL1 |= UCTXSTT;
    
                if (xSemaphoreTake(ucb1_i2c.tx_semaphore, pdMS_TO_TICKS(15)))
                {
                    if (!ctrl->arbitration_loss)
                    {
                        if (ctrl->with_stop == 0)
                        {
                            while(ctrl->rx_mode == MASTER_RECEIVE)
                            {
                                vTaskDelay(pdMS_TO_TICKS(15));
                            }
                        }
                        break;
                    }
                    else
                    {
                        vTaskDelay(pdMS_TO_TICKS(5));
                    }
                }
                else
                {
                    if (HWREG8(base + OFS_UCBxSTAT) & UCSCLLOW)
                    {
                        HWREG8(base + OFS_UCBxCTL1) |= UCSWRST;
                        HWREG8(base + OFS_UCBxCTL0) &= ~UCMODE_3;
                        HWREG8(base + OFS_UCBxCTL1) &= ~UCSWRST;
                        HWREG8(base + OFS_UCBxCTL1) |= UCSWRST;
                        HWREG8(base + OFS_UCBxCTL0) |= UCMODE_3 + UCSYNC;
                        HWREG8(base + OFS_UCBxCTL1) &= ~UCSWRST;
                    }
                    else
                    {
                        HWREG8(base + OFS_UCBxCTL1) |= UCTXSTP;// I2C stop condition
                    }
                    i2c_set_receive_mode(base);
                    HWREG8(base + OFS_UCBxIE) &= ~UCTXIE;
                    break;
                }
            }
        }
        timeout = 0;
        while ((HWREG8(base + OFS_UCBxCTL1) & UCTXSTP) && timeout<=5)
        {
            vTaskDelay(pdMS_TO_TICKS(5));
            timeout++;
        }
        i2c_set_slave(base);
        ctrl->arbitration_loss = 0u;
    
        EXIT:
        {
            return ret;
        }
    }

    And this is the function I used to set up and trigger the sending.


    Regards
    Kelvin

  • Hi Kelvin, 

    Thanks for sharing.

    A few questions I have:

    To get a better understanding, which example code from the TI resource explorer are you using? 

    Something might be keeping the SCL low. Have you tried probing to double check this?

    Assuming that if the slave address you initialized is correct, and the UCTXSTT flag was not cleared, that could mean the complete address is not being sent. 

    I would get rid of the polling and double check your initializations. There could be some interference there. 

    From MSP430x5xx I2C module User Guide: https://www.ti.com/lit/ug/slau425f/slau425f.pdf?ts=1653362513147&ref_url=https%253A%252F%252Fwww.google.com%252F

    (page14) Data is received from the slave, as long as:

    • No automatic STOP is generated , The UCTXSTP bit is not set , The UCTXSTT bit is not set.

    Also, checkout this similar post: https://e2e.ti.com/support/microcontrollers/msp-low-power-microcontrollers-group/msp430/f/msp-low-power-microcontroller-forum/865936/msp430f5340-msp430f5340-hangs-while-checking-uctxstt-bit

    Let me know your thoughts. 

    regards, 

    Henok

  • Hi Kelvin, 

    Any updates?

    Also wanted to had that you should double check that ACK bit is sent from the receiver (Master).

    regards, 

    Henok

  • Hi Henok,

    Sorry for the late reply, I was trying to test some of the stuff mentioned.

    I checked that the SCL was not kept low. I also checked my code again and realised that the program was hanging in the while(ctrl->rx_mode == MASTER_RECEIVE) loop in i2c_master_mode function when I set the stop bit early before the start bit is cleared. In this case, I won't be receiving response from the slave and I won't enter the receive isr where I change the rx_mode back to slave receive after receiving the expected bytes. A mistake from my part, sorry about that.

    Currently, I changed my transmit isr to set the rx_mode back to slave receive if the timeout has occurred as shown below

    case 12u:                                  // Vector 12: TXIFG
        if (ucb1_i2c.tx->dat_len)                          // Check TX byte counter
        {
          UCB1TXBUF = *ucb1_i2c.tx->data++;               // Load TX buffer
          ucb1_i2c.tx->dat_len--;                          // Decrement TX byte counter
        }
        else
        {
            if(1u == ucb1_i2c.with_stop)
            {
    //          UCB1IFG &= ~UCTXIFG;                  // Clear USCI_B1 TX int flag
              UCB1CTL1 |= UCTXSTP;                  // I2C stop condition
              UCB1CTL1 &= ~UCTR;
              UCB1IE &= ~UCTXIE;
              if(NULL != i2ca_tx_callback)
              {
                  (*i2ca_tx_callback)();
              }
              xSemaphoreGiveFromISR(ucb1_i2c.tx_semaphore,pdFALSE);
            }
            else
            {
                ucb1_i2c.rx_mode = MASTER_RECEIVE;
                xSemaphoreGiveFromISR(ucb1_i2c.tx_semaphore,pdFALSE);
                if(NULL != i2ca_tx_callback)
                {
                    (*i2ca_tx_callback)();
                }
                i2c_set_receive_mode(USCI_B1_BASE);
                uint8_t UCB1STT_timeout = 0;
                UCB1CTL1 |= UCTXSTT;
                if(1u == ucb1_i2c.master_rx->dat_len)
                {
                    while((UCB1CTL1 & UCTXSTT) && (UCB1STT_timeout < 1000))
                    {
                        UCB1STT_timeout++;
                    }
                    UCB1CTL1 |= UCTXSTP;             // Generate I2C stop condition
                    if (UCB1STT_timeout == 1000)
                    {
                        ucb1_i2c.rx_mode = SLAVE_RECEIVE;
                    }
                }
            }
        }

    I am currently testing the program to see if it causes other problems.

    Thanks
    Regards
    Kelvin

**Attention** This is a public forum