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.

[参考译文] MSP430F5529:I2C 和#39;Stuck 和#39;正在等待 UCTXIFG

Guru**** 2589265 points
Other Parts Discussed in Thread: MSP430F5529

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

https://e2e.ti.com/support/microcontrollers/msp-low-power-microcontrollers-group/msp430/f/msp-low-power-microcontroller-forum/625641/msp430f5529-i2c-stuck-waiting-for-uctxifg

器件型号:MSP430F5529
主题中讨论的其他器件: MSPWARE

大家好、

我一直在使用 MSP430F5529和 LSM6DS33分接传感器( 数据表)开发定制 PCB。 我使用 Arduino Mega 和 SparkFun 库(GitHub 库)测试了该传感器。 接下来、我尝试使用 MSP430的 USCI I2C 外设从该传感器读取数据。 我已经阅读并检查 了用户指南 和 MSPWARE 3.60.00.10示例。 我尝试将这些示例混合在一起以编写一个函数、该函数将从传感器读取多达12个连续寄存器。 但是、代码会一直等待 TX 缓冲区完成将字节(目标寄存器)移入外设。 我不知道为什么。

首先、我初始化 I2C 外设:

//将引脚3.0和3.1设置为 I2C
P3SEL |= 0x03;

//配置前复位 USCI
UCB0CTL1 |= UCSWRST;

//将 USCI 设置为 I2C 主模式并将 MSB 置1
UCB0CTL0 |= UCMST | UCMODE_3 | UCSYNC | UCMSB;

//设置波特率1MHz/10 = 100kHz
UCB0BR0 = 10;
UCB0BR1 = 0;

//设置时钟源,启用 I2C
UCB0CTL1 |= UCSSEL_2;

//禁用中断请求
UCB0IE &=~(UCTXIE + UCRXIE);

//保存从机地址
UCB0I2CSA = 0x6A; 
//启用 I2C 模块 UCB0CTL1 &=~UCSWRST;


>>假设:当添加 R/W 位时、从地址被外设向左移动一位。


接下来、我尝试按照 LSM6DS33数据表第30页的表15所述协议向从器件发送一个包含地址的字节。 根据用户指南、UCTXIFG 位将在从器件地址被发送后置位、以指示 UCB0TXBUF 已准备好接收一个字节。 然后、我将数据写入 UCB0TXBUF 并等待 UCTXIFG 位再次置位。 之后、必须再次发送"重复启动"、告知从机需要向我发送数据。

//发送启动条件。
UCB0CTL1 |= UCTR + UCTXSTT;

//等待开始条件被发送。
while (!(UCB0IFG & UCTXIFG)){;}

//将字节写入传输缓冲区
UCB0TXBUF = 0x28;

//等待数据被发送。
while (!(UCB0IFG & UCTXIFG)){;}


//开始接收 n 个字节

//发送重复启动条件
UCB0CTL1 &=~UCTR;
UCB0CTL1 |= UCTXSTT; 

然而、代码在数据被写入 UCB0TXBUF 后就会卡在第二个 while 环路中。


尝试的解决方案和其他信息:

  • 在 SDA 和 SCL 线路上添加外部上拉电阻;它们已经在分接板上(电路原理图)、因此无论如何都不必要。
  • 使能中断(我假定不管是否产生中断、UCB0IFG 中的相应位都被置位)。
  • 检查连接、PCB 和焊料误差(也测得3.27 VCC 稳定值)。
  • SDO 被焊接到接地上(使从器件地址的 LSB 为'0')。
  • 写入 UCB0TXBUF 后添加一个 UCSCLLOW 检查;没有外部器件将 SCL 拉至低电平。

遗憾的是、我没有示波器或数字分析仪。 我可能可以在下周借用一个、然后我可以添加 SDA 和 SCL 的信号图像。 目前、我需要知道:

  1. 的代码是否正确? 它不提供构建错误/警告。
  2. 的代码是否完整? 我可能忽略了一些关键的设置/中断/寄存器值。
  3. 硬件上是否有其他可能导致问题的东西?

任何帮助、见解、建议、设计技巧、代码技巧、 我们感谢您提供有用的链接或代码测试。 如有需要,我会乐意提供更多详情。 这是我开始工作所需的最后一段代码;之后、我最终可以完成演示并毕业...

此致、

