蓝桥杯-单片机设计与开发-AT24C02模块

yyhtbs电子元器件 2020-11-10 11:50:39 3640阅读 举报

一、AT24C02外设简介与代码编写

AT24C02是一个2K位串行CMOS E2PROM, 内部含有256个8位字节。该模块具有掉电存储功能,是蓝桥杯单片机比赛中常见考点。

//at24c02模块写操作
void write_24c(unsigned char addr,dat)
{
	IIC_Start();
	IIC_SendByte(0xa0);
	IIC_WaitAck();
	IIC_SendByte(addr);
	IIC_WaitAck();
	IIC_SendByte(dat);
	IIC_WaitAck();
	IIC_Stop();	
}
//at24c02模块读操作
unsigned char read_24c(unsigned char addr)
{
	unsigned char temp;
	IIC_Start();
	IIC_SendByte(0xa0);
	IIC_WaitAck();
	IIC_SendByte(addr);
	IIC_WaitAck();
	
	IIC_Start();
	IIC_SendByte(0xa1);
	IIC_WaitAck();
	temp=IIC_RecByte();
	IIC_SendAck(1);
	IIC_Stop();
	
	return temp;
}


经验之谈

AT24C02模块每一个地址只能写入0~256位,所以长数据需要进行拆分为两位数据分别储存,但是在对AT24C02模块进行连续读写时经常会发现数据错误现象,经过排查实验最终发现:AT24C02可以进行连续不间断的读数据操作,但是连续写操作中必须加入延时(鄙人最终采用的是3毫秒的延时,再无数据错误现象)或者在连续的写操作中穿插其他语句以达到延时的目的


二、示例代码

首先将 IIC 总线的底层驱动代码文件正确移植到工程中,在 CT107D 单片机综合训练平台上根据题目描述实现其要求的功能。


1.基础读写操作

系统上电后,从 24C04 存储器的 0x01、0x03 和 0x05这三个地址单元读取数据。

1.将 0x01 单元读出的数据进行加 1 操作,如果计算结果大于 10,复位为 0,然后将这个结果写回 0x01 单元。

2.将 0x03 单元读出的数据进行加 2 操作,如果计算结果大于 20,复位为 0,然后将这个结果写回 0x03 单元。

3.将 0x05 单元读出的数据进行加 3 操作,如果计算结果大于 30,复位为 0,然后将这个结果写回 0x05 单元。

4.将三个单元的当前数据从左到右依次显示在数码管上,各个数字之间用“-”分隔。

//需要在IIC.H中加入AT24C02的读写代码
#include "stc15f2k60s2.h"
#include "iic.h"
#define uchar unsigned char
#define uint unsigned int

uchar code leddata[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xbf};

uchar dat1,dat2,dat3;

void display();
void keydown();


void delay_ms(uint xx)
{
	uint i,j;
	for(i=0;i<xx;i++)
	for(j=0;j<900;j++);
}
void hc(uchar aa)
{
	switch(aa)
	{
		case 0:P2=P2&0x1f;break;
		case 4:P2=P2&0x1f|0x80;break;
		case 5:P2=P2&0x1f|0xa0;break;
		case 6:P2=P2&0x1f|0xc0;break;
		case 7:P2=P2&0x1f|0xe0;break;
	}
}
void disp(uchar wei,shu)
{
	hc(6);P0=0x01<<wei;
	hc(7);P0=leddata[shu];
	delay_ms(3);P0=0xff;
}
///
void init()
{
	hc(5);P0=0x00;
	hc(4);P0=0xff;
	hc(0);
	dat1=read_24c(0x01);
	write_24c(0x01,dat1+1);
	delay_ms(3);
	dat2=read_24c(0x02);
	write_24c(0x02,dat2+2);
	delay_ms(3);
	dat3=read_24c(0x05);
	write_24c(0x05,dat3+3);
}
void main()
{
	init();
	while(1)
	{
		display();
	}
}

/
void display()
{
	disp(0,dat1/10);
	disp(1,dat1%10);
	disp(2,10);
	disp(3,dat2/10);
	disp(4,dat2%10);
	disp(5,10);
	disp(6,dat3/10);
	disp(7,dat3%10);
}

2.按键触发

1.将 J5 的 23 脚短接,把 S4、S5 和 S6 设置为独立按键。用 24C02 存储器的 0x00、0x01 和 0x02 这个三个地址单元分别存储 S4、S5 和 S6 的按下次数。系统上电后,先从 24C04 存储器的 0x00、0x01 和 0x02这三个地址单元读取数据,接着判断读出的数据,如果大于 13,则复位清 0,然后从左到右依次显示在数码管上,各个数字之间用“-”分隔。

