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.

[参考译文] MSP430FR2433:UCBBUSY 置位、同时通过 I2C 与两个传感器进行通信

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

https://e2e.ti.com/support/microcontrollers/msp-low-power-microcontrollers-group/msp430/f/msp-low-power-microcontroller-forum/1385274/msp430fr2433-ucbbusy-set-while-communicating-with-two-sensors-through-i2c

器件型号:MSP430FR2433

工具与软件:

大家好!

我将通过 I2C 连接 MPU-6050加速计和 CCS811气体传感器。

使用逻辑分析仪、在 一段时间后、UCBBUSY 被置位、且一个开始位挂起。 执行两次时、它 在请求气体传感器的数据寄存器后挂起。

我想值得注意的是、SCL 线稍后会转换为高电平。

寄存器的写入/读取代码如下所示:

Fullscreen
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Wake up the MPU-6050
slaveAddress = MPU_ADDR;
TX_Data[1] = PWR_MGMT_1;
TX_Data[0] = 0x08; // Set 8 MHz clock; disable temperature sensor
TX_ByteCtr = 2;
i2c_wr(slaveAddress);
__delay_cycles(50000); // According to datasheet, hold ~30 ms
// Because of gyroscope based clock oscillator
slaveAddress = CCS811_ADDR; // Make transition from boot to application mode
TX_Data[0] = CCS811_APP_START;
TX_ByteCtr = 1;
i2c_wr(slaveAddress);
__delay_cycles(30000);
slaveAddress = CCS811_ADDR;
TX_Data[1] = CCS811_MEAS_MODE;
TX_Data[0] = 0x10; // Put CCS to normal mode, no interrupt enable
TX_ByteCtr = 2;
i2c_wr(slaveAddress);
__delay_cycles(15000);
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

寄存器 地址可从相应的器件数据表中提取。

作为参考、I2C 相关代码如下所示:

Fullscreen
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* Configuration of I2C module
*/
void i2c_conf(void)
{
// I2C pins configuration
P1SEL0 |= BIT2 | BIT3;
// Disable the GPIO power-on default high-impedance mode to activate
// previously configured port settings
PM5CTL0 &= ~LOCKLPM5;
// Configure USCI_B0 for I2C mode
UCB0CTL1 |= UCSWRST; // Software reset enabled
UCB0CTLW0 |= UCMODE_3 | UCMST; // I2C mode, Master mode, SMCLK
UCB0CTLW0 |= UCSSEL__SMCLK | UCSYNC; // Use SMCLK as clock source, sync
UCB0CTLW1 |= UCASTP_2; // Automatic stop generated by
// reaching data acquisition boundary
UCB0BR0 = 0x0008; // baudrate = SMCLK / 8 = ~100 kHz
UCB0CTL1 &= ~UCSWRST; // Disable SW reset
}
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

