1.IIC 简介
IIC(Inter-Integrated Circuit)总线是一种由 PHILIPS 公司开发的两线式串行总线,用于连接
微控制器及其外围设备。它由数据线 SDA 和时钟 SCL 构成,可发送和接收数据。在 CPU 与被控 IC 之间、 IC 与 IC 之间进行双向传送, 高速 IIC 总线一般可达 400kbps 以上。标准模式下可达100kbit/s,快速模式下可达400kbit/s,高速模式下可达3.4Mbit/s。
2.信号类型
空闲状态:I2C总线总线的SDA和SCL两条信号线同时处于高电平时,规定为总线的空闲状态
开始信号: SCL 为高电平时, SDA 由高电平向低电平跳变。

结束信号: SCL 为高电平时, SDA 由低电平向高电平跳变。

应答信号:接收数据的 IC 在接收到 8bit 数据后,向发送数据的 IC 发出特定的低电平脉冲,
表示已收到数据。 CPU 向受控单元发出一个信号后,等待受控单元发出一个应答信号, CPU 接收到应答信号后,根据实际情况作出是否继续传递信号的判断。若未收到应答信号,由判断为受控单元出现故障。这些信号中,起始信号是必需的,结束信号和应答信号,都可以不要。

3.硬件IIC和软件模拟IIC
3.1 STM32是有硬件IIC的,但是据说有bug,可以在网上查阅到相关资料,从稳定性和可移植性考虑,一般不去尝试,避免自己给自己挖坑,有探索精神的可以去试一试。
3.2 基于上面的原因,所以还有一种方法就是软件模拟IIC,只需要任意两个IO口就可以模拟I2C通讯,移植性也特别好。
4.电路图如下
STM32F103ZET6的两个IO口分别连接到EEPROM 芯片上,该芯片型号为 24C02,总容量是 256
个字节,可通过 STM32 来实现 24C02 的读写。

PS1:怎么理解下面这个函数:
//IO方向设置
#define SDA_IN() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)8<<28;}
#define SDA_OUT() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)3<<28;}
等效于//设置SDA为输出
void SDA_OUT(void)
{
GPIO_InitTypeDef GPIO_InitStructer;
GPIO_InitStructer.GPIO_Pin= GPIO_Pin_7;
GPIO_InitStructer.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStructer.GPIO_Mode=GPIO_Mode_Out_PP;
GPIO_Init(GPIOB, &GPIO_InitStructer);
}
//设置SDA为输入
void SDA_IN(void)
{
GPIO_InitTypeDef GPIO_InitStructer;
GPIO_InitStructer.GPIO_Pin= GPIO_Pin_7;
GPIO_InitStructer.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStructer.GPIO_Mode=GPIO_Mode_IPU;
GPIO_Init(GPIOB, &GPIO_InitStructer);
}

CRL、CRH是2个控制端口模式的寄存器,CRL控制引脚的0-7脚,CRH控制引脚的8-15脚。STM32的每组IO口有16个,(PA0–PA15)、(PB0–PB15)……(PG0–PG15),而我们SDA用到的是PB7,所以要用到的就是CRL寄存器。
CRL | CRH |
假设 CRL=0X########;
CRL & 0X0FFFFFFF = 0X######## & 0X0FFFFFFF = 0X0####### ;
即经过GPIOB->CRL&=0X0FFFFFFFCRL;第7个位清零,其它位不变。
(u32)8 将8转换为16进制为0X0000 0008,转换为二进制为0000 0000 0000 0000 0000 0000 0000 1000,GPIOB->CRL|=(u32)8<<28;将其左移28位变为0X8000 0000,8转为二进制就是1000 0000 0000 0000 0000 0000 0000 0000,即将GPIOB的CRL寄存器(第7个IO口)的地址位(CNF7[1:0] MODE7[1:0])置位为1000,即CNF7[1:0]=10,MODE7[1:0]=00,其余位和0按位或 后 保持不变。
查表可知:因为MODE7[1:0]=00,CNF7[1:0]=10表示上/下拉输入;MODE7[1:0]=00表示输入模式
//IO方向设置
#define SDA_IN() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)8<<28;}//上/下拉输入
同理,GPIOB->CRL|=(u32)3<<28;就是将16进制的0000 0003,即二进制的0000 0000 0000 0000 0000 0000 0000 0011左移28位最后变为0011 0000 0000 0000 0000 0000 0000 0000,即将GPIOB的CRL寄存器(第7个IO口)的地址位(CNF7[1:0] MODE7[1:0])置位为0011,即CNF7[1:0]=00,MODE7[1:0]=11,查表可知:因为MODE7[1:0]=11>00,CNF7[1:0]=00表示通用推挽输出模式,MODE7[1:0]=11表示输出模式,最大速度50MHz
//IO方向设置
#define SDA_OUT() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)3<<28;}//推挽输出
综上,这两句函数虽然功能很简单,只是配置两种IO口的不同模式,但是因为使用到了寄存器移位的操作,对于不太了解寄存器的同学可能还是比较难的。实现思路分两部分,首先确定要配置的IO口,然后进行寄存器移位配置,能看懂就行。
参考链接:I2C软件模拟中的IO方向设置问题
一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一
CRL
CRH