STM32 —— IIC 协议入门
STM32 —— IIC 协议入门
简介
IIC 也称 I²C,是一个多主从的串行总线,属于两线式串行总线,由飞利浦公司开发的用于微控制器( MCU )和外围设备(从设备)进行通信的一种总线,属于半双工同步传输类总线,一主多从(一个主设备(Master),多个从设备(Slave))的总线结构,总线上的每个设备都有一个特定的设备地址,以区分同一 IIC 总线上的其他设备,仅由两条线就能完成多机通讯,一条 SCL 时钟线,另外一条双向数据线 SDA ,IIC 总线要求每个设备 SCL / SDA 线都是漏极开路模式,因此必须带上拉电阻才能正常工作。I²C 协议占用引脚少,硬件实现简单,可扩展性强,I²C 数据传输速率有标准模式(100kbps)、快速模式(400kbps)和高速模式(3.4Mbps)
IIC 通信与 UART,还有 SPI 统称为串行接口通信,不过它们之间还是有区别的,如UART的负电平逻辑,还有 UART 通信不需要时钟,只需要特定的波特率即可,SPI 与 IIC 都可以有一个主机,多个从机的情况,不过 IIC 适用于短距离传输,如片间通信,摄像头的配置等场景
物理 IIC 接口有两根双向线,串行时钟线( SCL )和串行数据线( SDA )组成,可用于发送和接收数据,但是通信都是由主设备发起,从设备被动响应,实现数据的传输
![](https://img2022.cnblogs.com/blog/2829072/202211/2829072-20221117115812952-1015968073.png)
时序
一个控制器里面可以含有多个 IIC 控制器,一个 IIC 控制器可以挂载多个 IIC 设备,这些 IIC 设备都通过 SDA,SCL 和主机进行通信。
![](https://img2022.cnblogs.com/blog/2829072/202211/2829072-20221117114755335-649055881.png)
IDLE:
表示总线空闲状态。此状态下时钟信号 SCL 和数据信号 SDL 均为高电 平,此时无 IIC 设备工作
START:
表示起始状态。在 IDLE 状态下,时钟信号 SCL 继续保持高电平,数据信号 SDL 出现由高电平转换为低电平的下降沿,此时产生一个起始信号,与总线相连的I2C设备检测到起始信号之后,进入起始状态等待控制字节的输入
Read or Write:
表示 IIC 设备读写状态
STOP:
表示停止状态。完成数据读写后,SCL 保持高电平,SDL 产生一个由低电平转换为高电平的上升沿信号之后,产生停止信号,总线回到空闲状态
读写时序状态如下:
![](https://img2022.cnblogs.com/blog/2829072/202211/2829072-20221117115133367-314077847.png)
SCL 为低电平期间,SDA 允许数据变换(1 代表高电平,0 代表低电平)。SCL 为高电平期间,SDA 数据必须保持稳定,因此一个时钟周期传输 1bit 数据,每次传输 8 个 bit 即一个字节,主机将在最后一个数据时钟末释放 SDA 总线使从机应答,在传输完 1 字节数据后的第一个时钟周期 SCL 为高电平,如果 SDA 未被检测到低电平,则视为非应答,代表此次数据传输失败
注意:数据传输是以高位在前,低位在后
传输数据
通信过程
主设备与从设备的一般通信过程
主设备给从设备发送/写入数据
![](https://img2022.cnblogs.com/blog/2829072/202211/2829072-20221117120754658-1677334124.png)
-
主设备发送一个开始( START )信号
-
发出一个设备地址(用来确定是往哪一个芯片写数据)到从设备,方向(读/写,0 表示写,1 表示读)
-
等待从设备响应( ACK ),从设备回应(用来确定这个设备是否存在),然后就可以传输数据
-
主设备发送一个字节数据给从设备,并等待回应
-
每传输一字节数据,接收方要有一个回应信号(ACK 确定数据是否接受完成),然后再传输下一个数据
-
数据发送完之后,主设备就会发送一个停止( STOP )信号
主设备从从设备接收/读取数据
![](https://img2022.cnblogs.com/blog/2829072/202211/2829072-20221117121417756-126049629.png)
-
主设备发送一个开始( START )信号
-
发出一个设备地址(用来确定是往哪一个芯片写数据)到从设备,方向(读/写,0 表示写,1 表示读)
-
等待从设备响应( ACK )从设备回应(用来确定这个设备是否存在),然后就可以传输数据
-
主设备发送一个字节数据给从设备,并等待回应
-
每传输一字节数据,接收方要有一个回应信号(ACK 确定数据是否接受完成),然后再传输下一个数据。
-
数据发送完之后,一般接收到最后一个数据后会发送一个无效响应( NACK ),主设备就会发送一个停止( STOP )信号。
产生开始信号和停止信号
完整数据发送周期图如下:
![](https://img2022.cnblogs.com/blog/2829072/202211/2829072-20221117142351003-2065055986.png)
开始信号:在 SCL 线为高电平时,SDA 线由高电平变为低电平
结束信号:在 SCL 线为高电平时,SDA 线由低电平变为高电平
将数据发送出去
在 SCL 为高电平时,会将 SDA 上的电平信号传输出去,当 SCL 为低电平时可以改变 SDA 上的电平信号
注意:在传输数据的过程中 SCL 必须保持高电平。第一个发送的数据必须是从设备地址,只有找到从设备地址才可以开始发送下面的信息
![](https://img2022.cnblogs.com/blog/2829072/202211/2829072-20221117145559853-845252660.png)
IIC 页写与单字节写
IIC 单字节写与页写的最大的区别:单字节写按照 IIC 协议每次写入一个字节,产生一个 stop 信号,页写可以允许写入多个字节数据,但不能超过设备单页包含的最大存储数,写入多个字节后,最终主机发送一个 stop 信号
![](https://img2022.cnblogs.com/blog/2829072/202211/2829072-20221117151432303-1303278634.png)
通信的实现
使用 IIC 控制器实现(硬件 IIC )
就是使用芯片上的 IIC 外设,也就是硬件 IIC ,它有相应的 IIC 驱动电路,有专用的 IIC 引脚,效率更高,写代码会相对简单,只要调用 IIC 的控制函数即可,不需要用代码去控制 SCL、SDA 的各种高低电平变化来实现 IIC 协议,只需要将 IIC 协议中的可变部分(如:从设备地址、传输数据等等)通过函数传参给控制器,控制器自动按照I2C协议实现传输,但是如果出现问题,就只能通过示波器看波形找问题
使用 GPIO 通过软件模拟实现(软件 IIC )
软件模拟 IIC 比较重要,因为软件模拟的整个流程比较清晰,哪里出来 bug,很快能找到问题,模拟一遍会对 IIC 通信协议更加熟悉,这就是我们常说的软件 IIC
如果芯片上没有 IIC 控制器,或者控制接口不够用了,通过使用任意 I/O 口去模拟实现 IIC 通信协议,手动写代码去控制 I/O 口的电平变化,模拟IIC协议的时序,实现 IIC 的信号和数据传输
通信协议
IIC 总线协议无非就是几样东西:起始信号、停止信号、应答信号、以及数据有效性
空闲状态
时钟线( SCL )和数据线( SDA )接上拉电阻,默认高电平,表示总线是空闲状态
从设备地址
从设备地址用来区分总线上不同的从设备,一般发送从设备地址的时候会在最低位加上读/写信号,比如设备地址为 0x50,0 表示读,1 表示写,则读数据就会发送 0x50,写数据就会发送 0x51
起始( START )信号
IIC 通信的起始信号由主设备发起,SCL 保持高电平,SDA 由高电平跳变到低电平
![](https://img2022.cnblogs.com/blog/2829072/202211/2829072-20221117161920500-593824797.png)
// 起始信号
void IIC_start(void)
{
// 1.首先把数据线设置为输出模式
// 总线空闲, SCL和SDA输出高
SCL = 1;
SDA = 1;
delay_us(5);
// SDA由高变低
SDA = 0;
delay_us(5);
// 拉低SCL开始传输数据
SCL = 0;
}
停止( STOP )信号
IIC 通信的停止信号由主设备终止,SCL 保持高电平,SDA由低电平跳变到高电平
![](https://img2022.cnblogs.com/blog/2829072/202211/2829072-20221117162054539-1317224723.png)
// 停止信号
void IIC_stop(void)
{
// 1.首先把数据线设置为输出模式
// 拉高时钟线
SDA = 0;
delay_us(5);
SCL = 1;
delay_us(5);
// SDA由低变高
SDA = 1;
}
数据有效性
IIC 总线进行数据传送时,在 SCL 的每个时钟脉冲期间传输一个数据位,时钟信号 SCL 为高电平期间,数据线 SDA 上的数据必须保持稳定,只有在时钟线 SCL 上的信号为低电平期间,数据线 SDA 上的高电平或低电平状态才允许变化,因为当 SCL 是高电平时,数据线 SDA 的变化被规定为控制命令( START 或 STOP ,也就是前面的起始信号和停止信号)
![](https://img2022.cnblogs.com/blog/2829072/202211/2829072-20221117162244568-39583058.png)
应答信号( ACK:有效应答,NACK:无效应答 )
接收端收到有效数据后向对方响应的信号,发送端每发送一个字节( 8 位)数据,在第 9 个时钟周期释放数据线去接收对方的应答
当 SDA 是低电平为有效应答( ACK ),表示对方接收成功
当 SDA 是高电平为无效应答( NACK ),表示对方没有接收成功
发送数据需要等待接收方的应答:
// 等待ACK 1-无效 0-有效
u8 IIC_wait_ack(void)
{
u8 ack = 0;
// 数据线设置为输入
// 拉高时钟线
SCL = 1;
delay_us(5);
// 获取数据线的电平
if(SDA)
{ // 无效应答
ack = 1;
IIC_stop();
}
else
{ // 有效应答
ack = 0;
// 拉低SCL开始传输数据
SCL = 0;
delay_us(5);
}
return ack;
}
接收数据需要向发送方发送应答:
void IIC_ack(u8 ack)
{
// 数据线设置为输出
SCL = 0;
delay_us(5);
if(ack)
SDA = 1; // 无效应答
else
SDA = 0; // 有效应答
delay_us(5);
SCL = 1;
// 保持数据稳定
delay_us(5);
// 拉低SCL开始传输数据
SCL = 0;
}