1.储存器介绍
RAM(random access memory):随机访问存储器/随机存储器
特点:存储速度快并且掉电就丢失数据。
一、SRAM(static RAM,静态RAM):
一般用于电脑CPU高速缓存,但是容量较小,且成本较高。
二、DRAM(dynamic RAM,动态RAM):
利用电容进行存储数据(充完电时为高电平,放完电为低电平)。因为集成度很高,容值特别小。且因为漏电原因,需要配一个扫描电路,补上因为漏电丢失的电能(动态刷新)。但DRAM成本比SRAM更低,容量更大。如我们平常见到的电脑的内存条,手机的运行内存,都是动态RAM。
ROM(read only memory):只读存储器
特点:掉电不丢失数据并且存储较慢
一、Mask ROM(掩膜ROM):
仅依靠电路存储数据,且只能读不能写(即 烧录 在Mask ROM中的资料永远无法做修改)——ROM因此得名(read only memory)。
二、PROM(Program ROM,可编程ROM):
仅能写入一次数据,之后数据永远无法更改。(区别于Mask ROM连写都没法写)
三、EPROM(可擦除可编程ROM):
既可以写入,又可以擦除数据(但需要紫外线照射大约30分钟)。
EPROM中间存在一个洞,里面是直接裸露的芯片,上方贴了层黑纸,当需要擦除时直接弄开照射即可。
四、E2PROM(电可擦除可编程ROM):
实现了快速擦除(几毫秒)的功能,但容量较小。也就是相当于开发板上的ROM。
五、Flash(闪存):
应用范围广(U盘、内存卡、电脑的固态硬盘、手机的存储容量等)
六、硬盘、软盘、光盘等:
依靠磁介质存储信息。早期电脑存在软盘(A盘与B盘),但容量较小,后面被淘汰了。之后依靠硬盘存储(即C盘开始)。光盘依靠光信号存储。
2.储存器简化模型
我们先假设横向的为地址总线,纵向的为数据总线。
接上左上角第一行为1(高电平),然后左前三列接上1(高电平),那么数据总线就会接收到前三位均为1,后面的行和列因为没有接(可以视为0效果),于是就可以产生对应点数据。然后给第二行为1,给其他列接1即可实现数据存储。后面的以此类推即可。(也就是先第一行为1,存储器存储数据到第一行;然后让第二行为1,存储器存储数据到第二行,以此类推。)
Mask Rom
当存储断开时,即为左边的情况(交错时即为断开,相当于0)。
当存储接通时,即为右边的情况,而使用二极管是为了防止上一行的数据通过电路来对下一行产生干扰。
通过上面的显示,可以想到,当给对应行接通(为1)时,有二极管的列就会被传为1,没有接二极管的列就会保持断开(为0),因此就能做到存储数据的作用
因此,一旦做好芯片,由于二极管已经固定的缘故,也就没法进行更改或者写入的操作。
PRom
所有的电路节点都做成左边的状态,这样无论哪头都是无法通过的;
但是蓝色的二极管在反向电压超过一定数值时,会被击穿即二极管失效,因此只需要给行一个较高的电压,列为0,那么二极管就会被击穿,成为右边的状态,即为接通。
因为蓝色的二极管被击穿后,就没有用了,因此无法做二次写入——相当于烧毁二极管。
因为每次只能给一行连接为1(高电平),因此地址总线可以接一个138译码器(可以到数码管笔记处了解),来实现减少I/O口的同时,实现存储数据。
3.AT24C02
AT24C02是一种可以实现掉电不丢失的存储器,可用于保存单片机运行时想要永久保存的数据信息,其存储介质是E2PROM,它的通讯接口是I2C,总线容量为256字节(对于目前的单片机够用,且价格便宜)
AT24C02的某些通信协议和DS1302芯片的通信协议有些相似的特点
WP(即本单片机上的WE):图上的写保护接到了GND,因此没有写保护。(如果需要写保护,可以接到I/O口或开关上),本单片机写保护为低电平有效(上面有横线,代表低电平有效)。
A0、A1、A2(即本单片机上的E0、E1、E2):可以接到高电平(1),但这里全部接到GND(为0)了。
SCL、SDA:本单片机直接接的I/O口(因为单片机上每个I/O口都接了上拉电阻)。
4.I2C总线
I2C总线(Inter IC BUS)是由Philips公司开发的一种通用数据总线(通信的一种协议,相当于串口的通信协议)
两根通信线:SCL(Serial Clock)—也可叫SCLK、SDA(Serial Data)
同步(因为有同步的时钟线SCL)、半双工(只有SDA一根线,且负责来回通信),带数据应答(当发送一个字节数据后,要求对方在接受到后需要发送一个应答)
I2C时序结构
由于I2C时序结构总的来讲的话会很复杂,于是我们将其分解成几个模块,最后合起来时对号入座即可。
第一的模块:开始与终止
蓝色方块内的S代表start,红色方块内的P代表stop
开始时
SCL处于高电平状态,SDA处于低电平状态
终止时
SCL处于高电平状态,SDA处于高电平状态
第二个模块:发送与接收
绿色方块内的S代表send,紫色方块的R代表receive
发送时:
当SDA出现交叉时,就是代表从高电平转为低电平或者低电平转为高电平,
而当SCL为高电平(即图中上升)时,SDA不允许有数据变化(即不能出现交叉)
只有SCL为高电平时,才能读取SDA的数据
接收时:
接收数据的原理和发送时的原理基本相同,只有SCL为高电平时,才能接收SDA的数据。
第三个模块:发送与接收应答
发送与接收应答的原理
当SDA为0时,将线的控制权交给主机,说明其应答了;
当SDA为1时,意味着线的控制权依然留着,说明没有应答。
I2C数据帧
在完成起始后,即图中S,,后面发送的第一位必须是从机地址加读写位;
对于发送的第一位,A6~A0为从机地址,最后一位为读写位;
由图可知,读写位中,W上有一横线,说明低电平有效。即读取时置1,写入时置0。(即分配线的控制权,0给主机,1给从机)这里发送将其置0,为写入模式。
而对于A6~A0,前四位为固定端(这里24C02固定端为1010)
后面的A2~A0为可变地址位(本单片机全部接为0),当使用多个24C02芯片时,通过调配后面的可变地址位,实现多个通讯。如果是同一个地址会互相干扰。
每次完成一个字节需要进行一次应答,因为是发送数据,因此接着的是接收应答;RA为0,说明从机应答。
接收指令与发送指令有些类似,
在这里我们可以用通俗的例子来说明一下,在开始,老师会点名A同学起来回答问题,A同学收到,就会起来,以此来应答老师我起来了,然后老师就让A同学回答问题,A同学就收到回答的指令,这就是接收指令的流程。
上图其实是发送与接收的数据帧的结合,不过要注意的是就是只要接收或者发送时都要进行开始的指令,即要说明读写开始的标志。
5.代码
主函数代码
#include <REGX52.H>
#include "Delay.h"
#include "Key.h"
#include "LCD1602.h"
#include "AT24C02.h"
#include "I2C.h"
unsigned char KeyNum;
unsigned int Num;
void main()
{
LCD_Init();
LCD_ShowNum(1,1,Num,5);
while(1)
{
KeyNum=Key();
if(KeyNum==1)
{
Num++;
LCD_ShowNum(1,1,Num,5);
}
if(KeyNum==2)
{
Num--;
LCD_ShowNum(1,1,Num,5);
}
if(KeyNum==3)
{
AT24C02_WriteByte(0,Num%256);
Delay(5);
AT24C02_WriteByte(1,Num/256);
Delay(5);
LCD_ShowString(2,1,"Write OK");
Delay(1000);
LCD_ShowString(2,1," ");
}
if(KeyNum==4)
{
Num=AT24C02_ReadByte(0);
Num|=AT24C02_ReadByte(1)<<8;
LCD_ShowNum(1,1,Num,5);
LCD_ShowString(2,1,"Read OK");
Delay(1000);
LCD_ShowString(2,1," ");
}
}
}
部分模块代码
#include <REGX52.H>
sbit I2C_SCL=P2^1;
sbit I2C_SDA=P2^0;
/**
*@brief I2C开始
*@param 无
*@retval 无
*/
void I2C_Start(void)
{
I2C_SDA=1;
I2C_SCL=1;
I2C_SDA=0;
I2C_SCL=0;
}
/**
*@brief I2C停止
*@param 无
*@retval 无
*/
void I2C_Stop(void)
{
I2C_SDA=0;
I2C_SCL=1;
I2C_SDA=1;
}
/**
*@brief I2C发送一个字节
*@param Byte 要发送的字节
*@retval 无
*/
void I2C_SendByte(unsigned char Byte)
{
unsigned char i;
for(i=0;i<8;i++)
{
I2C_SDA=Byte&(0x80>>i);
I2C_SCL=1;
I2C_SCL=0;
}
}
/**
*@brief I2C接收一个字节
*@param 无
*@retval 接收到的一个字节数据
*/
unsigned char I2C_ReceiveByte(void)
{
unsigned char i,Byte=0x00;
I2C_SDA=1;
for(i=0;i<8;i++)
{
I2C_SCL=1;
if(I2C_SDA){Byte|=(0x80>>i);}
I2C_SCL=0;
}
return Byte;
}
/**
*@brief I2C发送应答
*@param AckBit 应答位,0为应答,1为非应答
*@retval 无
*/
void I2C_SendAck(unsigned char AckBit)
{
I2C_SDA=AckBit;
I2C_SCL=1;
I2C_SCL=0;
}
/**
*@brief I2C接收应答位
*@param 无
*@retval 接收到的应答位,0为应答,1为非应答
*/
unsigned char I2C_ReceiveAck(void)
{
unsigned char AckBit;
I2C_SDA=1;
I2C_SCL=1;
AckBit=I2C_SDA;
I2C_SCL=0;
return AckBit;
}
#include <REGX52.H>
#include "I2C.h"
#define AT24C02_ADDRESS 0xA0
/**
*@brief AT24C02写入一个字节
*@param WordAddress要写入字节的地址
*@param Data要写入的数据
*@retval 无
*/
void AT24C02_WriteByte(unsigned char WordAddress,Data)
{
I2C_Start();
I2C_SendByte(AT24C02_ADDRESS);
I2C_ReceiveAck();
I2C_SendByte(WordAddress);
I2C_ReceiveAck();
I2C_SendByte(Data);
I2C_ReceiveAck();
I2C_Stop();
}
/**
*@brief AT24C02读取一个字节
*@param WordAddress要读出字节的地址
*@retval 读出的数据
*/
unsigned char AT24C02_ReadByte(unsigned char WordAddress)
{
unsigned char Data;
I2C_Start();
I2C_SendByte(AT24C02_ADDRESS);
I2C_ReceiveAck();
I2C_SendByte(WordAddress);
I2C_ReceiveAck();
I2C_Start();
I2C_SendByte(AT24C02_ADDRESS|0x01);
I2C_ReceiveAck();
Data=I2C_ReceiveByte();
I2C_SendAck(1);
I2C_Stop();
return Data;
}