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