感谢任何帮助。

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

    您能告诉哪个事务发生了故障吗? (是不是总是同一个?)

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

    您好!

    我又测试了两次、是的、它在同一个事务中(请求从 CCS811_ALG_RESULT_DATA -存储 CO2和 TVOC 数据的寄存器读取);它也显示相同的条件:UCBBUSY 设定和开始位挂起。

    我还检查了相关寄存器有时没有数据、因此我想知道是配置还是电路是主要原因。

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

    我猜是 CCS811正在拉伸时钟-虽然您的代码可能无意中将总线挂起、但这些事故通常出现在前几次交易中。 (将时钟拉伸"很长一段时间"称为"挂起总线"、即这与总线上的现象相同)。 数据表和 AN000369均未提及时钟延展、但使用 Google 5分钟后告诉我、CCS811在某些情况下会执行此操作。

    我想知道传感器是否在扩展等待新数据的时钟;我已经在另一个传感器的某个地方看到过这种做法。 数据表(或应用说明)中没有任何内容表明、如果没有新数据、就不能读取寄存器0x02 --它们甚至包括状态寄存器的副本、所以你可以先读取然后检查--但我发现的每个示例都只能在 DATA_READY=1时读取寄存器0x02。 由于您每秒请求一次读数、因此快速实验是将循环(底部延迟)从100ms 延长到1000ms。 稍微更详尽:启用并随总线一起探测 nINT 引脚。

    您是否正在使用商业分线板? 我注意到 Adafruit 板连接 nWAKE 低,但我不知道其他人。 您不希望它在任何情况下浮动。

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

    我不想对 I2C 代码进行的一个操作是允许无限等待。 每当我有类似"while (UCB0CTL1 & UCTXSTP);"的代码时、我都会添加超时。 出于调试目的、我还倾向于针对每个错误返回唯一的代码、因为这样可以更轻松地查找问题。

    如果问题是读取开始时的挂起启动条件、我会首先检查一下上一次写入(其中将寄存器编号设置为读取)是否正确完成。 此外、执行此操作的常用方法是通过重复启动的单个事务。

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

    我 在检查数据寄存器(CCS811_ALG_RESULT_DATA)之前添加了验证代码(访问 CCS811_STATUS)、但错误仍然存在。

    我来看看这个。  

    nWAKE 引脚、它需要接地。 奇怪的是、我 在读取 CCS 地址时收到 NACK;恢复正常后、RST 和 INT 断开连接。  

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

    我 重新编码了 ISR 以实施重复启动:

    Fullscreen
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    #if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
    #pragma vector = USCI_B0_VECTOR
    __interrupt void USCIB0_ISR(void)
    #elif defined(__GNUC__)
    void __attribute__ ((interrupt(USCI_B0_VECTOR))) USCIB0_ISR (void)
    #else
    #error Compiler not supported!
    #endif
    {
    switch(__even_in_range(UCB0IV, USCI_I2C_UCBIT9IFG))
    {
    case USCI_I2C_UCRXIFG0:
    if(RX_ByteCtr)
    {
    RX_Data[RX_ByteCtr--] = UCB0RXBUF; // Get received byte
    UCB0IFG |= UCRXIFG0;
    }
    else
    {
    UCB0CTLW0 |= UCTXSTP;
    UCB0IFG &= ~UCRXIFG0;
    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

    更糟糕的是、因为我无法进行通信、从而使 SCL 和 SDA 在 第一次 I2C 通信尝试中丢失(设置 MPU)。

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

    通常、您向 TXBUF 写入一些内容来响应发送中断。

    接收中断会出现多余的奇怪现象。 冗余、因为 UCRXIFG0是通过读取 UCB0IV 和 UCB0RXBUF 来清零的、因此不需要它显式清除它。

    奇怪的是、在读取数据时有一种更简单的方法来处理停止条件。 当 ISR 开始时、I2C 硬件会等待、可能会出现以下两种情况之一。 您可以读取 RXBUF、它将开始接收下一个字节。 或者您可以设置 TXSTP、它将生成停止条件。 按照编写的内容、它会接收一个它只是忽略的额外字节。

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

    在 TX 部分、我犯了一个巨大的错误、过于强调。

    这是新版本:

    Fullscreen
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    #if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
    #pragma vector = USCI_B0_VECTOR
    __interrupt void USCIB0_ISR(void)
    #elif defined(__GNUC__)
    void __attribute__ ((interrupt(USCI_B0_VECTOR))) USCIB0_ISR (void)
    #else
    #error Compiler not supported!
    #endif
    {
    switch(__even_in_range(UCB0IV, USCI_I2C_UCBIT9IFG))
    {
    case USCI_I2C_UCRXIFG0:
    RX_Data[--RX_ByteCtr] = UCB0RXBUF; // Get received byte
    if(!RX_ByteCtr)
    {
    if(restart)
    {
    UCB0CTL1 |= UCTXSTT;
    while(UCB0CTL1 & UCTXSTT); // Ensure START sent
    restart = 0;
    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

    挂起总线问题已解决! 另一个问题是读数错误。

    看起来计数器混乱。

    在后续代码块中、应执行3次写入。

    Fullscreen
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    slaveAddress = CCS811_ADDR; // Make transition from boot to application mode
    TX_Data[0] = CCS811_APP_START;
    TX_ByteCtr = 1;
    restart = 1;
    i2c_wr(slaveAddress);
    __delay_cycles(22000);
    TX_Data[1] = CCS811_MEAS_MODE;
    TX_Data[0] = 0x10; // Put CCS to normal mode, no interrupt enable
    TX_ByteCtr = 2;
    i2c_wr(slaveAddress);
    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

    但这项工作是:

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

    我想我不能把这件事全部讲出来。

    首先、在执行重新启动时、必须将 I2C 端口从写入切换到读取。 (清除 UCTR)否则发送设置了写入位的器件地址。 一降再降。

    示例:

    Fullscreen
    1
    2
    3
    4
    5
    IFG2 &= ~UCB0RXIFG; // make sure no previous interrupt
    UCB0CTL1 &= ~UCTR; // restart as receiver
    UCB0CTL1 |= UCTXSTT; // I2C repeated start condition
    IE2 |= UCB0RXIE; // enable receive interrupt
    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

    (对于 G 系列器件、细节有所不同。)

    不知道为什么接收代码会检查 RESTART 标志。 您永远不会启动接收并切换至发送。

    发送 APP_START 的命令似乎错误。 通常必须发送寄存器地址/值对。 但查看器件数据表、这似乎是正确的。 简化视图。 您是否加载了应用程序? 您是否通过读取 STATUS 寄存器来检查它是否符合建议? 为什么设置了重新启动标志? 这将不会带来问题的结束。 (考虑您的字节计数器在下一个发送中断时将具有什么样的值。)

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

    您好!

    我在 TX 中断方面遇到了很多问题、触发错误的时间(在进入低功耗模式之前)、而不是在应该触发时触发;这导致一些字节的数据 读取比预期的要多。

    我 切换回使用自动停止、现在通信似乎更加稳定。 那么、我将 保持这种方式一段时间。

    关于 app_start、您是对的、这不是必需的。 当我 首次对气体传感器接口进行编码时、我认为有必要将其启动。