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.

[参考译文] MSP430FR2476:ISR 之后不生成 I2C 停止条件

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

https://e2e.ti.com/support/microcontrollers/msp-low-power-microcontrollers-group/msp430/f/msp-low-power-microcontroller-forum/1361062/msp430fr2476-i2c-stop-condition-not-generated-after-isr

器件型号:MSP430FR2476

您好!

我们在项目中实现了基于轮询的 I2C 主驱动程序。 在大多数情况下、我们会得到正确的行为、但外设在读取期间针对最后一个接收到的字节进入 ISR 后无法生成停止条件(请参阅下面的捕捉)。 我们的函数会转而阻止 等待 UCTXSTP 标志清零、直到我们达到超时周期。 如果 SDA 恰好在最后一个字节的末尾被保持在低电平、那么外设将检测到总线处于忙状态、从而导致所有后续事务永久锁定。 在这个状态下连接一个调试器来确认 UCTXSTP 被置位在固件中(请见下面的输出)。

到目前为止、我们发现了两种权变措施:

1.在 UCTXSTP 超时后、手动将串行线切换为 GPIO 以强制达到停止条件。 这将总线返回到一个空闲状态并防止一个完全锁定。

2.禁用 I2C 读功能内的中断。 这完全消除了该问题、但会增加 ISR 延迟。

我们的怀疑是违反了用户指南中所述的序列、但我们不清楚。 我们的驱动程序中的相关代码如下所示。

#define MAX_FLAG_WAIT_MS   (uint32_t) 100
#define FLAG_WAIT_STEP_US   100
#define FLAG_WAIT_LOOPS    (uint32_t)((MAX_FLAG_WAIT_MS*1000) / FLAG_WAIT_STEP_US)
static bool _waitForFlag(uint16_t flag)
{
    uint32_t retry_count = 0;
    while(++retry_count < FLAG_WAIT_LOOPS)
    {
        if(GET_INT_FLAGS() & UCNACKIFG)
        {
            CLR_INT_FLAGS(UCNACKIFG);

            // Generate stop condition.
            HWREG16(EUSCI_BASE_ADDR + OFS_UCBxCTLW0) |= UCTXSTP;
            return false;
        }
        else if(GET_INT_FLAGS() & flag)
        {
            CLR_INT_FLAGS(flag);
            return true;
        }

        __delay_cycles(DELAY_US_TO_CYCLES(FLAG_WAIT_STEP_US));

    }
    return false;
}

// Control word wait function
// Used for checking completion of start/stop conditions
static bool _waitForCwFlag(uint16_t flag)
{
    uint32_t retry_count = 0;
    while(++retry_count < FLAG_WAIT_LOOPS)
    {
        // Wait for flag to clear to indicate operation complete
        if(!(HWREG16(EUSCI_BASE_ADDR + OFS_UCBxCTLW0) & flag))
        {
            return true;
        }

        __delay_cycles(DELAY_US_TO_CYCLES(FLAG_WAIT_STEP_US));

    }
    return false;
}

static bool _I2C_MASTER_read(uint8_t i2cAddr, void *pDst, uint16_t nBytes)
{
    ASSERT((pDst != NULL) && (nBytes > 0));

    uint8_t *pByte = pDst;

	// Reinit if peripheral locked in busy state
    if(EUSCI_B_I2C_isBusBusy(EUSCI_BASE_ADDR))
    {
        _I2C_MASTER_reinit();
    }

    // Clear any previous NACK
    HWREG16(EUSCI_BASE_ADDR + OFS_UCBxIFG) &= ~UCNACKIFG;

    // Set I2C address.
    EUSCI_B_I2C_setSlaveAddress(EUSCI_BASE_ADDR, i2cAddr);

    // Enable rx mode and generate start condition.
    HWREG16(EUSCI_BASE_ADDR + OFS_UCBxCTLW0) &= ~UCTR;
    HWREG16(EUSCI_BASE_ADDR + OFS_UCBxCTLW0) |= UCTXSTT;
    // Wait for start condition
    if(!_waitForCwFlag(UCTXSTT)) {
        return true;
    }

    for((void) 0; nBytes > 0; nBytes--)
    {
        if(nBytes == 1)
        {
            // On last byte, generate stop condition.
            HWREG16(EUSCI_BASE_ADDR + OFS_UCBxCTLW0) |= UCTXSTP;
            // Wait for stop condition
            if(!_waitForCwFlag(UCTXSTP)) {
                return true;
            }
        }
        if(!_waitForFlag(UCRXIFG0))
        {
            // Generate stop condition.
            HWREG16(EUSCI_BASE_ADDR + OFS_UCBxCTLW0) |= UCTXSTP;
            // Wait for stop condition
            _waitForCwFlag(UCTXSTP);

            return true;
        }

        // Read a byte.
        *pByte = HWREG16(EUSCI_BASE_ADDR + OFS_UCBxRXBUF);
        pByte++;
    }

    return false;
}

I2C_Master_Handler_t *I2C_MASTER_initHandler(I2C_Master_Handler_t *pI2CHdl,
        uint32_t subsysClk)
{
    ASSERT(pI2CHdl != NULL);

    EUSCI_B_I2C_initMasterParam i2cParam;

    // Select I2C peripheral.
    GPIO_setAsPeripheralModuleFunctionOutputPin(I2C_MASTER_PORT,
            I2C_MASTER_SDA_PIN, GPIO_PRIMARY_MODULE_FUNCTION);
    GPIO_setAsPeripheralModuleFunctionOutputPin(I2C_MASTER_PORT,
            I2C_MASTER_SCL_PIN, GPIO_PRIMARY_MODULE_FUNCTION);

    // Initialize I2C parameters.
    i2cParam.selectClockSource = EUSCI_B_I2C_CLOCKSOURCE_SMCLK;
    i2cParam.i2cClk = subsysClk;
    I2C_MASTER_subSysClk = subsysClk;
    i2cParam.dataRate = EUSCI_B_I2C_SET_DATA_RATE_100KBPS;
    i2cParam.byteCounterThreshold = 0;
    i2cParam.autoSTOPGeneration = EUSCI_B_I2C_NO_AUTO_STOP;

    // Configure and enable the I2C bus.
    EUSCI_B_I2C_initMaster(EUSCI_BASE_ADDR, &i2cParam);
    EUSCI_B_I2C_setMode(EUSCI_BASE_ADDR, EUSCI_B_I2C_TRANSMIT_MODE);
    EUSCI_B_I2C_enable(EUSCI_BASE_ADDR);

    CLR_INT_FLAGS(UCTXIFG0);

    return pI2CHdl;
}

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

    我想我在您的数据捕获中看到的是 I2C 端口最后等待 SCL 为低电平。 可能在等待您读取 RXBUF。 您认为此问题是由 ISR 引起的。 大概是某个其他器件的 ISR。 是不是很短? 它是否做了可能干扰 I2C 端口的事情?

    设置 TXSTP 的时间并不重要。 如果您在接收字节时设置该位、则在该字节完成时将发生 NACK 和 STOP。 如果你在读取 RXBUF 之前设置它、NACK 和 STOP 应该立即发生。 指南上显示。