1.IIC通信简介:IIC是一种利用时钟线SCL和数据线SDA进行数据传输的通信协议。IIC的时序图如下所示:
我们需要看懂时序图中开始信号、数据传输、应答信号和停止信号。
开始信号:SCL为高电平时,SDA出现下降沿信号。
数据传输:SCL为低电平时,SDA进行电平转换(对应着数据的二进制表示0/1),然后给SCL一个高电平信号,告诉它开始读数据,读取的数据即为SDA引脚的电平状态。
应答信号:SCL为高电平期间,将SDA线拉低,产生应答信号。主机每传输一个字节的数据后,从机都需要传输一个应答信号,来向主机汇报是否收到了该字节。
停止信号:在SCL为高电平期间,SDA产生一个上升沿信号。
此外,在SCL和SDA电平变换之后,引脚的电平状态需要保持不变一段时间,这个时间是非常短的,并且,产生不同的信号时这个保持不变的时间是不同的,通常可有延时实习(需要精确延时)。具体的信息可以自行查看IIC的数据手册。
主机发送起始信号后,会发出寻址信号。器件的地址信号一般是两种:10位和8位。此处介绍一下8位的地址信号:高7位为地址信号。最后一位R/W为读写选择信号,为0时表示主机接下来对从机进行写操作,为1时表示主机接下来对从机进行读操作。
2.仿真模块主要有:AT89C52、LCD1602(LM016L)、24C02C和排阻(RESPACK-8)。电路图如下:
仿真代码:
#include <reg52.h>
#include <intrins.h>
#define unchar unsigned char
#define uint unsigned int
unchar code dis_table[] = "0123456789";
sbit button = P1^2; //定义按键
sbit lcden = P2^2; //定义lcd的E端口
sbit lcdrw = P2^1; //定义lcd的rw端口
sbit lcdrs = P2^0; //定义lcd的rs端口
sbit SCL = P3^0; //定义SCL时钟线端口
sbit SDA = P3^1; //定义SDA数据线端口
//IIC延时函数,延时必须精确,否则无法准确传输
void Delay()
{
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
}
void DelayMs(uint ms)
{
uint i;
while(ms--)
{
for(i = 110;i > 0;i--)
{
;
}
}
}
//往LCD1602中写命令
void Write_Com(unchar com)
{
lcdrs = 0; //lcdrs为低电平时,选择指令寄存器
P0 = com;
DelayMs(5);
lcden = 1;
DelayMs(5);
lcden = 0;
}
//往LCD1602中写数据
void Write_data(unchar date)
{
lcdrs = 1; //lcdrs为高电平时,选择数据寄存器
P0 = date;
DelayMs(5);
lcden = 1; //当lcden由高电平变为低电平时,LCD1602开始执行命令
DelayMs(5);
lcden = 0;
}
//LCD1602的初始化函数
void init()
{
lcdrw = 0; //lcdrw高电平时为读操作,低电平时为写操作
lcden = 0;
Write_Com(0x38);
Write_Com(0x0c);
Write_Com(0x06);
Write_Com(0x01);
}
//IIC初始化函数
void IIC_init()
{
SCL = 1;
Delay();
SDA = 1;
Delay();
}
//IIC起始信号函数
void IIC_start()
{
//当SCL信号为高电平期间,SDA出现下降沿时,就是起始信号
SDA = 1;
Delay();
SCL = 1;
Delay();
SDA = 0;
Delay();
}
//IIC应答信号函数
void IIC_respons()
{
//在SCL高电平期间,讲SDA线拉低,产生应答信号,表明数据传输成功
unchar i = 0;
SCL = 1;
Delay();
while(SDA == 1 && (i < 255))
{
i ++;
}
SCL = 0;
Delay();
}
//定义停止信号函数
void IIC_stop()
{
//当SCL在高电平时,SDA产生一个上升沿信号为停止信号
SDA = 0;
Delay();
SCL = 1;
Delay();
SDA = 1;
Delay();
}
//写一个字节函数
void IIC_writebyte(unchar date)
{
unchar i,temp;
temp = date;
for(i = 0;i < 8;i++)
{
temp = temp << 1; //溢出的一位自动保存到寄存器CY中
SCL = 0;
Delay();
SDA = CY;
Delay();
SCL = 1;
Delay();
}
SCL = 0;
Delay();
SCL = 1;
Delay();
}
//IIC读一个字节函数
unchar IIC_readbyte()
{
unchar i,Data;
SCL = 0;
Delay();
SDA = 1;
for(i = 0;i < 8;i++)
{
SCL = 1;
Delay();
SDA = 1;
Data = (Data << 1) | SDA;
SCL = 0;
Delay();
}
Delay();
return Data;
}
//往AT24C02的地址中写数据
void Write_add(unchar date,unchar address)
{
IIC_start();
IIC_writebyte(0xa0);
IIC_respons();
IIC_writebyte(address);
IIC_respons();
IIC_writebyte(date);
IIC_respons();
IIC_stop();
}
//从AT24C02的地址中读数据
unchar Read_add(unchar address)
{
unchar date;
IIC_start();
IIC_writebyte(0xa0); //AT24C02的地址为0xa0时是写入
IIC_respons();
IIC_writebyte(address);
IIC_respons();
IIC_start();
IIC_writebyte(0xa1); //AT24C02的地址为0xa1时是读
IIC_respons();
date = IIC_readbyte(); //将数据读取到date中
IIC_stop(); //停止信号
return date;
}
//显示数字函数
void display(unchar date)
{
Write_Com(0x80);
Write_data(dis_table[date/100]); //显示百位
Write_data(dis_table[date%100/10]); //显示十位
Write_data(dis_table[date%10]); //显示个位
}
void main()
{
unchar num,NUM;
init();
IIC_init();
while(1)
{
if(button == 0)
{
DelayMs(10);
if(button == 0)
{
num = Read_add(0x00);
num ++;
Write_add(num,0x00); //将num写入到0x00地址处
}
while(button == 0)
{
;
}
}
NUM = Read_add(0x00); //将0x00处的数据读取出来
display(NUM);
}
}
3.运行结果:
4.总结:IIC在数据传输时,需要在SCL高电平期间使SDA保持稳定,读取数据;在SCL为低电平期间,SDA才能进行高低电平转换。 IIC最主要的是SCL和SDA,通过SCL和SDA组合,完成不同的功能。当然,如何使用这些组合功能需要读懂IIC的数据手册。
本文中只是简单介绍了IIC的基本原理和使用方法,读者如需要进一步理解IIC可查看其数据手册。本文中使用是软件模拟IIC通信,使用软件模拟IIC通信的好处之一是方便移植。同样的IIC代码,移植其他的开发板上只需要修改一下引脚即可。