1.I2C主发送器
1.1 I2C主发送器时序
主发送时序主要包括5步:
- 主机产生起始位(Start)
- 主机发送从机地址+写方向(7位地址+0)
- 从机应答(ACK)
- 主机连续发送数据
- 主机产生停止位(Stop),结束通信
START → 设备地址(写) → ACK → 存储地址 → ACK → 数据 → ACK → STOP

1.2 I2C主模式写入代码(以EEPROM为例)
cpp
uint8_t EEPROM_Byte_Write(uint8_t* pData, uint8_t addr){
//产生起始位
I2C_GenerateSTART(EEPROM_I2C, ENABLE);
I2CTimeOutCount = I2CT_FLAG_TIMEOUT;
//检测事件EV5
while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_MODE_SELECT)!=SUCCESS){
if((I2CTimeOutCount--)==0) return I2C_TIMEOUT_UserCallback(1);
};
//发送地址
I2C_Send7bitAddress(EEPROM_I2C, EEPROM_ADDRESS, I2C_Direction_Transmitter);
I2CTimeOutCount = I2CT_FLAG_TIMEOUT;
//检测事件EV6
while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)!=SUCCESS){
if((I2CTimeOutCount--)==0) return I2C_TIMEOUT_UserCallback(2);
};
//发数据地址
I2C_SendData(EEPROM_I2C,addr);
I2CTimeOutCount = I2CT_FLAG_TIMEOUT;
//检测事件EV8
while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_BYTE_TRANSMITTING)!=SUCCESS){
if((I2CTimeOutCount--)==0) return I2C_TIMEOUT_UserCallback(3);
};
//发数据
I2C_SendData(EEPROM_I2C,*pData);
I2CTimeOutCount = I2CT_FLAG_TIMEOUT;
//检测事件EV8_2
while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_BYTE_TRANSMITTED)!=SUCCESS){
if((I2CTimeOutCount--)==0) return I2C_TIMEOUT_UserCallback(4);
};
//产生结束位
I2C_GenerateSTOP(EEPROM_I2C, ENABLE);
return 0;
}
1.3 逐步对照
1.3.1 主机产生I2C起始位
cpp
I2C_GenerateSTART(EEPROM_I2C, ENABLE);

函数作用:置位CR1寄存器的START位
硬件行为:STM32硬件自动产生I2C起始信号(SDA拉低),并且起始位发送完成后,START位清零,并且SR1_SB位置1(EV5事件)。注意:此时SB位已置1,但还没清除,必须执行"读SR1+写DR"才能清除,否则SB位一直为1,硬件无法进行下一步。


1.3.2 检测EV5事件
cpp
I2CTimeOutCount = I2CT_FLAG_TIMEOUT;
//检测事件EV5
while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_MODE_SELECT)!=SUCCESS){
if((I2CTimeOutCount--)==0) return I2C_TIMEOUT_UserCallback(1);
};

I2C_CheckEvent读取SR1与SR2寄存器,判断当前硬件事件是否匹配目标事件。
作用:读取SR1与SR2寄存器,为SB标志位清除做准备。
1.3.3 发送从机地址
cpp
I2C_Send7bitAddress(EEPROM_I2C, EEPROM_ADDRESS, I2C_Direction_Transmitter);
作用:将7位设备地址+写方向位(0)写入DR寄存器,硬件自动发送到DR寄存器。由于检测EV5事件调用了I2C_CheckEvent函数,已经读过了SR寄存器,那么写DR寄存器就可以清除SB位,将时序往下推进。发送完成ADDR位置1(EV6事件)。

1.3.4 检测EV6事件
cpp
//检测事件EV6
while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)!=SUCCESS){
if((I2CTimeOutCount--)==0) return I2C_TIMEOUT_UserCallback(2);
};
检测EV6事件,读取SR1和SR2,清除ADDR标志位。
1.3.5 发送存储地址
cpp
I2C_SendData(EEPROM_I2C,addr);
将地址写入DR寄存器。完全送入移位寄存器后,且第8个SCL时钟结束,SR1中的TXE置1(EV8事件)
1.3.6 检测EV8事件
cpp
//检测事件EV8
while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_BYTE_TRANSMITTING)!=SUCCESS){
if((I2CTimeOutCount--)==0) return I2C_TIMEOUT_UserCallback(3);
};
确认TXE置1,上一字节完全送入移位寄存器,数据寄存器空了。
1.3.7 发送数据
cpp
I2C_SendData(EEPROM_I2C,*pData);
将数据写入DR寄存器。完全送入移位寄存器后SR1中的TXE置1(EV8事件,可以继续发送),若停止发送,等待数据+应答全部结束,TXE置1,BTF置1(EV8_2事件)
1.3.8 检测EV8_2事件
cpp
while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_BYTE_TRANSMITTED)!=SUCCESS){
if((I2CTimeOutCount--)==0) return I2C_TIMEOUT_UserCallback(4);
};
确认TXE为1,BTF为1,完全发送完成,可以停止。
1.3.9 主机产生I2C停止位
cpp
I2C_GenerateSTOP(EEPROM_I2C, ENABLE);
函数作用:置位CR1寄存器的STOP位
硬件行为:STM32硬件自动产生I2C停止信号(SDA拉高),释放总线。

