C51—模拟IIC总线实现EEPROM存取数据
- 什么是IIC总线
IIC总线是同步通信的一种特殊形式,具有接线口少、控制简单、器件封装形式小、通信速率高等特点。在主从通信中,可以有多个IIC总线器件同时连接到IIC总线上,所有与IIC兼容的器件都具有标准的接口,通过地址来识别通信对象,使他们可以经由IIC总线互相直接通信。
IIC总线由SDA数据线和SCL时钟线俩条线构成通信线路,既可发送数据也可以接收数据。在CPU和IC之间、IC与IC间都可以双向传播,每个器件都有唯一的地址,这样就可以使信息进行准确的传输。CPU发出的信号分为地址码和数据码俩部分,地址码用来选址,数据码是通信的内容。
总而言之,IIC总线就是器件之间的通信线路,主器件可以通过IIC总线向从器件发送数据,也可以从从器件接收数据。51单片机不具备IIC总线接口,但我们可以通过软件模拟IIC总线的工作时序,只需要正确调用各个函数就能够方便的拓展IIC总线接口器件。
-什么是EEPROM
EEPROM就是一种存储介质,就像ROM、PROM、FLASH储存器等,下面我粗略地介绍一下EEPROM的由来
ROM:只读存储器,其中的内容在工厂中就烧录好了,内容只能读,不能改,所以用户只能读取其中数据,一旦数据出错,ROM就相当于废掉了。
PROM:可编程的ROM,相比于原来的ROM,用户可以将自己所需要的数据写入,但也只有一次机会,数据一旦写入之后就无法修改。
EPROM:可擦除可编程ROM,相比较PROM,此芯片可以重复擦除和写入,虽然它解决了PROM只能写入一次的弊端,但EPROM数据的擦除是通过紫外线的照射来完成的,所以使用此芯片时对紫外线的防护是很重要的。
EEPROM电可擦除可编程ROM,鉴于EPROM擦除数据的不容易,EEPROM应运而生,它是以电子信号来修改其内容的,而且是以byte为最小修改单位的,不需要将数据全部清除来写入,可以实现定向修改数据。而且因为其数据保存具有掉电不丢失的特性备受追捧。
在下的程序就是通过51单片机模拟IIC总线对AT24C02(EEPROM的型号)进行数据的存取和修改。
AT24C02的储存容量为256B一般通过片内子地址寻址对内部256B中任意一个进行读\写操作,其寻址范围为00~FF。
-IIC总线的通信格式
就像串口通讯和红外遥控一样,IIC总线也有一套通信方式。
硬件结构:
先来认识一下IIC总线的硬件结构,SDA是数据线,SCL是时钟线,总线上个器件均采用漏极开路结构与总线相连,因此SDA和SCL都需要上拉电阻,总线在空闲状态保持高电平,一旦某一器件输出低电平,都将使总线的信号变低,所以说各器件的SDA和SCL都是线“与”关系。

数据位有效性:
IIC中数据的传输是通过SDA和SCL进行高低电平变化来完成的,在传送数据时,只有当时钟线SCL为高电平的时候数据线SDA上的电平才有效(即SDA高电平代表1;SDA低电平代表0),所以在SCL为高电平的时候SDA上的数据一定要保持稳定才行,不然数据的传输会很不稳定的。那就是说SCL为低电平的时候SDA怎么变化都没有关系,但SCL为高电平时SDA一定要稳定,可以理解为传输信号的改变是在SCL为低电平的时候进行的(SCL高电平时SDA的改变即为传输信号的改变)。

启始信号、停止信号:
IIC每次进行数据传输时先由主机发出启始信号,代表数据传输的开始,信号格式为:在SCL高电平时期SDA产生下降沿(注意这与数据的传输不同,SCL高电平时器SDA要变化),这样从器件就会检测到该信号并做好准备。
相对应,停止信号时在SCL高电平时期SDA产生上升沿信号。俩种信号的时序图如下
寻址信号:
主机在发出启始信号后,再发出寻址信号,这里介绍一下七位寻址方式。
寻址信号由一个字节(8byte)构成,高七位为地址位,用来确定要进行通信的从器件,最低位是方向位,用来说明是传输数据还是接收数据。每一个从器件都会对比主机发送的地址码和自己的地址,如果安排上了,再判断从器件是接收主机数据还是向主机发送数据。
从器件的地址由固定地址和可编程地址俩部分组成,固定地址用于区别类别,可编程地址用来区别同类从器件。比如AT24C02有四位固定地址和三位可编程地址,三位可编程地址可以有八种表达方式,所以最多可以有八个AT24C02器件接再IIC中。
方向位为0时,主机向从机传输数据;方向位为1时,从机向主机传输数据。
应答信号:
IIC总线协议规定:每传输1字节数据(含地址及命令)后,都要有一个应答信号,以确定数据传送是否被对方收到。应答信号由接收方产生,应答信号的格式为:在SCL为高电平时SDA为低电平(被动的,我们到时候只要检测一下SDA是否被拉低即可),表示数据传输正确,产生应答。但当主机为接收方时,对最后一字节不应答。

