图片均来自江科大STM32教学PPT
IIC
IIC简介
- I2C(Inter IC Bus)是由Philips公司开发的一种通用数据总线
- 两根通信线:SCL(Serial Clock)、SDA(Serial Data)
- 同步,半双工
- 带数据应答
- 支持总线挂载多设备(一主多从、多主多从)
硬件电路
- 所有I2C设备的SCL连在一起,SDA连在一起
- 设备的SCL和SDA均要配置成开漏输出模式
- SCL和SDA各添加一个上拉电阻,阻值一般为4.7KΩ左右
IIC时序基本单元
- 起始条件:SCL高电平期间,SDA从高电平切换到低电平,然后主机要把SCL拽下来(占用SCL总线),也是方便基本时序单元拼接(低电平开始,低电平结束)。
- 终止条件:SCL先放手回弹至高电平,高电平期间,SDA从低电平切换到高电平,产生一个上升沿,作为终止条件。
- 起始和终止都是由主机产生,总线空闲状态时,从机始终放手。
实线代表主机控制,虚线代表从机控制
- 发送一个字节:SCL低电平期间,主机将数据位依次放到SDA线上(高位先行),然后释放SCL,从机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可发送一个字节。
- 接收一个字节:SCL低电平期间,从机将数据位依次放到SDA线上(高位先行),然后释放SCL,主机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可接收一个字节(主机在接收之前,需要释放SDA)
- 发送应答:主机在接收完一个字节之后,在下一个时钟发送一位数据,数据0表示应答,数据1表示非应答
- 接收应答:主机在发送完一个字节之后,在下一个时钟接收一位数据,判断从机是否应答,数据0表示应答,数据1表示非应答(主机在接收之前,需要释放SDA)
IIC时序
指定地址写
对于指定设备(Slave Address),在指定地址(Reg Address)下,写入指定数据(Data)
- 指定设备(Slave Address):从机地址
- 指定地址(Reg Address):某个设备内,寄存器地址
- 指定数据(Data):要在寄存器中写入的数据
- 第一个字节:从机地址(7位)+读写位(1位)
- 接跟着就是接收从机的应答位
- 最后主机不需要发送数据,就产生停止条件。在停止条件前,先拉低SDA,为后续 SDA的上升沿做准备。然后释放SCL,再释放SDA。这样就产生了SCL高电平期间,SDA产生上升沿。
当前地址读
对于指定设备(Slave Address),在当前地址指针指示的地址下,读取从机数据(Data)
主机发出了“读”的命令,随后数据传输方向就要反过来。从机发,主机读。要把SDA的控制权交给从机,主机调用接收一个字节的时序,进行接收操作。从机在SCL低电平写入SDA,主机在SCL高电平期间读取SDA。
✨ 【在读的时序中,IIC协议规定是,主机进行寻址时,一旦读(1)写(0)标志位给1,下一个字节就必须立马转为读的时序,所以主机还来不及指定,要读哪个寄存器,就得开始接收,所以这里就没有指定地址的环节,就需要用到当前地址指针】
指定地址读
对于指定设备(Slave Address),在指定地址(Reg Address)下,读取从机数据(Data)
- 第二个字节就是用来指定地址,地址指针就指向了这个地址,
- 紧接着不给发写入数据,而是直接再来个起始条件,重新寻址(从机地址),指定读写标志位(因为时要读,就写1),
- 接着,主机接收一个字节,这个字节就是地址指针指向的地址下的数据。
- 指针地址会在读写操作后自增。
- 如果只读一个字节就停止,就要给从机发个非应答,然后从机就会释放总线,把SDA的控制权交还给主机。
- 从机发送数据的权利开始于读写标志位为1,结束于主机应答为1。
软件模拟IIC代码示例
IIC.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
// 用于将SCL信号线设置为指定值。GPIOB的Pin10被用来控制SCL信号线
void MyI2C_W_SCL(uint8_t BitValue)
{
GPIO_WriteBit(GPIOB,GPIO_Pin_10,(BitAction)BitValue);
Delay_us(10);
}
// 用于将SDA信号线设置为指定值。GPIOB的Pin11被用来控制SDA信号线。
void MyI2C_W_SDA(uint8_t BitValue)
{
GPIO_WriteBit(GPIOB,GPIO_Pin_11,(BitAction)BitValue);
Delay_us(10);
}
// 用于读取SDA信号线的值。GPIOB的Pin11被用来读取SDA信号线的值
uint8_t MyI2C_R_SDA(void)
{
uint8_t BitValue;
BitValue=GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11);
Delay_us(10);
return BitValue;
}
// 用于初始化IIC总线
void MyI2C_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_OD;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10|GPIO_Pin_11;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);
// SCL和SDA都处于高电平,空闲状态
GPIO_SetBits(GPIOB,GPIO_Pin_10|GPIO_Pin_11);
}
// 用于发送起始信号
void MyI2C_Start(void)
{
MyI2C_W_SDA(1);
MyI2C_W_SCL(1);
MyI2C_W_SDA(0);
MyI2C_W_SCL(0)