-
1.什么是协议
-
协议就是双方在进行通讯时,都遵循的一系列规则,举个例子,在使用手机的时候,只有你的指纹才能打开手机,其他人的不行,这就是手机和人之间的通讯协议,(有些不恰当)
2.I2C协议的简单理解:
-
1) 他的作用
将主设备和从设备联系起来进行数据通讯(前提是设备有I2C通讯的能力)
-
2) 目前遇到的通讯设备
MPU6050,MFRC522,单片机
-
3) 目前遇到的用处:
单片机来模拟I2C协议和外界设备通讯,通过单片机和外设备通过I2C协议通讯,去读取或者修改外设备的寄存器的值,从而去控制外设备的运行状态,或者让来感知外界的某种物理量的结果。
3.I2C协议的接口:
- 1) SDA数据通讯接口
- 2) SCK时钟通讯接口 4.I2C协议的基础规则:
- 1) 通讯的起始信号
- 2) 通讯的关闭信号
- 3) 写入1Byte的值
- 4) 等待从设备应答(继续 or 终止)
- 5) 主动向从设备应答(继续 or 终止)
- 6) 读取1 Byte的值 5.规则的具体分析:
-
1) 通讯的起始信号
1、 首先将SCK拉低,
2、 然后将SDA 拉高,(为了确保在SCK高电平期间,SDA有高到底的过程),
3、 然后将SCK拉高(延迟一下,为了确保读取之前被拉高),
4、 然后SDA拉低(延迟一下,为了确保能读取到这个跳变),
5、 然后将SCK拉低。
那么开启这过程就完成了
void Start_I2C(void)
{
SDA_OUT();
SCK_State=0; //1.拉低时钟
Delay_1ms();
SDA_State=1; //2.拉高数据
Delay_1ms();
SCK_State=1; //3.拉高时钟
Delay_1ms();
SDA_State=0; //4.拉低数据 下降沿
Delay_1ms();
SCK_State=0; //5.拉低时钟
}
/*------------------I2C-开始信号函数----------*/
-
2) 通讯的关闭信号
1、 首先将SCK拉低,
2、 然后将SDA 拉低(为了确保在SCK高电平期间,SDA有低到高的过程),
3、 然后将SCK拉高(延迟一下,为了确保读取之前被拉高),然后SDA拉低(延迟一下,为了确保能读取到这个跳变),
4、 然后将SCK拉低。
void Stop_I2C(void)
{
SCK_State=0; //1.拉低时钟
SDA_OUT();
Delay_1ms();
SDA_State=0; //2.拉低数据
Delay_1ms();
SCK_State=1; //3.拉高时钟
Delay_1ms();
SDA_State=1; //4.拉高数据 上升沿
Delay_1ms();
SCK_State=0; //5.拉低时钟
}
/*-------------------------------------------I2C-停止信号函数-------------------------------------------*/
-
3.1) 主动向从设备应答(继续 )
1、 首先将SCK拉低,
2、 然后将SDA 拉低(为了确保在SCK高电平期间,SDA一直是低电平) ,
3、 然后将SCK拉高(延迟一下,为了确保读取之前被拉高),
4、 然后SCK拉低,
那么主动发送应答过程完成。 -
3.2) 主动向从设备应答(终止 )
1、 首先将SCK拉低,
2、 然后将SDA 拉高(为了确保在SCK高电平期间,SDA一直是高电平) ,
3、 然后将SCK拉高(延迟一下,为了确保读取之前被拉高),
4、 然后SCK拉低,
那么主动发送应答过程完成。
void Do_Ack(bool select) //1为终止,0为继续
{
SCK_State=0; //1.拉低时钟
SDA_OUT();
Delay_1ms();
SDA_State=select; //2.拉X数据
Delay_1ms();
SCK_State=1; //3.拉高时钟
Delay_1ms();
SCK_State=0; //4.拉低时钟
}
/*-------------------------------------------I2C-反馈-回应信号函数-------------------------------------------*/
-
4) 主动发送1Byte的值
1、 首先将SCK拉低,
2、 然后将SDA 拉X(为了确保在SCK高电平期间,SDA一直是X电平) (X为数据电平),
3、 然后将SCK拉高(延迟一下,为了确保读取之前被拉高),
4、 1~3步骤执行8次
5、 然后SCK拉低,
那么主动发送BYTE过程完成。
void Write_Byte(u8 Date)
{
u8 times=8; //数据的移位读取变量
SDA_OUT(); //数据口设置为输出态
Delay_1ms();
while(times) //循环8次,1Byte
{
SCK_State=0; //1.拉低时钟
Delay_1ms();
SDA_State=(bool)(Date&(1<<(times-1))); //2.送入数据
Delay_1ms();
SCK_State=1; //3.拉高时钟
Delay_1ms();
times--;
}
SCK_State=0; //4.拉低时钟
Delay_1ms();
}
/*--------------------------------I2C-写-BYTE函数---------------------*/
-
5.1) 等待从设备应答(继续 )
1、 首先将SCK拉低,
2、 然后将SDA 设置为上拉输入态,(为了确保在SCK高电平期间,SDA有高到底的过程),
3、 然后将SCK拉高(延迟一下,为了确保读取之前被拉高),
4、 然后通过询问的方式,在规定的检测时间内检测到SDA被拉低,
5、 然后将SCK拉低。
接受应答的过程(继续)就完成了 -
5.2) 等待从设备应答( 终止 )
1、 首先将SCK拉低(延迟一下,为了确保读SCK已经拉低)
2、 然后将SDA 设置为上拉输入态,(为了确保在SCK高电平期间,SDA有高到底的过程),
3、 然后将SCK拉高(延迟一下,为了确保读取之前被拉高),
4、 然后通过询问的方式,在规定的检测时间内SDA永远保持高电平
5、 然后将SCK拉低。
接受应答的过程(终止)就完成了
bool Wait_Ack(void) //1为失败 0为成功
{
int wait=100;
SCK_State=0; //1.拉低时钟
SDA_State=1; //2.拉高--设置为上拉输入,看网页收藏
Delay_1ms();
SDA_IN(); //数据口设置为输入模式
Delay_1ms();
SCK_State=1; //3.拉高时钟
while(SDA_Read)
{
wait--;
if(wait==0) //因为应答超时
{
Stop_I2C(); //关闭通讯
SCK_State=0; //4.拉低时钟
return 1;
}
}
SCK_State=0; //4.拉低时钟
return 0; //答应成功
}
/*------------ ------I2C-等待-回应信号函数------- ---*/
-
6) 读取1 Byte的值
1、 首先将SCK拉低(延迟一下,为了确保读SCK已经拉低)
2、 然后将SDA 设置为输入模式(延迟一下,保证已经设为输入模式)
3、 首先将SCK拉低(延迟一下,为了确保读SCK已经拉低)
4、 将SDA设置为上拉模式(在输入状态下,调节他的ODR寄存器为1是是上拉),(延迟一下,保证已经设为上拉模式)
5、 先默认该位数据为0,所以DATA左移一位
6、 然后将SCK拉高(延迟一下,为了确保读SCK已经拉高,并且确保从设备已经将SDA总线电平修改结束,主设备能读到正确的数据)
7、 读取数据,然后若为1则数据+1,否则不变
8、 3~7步骤执行8次
9、 然后SCK拉低(延迟一下,为了确保读SCK已经拉低)
那么被动接受BYTE过程完成。
u8 Read_Byte(void)
{
u8 Data=0,Read_time=8;
SCK_State=0; //1.拉低时钟
Delay_1ms();
SDA_IN(); //2.数据口设置为输入(没上拉,在下面的循环里设置了)
Delay_1ms();
while(Read_time) //3.8次循环读数据
{
SCK_State=0; //3_1.拉低时钟
Delay_1ms();
SDA_State=1; //3_2.拉高数据(为了做到上拉输入)
Delay_1ms();
Data<<=1; //3_3.先默认送0
SCK_State=1; //3_4.拉高时钟,准备读数
Delay_1ms();
if(SDA_Read) //3_5.读数,若为1,则数据+1
Data++;
Read_time--;
}
SCK_State=0; //4.拉低时钟
Delay_1ms();
return Data; //送出数据
}
/*------- -----I2C-读-BYTE函数----- -------*/
-
5.I2C协议的上层函数的调用:
-
1.1.主机发送寄存器访问信号
1、主机发送起始信号
2、主机发送从设备的I2C地址7位(第八位用于读写控制)
3、主机发送命令指令(一般直接是寄存器地址,具体看器件说明书)
4、等待回应(有一个时间上的等待,若超时直接退出并且关闭通讯)
5、若成功,也关闭通讯
bool send_one_Byte(u8 address,u8 data) //返回值的含义 1为失败 0为成功
{
Start_I2C(); //开启信号
Write_Byte(address<<1|0); //写入IIC地址
if(Wait_Ack()) //等待应答
{
Stop_I2C();
return 1; //若没有,直接输出 1
}
Write_Byte(data); //写数据或者寄存器
if(Wait_Ack()) //等待应答
{
Stop_I2C();
return 1; //若没有,直接输出 1
}
Stop_I2C();
return 0; //若有,直接输出 0
}
/*-------------------------------------------发送单个BYTE给从机 -------------------------------------------*/
-
1.2.主机向从机的某个寄存器写值
1、主机发送起始信号
2、主机发送从设备的I2C地址7位(第八位用于读写控制)
3、主机发送命令指令(一般直接是寄存器地址,具体看器件说明书)
4、等待回应(有一个时间上的等待,若超时直接退出并且关闭通讯)
5、若成功,主机发送要写的8bit数据
6、等待应答(有一个时间上的等待,若超时直接退出并且关闭通讯)
7、若成功,也关闭通讯
bool New_Write_DATA_to_Reg(u8 reg,u8 data)
{
Start_I2C();
Write_Byte(I2C_Address<<1|0);
if(Wait_Ack()) //等待应答
{
Stop_I2C();
return 1; //若没有,直接输出 1
}
Write_Byte(reg);
if(Wait_Ack()) //等待应答
{
Stop_I2C();
return 1; //若没有,直接输出 1
}
Write_Byte(data);
if(Wait_Ack()) //等待应答
{
Stop_I2C();
return 1; //若没有,直接输出 1
}
Stop_I2C();
return 0;
}
/**/
-
1.3.主机向从机的两个连续的寄存器写值()
1、主机发送起始信号
2、主机发送从设备的I2C地址7位(第八位用于读写控制)
3、主机发送命令指令(一般直接是寄存器地址,具体看器件说明书)
4、等待回应(有一个时间上的等待,若超时直接退出并且关闭通讯)
5、若成功,主机发送要写的8bit数据
4、等待回应(有一个时间上的等待,若超时直接退出并且关闭通讯)
5、若成功,主机的输送对象自动变为原来的寄存器地址+1,主机发送另外要写的8bit数据
6、等待应答(有一个时间上的等待,若超时直接退出并且关闭通讯)
7、若成功,也关闭通讯 -
2.1.主机接受上次寄存器访问的结果
1、主机发送起始信号
2、主机发送从设备的I2C地址7位(第八位用于读写控制)
3、等待回应(有一个时间上的等待,若超时直接退出并且关闭通讯)
4、若成功,主机接受上次主机发送访问后的数据(这个函数与1.1函数一般要连用)
5、主机主动发送一个应答(终止)信号
6、关闭通讯
u8 read_one_Byte(u8 address) //默认的返回值为0xff ,一般0xff为失败,其他值为寄存器的数据,会有误差,因为0xff有可能是寄存器的值 //欠缺一个标志位ERROR
{
u8 result=0XFF; //1.默认的返回值为0xff
Start_I2C(); //2.开启信号
Write_Byte(address<<1|1); //3.写入IIC地址
if(!Wait_Ack()) //4.等待应答
{
result=Read_Byte(); //若有应答,接受结果
Do_Ack(1); //自动给出应答
}
Stop_I2C(); //5.关闭信号
return result; //6.返回结果
}
/*---------------读出单个BYTE从从机 -------------*/
-
2.2.主机向单个寄存器读取值
1、主机发送起始信号
2、主机发送从设备的I2C地址7位(第八位用于读写控制)
3、等待回应(有一个时间上的等待,若超时直接退出并且关闭通讯)
4、若成功,主机发送命令指令(一般直接是寄存器地址,具体看器件说明书)
5、等待回应(有一个时间上的等待,若超时直接退出并且关闭通讯)
6、若成功,主机发送起始信号
7、主机发送从设备的I2C地址7位(第八位用于读写控制)
8、等待回应(有一个时间上的等待,若超时直接退出并且关闭通讯)
9、若成功,主机接受该寄存器内的数据
10、主机主动发送一个应答(终止)信号
11、关闭通讯
u8 New_Read_DATA_in_Reg(u8 reg)
{
static u8 DATA;
Start_I2C();
Write_Byte(I2C_Address<<1|0);
if(Wait_Ack()) //等待应答
{
Stop_I2C();
return 0XFF; //若没有,直接输出 1
}
Write_Byte(reg);
if(Wait_Ack()) //等待应答
{
Stop_I2C();
return 1; //若没有,直接输出 1
}
Start_I2C();
Write_Byte(I2C_Address<<1|1);
if(Wait_Ack()) //等待应答
{
Stop_I2C();
return 0XFF; //若没有,直接输出 1
}
DATA=Read_Byte();
Do_Ack(1);
Stop_I2C();
return DATA;
}
/**/
-
2.3.主机向连续的2个寄存器读取值
1、主机发送起始信号
2、主机发送从设备的I2C地址7位(第八位用于读写控制)
3、等待回应(有一个时间上的等待,若超时直接退出并且关闭通讯)
4、若成功,主机发送命令指令(一般直接是寄存器地址,具体看器件说明书)
5、等待回应(有一个时间上的等待,若超时直接退出并且关闭通讯)
6、若成功,主机发送起始信号
7、主机发送从设备的I2C地址7位(第八位用于读写控制)
8、等待回应(有一个时间上的等待,若超时直接退出并且关闭通讯)
9、若成功,主机接受该寄存器内的数据
10、主机发送一个主动应答(继续)
11、若成功,主机接受寄存器+1后地址的数据
10、主机主动发送一个应答(终止)信号
11、关闭通讯
u16 New_Read_DATAS_in_Reg(u8 reg)
{
static u16 DATA;
Start_I2C();
Write_Byte(I2C_Address<<1|0);
if(Wait_Ack()) //等待应答
{
Stop_I2C();
return 0XFF; //若没有,直接输出 1
}
Write_Byte(reg);
if(Wait_Ack()) //等待应答
{
Stop_I2C();
return 0XFF; //若没有,直接输出 1
}
Start_I2C();
Write_Byte(I2C_Address<<1|1);
if(Wait_Ack()) //等待应答
{
Stop_I2C();
return 0XFF; //若没有,直接输出 1
}
DATA=(Read_Byte())<<8;
Do_Ack(0);
DATA=DATA|Read_Byte();
Do_Ack(1);
Stop_I2C();
return DATA;
}
/**/
-
2.4.主机向连续的多个寄存器读取值(部分器件可用)
1、主机发送起始信号
2、主机发送从设备的I2C地址7位(第八位用于读写控制)
3、等待回应(有一个时间上的等待,若超时直接退出并且关闭通讯)
4、若成功,主机发送命令指令(一般直接是寄存器地址,具体看器件说明书)
5、等待回应(有一个时间上的等待,若超时直接退出并且关闭通讯)
6、若成功,主机发送起始信号
7、主机发送从设备的I2C地址7位(第八位用于读写控制)
8、等待回应(有一个时间上的等待,若超时直接退出并且关闭通讯)
9、若成功,主机接受该寄存器内的数据
10、主机发送一个主动应答(继续)
11、9~10的步骤,循环N-1次
12、主机接受该第N个寄存器内的数据
13、主机发送一个主动应答(终止)
14、关闭通讯
u32 New_Read_DATASS_in_Reg(u8 reg)
{
static u32 DATA;
int i=1;
Start_I2C();
Write_Byte(I2C_Address<<1|0);
if(Wait_Ack()) //等待应答
{
Stop_I2C();
return 0XFF; //若没有,直接输出 1
}
Write_Byte(reg);
if(Wait_Ack()) //等待应答
{
Stop_I2C();
return 0XFF; //若没有,直接输出 1
}
Start_I2C();
Write_Byte(I2C_Address<<1|1);
if(Wait_Ack()) //等待应答
{
Stop_I2C();
return 0XFF; //若没有,直接输出 1
}
while(1)
{
DATA=DATA|((Read_Byte())<<(32-8*i));
i++;
if(i==5) break;
Do_Ack(0);
}
Do_Ack(1);
Stop_I2C();
return DATA;
}
/**/