蓝桥杯单片机学习计划之AT24C02
前言
之前的章节我们把单片机外围最基础的一些外设如流水灯、数码管以及内部资源定时器和中断都学习完了,那么这一节开始我们学习比较重要的几个模块,今天先来学习存储模块AT24C02。
一、存储器介绍
存储器,通俗点讲就是能够存储数据的器件,我们都知道计算机只认识二进制,所以存储器里的数据也是以二进制的形式进行存储的,而存储器通常分为随机存储器(RAM)和只读存储器(ROM)两大类,下面我们分别来介绍一下。
1.随机存储器(RAM)
随机存储器是一种可读可写的存储器,它的特点是可以随时快速的向存储器里写入数据或从里面读写数据,但由于断电后随机存储器中数据也会随之丢失,所以随机存储器不宜用来存储需要长期保存的数据。
2.只读存储器(ROM)
只读存储器相对于随机存储器读写速度要慢,而且只能从中读取数据,不能快速地随时修改或重新写入数据,但它最大的特点是断电后数据不会丢失,所以只读存储器可以用来存储需要长期保存的数据,只读存储器又分为以下几类:
1.掩模只读存储器(Mask ROM)
这种存储器在出厂时,内部存储的数据就已经“固化”在里面,无法进行修改。
2.可编程只读存储器(OTPROM)
OTPROM的特点是数据只能写入一次,一经写入,数据就无法修改。
3.可擦除的可编程只读存储器(EPROM)
EPROM的特点是存储的数据可以擦除重写,最早的EPROM是利用紫外线照射进行擦除的。
4.用电信号可擦除的可编程只读存储器(E2PROM)
E2PROM的特点是可直接用电信号擦除,数据可重复修改,今天要讲的AT24C02就属于E2PROM。在这个基础上后来又有了快闪存储器,即“闪存”——FLASH Memory。
二、AT24C02介绍
1.引脚介绍
AT24C02属于E2PROM存储模块,数据掉电后不会丢失。02表示内部有256×8=2K的存储空间,即有256字节地址,每个地址可存储一字节(8位)数据。竞赛板上使用的是SOP-8的封装,其引脚如下图所示:
其中8脚和4脚分别是电源和地引脚;7脚是写保护位引脚,接地允许正常读写,可以看到竞赛板上是接到GND的,这里不做过多介绍;5脚和6脚是用来通信的数据线和时钟线,分别连接到单片机的P21和P20引脚;1、2、3脚是地址输入引脚,在竞赛板上都连接到GND。
AT24C02采用I2C协议进行通讯,所以会有每个AT24C02都会有自己的设备地址,可以根据三个地址脚接入的信号不同选择不同的设备地址,AT24C02的设备地址设置如下图所示:
高四位地址是固定的1010,低四位地址的前三位根据A2、A1、A0三个引脚接入的电平信号设置,最后一位R/W是读写选择位,置0时选择写操作,置1时选择读操作。我们可以从竞赛板原理图上看到,A2——A0三个引脚都是接地的,所以这三位都为0,即前7位地址都已经固定,根据要进行的读写操作设置最后一位读写选择位,即要写入数据时地址为0xA0,读出数据时地址为0xA1。
2.通信方式
AT24C02采用I2C协议进行通信,关于I2C协议这里就不介绍了,简单的来讲解一下AT24C02的通信过程。首先开始通信前,主机需要发送起始信号,一次通信结束时,主机需要发送结束信号。起始信号的目的就在于告诉从机要开始通信了,做好准备。结束信号的目的就在于告诉从机这一次通信结束了。
可以从上面的时序图看到,在SCL时钟线为高电平的时候,SDA数据线发送一个由高到低的跳变,产生起始信号;在SCL时钟线为高电平的时候,SDA数据线发送一个由低到高的跳变,产生结束信号。
然后是通信过程,从下面这张通信时序图当中,我们可以很清楚的看到,在SCL时钟线为低电平的期间,主机可以更改SDA数据线,即可以更改传输数据,当SCL为高电平的时候就不能修改数据了,因为这时候数据要发送出去了,即发送一位数据的过程为:将SCL时钟线置零,根据要发送的位数据更改SDA数据线电平状态,然后将SCL时钟线置1将数据发送给从机,如此循环8次,即发送一字节数据。读数据则是在SCL为高电平期间读取SDA数据线电平状态,然后将SCL线置零准备下一位数据的读取,循环8次,即读一字节数据。
发送完一字节数据,从机会产生一个应答信号,这时候需要主机去读取这个应答信号,如果读到零,则说明数据已经顺利发送过去了,如果读到1,则说明数据发送失败。主机可根据应答信号来判断数据是否发送成功。在读取数据的时候,应答信号是由主机发送的,即主机在读取数据的过程中,如果读完当前一字节数据,下一字节数据不准备读了,那么就可以在当前数据读取完毕后紧接着发送一个非应答信号(1)告诉从机不再接收数据,然后发送一个结束信号就可以了,如果想要继续读取下一个数据,则发送应答信号(0)告诉从机继续接收数据。
那么读写时序都有了,下面我们就来看一下读写数据的完整过程,首先是写入一字节数据:
从上图可以看到,首先主机发送开始信号,告诉从机要准备开始通信了,然后发送1字节的设备地址,因为是向AT24C02写入数据,所以最后一位要选择写操作,即地址字节要发送0xA0,这时候从机会产生一个应答信号,然后主机发送要写入数据的地址,地址范围为0-255,紧接着再发送要写入的数据,最后发送结束信号,这就是一个完整的写数据过程。
再来看读一个字节数据的通信过程:读数据相对于写数据过程稍微复杂一点,首先一样的主机发送起始信号和设备地址,注意这时候还是写操作,设备地址依然为0xA0,然后写入要读取数据的地址,然后再发送一个起始信号和设备地址,注意这时候要读数据了,所以最后一位要选择读操作,即设备地址为0xA1,然后接收读取的数据,因为是单字节读取,所以数据接收完后主机要发送一个非应答信号,告诉从机我读取完这一个数据后面不读了,最后发送结束信号。
读操作比较复杂的在于发送了两次起始信号和设备地址,第一次选择写操作,发送读取数据的地址,是要告诉从机我这次要读取的是这个地址里保存的数据,这样从机就知道了,哦主机要读取的是这个地址的数据,那么下一次主机发送起始信号和读操作的时候,从机就会把之前发送过来要读取数据的地址里保存的数据发送给主机。
特别值得注意的是,从AT24C02中读取数据是不需要时间的,但是写入数据的时候,因为AT24C02要先把原来存储的数据擦除,再将新的数据存入,所以是需要时间的,从下图中可以看到最大写入周期为5ms,所以每次写入完数据,都要延时5-10ms等待数据写入完毕。
三、代码
对于AT24C02这类具有需要通信协议的模块,底层驱动代码官方已经写好了,在赛点数据资源包里面有,我们直接添加这些文件即可。下面的程序实现的是使用AT24C02存储开机次数,每次重新上电,都会记录上电的次数然后通过数码管显示出来。
#include <STC15F2K60S2.H>
#include "iic.h"
unsigned char SEG_Code[]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90};//0-9段码
unsigned char SEG_Buf[8]={0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0};//显示数据缓冲区
void Seg_Display()//数码管扫描函数
{
static unsigned char seg_index=0;//显示数据索引
static unsigned char seg_temp=0x01;//位索引
P2 = (P2&0x1f) | 0xC0; //消隐
P0= 0x00;
P2=0x00;
P2 = (P2&0x1f) | 0xE0;//选通段锁存器
P0=SEG_Buf[seg_index++]; //送入段码
P2=0x00;
P2 = (P2&0x1f) | 0xC0;//选通位锁存器
P0= seg_temp;//送入位码
P2=0x00;
seg_temp<<=1;
if(seg_index>7)
{
seg_index=0;
seg_temp=0x01;
}
}
void Delay10ms() //@12.000MHz
{
unsigned char i, j;
i = 117;
j = 184;
do
{
while (--j);
} while (--i);
}
void main()
{
unsigned char pon_cnt=0;
AUXR &= 0x7F; //定时器时钟12T模式
TMOD &= 0xF0; //设置定时器模式
TL0 = 0x18; //设置定时初值
TH0 = 0xFC; //设置定时初值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0=1;
EA=1;
pon_cnt=AT24C02_Byte_Read(0x00);//读取上次保存的开机次数
pon_cnt+=1;
AT24C02_Byte_Write(0x00,pon_cnt+1);//加1后写入存储模块
Delay10ms();//延时10ms等待写入完毕
while(1)
{
SEG_Buf[0]=SEG_Code[0];
SEG_Buf[1]=SEG_Code[0];
SEG_Buf[2]=SEG_Code[0];
SEG_Buf[3]=SEG_Code[0];
SEG_Buf[4]=SEG_Code[0];
SEG_Buf[5]=SEG_Code[pon_cnt/100];
SEG_Buf[6]=SEG_Code[pon_cnt%100/10];
SEG_Buf[7]=SEG_Code[pon_cnt%10];
}
}
void Timer0_Rountine(void) interrupt 1 //定时器0中断,1ms
{
Seg_Display();//使用定时器进行数码管扫描
}
总结
当然对于AT24C02这个模块,还有多字节写入和多字节读取的方式,这里就不一一介绍了,另外相对于延时等待写入完毕,有更好的方法就是发送设备地址读取应答信号,如果AT24C02还在写入数据,这时候发送设备地址是不会回送应答信号的,因此可以重复获取这个应答信号来判断数据是否写入完毕。那么这一节关于AT24C02的使用就到这里,下一节我们来学习DS18B20温度传感器。