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 USCI30勘误权变措施

Guru**** 2586265 points


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

https://e2e.ti.com/support/microcontrollers/msp-low-power-microcontrollers-group/msp430/f/msp-low-power-microcontroller-forum/1123851/msp430f5438a-i2c-usci30-errata-workaround

器件型号:MSP430F5438A

大家好、我 已经观察到我的 I2C 总线悬挂在第二个字节的第7位上

我看到了 USCI30勘误表、因此我尝试实施了权变措施

  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 ((UCB1STAT & UCSCLLOW) != 0)
    {
        __delay_cycles(200);
    }
    else
    {
        UCB1IFG |= UCRXIFG;
        break;
    }
    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;
        }
    }

上面是我的 I2C ISR 的一个片段(STPIFG 和 RXIFG 部分的从器件接收)

因此、在勘误表中、权变措施是检查 UCSCLLOW 已被设定至少3个 USCI 位时钟周期。

我使用的是12.8MHz SMCLK 和400kHz I2C 时钟频率。

因此、在我的 RX ISR 处理中、我正在检查 UCSCLLOW 是否被置位、我将延迟200个周期。
我认为这应该足够长、因为12.8MHz 时钟的200个周期大约为15.625us、我只需要7.5us 来覆盖3个 USCI 位时钟周期

如果 UCSCLLOW 未被置位、我只会中断并让它重新进入 ISR 以进行再次检查。
对于接收到的最后一个字节、我希望 UCSCLLOW 不被置位。 因此、我将等待 STPIFG 被触发并读取其中的最后一个字节。

执行此操作后、我在示波器上获得 SCL 线路的该图像