(在敲代码时我们一般将应答信号的检验放在数据发送函数中)
模块化设计注解
这里只是挑重要模块进行注解,后面有完整代码。
延时10um函数:
void dy()
{
uint a;
for(a=3;a>0;a--)
;
}
IIC启始函数:
void IICstart()//产生下降沿,启动信号
{
SDA=1;
dy();
SCL=1;
dy();
SDA=0;
dy();
SCL=0;
}
IIC停止函数:
void IICstop()//产生上升沿,停止信号
{
SDA=0;
dy();
SCL=1;
dy();
SDA=1;
dy();
SCL=0;
}
IIC发送一个字节函数:
uchar IICsendbyte(uchar dat)//将dat一个字节的内容发送出去,先发送最高位,并检测应答
{
uint i;
SCL=0
for(i=0;i<8;i++)//8位逐次发送
{
dat<<=1;
SDA=CY;//进行左移操作后最高位自动移入PSW寄存器中的CY位(可以用来zb)
dy();
SCL=1;
dy();
SCL=0;
dy();
}
SDA=1;//拉高SDA电平,准备检测应答信号
dy();
SCL=1;
while(SDA)
{
j++;
if(j>200)//当超过2000umSDA还没被拉低,算应答失败或非应答
{
SDA=0;
dy();
return 0;//发送失败返回0
}
}
SCL=0;
dy();
return 1;//发送成功返回1
}
IIC接收一个字节函数:
uchar IICreadbyte()//读取一个字节数据,并返回
{
uint i;
uchar dat;
for(i=0;i<8;i++)
{
SCL=1;
dy();
dat<<=1;//一定要先移位
dat |=SDA;//与运算
dy();
SCL=0;
dy();
}
return dat;
}
向AT24C02写字节:
void AT24C02write(uchar addr,uchar dat)//往addr地址写入dat数据
{
IICstart();//开始
IICsendbyte(0Xa0);//发送寻址 0Xa0代表寻址AT24C02并且方向位为0,主机向从机传输数据
IICsendbyte(addr);//发送储存地址
IICsendbyte(dat);//发送储存数据
ICCstop();//结束
}
从AT24C02读字节:
uchar AT24C02read(uchar addr)//读取AT24C02中addr位置数据并返回
{
uchar dat;
IICstart();
IICsendbyte(0Xa0);//伪写
IICsendbyte(addr);
IICstart();//开始读取
IICsendbyte(0Xa1);//发送寻址 0Xa0代表寻址AT24C02并且方向位为1,从机向主机传输数据
dat=IICreadbyte(addr);//读取数据
IICstop();
return dat;//返回数据
}
整体代码
在下通过按键k1、k2、k3、k4实现数据在AT14C02中的保存、读取、自增、清零等操作,并通过数码管显示数值(38译码器),亲自测试可以运行。
#include"reg52.h"
#define uint unsigned int
#define uchar unsigned char
uchar num,duan[10]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
sbit SDA=P2^0;
sbit SCL=P2^1;
sbit a=P2^2;
sbit b=P2^3;
sbit c=P2^4;
sbit k1=P1^3;
sbit k2=P1^2;
sbit k3=P1^1;
sbit k4=P1^0;
void delay10us() //时间>4.7um就ok
{
unsigned char a;
for(a=3;a>0;a--);
}
void dy(uint x) //按键时用的延时函数
{
while(x--);
}
void xs() //数码管显示
{
uint i;
for(i=0;i<4;i++)
{
switch(i)
{
case 0:a=0;b=0;c=0;P0=duan[num/1000];break; //千位
case 1:a=1;b=0;c=0;P0=duan[num/100];break; //百位
case 2:a=0;b=1;c=0;P0=duan[num/10%10];break; //十位
case 3:a=1;b=1;c=0;P0=duan[num%10];break; //个位
}
dy(100);
P0=0X00;
}
}
void IICstart() //起始信号
{
SDA=1;
delay10us();
SCL=1;
delay10us();
SDA=0;
delay10us();
}
void IICstop() //停止信号
{
SDA=0;
delay10us();
SCL=1;
delay10us();
SDA=1;
delay10us();
}
uchar IICsendbyte(uchar dat) //发送一个字节的数据
{
uint i,j=0;
SCL=0;
for(i=0;i<8;i++) //从最高位开始发送
{
dat<<=1;
SDA=CY; //左移时最高位会移入PSW寄存器的CY位中
delay10us();
SCL=1;
delay10us();
SCL=0;
delay10us();
}
SDA=1;
delay10us();
SCL=1;
while(SDA) //等待应答 即等待设备把SDA拉低
{
j++;
if(j>200) //如果超过2000um没有应答就算发送失败或非应答 表示接收结束
{
SCL=0;
delay10us();
return 0;
}
}
SCL=0; //不管应答或非应答,SCL都置0
delay10us();
return 1;
}
uchar IICreadbyte() //读一个字节,从最高位开始
{
uint i,dat=0;
SDA=1; //因为下面开始时SCL=1,所以SDA要稳定
delay10us();
for(i=0;i<8;i++)
{
SCL=1;
delay10us();
dat<<=1;
dat |=SDA;
delay10us();
SCL=0;
delay10us();
}
return dat; //返回读到的数据dat(一个字节)
}
void AT24c02write(uchar addr,uchar dat) //往AT24c02的addr地址写入dat数据
{
IICstart();
IICsendbyte(0Xa0); //发送要与之通信的器件地址,此处为AT24c02的地址
IICsendbyte(addr); //发送要写入的内存地址,AT24c02内部可储存256字节,故地址可以为0~255
IICsendbyte(dat); //发送数据
IICstop();
}
uchar AT24c02read(uchar addr) //读取AT24c02中addr地址的值,并返回给函数
{
uchar dat;
IICstart();
IICsendbyte(0Xa0); //伪写
IICsendbyte(addr);
IICstart(); //真正开始读数据
IICsendbyte(0Xa1);
dat=IICreadbyte();
IICstop();
return dat;
}
void key()
{
if(k1==0)
{
dy(1000);
if(k1==0)
{
AT24c02write(1,num);
}
while(!k1); //检测放手
}
if(k2==0)
{
dy(1000) ;
if(k2==0)
{
num=AT24c02read(1);
}
while(!k2); //检测放手
}
if(k3==0)
{
dy(1000);
if(k3==0)
{
num++;
if(num==256)
num=0;
}
while(!k3); //检测放手
}
if(k4==0)
{
dy(1000);
if(k4==0)
{
num=0;
}
while(!k4); //检测放手
}
}
void main()
{
while(1)
{
key();
xs();
}
}
C51—模拟IIC总线实现EEPROM存取数据的更多相关文章
- stm32软件模拟IIC读取PX4FLOW光流传感器数据
这段时间在做全国光电设计大赛,用到了px4的px4flow光流传感器,用软件模拟iic读取数据不定期会导致px4flow死机,查了资料和光流的源码,发现这个光流用了stm32的硬件iic,所以对软件模 ...
- 用I/O口模拟IIC总线协议遇到的一些问题
最近做的一个项目,是基于IIC总线通信的传感器系统.由于另外一个传感器使用的是类IIC协议,而不是标准IIC,所以MCU不能与其通信,最后没有办法,只有通过I/O口模拟的方式实现IIC的总线通信.具体 ...
- 模拟IIC总线多设备挂载(12864OLED屏幕+GY30光照传感器)
最终效果展示 OLED屏幕和GY30光照传感器(BH1750FVI)都连接在一个IIC(I2C)总线上,所以只需要接4根线即可.获取到的光照强度可以在OLED上实时显示并通过串口打印.IIC是IO模拟 ...
- 模拟IIC总线汇编程序软件包
一.概述 为了非常方便地对 I2C 从器件进行快速的.正确的读写操作,我们为此而设计出虚拟 I2C 总线操作平台软件包.本软件包是主方式下的虚拟 I2C 总线软件包,只要用户给子程序提供几个主要的参数 ...
- IIC总线
一. 概述 1. IIC总线是PHILIPS公司推出的一种串口总线,是具备多主机系统所需的包括总线裁决和高低速器件同步功能的高性能串口总线. 2. IIC总线只有两根双向信号线.一根是数据线SDA,一 ...
- 对 IIC 总线的理解、调用函数以及常见面试问题
一.IIC 总线概述: IIC 即Inter-Integrated Circuit(集成电路总线) I2C总线是PHLIPS公司推出的一种串行总线, I2C总线只有两根双向信号线.一根是数据线SDA, ...
- Exynos4412 IIC总线驱动开发(一)—— IIC 基础概念及驱动架构分析
关于Exynos4412 IIC 裸机开发请看 :Exynos4412 裸机开发 —— IIC总线 ,下面回顾下 IIC 基础概念 一.IIC 基础概念 IIC(Inter-Integrated Ci ...
- RT-thread 设备驱动组件之IIC总线设备
本文主要介绍RT-thread中IIC总线设备驱动,涉及到的主要文件有:驱动框架文件(i2c_core.c,i2c_dev.c,i2c-bit-ops.c,i2c_dev.h,i2c.h):底层硬件驱 ...
- 十天学会单片机Day5 IIC总线AT2402芯片(EEPROM)应用
1.采用串行总线技术可以使系统的硬件设计大大简化.系统的体积减小.可靠性提高.同时,系统的更改和扩充极为容易. 常用的串行扩展总线有: IIC (Inter IC BUS)总线.单总线(1-WIRE ...
随机推荐
- centos安装报错:license information (license not accepted)
前言:在最近部署的centos系统发现个问题 出现报错:安装配置完成后,重启虚拟机出现license information (license not accepted) 截图: 解决方案: 在界 ...
- mysql 索引介绍与运用
索引 (1)什么是索引? 是一种提升查询速度的 特殊的存储结构. 它包含了对数据表里的记录的指针,类似于字典的目录. 当我们添加索引时会单独创建一张表来去存储和管理索引,索引比原数据大,会占用更多的资 ...
- asp.net mvc中的路由
[Route] 路由 [Route("~/")] 忽略路由前缀 [Route("person/{id:int}")] 路由内联约束 [Route("h ...
- FA转发地址
1.FA地址诞生背景和作用 FA 是Forwarding Address的简写.FA是ASBR通告的TYPE 5 LSA中的字段,它的作用是告诉OSPF域内的路由器如何能够更快捷地到达LSA 5所通告 ...
- java二叉树的遍历(1)
树(tree)是一种抽象数据类型(ADT),用来模拟具有树状结构性质的数据集合.它是由n(n>0)个有限节点通过连接它们的边组成一个具有层次关系的集合 节点:上图的圆圈,比如A,B,C等都是表示 ...
- C语言:位运算
所谓位运算,就是对一个比特(Bit)位进行操作.在<数据在内存中的存储>一节中讲到,比特(Bit)是一个电子元器件,8个比特构成一个字节(Byte),它已经是粒度最小的可操作单元了.C语言 ...
- Leetcode13. 罗马数字转整数Leetcode14. 最长公共前缀Leetcode15. 三数之和Leetcode16. 最接近的三数之和Leetcode17. 电话号码的字母组合
> 简洁易懂讲清原理,讲不清你来打我~ 输入字符串,输出对应整数 : 基于CellPhoneDB的细胞通讯分析及可视化 (下篇)
在上一篇帖子中,我介绍了CellPhoneDB的原理.实际操作,以及一些值得注意的地方.这一篇继续细胞通讯分析的可视化. 公众号后台回复20210723获取本次演示的测试数据,以及主要的可视化代码. ...
- Java实战:教你如何进行数据库分库分表
摘要:本文通过实际案例,说明如何按日期来对订单数据进行水平分库和分表,实现数据的分布式查询和操作. 本文分享自华为云社区<数据库分库分表Java实战经验总结 丨[绽放吧!数据库]>,作者: ...
- Mysql数据量较大时分页查询优化
据表 collect ( id, title ,info ,vtype) 就这4个字段,其中 title 用定长,info 用text, id 是主键,vtype是int,vtype是索引. 最后co ...