Martin Janse

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    当我读取原理图时、SA0被上拉、因此地址应为0x6B。

    更一般地说:您可能应该在用户指南(SLAU208O)第38章的末尾查看这些图表--图38-12与此相关--这些图表描述了状态机。 从左向右扫描可提供时间序列、从上到下显示故障模式。 在您的情况下、我希望它向下转换到 nack、恰好在其中包含"A"的框之后。

    更普遍的情况是:I2C 状态机非常容易使用中断驱动,但由于故障模式,它作为内联代码变得更复杂(或至少更乏味)。 示例程序可以提供框架 ISR。
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    感谢您花时间查看问题并回复!

    [引用用户="Bruce McKenney47378"]当我阅读原理图时,SA0被上拉,因此地址应为0x6B。

    如果插头上的第6个引脚保持悬空、则实际应为6B。 然而、这个引脚被接地。 当然、我移除了连接并相应地更改了从器件地址。 不幸的是,它没有解决我的问题。

    [引用 USER="Bruce McKenney47378]]更一般地说:您可能应该在用户指南(SLAU208O)第38章的末尾查看这些图表--图38-12在这里是相关的--这些图表描述了状态机。 从左向右扫描可提供时间序列、从上到下显示故障模式。 在您的情况下、我希望它在包含"A"的方框后立即转变为 NACK。

    我看了图表、您的评论促使我在每个步骤中进行广泛的寄存器检查。 我编写了一个小函数、用于读取所有相关寄存器值。 然后、我将这些值与我预期的位进行比较、以便可以看到任何差异。 两件事都不合适。

    首先、设置了多主机使能位。 这是奇数、因为我从未设置该位、默认情况下它应该为"0"。 我添加了一行、将位强制为"0"至 I2C 初始化函数。

    其次、即使在开始任何传输之前、UCBBUSY 位也被置位。 这是意外行为;与数据表不符。 毕竟、当 UCSWRST 被释放并且 SCL 引脚为高电平时、UCBBUSY 位应该被复位。 但是、该位仍然为"1"。 我测量了 SCL 和 SDA 线路、它们稳定在3.27V。 在为这种奇怪的行为进行谷歌搜索后、我又发布了另 一篇文章 、将这种现象描述为"未记录的勘误表"。 提出了两个解决方案。

    首先、防止 UCBBUSY 位被置位。 这要求在配置期间 SCL 线路为高电平。 为此、我将 I2C 引脚初始化为 GPIO、启用内部上拉电阻并将其打开。 然后我完成配置并再次启用 USCI。 之后、引脚再次设置为 I2C。 这可以解决永久性的 UCBBUSY 问题。 我的初始化代码现在如下所示:

    int IMU_init (void){
    
    //将引脚3.0和3.1设置为高输出。
    //这可防止 USCI 模块错误地假设
    // I2C 总线忙。
    P3SEL &=~0x03;
    P3REN |= 0x03;
    P3DIR |= 0x03;
    P3OUT |= 0x03;
    
    //配置前复位 USCI。
    UCB0CTL1 |= UCSWRST;
    
    //将 USCI 设置为单主控模式。
    UCB0CTL0 |= UCMST | UCMODE_3 | UCSYNC | UCMSB;
    UCB0CTL0 &=~UCMM;
    
    //将波特率设置为@100kHz。
    UCB0BR0 = 10;
    UCB0BR1 = 0;
    
    //设置时钟源,启用 I2C
    UCB0CTL1 |= UCSSEL_2;
    
    //禁用中断请求
    UCB0IE &=~(UCTXIE + UCRXIE);
    
    //保存从机地址
    UCB0I2CSA = 0x6B;
    
    //启用 I2C 模块
    UCB0CTL1 &=~UCSWRST;
    
    //将 P3.0和 P3.1提供给 USCI 模块
    P3OUT &=~0x03;
    P3DIR &=~0x03;
    P3REN &=~0x03;
    P3SEL |= 0x03;
    
    返回0;
    } 

    但是、仍然出现第二个问题。 有时-我不确定是什么触发它-即使传输结束时有一个停止位、UCBBUSY 位也会保持置位。 对于这些罕见的情况、我添加了一个包含第二个解决方案的简短功能。

    第二种解决方案是在 GPIO 时多次切换 I2C 引脚、以"欺骗" USCI 认为传输已结束。 我使用的函数调用:

    if (UCB0STAT & UCBBUSY){toggleBusy ();} 

    该功能本身只需在以下两种模式之间以较小的延迟切换 I2C 引脚几次:

    void toggleBusy (void){
    
    //将 P3.0和 P3.1设置为 GPIO、并将上拉电阻器设置为输出
    P3SEL &=~0x03;
    P3REN |= 0x03;
    P3DIR |= 0x03;
    
    //将引脚值切换10次
    int i = 0;
    对于(I = 10;I > 0;I -){
    P3OUT |= 0x03;
    _DELAY_CYCLES (150);
    P3OUT &=~0x03;
    _DELAY_CYCLES (150);
    }
    
    //禁用内部上拉并将引脚重新接至 I2C
    P3REN &=~0x03;
    P3DIR &=~0x03;
    P3SEL |= 0x03;
    } 

    在极少数情况下、它会在该位上卡住、但延迟一点比无限期卡住更好! 我希望这澄清了问题,并希望我能对其他有类似问题的人有所帮助。

    此致、

    Martin Janse