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.

[参考译文] MSP430FR5962:I2C 通信随机停止

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

https://e2e.ti.com/support/microcontrollers/msp-low-power-microcontrollers-group/msp430/f/msp-low-power-microcontroller-forum/1596326/msp430fr5962-i2c-comminication-getting-stop-randomly

器件型号: MSP430FR5962

我的 μ I²C 通信遇到了问题、从器件正确初始化、但当我尝试读取多个字节时、通信随机停止、SCL 和 SDA 线路都一直处于低电平。 单字节读取始终有效、没有任何问题、但只要我执行多字节读取、总线就会在事务期间意外挂起。 似乎从器件未释放线路、或者主器件在等待字节就绪情况时卡住、从而导致总线保持繁忙状态、直到复位。 我需要帮助了解 I²C I ² C 总线为何专门在多字节读取期间锁定、以及如何防止 SCL 和 SDA 被无限期拉至低电平。  

#include <msp430.h>
#include <stdint.h>
#include <msp430fr5992.h>

/* Generic typedefs */
typedef unsigned char  UINT8;
typedef unsigned short UINT16;

/* -----------------------
   GENERIC DEFINITIONS
------------------------ */
#define DEVICE_ADDR_1   0x42
#define DEVICE_ADDR_2   0x44
#define DEVICE_ADDR_3   0x46
#define REG_READ_FIFO   0x01

UINT8 ReceiveBuffer[20] = {0};
UINT8 TransmitBuffer[20] = {0};
UINT8 RXByteCtr = 0, TXByteCtr = 0;
UINT8 ReceiveIndex = 0, TransmitIndex = 0;
UINT8 i2c_busy = 0;

volatile UINT8 *i2c_tx_ptr, *i2c_rx_ptr;

/* ============================================================
   I2C MASTER INITIALIZATION
============================================================ */
void clock_init( void )
{
	PM5CTL0 &= ~LOCKLPM5;
	CSCTL0_H = CSKEY_H ;									// Unlock CS registers
	CSCTL1 = DCOFSEL_4 + DCORSEL ;								// Set DCO to 16MHz
	CSCTL2 = SELA__LFXTCLK | SELS__DCOCLK | SELM__HFXTCLK ; 	// Set set ACLK =  LFXT1;MCLK = DCO
	CSCTL3 = DIVA__1 | DIVS__1 | DIVM__1 ;						// Set all dividers to 1
	CSCTL4 = HFFREQ_2 | HFXTDRIVE_3;
	CSCTL4 &= ~HFXTOFF ;
	CSCTL0_H = 0;
}

void init_i2c_master(void)
{
    /* Configure pins P7.0 = SDA, P7.1 = SCL (eUSCI_B2) */
    P7SEL0 |= BIT0 | BIT1;
    P7SEL1 &= ~(BIT0 | BIT1);

    UCB2CTLW0 = UCSWRST;
    UCB2CTLW0 |= UCSSEL__SMCLK | UCMODE_3 | UCMST | UCSYNC | UCSWRST;
    UCB2BRW = 100;
    UCB2I2CSA = DEVICE_ADDR_1;    /* default – will be changed per call */
    UCB2IFG = 0;
    UCB2CTLW0 &= ~UCSWRST;
}

/* Simple memcpy */
void CopyArray(UINT8 *source, UINT8 *dest, UINT8 count)
{
    for (UINT8 i = 0; i < count; i++)
        dest[i] = source[i];
}

/* Timeouts */
#define I2C_TIMEOUT_LOOPS  30000u
#define I2C_POST_STOP_DELAY_CYCLES  0u

static int i2c_wait_for_ifg(UINT16 mask)
{
    UINT16 t = I2C_TIMEOUT_LOOPS;
    while (!(UCB2IFG & mask))
    {
        if (--t == 0) return 0;
    }
    return 1;
}

static int i2c_wait_for_ctl_clear(UINT16 mask)
{
    UINT16 t = I2C_TIMEOUT_LOOPS;
    while (UCB2CTLW0 & mask)
    {
        if (--t == 0) return 0;
    }
    return 1;
}

static void i2c_issue_stop_and_wait(void)
{
    UCB2CTLW0 |= UCTXSTP;
    i2c_wait_for_ctl_clear(UCTXSTP);
    __delay_cycles(I2C_POST_STOP_DELAY_CYCLES);
}

/* ============================================================
   GENERIC I2C WRITE
============================================================ */
UINT8 i2c_master_write(UINT8 dev, UINT8 reg, UINT8 *data, UINT8 count)
{
    if (!i2c_wait_for_ctl_clear(UCTXSTP)) return 0;

    UCB2I2CSA = dev;
    UCB2IFG = 0;

    UCB2CTLW0 |= UCTR | UCTXSTT;

    if (!i2c_wait_for_ifg(UCTXIFG)) { i2c_issue_stop_and_wait(); return 0; }
    if (UCB2IFG & UCNACKIFG)        { i2c_issue_stop_and_wait(); return 0; }

    UCB2TXBUF = reg;

    if (!i2c_wait_for_ifg(UCTXIFG)) { i2c_issue_stop_and_wait(); return 0; }
    if (UCB2IFG & UCNACKIFG)        { i2c_issue_stop_and_wait(); return 0; }

    for (UINT8 i = 0; i < count; i++)
    {
        UCB2TXBUF = data[i];
        if (!i2c_wait_for_ifg(UCTXIFG)) { i2c_issue_stop_and_wait(); return 0; }
        if (UCB2IFG & UCNACKIFG)        { i2c_issue_stop_and_wait(); return 0; }
    }

    i2c_issue_stop_and_wait();
    return 1;
}

