STM32F3寄存器级开发I2C切换从机地址返回NACK传输失败问题排查
问题原因定位及解决方案
核心故障原因
- I2C_CR2寄存器未清残留值导致从机地址错误
你的I2C_WriteByte函数中对CR2寄存器全部使用|=(或运算)赋值,第一次调用写入加速度计地址0x19<<1后,CR2的SADD(从机地址)位已经被写入对应值,第二次调用写入磁力计地址0x1E<<1时,两个地址做或运算得到错误的混合地址,从机识别不到正确地址自然返回NACK,这是最可能的故障原因。 - 传输逻辑时序错误
写完两个字节后未等待I2C_ISR_TC(传输完成)标志置1就直接触发STOP条件,可能截断当前传输;且清除STOPCF标志的操作未等待STOP条件硬件完成就执行,会导致I2C状态机异常,影响下一次传输。 - 外设配置不严谨
配置TIMINGR寄存器时使用|=运算,若寄存器存在非0复位值或上一次配置残留位,会导致I2C时序参数错误;STM32F3的I2C要求配置核心参数时必须先关闭PE外设使能位,否则配置不生效。 - 磁力计硬件地址不匹配
LSM303AGR的磁力计地址由SA0引脚电平决定,若硬件上SA0接高电平,磁力计的7位地址为0x1F而非0x1E,会导致地址帧NACK。 - 总线状态未复位
前一次传输产生的错误标志(如NACK、OVR等)未清除,会导致后续传输直接触发异常停止。
修正代码示例
修正后的I2C_WriteByte函数
void I2C_WriteByte(uint8_t sAddress,uint8_t* sData) { // 先清除CR2的传输相关残留位 I2C1->CR2 &= ~(I2C_CR2_SADD_Msk | I2C_CR2_NBYTES_Msk | I2C_CR2_RD_WRN); // 写入新的地址和传输字节数 I2C1->CR2 |= (0x02 << I2C_CR2_NBYTES_Pos) | sAddress; // 清空所有错误标志 I2C1->ICR = 0xFFFFFFFF; // 发起起始 I2C1->CR2 |= I2C_CR2_START; // 发送第一个字节 while(!(I2C1->ISR & I2C_ISR_TXIS)) { // 加NACK错误判断,避免卡死 if(I2C1->ISR & I2C_ISR_NACKF) { I2C1->ICR |= I2C_ICR_NACKCF; I2C1->CR2 |= I2C_CR2_STOP; return; } } I2C1->TXDR = sData[0]; // 发送第二个字节 while(!(I2C1->ISR & I2C_ISR_TXIS)) { if(I2C1->ISR & I2C_ISR_NACKF) { I2C1->ICR |= I2C_ICR_NACKCF; I2C1->CR2 |= I2C_CR2_STOP; return; } } I2C1->TXDR = sData[1]; // 等待传输完成 while(!(I2C1->ISR & I2C_ISR_TC)); // 触发STOP I2C1->CR2 |= I2C_CR2_STOP; // 等待STOP完成再清标志 while(I2C1->CR2 & I2C_CR2_STOP); I2C1->ICR |= I2C_ICR_STOPCF; }
I2C初始化部分修正建议
// 配置TIMINGR前先关闭I2C,且直接赋值不要用|= I2C1->CR1 &= ~I2C_CR1_PE; I2C1->TIMINGR = 0x2010091A; // 直接赋值,避免残留位 I2C1->CR1 |= I2C_CR1_PE|I2C_CR1_RXDMAEN;
内容的提问来源于stack exchange,提问作者user13909066
相关产品推荐
相关产品推荐

