主题中讨论的其他器件: MSP430FR5949
我通过 I2C 总线连接了 MSP430FR5949和 MSP430FR5962、总线上没有其他单元和上拉电阻。
I2C 链路配置为多主器件、在本例中、MSP430FR5949充当主器件、MSP430FR5962充当从器件接收器。
我看到的问题是、接收器随机保持时钟低电平。 然后由时钟低电平超时和外设复位根据建议执行此操作。
这种情况的时间似乎是随机的、难以衡量、但大约每10万条消息中有1条消息这样失败。
当观察到故障时、它始终位于字节的第7位之后、但字节位置显示为随机。
这种情况看起来好像 UCBnRXBUF 寄存器没有被读取、并且由于接收溢出条件、外设时钟被拉伸。
实际上、如果我故意引入随机不读取 UCBnRXBUF、我会观察到同样的症状。
但是、我确信、在正常情况下发生这种情况时、我已读取并缓冲(通过外部回调函数)接收到的数据、因为我可以在调试器中捕获此数据并观察我的缓冲数据。
此应用程序有多个其他中断处于活动状态、因此在处理任何单个中断时会有一些可变延迟、但其长度与时钟低电平超时无关。
我在中断中添加了一些 GPIO 引脚切换、以观察接收器中的事件序列、并可以看到检测到开始中断、 许多接收中断、包括在第7位挂起的中断之前读取字节的中断、因此我确信我已经读取了上一个接收字节的 UCBnRXBUF 寄存器。
我只能想象会有一些时序或中断交互导致这种情况。
那么、我的问题是:这个操作是否有任何已知问题、如果没有、详细的情况是、触发接收器在接收到第7位数据后进入时钟扩展的确切条件是什么?
很抱歉、由于重新使用 I2C 中断代码、它有点乱、但您得到了一般的想法。
如果这个请求没有得到任何答案、我将不得不尝试用更简单的代码重现问题、但这不是我在项目中预算的时间。
示例捕捉显示了初始写入(CH7上有中断处理程序)、后跟2个成功的 Rx 字节(CH7上有中断处理程序)、后跟第3个数据字节的7位、这会导致时钟低电平、直到随后被处理。 如果我已经读取前2个数据字节的 UCBnRXBUF、为什么第三个字节被保持?
//########################################################################################################################## ///@fn void USCI_B0_ISR (void) //@param[in] <无> ///@返回 <无> //@请参阅说明 ISR、了解逻辑 I2C 通道0上的 eUSCIB0 //@警告 用途:USCI_B0 //############################################################################################################## #pragma vector = USCI_B0_vector _interrupt_ void I2C_USCI_B0_ISR (void) { uint8数据字节; /*推断中断源*/ 开关(_偶数_IN_RANGE (UCB0IV、USCI_I2C_UCBIT9IFG)) { USCI_NONE 案例: 中断; 案例 USCI_I2C_UCALIFG://*仲裁丢失中断* 开关(CurrentConfig[I2C_CHAN_0].IntState) { 案例 I2C_INT_State_MA_TX: /*发送结果*/ CurrentConfig[I2C_CHAN_0].TxResult = I2C_EOM_RESULT_FAIL_TRICT; CurrentConfig[I2C_CHAN_0].IntState = I2C_INT_State_Idle; 中断; 案例 I2C_INT_State_MA_RX: /*发送结果*/ CurrentConfig[I2C_CHAN_0].RxResult = I2C_EOM_RESULT_FAIL_TRENTRIC; CurrentConfig[I2C_CHAN_0].IntState = I2C_INT_State_Idle; 中断; 案例 I2C_INT_State_SL_TX: /*意外操作*/ CurrentConfig[I2C_CHAN_0].TxResult = I2C_EOM_RESULT_UNKNOWN; CurrentConfig[I2C_CHAN_0].IntState = I2C_INT_State_Idle; 中断; 案例 I2C_INT_State_SL_RX: /*意外操作*/ CurrentConfig[I2C_CHAN_0].RxResult = I2C_EOM_RESULT_UNKNOWN; CurrentConfig[I2C_CHAN_0].IntState = I2C_INT_State_Idle; 中断; 默认值: /*无事可做*/ 中断; } /*结束开关(CurrentConfig[I2C_CHAN_0].IntState)*/ 中断; 案例 USCI_I2C_UCNACKIFG://*未接收到 ACK (仅限主器件)*/ 开关(CurrentConfig[I2C_CHAN_0].IntState) { 案例 I2C_INT_State_MA_TX: /*生成停止和终止*/ UCB0CTLW0 |= UCTXSTP;/*生成 STOP */ CurrentConfig[I2C_CHAN_0].TxResult = I2C_EOM_RESULT_NACK; CurrentConfig[I2C_CHAN_0].IntState = I2C_INT_State_Idle; 中断; 案例 I2C_INT_State_MA_RX: /*生成停止和终止*/ UCB0CTLW0 |= UCTXSTP;/*生成 STOP */ CurrentConfig[I2C_CHAN_0].RxResult = I2C_EOM_RESULT_NACK; CurrentConfig[I2C_CHAN_0].IntState = I2C_INT_State_Idle; 中断; 案例 I2C_INT_State_SL_TX: /*意外操作*/ CurrentConfig[I2C_CHAN_0].TxResult = I2C_EOM_RESULT_UNKNOWN; CurrentConfig[I2C_CHAN_0].IntState = I2C_INT_State_Idle; 中断; 案例 I2C_INT_State_SL_RX: /*意外操作*/ CurrentConfig[I2C_CHAN_0].RxResult = I2C_EOM_RESULT_UNKNOWN; CurrentConfig[I2C_CHAN_0].IntState = I2C_INT_State_Idle; 中断; 默认值: /*无事可做*/ 中断; } /*结束开关(CurrentConfig[I2C_CHAN_0].IntState)*/ 中断; USCI_I2C_UCSTTIFG 案例: /*起始条件(检测到从器件的自有地址) 仅在从器件确定的运行模式时使用 此事务*/ SET_CHANNEL7_HIGH; IF (0U =(UCB0CTLW0和 UCMST) { /*从模式*/ if (0U!=(UCB0CTLW0和 UCTR)) { CurrentConfig[I2C_CHAN_0].IntState = I2C_INT_State_SL_TX; } 其他 { CurrentConfig[I2C_CHAN_0].IntState = I2C_INT_State_SL_RX; CurrentConfig[I2C_CHAN_0].RxResult = I2C_EOM_RESULT_IN_PROGRESS; CurrentConfig[I2C_CHAN_0].IntSlaveRxd = false; } } /*开始表示这是一个新事务*/ CurrentConfig[I2C_CHAN_0].ByteCounter = 0U; SET_CHANNEL7_LOW; 中断; USCI_I2C_UCSTPIFG 案例: /*检测到停止条件*/ SET_CHANNEL7_HIGH; 开关(CurrentConfig[I2C_CHAN_0].IntState) { 案例 I2C_INT_State_SL_TX://故意掉电* 案例 I2C_INT_State_MA_TX: CurrentConfig[I2C_CHAN_0].TxResult = I2C_EOM_RESULT_OK; CurrentConfig[I2C_CHAN_0].IntState = I2C_INT_State_Idle; 中断; 案例 I2C_INT_State_SL_RX: /*在从机 Rx 模式下、停止条件在物理上发生在接收到最后一个字节后 但中断处理程序中偶尔会有足够的延迟 进入中断处理程序时、Rx 数据和停止条件都存在。 因为停止条件在 UCBxIV 寄存器中具有优先级、用于控制哪一个 中断源已处理、我们需要检查此项并处理最后一个字节 在更改 IntState 之前、否则我们将最终设置接收到的消息 条件、其中一个字节更少、并且忽略后续的 Rx 字节中断* if (0U!=(UCB0IFG &(UCRXIFG0 + UCRXIFG1 + UCRXIFG2+ UCRXIFG3))) { DataByte = UCB0RXBUF; if (NULL!= CurrentConfig[I2C_CHAN_0].SuppliedCfg.CfgRxByteCBPtr) { CurrentConfig[I2C_CHAN_0].SuppliedCfgRxByteCBPtr (DataByte、CurrentConfig[I2C_CHAN_0].ByteCounter); CurrentConfig[I2C_CHAN_0].ByteCounter++; } } /*检查这是一个 Gen 调用还是已寻址消息*/ if (0U!=(UCB0STATW 和 UCGC) { CurrentConfig[I2C_CHAN_0].RxResult = I2C_EOM_RESULT_OK_GENCALL; } 其他 { CurrentConfig[I2C_CHAN_0].RxResult = I2C_EOM_RESULT_OK; } CurrentConfig[I2C_CHAN_0].IntSlaveRxd = true; CurrentConfig[I2C_CHAN_0].IntState = I2C_INT_State_Idle; 中断; 案例 I2C_INT_State_MA_RX: /*检查这是一个 Gen 调用还是已寻址消息*/ if (0U!=(UCB0STATW 和 UCGC) { CurrentConfig[I2C_CHAN_0].RxResult = I2C_EOM_RESULT_OK_GENCALL; } 其他 { CurrentConfig[I2C_CHAN_0].RxResult = I2C_EOM_RESULT_OK; } CurrentConfig[I2C_CHAN_0].IntState = I2C_INT_State_Idle; 中断; 默认值: /*无事可做*/ 中断; } /*结束开关(CurrentConfig[I2C_CHAN_0].IntState)*/ SET_CHANNEL7_LOW; 中断; USCI_I2C_UCRXIFG3案例: /*故意掉电、接收到从器件3数据(addr idx = I2C_my_ADDR_quaternary)*/ USCI_I2C_UCRXIFG2案例: /*故意掉电、接收到从器件2数据(addr idx = I2C_my_ADDR_Tertiary)*/ USCI_I2C_UCRXIFG1案例: /*故意掉电、接收到从器件1数据(addr idx = I2C_my_ADDR_Secondary)*/ USCI_I2C_UCRXIFG0案例: /*接收到的数据(addr idx = I2C_my_ADDR_PRIMARY)*/ /*无条件读取数据字节以避免溢出、即使我们放弃它也是如此*/ DataByte = UCB0RXBUF; SET_CHANNEL7_HIGH; 开关(CurrentConfig[I2C_CHAN_0].IntState) { 案例 I2C_INT_State_SL_RX: /*从机不能控制字节数*/ if (NULL!= CurrentConfig[I2C_CHAN_0].SuppliedCfg.CfgRxByteCBPtr) { CurrentConfig[I2C_CHAN_0].SuppliedCfgRxByteCBPtr (DataByte、CurrentConfig[I2C_CHAN_0].ByteCounter); CurrentConfig[I2C_CHAN_0].ByteCounter++; } 中断; 案例 I2C_INT_State_MA_RX: /*主设备控制字节数并生成终止操作。 如果这是倒数第二个字节、我们需要向 STOP 发出信号 未执行自动停止生成*/ if ((I2C_AUTO_STOP_Generate!= CurrentConfig[I2C_CHAN_0].AutoStopType)&& (CurrentConfig[I2C_CHAN_0].ByteCounter ==(CurrentConfig[I2C_CHAN_0].DataLen - 2U))) { UCB0CTLW0 |= UCTXSTP;/*生成停止*/ } /*处理数据*/ if (NULL!= CurrentConfig[I2C_CHAN_0].SuppliedCfg.CfgRxByteCBPtr) { CurrentConfig[I2C_CHAN_0].SuppliedCfgCxByteCBPtr (DataByte、CurrentConfig[I2C_CHAN_0].ByteCounter); } CurrentConfig[I2C_CHAN_0].ByteCounter++; 中断; 案例 I2C_INT_State_SL_TX://*故意掉电-意外操作* 案例 I2C_INT_State_MA_TX://*故意掉电-意外操作* 默认值: /*无事可做*/ 中断; } /*结束开关(CurrentConfig[I2C_CHAN_0].IntState)*/ SET_CHANNEL7_LOW; 中断; USCI_I2C_UCTXIFG3案例: /*故意掉电、从器件3 Tx 缓冲器空(addr idx = I2C_my_ADDR_quaternary)*/ USCI_I2C_UCTXIFG2案例: /*故意掉电、从器件2 Tx 缓冲器空(addr idx = I2C_my_ADDR_Tertiary)*/ USCI_I2C_UCTXIFG1案例: /*故意掉电、从器件1 Tx 缓冲器空(addr idx = I2C_my_ADDR_Secondary)*/ USCI_I2C_UCTXIFG0案例: /* Tx 缓冲器空(addr idx = I2C_my_ADDR_PRIMARY)*/ 开关(CurrentConfig[I2C_CHAN_0].IntState) { 案例 I2C_INT_State_SL_TX: /*从机使用 TxPending 指示响应已可用 一旦所有数据都已用尽、如果主器件请求更多数据 我们只能忽略它并允许主器件对超时做出反应 因为从器件会在等待更多数据时将时钟保持在低电平*/ if ((TRUE =CurrentConfig[I2C_CHAN_0].txPending)&& (CurrentConfig[I2C_CHAN_0].ByteCounter < CurrentConfig[I2C_CHAN_0].DataLen) { /*如果我们使用 I2C 地址对数据进行前缀处理,则首先进行*/ if (true =CurrentConfig[I2C_CHAN_0].TxPfixMyAddr) { UCB0TXBUF = CurrentConfig[I2C_CHAN_0].SuppliedCfg.CfgMyAddr; CurrentConfig[I2C_CHAN_0].TxPfixMyAddr = false;//将其清除,因为它只能执行一次并且传输请求特定*/ } 其他 { UCB0TXBUF =*(CurrentConfig[I2C_CHAN_0].TxDataPtr + CurrentConfig[I2C_CHAN_0].ByteCounter); CurrentConfig[I2C_CHAN_0].ByteCounter++; } } 中断; 案例 I2C_INT_State_MA_TX: /*主器件在进入此状态时将清除 TxPending */ if (CurrentConfig[I2C_CHAN_0].ByteCounter < CurrentConfig[I2C_CHAN_0].DataLen) { /*如果我们使用 I2C 地址对数据进行前缀处理,则首先进行*/ if (true =CurrentConfig[I2C_CHAN_0].TxPfixMyAddr) { UCB0TXBUF = CurrentConfig[I2C_CHAN_0].SuppliedCfg.CfgMyAddr; CurrentConfig[I2C_CHAN_0].TxPfixMyAddr = false;//将其清除,因为它只能执行一次并且传输请求特定*/ } 其他 { UCB0TXBUF =*(CurrentConfig[I2C_CHAN_0].TxDataPtr + CurrentConfig[I2C_CHAN_0].ByteCounter); CurrentConfig[I2C_CHAN_0].ByteCounter++; } } 其他 { /*将数据的最后一个字节传输到移位寄存器、随后的 TXIFG 中断需要在下一个 ACK 后设置停止位的产生 除非我们使用自动停止生成*/ if (I2C_AUTO_STOP_Generate!= CurrentConfig[I2C_CHAN_0].AutoStopType) { UCB0CTLW0 |= UCTXSTP;/*生成停止*/ } } 中断; 案例 I2C_INT_State_SL_RX://故意掉电-意外操作*/ 案例 I2C_INT_State_MA_RX://故意掉电-意外操作*/ 默认值: /*无事可做*/ 中断; } /*结束开关(CurrentConfig[I2C_CHAN_0].IntState)*/ 中断; 案例 USCI_I2C_UCBCNTIFG://*达到字节计数阈值* 由于外设固有的255字节限制、/*当前未使用*/ 中断; 案例 USCI_I2C_UCCLTOIFG://*时钟低电平超时* CurrentConfig[I2C_CHAN_0].IntState = I2C_INT_State_CLK_ERROR; 中断; 案例 USCI_I2C_UCBIT9IFG://除地址*之外的每位9个时钟周期(ACK 位置) /*无事可做*/ 中断; 默认值: /*无事可做*/ 中断; } /*结束开关(__even_in_range (UCB0IV、USCI_I2C_UCBIT9IFG))*/ }/*结束 I2C_USCI_B0_ISR ()*