STM32F103C8T6中断式I2C重复起始位无法发送问题求助
STM32F103C8T6 I2C中断模式下重复起始位无法触发的问题
问题描述
使用STM32F103C8T6的I2C2外设,通过中断实现通信,仅启用ITEVFEN中断,禁用ITBUFEN中断。通信序列为:
起始位→发送设备地址→发送待读取的寄存器地址→发送重复起始位(每步均通过中断触发执行)
发送第二个起始位后无任何反应,程序卡住,但调试时读取I2C相关寄存器状态显示正常。已尝试添加延时,无效果;确认发送起始位的中断代码已执行,但硬件未生成起始位。
临时可行的规避操作
- 发送重复起始位前读取DR寄存器:
(void)I2C2->DR; I2C2->CR1|=I2C_CR1_START;
- 设置起始位后向DR写入任意无效数据(无效数据不会被传输):
I2C2->CR1|=I2C_CR1_START; I2C2->DR=0x55;
- 在发送寄存器地址的最后一字节后立即写入起始位,进入中断后再次发送起始位可成功。
问题根源与解决方法
核心原因
你的问题出在I2C发送完寄存器地址后,外设仍处于等待从机ACK的状态,此时直接设置START位会被硬件忽略。
STM32F1的I2C外设逻辑:发送完最后一个字节(寄存器地址)后,TXE(发送寄存器空)标志会先置位,但硬件要等到收到从机的ACK信号后,才会触发EV8_2事件(BTF置位,代表字节发送完成且ACK已接收)。如果在TXE触发的中断里直接设置START,硬件因处于等待ACK的状态,不会响应重复起始位请求。
规避操作有效的原因
- 读取DR寄存器:读取操作会清除TXE标志,迫使外设等待EV8_2事件(ACK收到),此时再设置START,硬件处于正确状态,能响应请求。
- 写入DR无效数据:写入操作让外设重新进入发送流程,覆盖之前的等待ACK状态,START请求会被硬件优先处理(重复起始位会中断当前发送)。
- 提前写入START:此时外设还未进入等待ACK的阶段,START请求能被及时响应。
正确操作流程
必须等待EV8_2事件(BTF置位,确认从机已ACK寄存器地址)触发中断后,再设置重复起始位。
中断处理核心逻辑示例
void I2C2_EV_IRQHandler(void) { uint32_t sr1 = I2C2->SR1; uint32_t sr2 = I2C2->SR2; // 读取SR2会自动清除ADDR等标志 if(sr1 & I2C_SR1_SB) // 起始位发送完成 { I2C2->DR = DEVICE_ADDR_W; // 发送写方向设备地址 } else if(sr1 & I2C_SR1_ADDR) // 地址发送完成并收到ACK { // 无需额外操作,读取SR2已清除ADDR标志 } else if(sr1 & I2C_SR1_TXE) // 发送寄存器空,发送寄存器地址 { I2C2->DR = REG_ADDR; } else if(sr1 & I2C_SR1_BTF) // 字节发送完成,已收到ACK { I2C2->CR1 |= I2C_CR1_START; // 发送重复起始位 } }
注意事项
- 确保中断中完整处理所有I2C事件,不要遗漏BTF触发的EV8_2事件
- 禁用ITBUFEN后,BTF属于ITEVFEN覆盖的EV事件范畴,会正常触发中断
- 不要在TXE事件触发时直接发送重复起始位,必须等待BTF确认ACK已接收
内容的提问来源于stack exchange,提问作者Ramzess
相关产品推荐
相关产品推荐

