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.

[参考译文] MSP430FR5989:当使用外部 HFXT 时、I2C 寄存器 ctw0和 txbuf 不再按预期工作

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

https://e2e.ti.com/support/microcontrollers/msp-low-power-microcontrollers-group/msp430/f/msp-low-power-microcontroller-forum/1395398/msp430fr5989-i2c-register-ctw0-and-txbuf-no-longer-working-as-expected-when-using-an-external-hfxt

器件型号:MSP430FR5989

工具与软件:

之前、在以8MHz 的频率使用内部生成的时钟的 I2C 外设时、该外设没有问题、从我的可观察用例来看、它按预期运行。  

切换到使用16MHz 的外部 HFXT 输入、设置 SMCLK 和 MCLK 以使用具有2分频因子的 HFXT 输入。 (8MHz)。 我的 I2C 外设不再可靠工作。

    在初始化后执行以下序列时出现此问题:

注意:

下面显示的宏是针对目标寄存器的读取和写入位

// First write to slave device
  WRITE_REG(hi2c_.reg->I2CSA, dev_address);

  // Send start, and configure to send slave address
  SET_BIT(hi2c_.reg->CTLW0, UCTR + UCTXSTT);

  // Wait until address has been sent
  while (READ_BIT(hi2c_.reg->CTLW0, UCTXSTT))
    ;

上面的代码片段 会发送 从器件 IC 地址和块、直到发送启动条件为止

之后、我会尝试在发送地址后向总线写入一个字节。

      WRITE_REG(instance->reg->TXBUF, (uint8_t)(reg_address & (uint16_t)0x00FF));
      while (READ_BIT(instance->reg->IFG, UCTXIFG0) == 0);

这是尝试在 I2C 外设停止工作的地址之后写入一个字节的情况。

在探测线路时、每次都正确发送地址。  

监控 STATW 寄存器、我确实看到在预期的运行下、它会报告以下内容

已传输1字节

UCSCLLOW 被激活

UCBBUSY 忙

请求通过 I2C 总线发送的所有序列字节也会进行发送、而不会出现任何问题。

OFF 的标称行为显示了相反的情况  

传输0字节

UCSCLLOW 未激活

UCBBUSY 未忙

时钟设置代码

