虫子 IIC 基本通讯
I^2^C协议的简单应用
I^2^C协议总体概述
I^2^C 总线,是飞利浦推出的一种串行总线,是具备多主机系统所需的包括总线仲裁、高低速兼容的高性能==同步==
I^2^C总线的硬件,是由一根数据线SDA,一根==时钟线SCL==构成。==(由于有时钟线才有同步功能)==不同的器件,都是==并联==接在这两条线上。通过不同的==硬件地址==来识别通信对象。
I^2^C总线==必须都接==上拉电阻接到电源VCC ,电阻大小要根据设备实际测量。==总线空闲时候==,==SDA和SCL都是高电平==。当其中一个设备拉低总线,整条线就全是低电平,器件与器件之间是“与”逻辑关系。
I^2^C总线可能会有多个主机进行互相控制多个从机,多个主机通信就会出现仲裁的情况。我们51 单片机主要是单主机通信 ==多主机仲裁我们不概述==
I^2^C协议,是在同一根SDA线上进行==双向通信==,属于==半双工类==。I^2^C协议首先是发送从机硬件地址,然后发送命令,再发送数据/寄存器编号或者读取数据。I^2^C协议可以多字节连续读写数据
I^2^C协议规则
==I^2^C协议的数据有效性==
规定,必须是在SCL的低电平期间,才能改变SDA的电平状态。SCL高电平期间SDA线必须是稳定的,否则会错误的识别成起始信号或者停止信号。
==I^2^C协议的起始信号、停止信号==
SCL高电平期间,==SDA下降沿==是起始信号,之后就是总线忙碌,比如发送设备唯一地址
SCL高电平期间,==SDA上升沿==是终止信号。之后是总线的空闲
==起始信号和终止信号,都是主机发送==
==I^2^C协议的通讯格式==
I^2^C协议,起始信号之后,可以连续传输多个字节。然后以停止信号结束这一帧数据的传输。第一字节是 7位硬件地址 + 1位传输方向控制位。地址是根据硬件配置的,比如AT24C02的地址是4位固定 +3位接地,可以挂8个相同的AT24C02芯片。只有地址匹配的设备,才会处理总线上的数据
I^2^C协议,每个字节必须是==8 + ACK==位。字节是先传输高位后传输低位。标准 I^2^C协议是每位4微秒以上。但是有些器件是兼容各种速度的。
==应答信号==是接收端接收数据后,把 A SDA 拉低。告诉发送端已经接收完毕。
==非应答信号==是接收端未能拉低,或者单片机读取数据的最后一个字节不需要应答。
I^2^C协议的数据读写三种方式
I^2^C 协议单向发送数据
比如显示设备,一般情况下我们只需要==写入数据==,传输过程如下
①首先是起始信号 S
②然后是发送 7 位从机地址+传输方向位,0写。
③SCL 拉高,判断 ACK ,再拉低 SCL 。
④发送第 1个字节,一般情况下都是指令,或者寄存器地址。
⑤SCL拉高,判断 ACK ,再拉低 SCL 。
⑥发送第2个字节,一般情况下都是数据。
⑦SCL拉高,判断 ACK ,再拉低 SCL 。
⑧可以继续传输数据,或者停止。
I^2^协议发送地址后立即读取数据
比如传感器设备,一般情况下我们只需要读出数据,传输过程如下:
①首先是起始信号 S
②然后是发送 7 位从机地址+传输方向位,1 读。
③SCL 拉高,判断 ACK ,再拉低 SCL 。
④读取第 1 个字节,一般情况下都是指令,或者寄存器地址。
⑤发送应答 ACK
⑥读取第 2个字节,一般情况下都是数据。
⑦发送应答 ACK ,或者不应答 NACK 。
⑧可以继续传输数据,或者停止。
I^2^协议先指定寄存器地址,再读取该寄存器的数据
比如存储芯片,一般情况下我们要先制定存储地址,再读取数据。传输过程如下:
①首先是起始信号 S
②然后是发送7位从机地址+传输方向位,0 写。
③SCL 拉高,判断 ACK ,再拉低 SCL 。
④写数据,一般是写某个寄存器地址或者指令。
⑤SCL 拉高,判断 ACK ,再拉低 SCL 。
⑥ 再次起始信号 S
⑦然后是发送 7 位从机地址+传输方向位,1 读。
⑧SCL 拉高,判断 ACK ,再拉低 SCL 。
⑨写数据,一般是写某个寄存器地址或者指令。
⑩读取字节,一般情况下都是数据。
11.发送应答 ACK ,或者不应答 NACK 。
12 .可以继续传输数据,或者停止
代码思路分析
IIC初始化
//IIC初始化
void IIC_Init()
{
P2M1 &= 0x3f;
P2M0 &= 0x3f; //配置P2.6,P2.7为标准输入输出口
SDA_GPIO = 1;
SCL_GPIO = 1; //默认情况下数据和时钟都是高电平
}
IIC开始信号
//IIC起始信号
void IIC_Start()
{
SDA_GPIO = 1; //开始数据是高电平
SCL_GPIO = 1; //开始时钟是高电平
IIC_Delay(); //延时一小会
SDA_GPIO = 0; //数据脚拉成低电平
IIC_Delay(); //然后再延时一小会
SCL_GPIO = 0; //再把时钟线拉成低电平
IIC_Delay(); //然后再延时一小会
}
IIC停止信号
//IIC停止信号
void IIC_Stop()
{
SCL_GPIO = 1; //时钟线拉成高电平
SDA_GPIO = 0; //数据线保持低电平
IIC_Delay(); //然后再延时一小会
SDA_GPIO = 0; //数据线拉高
IIC_Delay(); //然后再延时一小会
}
以下不解读了,直接说我不想解读了,没时间
#include "all.h"
//IIC微秒级别延时
void IIC_Delay()
{
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
}
//IIC初始化
void IIC_Init()
{
P2M1 &= 0x3f;
P2M0 &= 0x3f; //配置P2.6,P2.7为标准输入输出口
SDA_GPIO = 1;
SCL_GPIO = 1; //默认情况下数据和时钟都是高电平
}
//IIC起始信号
void IIC_Start()
{
SDA_GPIO = 1; //开始数据是高电平
SCL_GPIO = 1; //开始时钟是高电平
IIC_Delay(); //延时一小会
SDA_GPIO = 0; //数据脚拉成低电平
IIC_Delay(); //然后再延时一小会
SCL_GPIO = 0; //再把时钟线拉成低电平
IIC_Delay(); //然后再延时一小会
}
//IIC停止信号
void IIC_Stop()
{
SDA_GPIO = 0; //数据线保持低电平
SCL_GPIO = 1; //时钟线拉成高电平
IIC_Delay(); //然后再延时一小会
SDA_GPIO = 0; //数据线拉高
IIC_Delay(); //然后再延时一小会
}
//IIC写入一个字节 向总线发送一个字节
void IIC_Write_Byte(u8 IIC_Byte)
{
u8 i = 0;//8个数据位
SCL_GPIO = 0; //时钟线保持低电平
SDA_GPIO = 1; //数据线拉到高电平
for(i = 0;i<8;i++)
{
//先发高位
SDA_GPIO = (bit)(IIC_Byte & 0x80)
//高位发完后再移位
IIC_Byte = IIC_Byte << 1;
SCL_GPIO = 1; //时钟线拉到高电平
IIC_Delay(); //再延时一小会
SCL_GPIO = 0; //时钟线拉到低电平
IIC_Delay(); //然后再延时一小会
}
}
//IIC读取字节
u8 IIC_Read_Byte()
{
u8 i = 0;
u8 value = 0;
SCL_GPIO = 0; //时钟线保持低电平
SDA_GPIO = 1; //数据线拉到高电平
for(i = 0;i<8;i++)
{
value = (value<<1)|SDA_GPIO;
SCL_GPIO = 1; //时钟线拉到高电平
IIC_Delay(); //再延时一小会
SCL_GPIO = 0; //时钟线拉到低电平
IIC_Delay(); //然后再延时一小会
}
return value;
}
//IIC读应答
u8 IIC_Read_Ack()
{
//应答变量
u8 Ack = 1;
u8 i = 0;
SCL_GPIO = 1; //时钟线拉到高电平
IIC_Delay(); //再延时一小会
while(Ack && i<5)
{
Ack = SDA_GPIO;
i++;
}
SCL_GPIO = 0; //时钟线拉到低电平
return Ack;
}
//IIC写应答
//单片机发送应答给从机
void IIC_Read_Ack(u8 ack)
{
SDA_GPIO = ack;
IIC_Delay(); //再延时一小会
SCL_GPIO = 1; //时钟线拉到高电平
IIC_Delay(); //再延时一小会
SCL_GPIO = 0; //时钟线拉到低电平
IIC_Delay(); //再延时一小会
SDA_GPIO = 1; //数据线再拉成高电平
IIC_Delay(); //再延时一小会
}
- 点赞
- 收藏
- 关注作者
评论(0)