一、 存储器介绍

存储器分类图

1. RAM

这类存储器中的数据都是掉电即失的,例如计算机中的内存就是DRAM,但它们数据读写速度都是要比ROM要快得多的。

  • SRAM:本质是电路,使用电路构成的触发器来存储数据(如JK触发器),因此这种存储器读写数据是最快的,而它们的成本也比较高,一般用作计算机的高速存储器寄存器
  • DRAM:使用电容来存储数据,因为电容存在漏电现象,因此需要每隔一段时间进行扫描重新充电。它们一般用来构成计算机的内存,手机的闪存等。

2. ROM

这类存储器中的数据有着掉电不丢失的特性,但它们读写数据的速度远小于RAM

  • Mask ROM:仅由电路构成,只读的ROM,就是只可读不可写
  • PROM:可以写入数据,但只能写入一次数据
  • EPROM:可读可写
  • ...

二、AT24C02简介

AT24C02是一种可以实现掉电不丢失的存储器,可用于保存单片机运行时想要永久保存的数据信息

  • 存储介质:E2PROM
  • 通讯接口:I2C总线
  • 容量:256字节

电路连接

三、I2C总线和AT24C02数据帧

I2C总线(Inter IC BUS)是由Philips公司开发的一种通用数据总线(通信协议)

  • 两根通信线:SCL(Serial Clock)、SDA(Serial Data)
  • 同步、半双工,带数据应答
  • 通用的I2C总线,可以使各种设备的通信标准统一,对于厂家来说,使用成熟的方案可以缩短芯片设计周期、提高稳定性,对于应用者来说,使用通用的通信协议可以避免学习各种各样的自定义协议,降低了学习和应用的难度

1. 电路规范

  • 所有I2C设备的SCL连在一起SDA连在一起
  • 设备的SCL和SDA均要配置成开漏输出模式
  • SCL和SDA各添加一个上拉电阻,阻值一般为4.7KΩ左右
  • 开漏输出和上拉电阻的共同作用实现了“线与”的功能,此设计主要是为了解决多机通信互相干扰的问题

2. I2C的时序结构

我们可以将通过I2C协议实现主机与从机通信的过程分为以下六个部分这六个部分可以像拼图一样拼凑出所有的通信过程

2.1 发送起始信息

起始条件:SCL高电平期间SDA从高电平切换到低电平,如下图所示:

同时为了使这块拼图可以和其他的部分连接上,我们在发送起始信息(start)之后,也将SCL拉低。

2.2 发送终止信息

终止条件:SCL高电平期间SDA从低电平切换到高电平

同理,为了和其他拼图拼接上,我们得到的SCL原来是低电平的(这是因为后面的4块拼图结束时SCL都是低电平,因此来到终止信息时也是低电平),我们先拉高SCL然后拉高SDA即可发送终止信号了。

2.3 发送一个byte的信息

发送一个byte(字节)信息可以分解为循环发送8个bit 的信息,因此我们只需要知道如何发送一个bit的信息即可。

发送一个bit信息的操作:(1)SCL低电平期间,主机将数据位依次放到SDA线上(高位在前),(2)然后拉高SCL从机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化