void setup_clock()
{
  // Configure DCO to 8 MHz (CPU clock speed)
  WRITE_REG(CSCTL0_H, CSKEY >> 8);  // Unlock CS registers

  WRITE_REG(CSCTL3, DIVA__1 | DIVS__2 | DIVM__2);

  WRITE_REG(CSCTL1, DCOFSEL_2 | DCORSEL);  // Set DCO to 8 MHz

  // Enable external HXFT operation and configure LFXT to not be utilized
  SET_BIT(CSCTL4, HFXTBYPASS | HFXTDRIVE_3 | HFFREQ_2 | LFXTOFF);
  // Configure ACLK = VLOCLOCK ~ 10kHz, SMCLK = HFXT, MCLK = HFXT
  WRITE_REG(CSCTL2, SELA__VLOCLK | SELM__HFXTCLK | SELS__HFXTCLK);
  // Ensure to configure HFXT to use external input clock
  CLEAR_BIT(CSCTL4, HFXTOFF);
  // Disable startup fault counter for both HFXT and LFXT
  CLEAR_BIT(CSCTL5, ENSTFCNT2 | ENSTFCNT1);

  // Test oscillator fault flag, wait till HFXT clock switch over is complete
  while (SFRIFG1 & OFIFG)
  {
    CSCTL5 &= ~(HFXTOFFG | LFXTOFFG);  // Clear XT1 and XT2 fault flag
    SFRIFG1 &= ~OFIFG;
  }

  CLEAR_REG(CSCTL0_H);  // Lock CS registers
}
I2C 外设设置代码
void i2c_b1_init(PIL::I2C_t *instance)
{
  /*
   *              P3DIR P3SEL1 P3SEL0
   * P3.1 SDA     X       0       1
   * P3.2 SCL     X       0       1
   */

  P3SEL0 |= BIT1 | BIT2;
  instance->reg = (PIL::MSP430FR5989_I2C_reg_t *)eUSCI_B1_BASE_ADDRESS;

  // Disable peripheral
  WRITE_REG(instance->reg->CTLW0, UCSWRST);
  // see family user guide example 32-1
  SET_BIT(instance->reg->CTLW0, UCMST | UCMODE_3 | UCSSEL__SMCLK | UCSYNC);
  // Desired output frequency 100khz
  WRITE_REG(instance->reg->BRW, 0x4F);
  // Enable peripheral
  CLEAR_BIT(instance->reg->CTLW0, UCSWRST);
}
尝试监控  UCTXIFG0的 IFG 寄存器也不会解决问题。  
外设停止工作的时间基本上与前30秒内的时间一致。 在首次尝试时可能发生、直至约30秒。 因为我有一个1Hz 环路尝试读取传感器。
请注意、1Hz 环路在使用8MHz 的内置 DCO 时钟时在标称值下工作。
FRAM 访问也限制为4个周期。
除了该外设操作、我还有2个计时器模块、分别在100ms (切换心跳 LED)和1ms (SysTick)生成中断。 当 I2C 外设进入等待的 while 环路陷阱时、LED 仍正常运行、系统时间仍在预期的间隔内递增。  
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您指出了几行代码失败、但我不清楚失败的原因是什么。 是否从未设置 TXIFG (永远旋转)?

    我不知道为什么使用外部时钟源(速度相同)会影响 I2C 单元、除非外部源具有(间歇性?) 故障、I2C 单元受故障转移影响。

    > SET_BIT (CSCTL4、HFXTBYPASS | HFXTDRIVE_3 | HFFREQ_2 | LFXTOFF);

    您使用的是外部时钟(HFXTBYPASS)还是晶体(HFXTDRIVE_3)?  

    这将设置(保留) HFFREQ=3、对于该值、16MHz 几乎超出了范围。 我不知道这会有什么影响。

    即使您为 ACLK 使用 VLOCLK、此情况也仍然使 VLOOFF = 1。 (但与症状没有明显关联。)

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

    看起来忘了最重要的位!

    它的故障部分是 STATW 寄存器在地址发送后未更新。 之后、尝试向 TX 缓冲区写入一个字节不会产生总线活动、而 while 循环始终评估为真、就像正在进行传输或者在该逻辑中有正在进行的传输一样、等待该标志被设置。 表明已发送该字节。
    在尝试写入一个字节之前添加多个 ms 延迟、因为测试不会影响该行为。

    我确实将 MCLK 连接到 GPIO 引脚(使用硬件、而不是软件切换)、并且我没有看到 clk 开关频率、因为故障时钟的频率~4.8MHz

    我在 HFXT IN 引脚上使用外部连接的 MEMS 振荡器。

    我还没有尝试在16-24和(我认为) 8个-16 MHz 范围选择之间进行切换

    我不会认为 vloclk 会影响这里的事情、因为针对这一点的时钟树不应连接任何东西。 但这是我的假设  

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

    如果您卡在 TXIFG0环路中、何时检查 STATW? 另外、听起来就像您有示波器;您是否能够捕获故障序列的轨迹? 如果 UCBBUSY=0自动、这听起来相当奇怪。

    从您的描述来看、好像在这个更改之前您运行的是 DCO = 8MHz (7MHz?) 但在使用 SELM/SELS_2时、即 CPU/I2C 实际上基于4MHz、并且您的外部时钟加快了运行速度。 这将增加种族的可能性。

    基于上述片段、您写入 TXBUF 而不检查 TXIFG。 如果存在竞争、这可能会导致字节丢失。

    我(个人)绝不会等待 UCTXSTT 变为低电平、因为 I2C 流程图[参考用户指南(SLAU367P)图32-12]会说在您写入 TXBUF (->Deadlock)之前它不会变为低电平。 相反、我只检查 TXIFG/NACKIFG。 (话虽如此、我看到 论坛中到处都有代码 可以实现这一点、据称是有效的。)

    您禁用时钟启动监视器(ENSTFCNT2)是否有原因? UG 对此一无所知;我总是想知道它是否真正起到完全禁用时钟监控器的作用。

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

    调试时、我在写入 TXBUF 之前检查 STATW。

    有。 我将在几小时后上传示波器屏幕截图。

    当 DCO 设置为8MHz 时、时钟分频器设置为1

    I2C 具有相同的时钟分频器 BRW 设置

    在其他调试尝试中、我在写入缓冲区之前确实尝试了监控 TXIFG、但仍然会死锁、同样的进入条件是 STATW 报告返回0而不是0x0150、自启动条件以来发送了1个字节、总线处于运行状态并且 CLK 保持低电平。

    有意思的是、在用户手册中、它说 STT 标志在器件地址发送后变为低电平。 我放置轮询等待只是为了避免移位寄存器缓冲器冲突。

    今天我来试试。 可能是、通过等待地址标志、我无意中使总线处于空闲状态、而到 i2c 状态机、这 将指示停止事务?

    文中提到、如果我使用外部时钟、则无需启用。 我禁用这两者、因为另一个内部时钟应该关闭、并且我使用外部输入来对 CNT2进行补充。

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

    第一张图像显示了正常 I2C 存储器读取和存储器写入事务的开始

    第二幅图显示了未执行 TXBUF 写入的情况。  

    发送的最后一个字节是新内存写入的开始、在写入时仅发送了地址

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

    我看到在失败的情况下

    1) 1)[正常启动] 写入(寄存器地址)

    2) 2)[重复启动]读取  

    3) 3)[重复启动] 写入(无内容)  

    4) 4)[停止]

    步骤(3)不是非法的、但很不寻常--我预期是无意的。 这看起来更像程序流。

    如果在执行第(4)步后查看 TXIFG、它确实会(永久)=0。

    [编辑:我不知道 I2C 单元会自动发送停止,但 UCASTP=2除外。 您是否正在使用该功能?]

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

    是的、目前很奇怪的是、为什么在第3步中它没有写入。

    尽管我今天重新审视了一番、但我相信、HFXT 的比赛条件现在比以前更加突出、而且我以前的测试可能还不够长、无法看到它的发生。

    我正在经历的情况是、for 循环的迭代次数比预期的多1次  

    在本例中、当它失败时、  

    通过函数调用来读取2个字节

    后续写入是下一个 I2C 调用、但我认为该调用可能会由于第一个存储器读取请求而损坏。

    应用程序逻辑是什么

    STATUS_CODE_e I2C::master_mem_read(uint16_t dev_address, uint16_t reg_address, addr_size reg_address_size, uint8_t *rx,
                                       uint16_t size)
    {
      // First write to slave device
      WRITE_REG(hi2c_.reg->I2CSA, dev_address);
    
      // Send start, and configure to send slave address
      SET_BIT(hi2c_.reg->CTLW0, UCTR + UCTXSTT);
    
      // Wait until address has been sent
      while (READ_BIT(hi2c_.reg->CTLW0, UCTXSTT))
        ;
    
      register_access(get_self(), reg_address, reg_address_size);
      // Send stop
      SET_BIT(hi2c_.reg->CTLW0, UCTXSTP);
    
      // Wait until stop has been sent
      while (READ_BIT(hi2c_.reg->CTLW0, UCTXSTP))
        ;
      blinky_led_g.set_state(true);
      blinky_led_g.set_state(false);
      blinky_led_g.set_state(true);
      blinky_led_g.set_state(false);
      // switch to RX mode to read out memory address content
      CLEAR_BIT(hi2c_.reg->CTLW0, UCTR);
      SET_BIT(hi2c_.reg->CTLW0, UCTXSTT);
      // Wait until address has been sent
    
      while (READ_BIT(hi2c_.reg->CTLW0, UCTXSTT))
        ;
      blinky_led_g.set_state(true);
      blinky_led_g.set_state(false);
      for (uint16_t buf_idx = 0; buf_idx < size - 1; buf_idx++)
      {
        // Poll for RX buffer flag
        while (READ_BIT(hi2c_.reg->IFG, UCRXIFG) == 0)
          ;
        rx[buf_idx] = hi2c_.reg->RXBUF;
      }
      blinky_led_g.set_state(true);
      blinky_led_g.set_state(false);
    
      // Prepare to send stop condition on incoming last byte
      SET_BIT(hi2c_.reg->CTLW0, UCTXSTP);
      // Ensure to empty RXBUF
      while (READ_BIT(hi2c_.reg->IFG, UCRXIFG) == 0)
        ;
      rx[size - 1] = hi2c_.reg->RXBUF;
    
      blinky_led_g.set_state(true);
      blinky_led_g.set_state(false);
      blinky_led_g.set_state(true);
      blinky_led_g.set_state(false);
      return STATUS_CODE_e::OK;
    }

    在上面的屏幕截图中、直到这个交易之前、所有之前的交易为标称值。  

    在本例中、我看到地址已写入器件

    I2C 发送器模式

    写入从器件地址以进行写入

    写入寄存器以访问

    写入 STOP

    后跟 I2C 接收器模式  

    写入从器件地址以进行读取

    读取2个字节

    在要读取的最后一个字节上发送 STOP

    我今天添加了这一点。 我没有启用自动停止

    [编辑]删除了最后一个阻止等待停止标志,这是我刚才添加的东西几分钟前,屏幕截图后。

    现在对其进行了测试、因此它似乎可以可靠地访问 I2C 总线。

    我将让它运行很长时间、直到我看到另一个锁定。

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

    似乎我的竞争条件是我没有明确地轮询/设置停止标志在我的所有交易结束(当从发送器切换到接收器时添加停止标志). 如果不这样做、我可以在前一个 I2C 外设 仍在发送数据的中间连续执行两个事务、最终导致错误行为。
    使用 HFXT 时、为什么会发生这种情况?

    最有可能的假设是、我已经处于边缘状态、通过添加一个最有可能略快的稳定时钟、我超过了未知裕度。