2.S4、S5 和 S6 按键每按下一次,就在对应读出的历史按下次数基础上进行加 1 累计,

当累计值大于 13 时,复位清 0。将按键按下的最新次数写入 24C02 的对应单元,并在数码管上刷新显示

//需要在IIC.H中加入AT24C02的读写代码
#include "stc15f2k60s2.h"
#include "iic.h"
#define uchar unsigned char
#define uint unsigned int

uchar code leddata[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xbf};

sbit S6=P3^1;
sbit S5=P3^2;
sbit S4=P3^3;

uchar dat1,dat2,dat3;

void display();
void keydown();

/
void delay_ms(uint xx)
{
	uint i,j;
	for(i=0;i<xx;i++)
	for(j=0;j<900;j++);
}
void hc(uchar aa)
{
	switch(aa)
	{
		case 0:P2=P2&0x1f;break;
		case 4:P2=P2&0x1f|0x80;break;
		case 5:P2=P2&0x1f|0xa0;break;
		case 6:P2=P2&0x1f|0xc0;break;
		case 7:P2=P2&0x1f|0xe0;break;
	}
}
void disp(uchar wei,shu)
{
	hc(6);P0=0x01<<wei;
	hc(7);P0=leddata[shu];
	delay_ms(3);P0=0xff;
}
/
void init()
{
	hc(5);P0=0x00;
	hc(4);P0=0xff;
	hc(0);
	dat1=read_24c(0x09);
	if(dat1==9)
	{
		dat1=read_24c(0x01);
		dat2=read_24c(0x03);
		dat3=read_24c(0x05);
	}
	else
	{
		write_24c(0x09,9);delay_ms(3);
		dat1=0;dat2=0;dat3=0;
		write_24c(0x01,dat1);delay_ms(3);
		write_24c(0x03,dat2);delay_ms(3);
		write_24c(0x05,dat3);
	}
}
void main()
{
	init();
	while(1)
	{
		display();
		keydown();
	}
}
///
void display()
{
	disp(0,dat1/10);
	disp(1,dat1%10);
	disp(2,10);
	disp(3,dat2/10);
	disp(4,dat2%10);
	disp(5,10);
	disp(6,dat3/10);
	disp(7,dat3%10);
}
void keydown()
{
	if(S6==0)
	{
		delay_ms(5);
		if(S6==0)
		{
			dat1++;
			if(dat1>13)
				dat1=0;
			write_24c(0x01,dat1);
			while(S6==0)
			{
				while(S6==0);
				delay_ms(5);
			}
		}
	}
	if(S5==0)
	{
		delay_ms(5);
		if(S5==0)
		{
			dat2++;
			if(dat2>13)
				dat2=0;
			write_24c(0x03,dat2);
			while(S5==0)
			{
				while(S5==0);
				delay_ms(5);
			}
		}
	}	
	if(S4==0)
	{
		delay_ms(5);
		if(S4==0)
		{
			dat3++;
			if(dat3>13)
				dat3=0;
			write_24c(0x05,dat3);
			while(S4==0)
			{
				while(S4==0);
				delay_ms(5);
			}
		}
	}	
}

三、拓展应用和心得

不知道在座的各位大佬有没有遇到过这样一个问题和烦恼,一些含有AT24C02代码的程序题目都是有上电读取数据的功能的要求的,可是如果是一个全新的单片机中运行那么读出来的数据是不是错误的呢,当我没有从小白成长为大白时,我总是写一个带有写操作的代码来让单片机完成初始化的,随后删掉这个写操作代码让单片机正常运行。

这里有两种解决方案:

第一个是上电读取数据并判断该数据是否为FF,如果是则证明该单片机为新的,随即运行初始化程序,否则的话正常读取数据。 但这又有一个问题,在蓝桥杯的比赛中,老师应该是用一个板子来测试程序的,如果你读取的这个地方在先前的几个程序中被写过数据呢?当然老师是可以擦除片内数据的,这样的话这个方法就完全行得通。

第二个是挑选某个地址设定一个特定的数值,单片机上电后判断该地址的数值是否为自己设定的数值,如果不是则证明该单片机为新的,随即初始化并且将该地址写入特定的值,如果是那就正常读取数据。 该方法进一步减少了第一次上电时单片机读取数据出现错误的概率。

关于AT24C02模块上电需要初始化的问题其实题目并无明确要求,在很多的赛题中也没必要去写,大家自行考虑即可。


版权声明:
作者:yyhtbs
链接:https://www.dianziwang.net/p/136a633017db4a.html
来源:电子元器件
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以点击 “举报”


登录 后发表评论
0条评论
还没有人评论过~