即如果我们希望发送0,则:

  1. 在SCL在低电平时,将SDA的电平拉低(置零
  2. 再将SCL的电平拉高,提醒从机读取信息
  3. 过一段时间(等待从机把信息读取完成)后再次将SCL拉低

具体的过程如图所示:

例如在B7时间内,如果SDA为低电平,则发送的数据为0,如果为高电平则发送的数据为1

2.4 接收一个byte的信息

和发送信息类似,我们只需要知道如何接收一个bit的信息,然后只需要循环进行8次即可接收一个字节的信息了。

接收一个bit信息的操作:(1)SCL低电平期间从机将数据位依次放到SDA线上(高位在前),(2)然后拉高SCL(相当于通知从机主机正在读取这个bit的数据),主机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化。(主机在接收之前,需要释放SDA

具体的过程如图所示:

这个过程我个人的理解是:

  1. 释放SDA(拉高电平),将写入数据的主动权交给从机
  2. 从机写入下一位bit到SDA中
  3. 主机拉高SCL,提示从机我正在读取这个bit的数据,你先不要变化
  4. 主机读取完数据,将SCL拉低,实际上这个过程是在告诉从机我已经读完这个bit了,你给我下一个bit的数据吧,然后如果还未满8位则转到第2步继续接受数据,如果满了一个字节则接收结束

对比发送数据和接收数据可以发现,这两个过程非常的相似,只不过(1)发送信息时写入信息的一方是主机,而接收信息时写入数据的是从机,(2)发送信息时拉高和拉低SCL电平是通知从机读取信息,而在接收信息时则是通知从机主机当前正在读取信息。

可以发现单从开始时的SCLSDA的状态是无法区分主机是想发送还是接收信息的,其实接收数据还是发送数据是由后面的时序过程(数据帧)所决定的,不需要起始状态进行区分

2.5 发送应答

在接收完一个字节之后,主机在下一个时钟发送一位数据数据0表示应答数据1表示非应答

其实发送应答的操作就是发送一个bit的操作而已:

2.6 接收应答

在发送完一个字节之后,主机在下一个时钟接收一位数据,判断从机是否应答,数据0表示应答数据1表示非应答(主机在接收之前,需要释放SDA)

和发送应答类似,接收应答的操作也就是接收一个bit数据的操作。

3. I2C数据帧

3.1 发送一帧数据

其过程是(过程中的接收应答省略不写了):

  1. 发送一个开始信号
  2. 发送从机地址(加上写入标记,最后一位为0)
  3. 循环发送字节数据
  4. 发送完后发送结束信号

3.2 接收一帧数据

过程和发送一帧数据的过程非常类似,只是中间的发送字节数据变成了接收字节数据,此外还有地址部分的最后一位为1,代表读取数据。

3.3 先发送后接收数据

4. AT24C02数据帧

AT24C02是使用I2C通讯协议进行通讯的,I2C数据帧相当于是一辆卡车,而AT24C02数据帧则是在原来卡车的基础上装上了特定货物的卡车。

4.1 字节写

WORD ADDRESS处写入数据DATA

4.2 随机读

读出在WORD ADDRESS处的数据DATA

AT24C02的固定地址为1010,可配置地址本开发板上为000,所以SLAVE ADDRESS+W0xA0SLAVE ADDRESS+R0xA1

四、代码实现

1. I2C模块

实现I2C模块,即实现上面介绍的6块拼图。

首先定义出SCLSDA连接的引脚:

sbit I2C_SCL = P2 ^ 1;
sbit I2C_SDA = P2 ^ 0;

1.1 发送起始信息和发送终止信息


void I2C_Start() {
I2C_SDA = 1;
I2C_SCL = 1;
// 在SCL为高电平拉低SDA
I2C_SDA = 0;
I2C_SCL = 0;
} void I2C_Stop() {
I2C_SDA = 0;
// 在SCL为高电平时拉高SDA
I2C_SCL = 1;
I2C_SDA = 1;
}

1.2 发送字节信息和接收字节信息


void I2C_SendByte(unsigned char byte) {
unsigned char i = 0;
for (i = 0; i < 8; i++) {
// 先将一个bit的数据写入到SDA中
I2C_SDA = byte & (0x80 >> i);
// SCL先拉高,后拉低,通知从机接收数据
I2C_SCL = 1;
I2C_SCL = 0;
}
} unsigned char I2C_ReceiveByte() {
unsigned char byte = 0x00;
unsigned char i;
// 先释放SDA(将主动权交给从机)
I2C_SDA = 1; for (i = 0; i < 8; i++) {
// 通知从机主机正在读取数据
I2C_SCL = 1;
// 如果是1则置一,否则默认为0
if (I2C_SDA) {
byte |= (0x80 >> i);
}
// 通知从机这个bit已经读取完毕,可以发送下一个bit
I2C_SCL = 0;
}
return byte;
}

1.3 发送应答和接收应答

void I2C_SendAck(unsigned char AckBit) {
I2C_SDA = AckBit;
// SCL先拉高,后拉低,通知从机接收数据
I2C_SCL = 1;
I2C_SCL = 0;
} unsigned char I2C_ReceiveAck() {
unsigned char AckBit;
// 先释放SDA(将主动权交给从机)
I2C_SDA = 1;
// 通知从机主机正在读取数据
I2C_SCL = 1;
AckBit = I2C_SDA;
// 通知从机这个bit已经读取完毕
I2C_SCL = 0;
return AckBit;
}

2. AT24C02模块

这个模块依赖于I2C模块,即利用I2C发送和接收数据。

首先定义从机地址

// 最后一位为0代表写,即发送数据,为1代表读,即接受数据
#define AT24C02_ADDRESS 0xA0

2.1 字节写

前面已经提到,AT24C02可以存储256个字节的数据,因此我们的数据可以任意0~255号地址的空间进行存储:

void AT24C02_WriteByte(unsigned char WordAddress, unsigned char Byte) {
I2C_Start();
// 从机地址
I2C_SendByte(AT24C02_ADDRESS);
I2C_ReceiveAck();
// 字地址
I2C_SendByte(WordAddress);
I2C_ReceiveAck();
// 发送真正的数据
I2C_SendByte(Byte);
I2C_ReceiveAck(); I2C_Stop();
}

2.2 随机读

读取WordAddress地址中存储的字节信息:

unsigned char AT24C02_ReadByte(unsigned char WordAddress) {
unsigned char Byte;
I2C_Start();
// 从机地址
I2C_SendByte(AT24C02_ADDRESS);
I2C_ReceiveAck();
// 字地址
I2C_SendByte(WordAddress);
I2C_ReceiveAck(); I2C_Start();
// 从机地址,read模式
I2C_SendByte(AT24C02_ADDRESS|0x01);
I2C_ReceiveAck();
// 读取信息
Byte = I2C_ReceiveByte();
I2C_SendAck(1); I2C_Stop();
return Byte;
}

3. 使用AT24C02进行数据存储

我们使用LCD_1602进行显示,第二行显示num数字,当我们单击按钮时:

  • 点击k1,num--
  • 点击k2,num++
  • 点击k3,将num的数据存储到AT24C02中地址为1的空间中
void main() {
unsigned char key, num;
unsigned char storageData;
LCD_Init();
LCD_ShowString(1, 1, "Hello world");
LCD_ShowString(2, 1, "num:");
storageData = AT24C02_ReadByte(1);
num = storageData;
LCD_ShowNum(2, 5, storageData, 3); Timer0_Init(); while (1) {
key = Key();
if (key) {
switch (key) {
case 1:
num--;
break;
case 2:
num++;
break;
case 3:
AT24C02_WriteByte(1, num);
break;
}
}
LCD_ShowNum(2, 5, num, 3);
}
} void Timer0_Routine() interrupt 1 {
static unsigned int T0Count..;
TL0 = 0x18;//设置定时初值
TH0 = 0xFC;//设置定时初值
T0Count++;
if (T0Count >= 20) {
T0Count = 0;
Key_Loop();//每20ms调用一次按键驱动函数
}
}

这样我们每次重启时就可以看到上次存储的数字了。

单片机学习(十一)I2C总线和AT24C02的使用的更多相关文章

  1. I2C总线完全版——I2C总线的结构、工作时序与模拟编程

    I2C总线的结构.工作时序与模拟编程 I2C总线的结构.工作时序与模拟编程I2C总线(Inter Integrated Circuit)是飞利浦公司于上个世纪80年代开发的一种"电路板级&q ...

  2. 自制单片机之六……串行I2C总线E2PROM AT24CXXX的应用

    这一篇介绍I2C存储器的使用.主要是介绍AT24CXX系列器件,它分为两类,主要是通过被存储容量地址来分的,一类是AT24C02-AT24C16,它的存储容量从256字节到2048字节.另一类是AT2 ...

  3. EEPROM读写学习笔记与I2C总线(转)

    reference:https://www.cnblogs.com/uiojhi/p/7565232.html 无论任何电子产品都会涉及到数据的产生与数据的保存,这个数据可能并不是用来长久保存,只是在 ...

  4. EEPROM读写学习笔记与I2C总线(二)

    无论任何电子产品都会涉及到数据的产生与数据的保存,这个数据可能并不是用来长久保存,只是在运行程序才会用到,有些数据体量较大对于获取时效性并不太强,各种各样的数据也就有不同的存储载体,这次在EEPROM ...

  5. 51单片机 | 基于I2C总线的秒表模拟应用

    ———————————————————————————————————————————— 参考地址: http://blog.csdn.net/junyeer/article/details/4648 ...

  6. 基于I2C总线的MPU6050学习笔记

    MPU6050学习笔记 1. 简述 一直想自己做个四轴飞行器,却无从下手,终于狠下决心,拿出尘封已久的MPU6050模块,开始摸索着数据手册分析,一步一步地实现了MPU6050模块的功能,从MPU60 ...

  7. 51单片机I2C总线

    I2C总线是飞利浦公司推出的一种串行总线,所有器件共用两根信号线,实现数据的传输. 总线接口接了上拉电阻,默认为高电平,所以就可以用"当低电平出现"来标记出一种起始信号.我个人把它 ...

  8. C51单片机模拟I2C总线驱动程序设计

    /********************************** I2C总线驱动 ******************************** 模块名:I2C总线驱动 型号:I2C 功能描述 ...

  9. I2C总线协议学习笔记 (转载)

    1.I2C协议   2条双向串行线,一条数据线SDA,一条时钟线SCL.   SDA传输数据是大端传输,每次传输8bit,即一字节.   支持多主控(multimastering),任何时间点只能有一 ...

随机推荐

  1. 深入刨析tomcat 之---第11篇 how tomcat works( 第15章 ) 如何解析web.xml 文件

    writedby 张艳涛 记得当年是学习jsp的时候,写过web.xml中的标签.在之后的springmvc中也是有关于配置mvc 过滤器 和dispatchServlet的标签,之前是看不懂呢!看到 ...

  2. 为ScrollView增加圆角的三种方式,及自定义属性【在Linearlayout中新增ScrollView支持滚动 后续】

    获取圆角的几种方案如下:方案一:通过shape来实现,给scrollView增加背景来实现方案二:通过自定义ScrollView,还要自定义属性,在dispatchDraw中不停的裁剪方案三:用And ...

  3. linux中的防火墙netfilter iptables

    目录 一.Linux防火墙基础 1.1 ptables的表.链结构 1.2 数据包控制的匹配流程 二.编写防火墙规则 1.iptables的安装 2.1 基本语法.控制类型 一般在生产环境中设置网络型 ...

  4. 凯撒密码(Caesar cipher) 详解

    ------------恢复内容开始------------ 最近训练CTF的时候,发现密码学这块的知识不太系统,所以自己接下来会陆陆续续整理出来 就先从古典密码中的凯撒密码说起吧 凯撒密码内容比较简 ...

  5. 基于CAS实现SSO单点登录

    1. 概述 1.1. 什么是SSO? 单点登录( Single Sign-On , 简称 SSO )是目前比较流行的服务于企业业务整合的解决方案之一, SSO 使得在多个应用系统中,用户只需要 登录一 ...

  6. 阿里饿死了么Android面试凉经,两轮面完被虐哭了,怒清购物车。。。卸载饿死了么

    大家应该看过很多分享面试成功的经验,但根据幸存者偏差的理论,也许多看看别人面试失败在哪里,对自己才更有帮助. 最近跟一个朋友聊天,他漫不经心地复习了几个月,就去参加了饿了么面试,第二面结束后,嗯,挂了 ...

  7. docker容器存储

    写在前面 我们在上篇学习了容器网络,对容器网络驱动bridge工作原理做了较为详细的介绍,今天小作文一起看看容器中另一个关键域-存储. 容器的存储可以分为两大类: 一种是与镜像相关的即我们在<d ...

  8. 数据结构与算法-排序(六)堆排序(Heap Sort)

    摘要 堆排序需要用到一种数据结构,大顶堆.大顶堆是一种二叉树结构,本质是父节点的数大于它的左右子节点的数,左右子节点的大小顺序不限制,也就是根节点是最大的值. 这里就是不断的将大顶堆的根节点的元素和尾 ...

  9. 812考试总结(NOIP模拟37)[数列·数对·最小距离·真相]

    前言 考得挺憋屈的... 先是搞了两个半小时的 T1 后来发现假了,又没多想跳了.. 然后一看 T2 这不是队长快跑嘛... 先是根据自己的想法打了一遍(考完之后发现是对的..) 然后回想了一下之前的 ...

  10. nat转换技术,且用且珍惜

    一.NAT转换技术 1.1.NAT技术概述 随着Internet的发展和网络应用的增多,IPv4地址枯竭已经成为制约网络发展的瓶颈.尽管IPv6可以从根本上解决IPv4地址空间不足的问题,但目前众多的 ...