2.I2C主接收器
2.1 I2C主接收器时序
主接收时序主要包括5步:
- 主机产生起始位(Start)
- 主机发送从机地址+读方向(7位地址+1)
- 从机应答(ACK)
- 从机连续发送数据给主机
- 主机产生停止位(Stop),结束通信
START → 设备地址 (读) → ACK ← 数据 ← ACK ← STOP

这个是顺序读取的时序。
随机读取按以下顺序来,START→设备地址 (写)→ACK→存储地址→START (重复)→设备地址 (读)→ACK←数据←NACK→STOP(例程为随机读取)
2.2 I2C 主模式读取代码(EEPROM 示例)
cpp
uint8_t EEPROM_Byte_Read(uint8_t addr){
uint8_t readTemp;
Wair_For_EEPROM();
//产生起始位
I2C_GenerateSTART(EEPROM_I2C, ENABLE);
//检测事件EV5
while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_MODE_SELECT)!=SUCCESS){
};
//发送地址
I2C_Send7bitAddress(EEPROM_I2C, EEPROM_ADDRESS, I2C_Direction_Transmitter);
//检测事件EV6
while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)!=SUCCESS){
};
//发数据地址
I2C_SendData(EEPROM_I2C,addr);
//检测事件EV8
while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_BYTE_TRANSMITTING)!=SUCCESS){
};
//产生起始位
I2C_GenerateSTART(EEPROM_I2C, ENABLE);
//检测事件EV5
while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_MODE_SELECT)!=SUCCESS){
};
//发送地址
I2C_Send7bitAddress(EEPROM_I2C, EEPROM_ADDRESS, I2C_Direction_Receiver);
//检测事件EV6
while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)!=SUCCESS){
};
I2C_AcknowledgeConfig(EEPROM_I2C, DISABLE); //只接受一个数据,NO ACK
//检测事件EV7
while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_BYTE_RECEIVED)!=SUCCESS){
};
readTemp = I2C_ReceiveData(EEPROM_I2C);
//产生结束位
I2C_GenerateSTOP(EEPROM_I2C, ENABLE);
//恢复应答
I2C_AcknowledgeConfig(EEPROM_I2C, ENABLE);
return readTemp;
}
2.3 逐步对照
2.3.1 主机产生起始位(同1.3.1)
cpp
I2C_GenerateSTART(EEPROM_I2C, ENABLE);
2.3.2 检测 EV5事件(同1.3.2)
cpp
while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_MODE_SELECT)!=SUCCESS){
};
2.3.3 发送从机写地址(同1.3.3)
cpp
I2C_Send7bitAddress(EEPROM_I2C, EEPROM_ADDRESS, I2C_Direction_Transmitter);
2.3.4 检测 EV6 事件(同1.3.4)
cpp
while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)!=SUCCESS){
};
2.3.5 发送目标读取存储地址(同1.3.5)
cpp
I2C_SendData(EEPROM_I2C,addr);
2.3.6 检测 EV8 事件(同1.3.6)
cpp
while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_BYTE_TRANSMITTING)!=SUCCESS){
};
2.3.7 发送重复起始位(同1.3.1)
cpp
I2C_GenerateSTART(EEPROM_I2C, ENABLE);
2.3.8 再次检测 EV5 事件(同1.3.2)
cpp
while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_MODE_SELECT)!=SUCCESS){
};
2.3.9 发送从机读地址(同1.3.3,但改为读写为改为1)
cpp
I2C_Send7bitAddress(EEPROM_I2C, EEPROM_ADDRESS, I2C_Direction_Receiver);
2.3.10 检测 接收EV6 事件
cpp
while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)!=SUCCESS){
};
2.3.11 关闭应答配置(只接收一个字节)
cpp
I2C_AcknowledgeConfig(EEPROM_I2C, DISABLE);
将CR_ACK复位为0。接受完一字节后,主机发送NACK。告诉从机不要发了,已经完成了。
2.3.12 检测 EV7 事件
cpp
while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_BYTE_RECEIVED)!=SUCCESS){
};
从机端接收到地址后,开始向主机端发送数据。当主机接收到这些数据后,会产生"EV7"事
件,SR1 寄存器的 RXNE 被置 1,表示接收数据寄存器非空,我们读取该寄存器后,可对数据寄存器清空,以便接收下一次数据。此时可以控制I2C发送应答信号(ACK)或非应答信号(NACK),若应答继续接收数据,若非应答, 停止传输。
2.3.13 读取数据
cpp
readTemp = I2C_ReceiveData(EEPROM_I2C);

读取DR位,RXNE 位硬件自动清零。
2.3.14 主机产生 I2C 停止位(同1.3.9)
cpp
I2C_GenerateSTOP(EEPROM_I2C, ENABLE);
2.3.15 恢复应答使能
将CR_ACK为1。
cpp
I2C_AcknowledgeConfig(EEPROM_I2C, ENABLE);