我观察到、即使在实现 I2C 后、我仍然面临同样的问题、I2C 偶尔悬挂在第二个字节的第7位上。
我是否可以询问我是否对该勘误表/权变措施有误解、并检查我的权变措施实施是否正确?

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

    您好!

    我目前正在为您制定解决方案、我建议您尝试。 在勘误表中、建议的代码流程如下:

    (1)输入 RX ISR 以读取接收字节

    (2)检查 UCSCLLOW-UCBxSTAT 是否= 1

    (3)如果否、重复步骤2直到设置

    (4)如果是、请在大于3 x t (位时钟)的时间段内重复步骤2、其中 t (位时钟)= 1/ f (位时钟)

    (5)如果3 x t (位时钟)周期的窗口已经过去、那么读取 UCBxRXBUF 是安全的

    在您的代码中、一旦 UCSCLLOW 位为0、您就跳过了延迟并中断了 case 语句。 如果 UCSCLLOW 位为1、那么您延迟并读取 RX 缓冲区。 这与勘误表中建议的权变措施不匹配。

    您可能需要尝试以下类似操作:

    while(UCB0STAT & UCSCLLOW == 0);    // continually check to see if UCSCLLOW is set

    __delay_cycles(200);    // delay the time period that you've calculated to be safe

    while(UCB0STAT & UCSCLLOW == 0);    // final check to ensure that after the delay the UCSCLLOW bit is set

    然后执行读取操作。尝试在代码中实现类似的内容、并发送更新以了解您是否能够使内容正常工作。

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

    您好!

    我尝试使用 while 循环来持续检查 UCSCLLOW 是否被置位。 但是、对于最后一个字节、此条件不满足、我的代码将停留在最后一个字节的 while 循环中。 我认为前面提到的勘误表中不打算在最后一个字节上应用该变通办法。

    我还尝试了不同的接收中断实现

    case 10u:                                  // Vector 10: RXIFG
      {
        uint8_t clk_low_counter = 0;
        uint8_t clk_high_counter = 0;
        while (clk_low_counter < 16 && clk_high_counter < 16)
        {
            if ((UCB1STAT & UCSCLLOW) != 0)
            {
                clk_low_counter++;
                clk_high_counter = 0;
            }
            else
            {
                clk_high_counter++;
                clk_low_counter = 0;
            }
            //400 KHz is 2.5 us
            //sampling frequency should be more than 3 times of the clock frequency
            //four times the 400 KHz frequency will give delay, 2.5 us / 4 = 625 ns
            __delay_cycles(8); // 625 ns
        }
        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;
            }
        }

    在本例中、我尝试每625ns (约为400kHz 时钟周期的1/4)对 UCSCLLOW 位采样一次
    如果在16个循环中时钟一直保持低电平、这意味着4个时钟周期(勘误表中提到的>3个时钟周期)、那么我将读取 RXBUF。
    同样、如果在16个循环中时钟保持高电平、我将认为这是接收到的最后一个字节、并且我也将在之后读取 RXBUF。

    但是、这种实施也没有解决我的问题。 一段时间后、I2C 总线仍然挂起在第二个字节的第7位

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

    这种新的实现似乎也没有正确地利用勘误表中的权变措施。 当你说你已经尝试使用 while 循环来检查 UCSCLLOW 位是否被置位时、你是否意味着你执行了如描述的建议的权变措施? 或者、您是否意味着您已经稍微调整了实施方案以利用 while 环路?

    尝试使用变通办法时、发布 ISR 实现可能会有所帮助、以便我可以查看您是否正确实施了该解决方案。

    是否有原因无法使用勘误表中建议的其他变通办法? 第一个建议似乎易于实施、您可能会发现这些解决方案比使用第二个变通办法更成功。

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

    您好!
    我在 RX 中断中实现了这一点

      case 10u:                                  // Vector 10: RXIFG
      {
        while(UCB1STAT & UCSCLLOW == 0);
        __delay_cycles(400);
        while(UCB1STAT & UCSCLLOW == 0);
        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;
      }

    但是、一段时间后、程序仍然在第二个字节的第7位挂起。

    我是否可以验证当您提到勘误表的主要权变措施时、您是否提到了这一点?

    遗憾的是、我的程序还有其他部分会触发更高优先级的 ISR、因此我无法保证 I2C RX ISR 能够及时提供服务。 因此、我将查看次级权变措施。

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

    是的、这是我之前提到的解决方法。 我看到、由于您具有较高优先级的中断、您无法执行此操作。  

    您能否尝试实现类似于最近回复中的变通办法、但包含一个计数器、以确保不会像您提到的那样对最后一个字节执行延迟? 例如、在案例10U 的开头、使用类似的内容

    if(numberOfBytes < (totalBytes - 2)) // minus 2 so if you start the byte count at zero, the loop wont execute for the last byte

    {

    while(UCB0STAT & UCSCLLOW == 0);    // continually check to see if UCSCLLOW is set

    __delay_cycles(200);    // delay the time period that you've calculated to be safe

    while(UCB0STAT & UCSCLLOW == 0);    // final check to ensure that after the delay the UCSCLLOW bit is set

    }

    其中 numberOfBytes 是传输过程中到目前为止接收到的字节数、totalBytes 是要接收的字节数。 这样、您仍然可以简单地实现勘误表中所示的延迟、并在您指出的最后一个字节上跳过该延迟。

    如果这不起作用、通过在此处发布或发送私人消息来提供您的完整代码可能会有所帮助、以便我可以更深入地了解情况。

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

    不幸的是、作为一个从设备接收、我不知道我将要接收多少字节。 我正在接收的字节数不是固定的。 在我最近的代码中, ucb1_i2c.slave_rx->dat_len 是我为接收分配的字节数。 因此、这更多是我在一个事务中可以接收的最大字节数。

    目前、我正在使用停止位来知道事务已结束。

    #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
      {
        while(UCB1STAT & UCSCLLOW == 0);
        __delay_cycles(400);
        while(UCB1STAT & UCSCLLOW == 0);
        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
                    if (UCB1STT_timeout == 1000)
                    {
                        ucb1_i2c.rx_mode = SLAVE_RECEIVE;
                    }
                }
            }
        }
      default: break;
      }
    }

    这是我的完整 ISR 代码。 情况8是我的停止位 ISR、情况10是我的接收 ISR。

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

    我已经检查了权变措施的证明、在该代码中、该解决方案采用以下形式:

    #pragma vector = USCI_B0_VECTOR
    __interrupt void USCI_B0_ISR(void)
    {
        switch(__even_in_range(UCB0IV,30))
        {
            case -----: // test case for UCRXIFG
            while(!(UCB0STAT & UCSCLLOW)); // Is the Clock line low?
            while(!(UCB0STAT & UCSCLLOW)); // Check once again
            ReadBuf = UCB0RXBUF; // Now it is safe to read
            break;
        

    它似乎没有以不同的方式处理最后一个字节。

    代码是否始终挂起在第二个字节的第7位上? 我看到您提到的数据并非总是相同的字节数、因此我想知道是否存在不同的情形、即数据在其他地方挂起、或者数据在何处永远不挂起。

    此致、

    Luke

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

    Luke、

    经过进一步观察、可能没有必要出现 USCI30勘误问题。  

    我 观察到、在 i2c 总线挂起之前、接收数据的微控制器在接收后未回复。
    如屏幕截图所示、地址0x04首先向地址0x06发送6个字节。地址0x06、然后用38个字节进行回复。
    当0x04向0x06发送另外6个字节时、0x06未应答。 当0x04再次尝试发送到0x06时、i2c 总线在第二个字节的第7位挂起。 我猜出于某种原因、0x06不处理接收中断。 目前、我正在尝试进一步调试这个问题。

    此致
    开尔文