### **一、I2C在STM32中的作用**
1. **高效外设控制**
- 仅需两根线(SDA数据线 + SCL时钟线)连接多个低速设备(如EEPROM、温湿度传感器、RTC时钟等),节省引脚资源。(半双工通信)
- 硬件自动处理协议时序(起始/停止条件、应答位、时钟同步),释放CPU算力(对比软件模拟I2C可提升效率30%以上)。
2. **多主机支持**
- 支持**可变多主机模型**:任何设备可在总线空闲时成为主机(需总线仲裁),符合分布式系统设计思想。
3. **速率灵活**
- 标准模式(100kHz)、快速模式(400kHz),满足不同外设需求。
I2C使用的设备的应答
是由主机个从机发送一个开始信号(A)->然后发送设备的地址(大部分是8位,其中最后一位是读或者写就是下文D的解释)->有没有响应(C)->然后发送一个停止信号(B)->后面就是读取或者写入数据(后面揭晓)
ErrorStatus IIC_Verification(uint8_t Address) { ErrorStatus temp=ERROR; IIC_START(); temp = IIC_VALUE(Address,WRITE); if(temp != SUCCESS) { return temp; } IIC_STOP(); return SUCCESS; } //函数实现在下文A.开始信号过程(在此之前SCL,SAD配置开漏输出,开漏出需要设备有上拉电阻不然容易误判,没有设备也判定成功,因为返回SDA一直是低电频,所以要测试时候要给一个错误地址判定是否有一个错误):
- SCL,SDA开始都要是高电频
- SDA先拉低,等待稳定后SCL拉低
//#define INSTATUA_SCL(value) if(value) GPIO_SetBits(IIC_GPIOx_SCL,IIC_GPIO_Pin_SCL);\ // else GPIO_ResetBits(IIC_GPIOx_SCL,IIC_GPIO_Pin_SCL); //#define INSTATUA_SDA(value) if(value) GPIO_SetBits(IIC_GPIOx_SDA,IIC_GPIO_Pin_SDA);\ // else GPIO_ResetBits(IIC_GPIOx_SDA,IIC_GPIO_Pin_SDA); //发送开始信号(value==0?低电频:高电频) void IIC_START(void) { INSTATUA_SDA(1); INSTATUA_SCL(1); stopover_IIC(); //阻塞延迟1us,等待电频变化(等待时间可以更长) INSTATUA_SDA(0); stopover_IIC(); INSTATUA_SCL(0); stopover_IIC(); }
B.停止信号过程:
- 开始SCL是高电频,SDA是低电频
- 要把SDA拉高
//#define INSTATUA_SCL(value) if(value) GPIO_SetBits(IIC_GPIOx_SCL,IIC_GPIO_Pin_SCL);\ // else GPIO_ResetBits(IIC_GPIOx_SCL,IIC_GPIO_Pin_SCL); //#define INSTATUA_SDA(value) if(value) GPIO_SetBits(IIC_GPIOx_SDA,IIC_GPIO_Pin_SDA);\ // else GPIO_ResetBits(IIC_GPIOx_SDA,IIC_GPIO_Pin_SDA); //发送停止信号(value==0?低电频:高电频) void IIC_STOP(void) { INSTATUA_SDA(0); INSTATUA_SCL(1); stopover_IIC(); //阻塞延迟1us INSTATUA_SDA(1); stopover_IIC(); //阻塞延迟1us }
开始后注意:
SCL是高电频时候SAD不可以乱动
C.应答信号
1.读取SDA电频如果是高电频无应答,可能以上发送信号有问题,或者配置没好,读取SDA电频如果是低电频就是应答就通信成功后面可以发送数据;(是因为结束信号放开之后,SDA会立马拉低电频)
//#define INSTATUA_SCL(value) if(value) GPIO_SetBits(IIC_GPIOx_SCL,IIC_GPIO_Pin_SCL);\ // else GPIO_ResetBits(IIC_GPIOx_SCL,IIC_GPIO_Pin_SCL); //#define INSTATUA_SDA(value) if(value) GPIO_SetBits(IIC_GPIOx_SDA,IIC_GPIO_Pin_SDA);\ // else GPIO_ResetBits(IIC_GPIOx_SDA,IIC_GPIO_Pin_SDA); //接受应答信号(value==0?低电频:高电频) uint8_t Answer_back(void) { //NOACK是非应答(是1),ACK是应答(是0) uint8_t ret = NOACK; INSTATUA_SDA(1); stopover_IIC(); INSTATUA_SCL(1); IIC_STOP(); if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_7)==0)//这个是查看SDA电频 { ret = ACK; } else { ret = NOACK; } INSTATUA_SCL(0); stopover_IIC(); return ret; }
D.数据有效性(有8位和11的,最后一位是设备地址,后一位是读(1):主机在从机上读取数据,写(0):主机在从机上写入数据)
1.如果SCL是高电频时候读取SAL数据SAL高电频为 ’1’,为低电频时候为’0’,当SCL为低电频时候读取数据是无效的,这个时候SAL发送数据;
//#define INSTATUA_SCL(value) if(value) GPIO_SetBits(IIC_GPIOx_SCL,IIC_GPIO_Pin_SCL);\ // else GPIO_ResetBits(IIC_GPIOx_SCL,IIC_GPIO_Pin_SCL); //#define INSTATUA_SDA(value) if(value) GPIO_SetBits(IIC_GPIOx_SDA,IIC_GPIO_Pin_SDA);\ // else GPIO_ResetBits(IIC_GPIOx_SDA,IIC_GPIO_Pin_SDA); //接受应答信号(value==0?低电频:高电频) ErrorStatus IIC_DATA(uint8_t Address) { for(uint8_t i=0;i<8;i++) { INSTATUA_SDA((0x80>>i)&Address); stopover_IIC(); INSTATUA_SCL(1); stopover_IIC(); INSTATUA_SCL(0); } if(Answer_back()) { IIC_STOP(); return ERROR; } return SUCCESS; } ErrorStatus IIC_VALUE(uint8_t Address,uint8_t Mode) { return IIC_DATA(((Address<<1)|Mode));//Mode是读和写选择 }如果有错误请多多指教!