您好!
我们在项目中实现了基于轮询的 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;
}