/* ============================================================
   GENERIC I2C READ
============================================================ */
UINT8 i2c_master_read(UINT8 dev, UINT8 reg, UINT8 count, UINT8 *rx_buf)
{
    if (!i2c_wait_for_ctl_clear(UCTXSTP)) return 0;

    UCB2I2CSA = dev;
    UCB2IFG = 0;

    UCB2CTLW0 |= UCTR | UCTXSTT;

    if (!i2c_wait_for_ifg(UCTXIFG)) { i2c_issue_stop_and_wait(); return 0; }
    if (UCB2IFG & UCNACKIFG)        { i2c_issue_stop_and_wait(); return 0; }

    UCB2TXBUF = reg;
    if (!i2c_wait_for_ifg(UCTXIFG)) { i2c_issue_stop_and_wait(); return 0; }
    if (UCB2IFG & UCNACKIFG)        { i2c_issue_stop_and_wait(); return 0; }

    UCB2CTLW0 &= ~UCTR;
    UCB2CTLW0 |= UCTXSTT;

    if (!i2c_wait_for_ctl_clear(UCTXSTT)) { i2c_issue_stop_and_wait(); return 0; }

    if (count == 1) UCB2CTLW0 |= UCTXSTP;

    for (UINT8 i = 0; i < count; i++)
    {
        if (!i2c_wait_for_ifg(UCRXIFG)) { i2c_issue_stop_and_wait(); return 0; }
        rx_buf[i] = UCB2RXBUF;

        if (i == count - 2) UCB2CTLW0 |= UCTXSTP;
    }

    if (!i2c_wait_for_ctl_clear(UCTXSTP)) return 0;

    return count;
}

/* ============================================================
   MAIN LOOP (GENERIC VERSION)
============================================================ */
int main(void)
{
clock_init();
    init_i2c_master();

    UINT8 buf1[5], buf2[5], buf3[5];

    while (1)
    {
        i2c_master_read(DEVICE_ADDR_1, REG_READ_FIFO, 5, buf1);
        i2c_master_read(DEVICE_ADDR_2, REG_READ_FIFO, 5, buf2);
        i2c_master_read(DEVICE_ADDR_3, REG_READ_FIFO, 5, buf3);

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

    您好 Ankush、

    您能否分享通信线路的截屏?

    此致、

    Diego Abad

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

    尊敬的 Diego Abad Sajamin:

    当然、我会分享数据采集。
    下面,我分享的捕获显示它是在阅读期间卡住.

    谢谢你。

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

    您好 Ankush、

    您可以重新启动 I2C 模块、然后再次重试 I2C 通信。 但是、从器件似乎没有响应 任何信息、并且它的时钟延展。 因此、器件可能出了问题。 哪个器件是从器件?

    此致、

    Diego Abad

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

    您好、Diego、
    我等待读取标志(RX 寄存器)、然后读取数据。 如果我在等待之前移动读取操作、代码会连续运行、但缓冲区无法获取正确的数据。 我正在等待设置读取标志(RX 寄存器)、然后加载数据、但数据会卡住。 我的从设备是一个支持高达 1MHz I²C 频率的传感器。

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

    如果是 MSP430 主设备等待读取数据、它将在当前字节的最后一位期间暂停。 您可以对时钟周期进行计数来进行检查。

    超时是一件好事、但为每个错误返回一个唯一的错误代码可以更容易地确定错误发生的位置。 喜欢设置断点。

    当您执行超时操作时、告诉硬件发送 STOP 可能不是恢复顺序的方法。 至少不是每一个错误。 (这些代码的另一个用途。) 重置 I2C 端口并手动生成停止会更好。  

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

    迹线显示了 MSP430 侧 I2C、等待您的程序识别来自第一个字节的 RXIFG。 这与您的程序在等待 UCTXSTT 在以下情况下变为低电平是一致的:

    >   if (!i2c_wait_for_ctl_clear(UCTXSTT)) { i2c_issue_stop_and_wait(); return 0; }

    这“应该“在从器件确认 SLA 字节(很久以前,在第一个 Rx 字节之前)之后完成。 个人而言、我通常不会这样做——而我只是等待 (a) RXIFG 或 (b) NACKIFG [你可以为每个字节检查这两个字节、虽然你只会在第一个字节看到 NACK ]。

    如果删除这一行会发生什么情况?

    -----

    我也怀疑 CPU (MCLK) 和 I2C 单元 (SMCLK) 在不同的时钟 (HFXT 与 DCO) 上运行。 这不是时钟边界问题通常存在的方式;也就是说、如果两者在同一个时钟 (HFXT 或 DCO) 上运行、是否会获得相同的结果?