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 主器件接收一个字节

Guru**** 2587365 points


请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

https://e2e.ti.com/support/microcontrollers/msp-low-power-microcontrollers-group/msp430/f/msp-low-power-microcontroller-forum/1103158/msp430f5438a-i2c-master-receive-one-byte

器件型号:MSP430F5438A

您好!

根据用户指南、对于主器件接收、如果我希望仅接收一个字节、则需要设置起始位、等待将其清除、然后设置停止位。

        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
            }
        }

当前、在我的 ISR 内部、在主发送模式下发送最后一个字节后、我将更改为主接收模式。 然后、我将设置起始位、轮询 UCTXSTT 标志、然后针对预计接收到1个字节的情况设置停止位。

但是、如果总线上出现某些错误并且 UCTXSTT 标志未被清除、我不希望代码停留在 ISR 中。 因此、我添加了一个超时计数器来限制 while 循环的发生次数。
之前我检查过循环通常发生57次、因此我使用了200的超时计数。 但是、最近它超过了200个计数、当我在开始位被清零之前设置停止位时、它导致总线挂起。

首先、我想问的是、除了在 ISR 中轮询 UCTXSTT 标志之外、在主器件接收模式下接收一个字节的情况还有其他替代方法吗?
如果在轮询一定次数后 UCTXSTT 标志没有被清除、如何安全地停止事务而不会导致总线挂起?

谢谢
开尔文

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好、开尔文、  

    您能否发送完整代码?

    此致、  

    Henok

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好、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;
      }
    
    }

    这是 ISR 代码部分

    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;
        }
    }

    这是我用来设置和触发发送的函数。


    此致
    开尔文

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好、开尔文、  

    感谢您的分享。

    我有几个问题:

    为了更好地理解、您使用的是 TI Resource Explorer 中的哪个示例代码?  

    可能会将 SCL 保持在低电平。 您是否尝试进行探测以仔细检查此情况?

    假定您初始化的从地址正确、 并且 UCTXSTT 标志未被清除、这可能意味着不会发送完整的地址。  

    我将删除轮询并仔细检查您的初始化。 可能存在一些干扰。  

    来自 MSP430x5xx I2C 模块用户指南: https://www.ti.com/lit/ug/slau425f/slau425f.pdf?ts=1653362513147&ref_url=https%253A%252F%252Fwww.google.com%252F

    (第14页)只要:

    •不会产生自动停止、UCTXSTP 位未置位、UCTXSTT 位未置位。

    此外、请查看此类似文章 :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

    请告诉我您的想法。  

    此致、  

    Henok

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好、开尔文、  

    是否有更新?

    还需要让您再次检查接收器(主器件)是否发送 ACK 位。

    此致、  

    Henok

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好、Henok、

    很抱歉、我的回答很晚、我正在尝试测试提到的一些东西。

    我检查了 SCL 是否未保持低电平。 我还再次检查代码、并意识 到当我在开始位被清除之前设置停止位时、程序正在 i2c_master_mode 函数的 while (ctrl->rx_mode = master_receive)循环中挂起。 在这种情况下、我不会接收到从器件的响应、也不会进入接收 ISR、在该 ISR 中、我在接收到预期字节后将 Rx_MODE 改回从器件接收。 我自己犯了一个错误、抱歉。

    目前、如果超时发生、我更改了发送 ISR、将 RX_MODE 设置回从器件接收、如下所示

    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;
                    }
                }
            }
        }

    我目前正在测试该程序、以查看它是否会导致其他问题。

    谢谢
    此致
    开尔文