目录
3.2.1 主机读操作 uint8_t IIC3_Revice_Byte(uint8_t ack)
3.2.2 主机发送应答信号 void IIC3_SendAck(uint8_t ack)
3.3.1 主机写操作 uint8_t IIC3_SendByte(uint8_t data)
3.3.2 主机收到应答信号 uint8_t IIC3_ReviceAck(void)
1.IIC简介
I2C (Inter-Integrated Circuit)总线产生于在 80 年代, 由 PHILIPS 公司开发的两线式串行总线,用于连接微 控制器及其外围设备, 最初为音频和视频设备开发。
I2C总线的设备分类:有主机和从机之分,类型一般为一主多从,主机一般为微控制器(MCU),从机一般是外设(如AT24C02模块),主机和从机之间可以有数据交互,但是主动权一直都在主机身上。
I2C 总线两线制控制方式:由时钟线SCL(Serial Clock)和数据线SDA(Serial Data)组成。时钟线只能由主机来控制,时钟线(SCL)用于控制产生起始信号和停止信号。SDA又可以称呼为双向串行数据线,数据可以主机发给从机,也可以是从机发给主机。
总线的空闲状态:SCL和SDA都处于高电平状态时,即SCL=1,SDA=1。
2.IIC原理介绍
2.1 IIC的三种通信过程
单独读(读的过程也就是:接收数据的过程)
单独写(写的过程也就是:发送数据的过程)
又读又写(前两者都有)
2.2 IIC的寻址方式
器件地址:包含从设备的地址(7位),以及主机读写标识(1位)
从设备地址:包含4位固定的地址(厂家决定),以及3位可编程地址(编程者决定)
主机读写标识:又称呼为方向位,X代表读(收数据),Y代表写(发数据)。
1. 主机发送起始条件(主机占用总线,唤醒总线上所有的从设备)
2. 主机发送器件地址(总线上的从设备会拿这个地址跟自身作比较,如果匹配,就回发一个应答给主机)
3. 主机和从机进行数据交流 (根据器件地址上的方向位来决定是主机发送数据给从机还是主机读取从机的数据)
4. 主机发送停止条件(主机释放总线)
寻址原理图如下所示:
2.3 IO口模拟IIC总线
由于 IIC 控制器存在缺陷,一般都不会采用芯片内部的 IIC 控制器。所以要想采用 IIC 通信跟外设进行数据 交流,就需要用 IO 口模拟 IIC 时序,IIC 总线是两线制通信协议,所以只需要采用两个 IO 口即可,一个 IO 口作为 SCL,另一个作为 SDA。
作为 SCL 的 IO 口:SCL 只能由主机发出,把这个 IO 配置成输出模式,推挽输出和开漏输出均可
作为 SDA 的 IO 口:SDA 是双向数据线,既能从主机发出数据,主机也能在 SDA 读取数据。刚好在 IO 口 配置成输出模式时,输入电路并没有被关闭。但是,在采用输入的时候,不能让输出 电路影响到输入电路,必须配置成开漏输出,在读取数据前,输出“1”把输出电路 从 IO 口断开。
3.IIC编程讲解
编译时,需要注意,延时相关的参数,要在允许的范围内,官方图如下所示:
3.1 起始条件
void IIC_Start(void)
{
IIC_SCL=1;
IIC_SDA_OUT=1;
Delay_nus(5); //SCL置1和SDA置1,随后延时5us,起始条件建立
IIC_SDA_OUT=0; // SDA置0
Delay_nus(5); // 持续5us
IIC_SCL=0; //SCL置0,SDA保持0,起始条件建立完毕
}
3.2 主设备读数据(从机发数据,主机收数据)
3.2.1 主机读操作 uint8_t IIC3_Revice_Byte(uint8_t ack)
传参:uint8_t ack 指代从机返回的标识符
uint8_t IIC3_Revice_Byte(uint8_t ack)
{
uint8_t i=0;
uint8_t data=0;
for(i=0;i<8;i++)
{
IIC3_SCL_L; //(从机准备数据)SCL=0
IIC3_SDA_OUT_H; //读模式-----让输出电路与管脚断开!SDA=1
Delay_nus(5); //延时(给时间从机准备数据并且数据稳定在数据线上)
IIC3_SCL_H; //SCL=1
data<<=1; //空出最低位来存储本次读取的
if(IIC3_SDA_IN) //主机读取SDA线上的数据
data |=1;
Delay_nus(5); //延时5us (给时间主机读取数据)
}
IIC3_SCL_L; //方便后续的操作;防止意外产生了停止条件
Delay_nus(5);
IIC3_SendAck(ack);
return data;
}
3.2.2 主机发送应答信号 void IIC3_SendAck(uint8_t ack)
void IIC3_Send_Ack(uint8_t ack)
{
IIC3_SCL_L; //scl=0
if(ack) //(主机准备数据)
IIC3_SDA_OUT_H;
else
IIC3_SDA_OUT_L;
Delay_nus(5); //延时(数据稳定在数据线上)
IIC3_SCL_H; //(从机在时钟线上升沿从SDA上采集数据)//scl=1
Delay_nus(5); //延时(给时间从机读取数据)
IIC3_SCL_L; //方便后续的操作;防止意外产生了停止条件
Delay_nus(5);
}
3.3 主设备写数据(主机发数据,从机收数据)
3.3.1 主机写操作 uint8_t IIC3_SendByte(uint8_t data)
uint8_t IIC3_SendByte(uint8_t data)
{
uint8_t i=0;for(i=0;i<8;i++)
{
IIC3_SCL_L;
if((data<<i)&0x80) //(主机准备数据)
IIC3_SDA_OUT_H;
else
IIC3_SDA_OUT_L;
Delay_nus(5); //延时(数据稳定在数据线上)
IIC3_SCL_H; //(从机在时钟线上升沿从SDA上采集数据)
Delay_nus(5); //延时(给时间从机读取数据)
}
IIC3_SCL_L; //方便后续的操作;防止意外产生了停止条件
return IIC3_ReviceAck( );
}
3.3.2 主机收到应答信号 uint8_t IIC3_ReviceAck(void)
uint8_t IIC3_Revice_Ack(void)
{
uint8_t ack=0;
IIC3_SCL_L; //(从机准备数据)SCL=0
IIC3_SDA_OUT_H; //读模式-----让输出电路与管脚断开!!!!!!!!!!sda=1
Delay_nus(5); //延时(给时间从机准备数据并且数据稳定在数据线上)
IIC3_SCL_H;
Delay_nus(5); //延时 (给时间主机读取数据)
if(IIC3_SDA_IN) //主机读取SDA线上的数据
ack=1;
IIC3_SCL_L; //方便后续的操作;防止意外产生了停止条件
return ack;}
3.4 停止条件
void IIC_Stop(void)
{
IIC_SCL=1;
IIC_SDA_OUT=0;
Delay_nus(5); //SCL置1,SDA置0,随后延时1us,停止条件建立
IIC_SDA_OUT=1; // SDA置1--产生了停止条件
Delay_nus(5); //延时1us,停止条件建立完毕
}
4.IIC编程实例
4.1 At24c02按照字节写操作
//字节写
//addr内部地址(0~255) data待写入的数据 0成功写入 其他数字均为:错误码
uint8_t At24c02_ByteWrite(uint16_t addr,uint8_t data)
{
uint8_t ack=0; //发送开始位
//1.发送开始信号
IIC3_Start( );
//2.主机寻址(设备地址),同时传输读写标识
ack=IIC3_SendByte(AT24C02_ADDR_W);
if(ack)
{ IIC3_Stop( );
return WRITE_ERR1; }
//3.主机寻址(设备内部地址)
ack=IIC3_SendByte(addr);
if(ack)
{ IIC3_Stop( );
return WRITE_ERR2; }
//4.发送数据---并返回应答信号ack,成功返回0,失败返回1
ack=IIC3_SendByte(data);
if(ack)
{ IIC3_Stop( );
return WRITE_ERR3; }//5.发送停止信号
IIC3_Stop( );
Delay_nms(5); //注意这里延时的单位是ms
return 0;
}
4.2 At24c02随机读操作
该部分信息未梳理,先凑活看理解。
//随机读
uint8_t At24c02_RandomRead(uint16_t addr,uint16_t num,uint8_t *p)
{
uint8_t ack=0;
//起始条件
IIC3_Start( );
//发送器件地址 应答信号
ack=IIC3_SendByte(AT24C02_ADDR_W);//发送器件地址+写方向
if(ack)
{
IIC3_Stop( );
return READ_ERR1;
}
//发送数据存放地址 应答信号
//发送内部地址
ack=IIC3_SendByte(addr);
if(ack)
{
IIC3_Stop( );
return READ_ERR2;
}
//起始条件
IIC3_Start( );//重复起始条件
//发送设备地址 应答信号
ack=IIC3_SendByte(AT24C02_ADDR_R);//发送器件地址+读方向
if(ack)
{
IIC3_Stop( );
return READ_ERR3;
}
//循环发送数据
while(num)
{
num--;//还剩下多少个字节没有读
if(num==0)
{
*p=IIC3_ReceByte(1);
break;
}
*p++=IIC3_ReceByte(0);
}
//结束信号
IIC3_Stop( );
return 0;
}