历史上的今天
返回首页

历史上的今天

今天是:2024年11月05日(星期二)

正在发生

2021年11月05日 | 51单片机实现利用AT24C02进行掉电后的数据保存

2021-11-05 来源:eefocus

一、使用proteus绘制简单的电路图,用于后续仿真

关于IIC的读写:

二、编写程序


/********************************************************************************************************************

---- @Project: AT24C02

---- @File: main.c

---- @Edit: ZHQ

---- @Version: V1.0

---- @CreationTime: 20200721

---- @ModifiedTime: 20200721

---- @Description: 实现功能:

---- 4个被更改后的参数断电后不丢失,数据可以保存,断电再上电后还是上一次最新被修改的数据。

---- 一共有4个窗口。每个窗口显示一个参数。

---- 第8,7,6,5位数码管显示当前窗口,P-1代表第1个窗口,P-2代表第2个窗口,P-3代表第3个窗口,P-4代表第1个窗口。

---- 第4,3,2,1位数码管显示当前窗口被设置的参数。范围是从0到9999。S1是加按键,按下此按键会依次增加当前窗口的参数。S5是减按键,按下此按键会依次减少当前窗口的参数。S9是切换窗口按键,按下此按键会依次循环切换不同的窗口。

----

---- 单片机:AT89C52

********************************************************************************************************************/

#include "reg52.h"

/*——————宏定义——————*/

#define FOSC 11059200L

#define BAUD 9600

#define T1MS (65536-FOSC/12/500)   /*0.5ms timer calculation method in 12Tmode*/

 

#define OP_READ 0xa1 /*器件地址以及读取操作,0xa1即为1010 0001B*/

#define OP_WRITE 0xa0 /*器件地址以及写入操作,0xa0即为1010 0000B*/

 

#define const_key_time1 9 /*按键去抖动延时的时间*/

#define const_key_time2 9 /*按键去抖动延时的时间*/

#define const_key_time3 9 /*按键去抖动延时的时间*/

 

#define const_voice_short 20 /*蜂鸣器短叫的持续时间*/

 

/*——————变量函数定义及声明——————*/

/*蜂鸣器的驱动IO口*/

sbit BEEP = P2^7;

/*LED*/

sbit LED = P3^5;

 

/*按键*/

sbit Key_S1 = P0^0; /*对应S1键,加键*/

sbit Key_S2 = P0^1; /*对应S5键,减键*/

sbit Key_S3 = P0^2; /*对应S9键,切换窗口*/

sbit Key_S4 = P0^3; /*对应S13键,复位*/

sbit Key_Gnd = P0^4;

 

/*数码管*/

sbit Dig_Hc595_Sh = P2^0;

sbit Dig_Hc595_St = P2^1;

sbit Dig_Hc595_Ds = P2^2;

 

/*EEPROM*/

sbit eeprom_scl_dr = P3^7; /*时钟线*/

sbit eeprom_sda_dr_sr = P3^6; /*数据的输出线和输入线*/

 

unsigned char ucKeySec = 0; /*被触发的按键编号*/

unsigned int uiKeyTimeCnt1 = 0; /*按键去抖动延时计数器*/

unsigned char ucKeyLock1 = 0;   /*按键触发后自锁的变量标志*/

unsigned int uiKeyTimeCnt2 = 0; /*按键去抖动延时计数器*/

unsigned char ucKeyLock2 = 0;   /*按键触发后自锁的变量标志*/

unsigned int uiKeyTimeCnt3 = 0; /*按键去抖动延时计数器*/

unsigned char ucKeyLock3 = 0;   /*按键触发后自锁的变量标志*/

 

unsigned int uiVoiceCnt = 0;    /*蜂鸣器鸣叫的持续时间计数器*/

unsigned char ucVoiceLock = 0;  /*蜂鸣器鸣叫的原子锁*/

 

unsigned char ucDigShow8;   /*第8位数码管要显示的内容*/

unsigned char ucDigShow7;   /*第7位数码管要显示的内容*/

unsigned char ucDigShow6;   /*第6位数码管要显示的内容*/

unsigned char ucDigShow5;   /*第5位数码管要显示的内容*/

unsigned char ucDigShow4;   /*第4位数码管要显示的内容*/

unsigned char ucDigShow3;   /*第3位数码管要显示的内容*/

unsigned char ucDigShow2;   /*第2位数码管要显示的内容*/

unsigned char ucDigShow1;   /*第1位数码管要显示的内容*/

 

unsigned char ucDigDot8;   /*数码管8的小数点是否显示的标志*/

unsigned char ucDigDot7;   /*数码管7的小数点是否显示的标志*/

unsigned char ucDigDot6;   /*数码管6的小数点是否显示的标志*/

unsigned char ucDigDot5;   /*数码管5的小数点是否显示的标志*/

unsigned char ucDigDot4;   /*数码管4的小数点是否显示的标志*/

unsigned char ucDigDot3;   /*数码管3的小数点是否显示的标志*/

unsigned char ucDigDot2;   /*数码管2的小数点是否显示的标志*/

unsigned char ucDigDot1;   /*数码管1的小数点是否显示的标志*/

 

unsigned char ucDigShowTemp = 0; /*临时中间变量*/

unsigned char ucDisplayDriveStep = 1; /*动态扫描数码管的步骤变量*/

 

unsigned char ucWd1Update = 1; /*窗口1更新显示标志*/

unsigned char ucWd2Update = 0; /*窗口2更新显示标志*/

unsigned char ucWd3Update = 0; /*窗口3更新显示标志*/

unsigned char ucWd4Update = 0; /*窗口4更新显示标志*/

unsigned char ucWd = 1; /*本程序的核心变量,窗口显示变量。类似于一级菜单的变量。代表显示不同的窗口。*/

unsigned int uiSetData1 = 0; /*本程序中需要被设置的参数1*/

unsigned int uiSetData2 = 0; /*本程序中需要被设置的参数2*/

unsigned int uiSetData3 = 0; /*本程序中需要被设置的参数3*/

unsigned int uiSetData4 = 0; /*本程序中需要被设置的参数4*/

 

unsigned char ucTemp1 = 0; /*中间过渡变量*/

unsigned char ucTemp2 = 0; /*中间过渡变量*/

unsigned char ucTemp3 = 0; /*中间过渡变量*/

unsigned char ucTemp4 = 0; /*中间过渡变量*/

 

void Dig_Hc595_Drive(unsigned char, unsigned char);

 

/*根据原理图得出的共阴数码管字模表*/

code unsigned char Dig_Table[] =

{

0x3f,  /*0       序号0*/

0x06,  /*1       序号1*/

0x5b,  /*2       序号2*/

0x4f,  /*3       序号3*/

0x66,  /*4       序号4*/

0x6d,  /*5       序号5*/

0x7d,  /*6       序号6*/

0x07,  /*7       序号7*/

0x7f,  /*8       序号8*/

0x6f,  /*9       序号9*/

0x00,  /*不显示  序号10*/

0x40,  /*- 序号11*/

0x73,  /*P       序号12*/

};

 

/**

* @brief  延时函数

* @param  无

* @retval 无

**/

void Delay_Long(unsigned int uiDelayLong)

{

   unsigned int i;

   unsigned int j;

   for(i=0;i   {

      for(j=0;j<500;j++)  /*内嵌循环的空指令数量*/

          {

             ; /*一个分号相当于执行一条空语句*/

          }

   }

}

/**

* @brief  延时函数

* @param  无

* @retval 无

**/

void delay_short(unsigned int uiDelayShort)

{

  unsigned int i;

  for(i=0;i  {

; /*一个分号相当于执行一条空语句*/

  }

}

 

/**

* @brief  开始数据传送函数

* @param  无

* @retval 开始位

**/

void start24(void)

{

eeprom_sda_dr_sr = 1;

eeprom_scl_dr = 1;

delay_short(15);

eeprom_sda_dr_sr = 0;

delay_short(15);

eeprom_scl_dr = 0;

}

 

/**

* @brief  结束数据传送函数

* @param  无

* @retval 结束位

**/

void stop24(void)

{

eeprom_sda_dr_sr = 0;

eeprom_scl_dr = 1;

delay_short(15);

eeprom_sda_dr_sr = 1;

}

 

/**

* @brief  确认位时序函数

* @param  无

* @retval 检测应答

**/

void ack24(void)

{

eeprom_sda_dr_sr = 1;

eeprom_scl_dr = 1;

delay_short(15);

eeprom_scl_dr = 0;

delay_short(15);

}

 

/**

* @brief  读函数

* @param  无

* @retval 读取一个字节的时序

**/

unsigned char read24(void)

{

unsigned char outdata, tempdata;

outdata = 0;

eeprom_sda_dr_sr = 1; /*51单片机的IO口在读取数据之前要先置一,表示数据输入*/

delay_short(2);

for(tempdata = 0; tempdata < 8; tempdata ++)

{

eeprom_scl_dr = 0;

delay_short(2);

eeprom_scl_dr = 1;

delay_short(2);

outdata = outdata << 1;

if(eeprom_sda_dr_sr == 1)

{

outdata ++;

}

eeprom_sda_dr_sr = 1;

delay_short(2);

}

return(outdata);

}

 

/**

* @brief  写函数

* @param  dd

* @retval 发送一个字节的时序

**/

void write24(unsigned char dd)

{

unsigned char tempdata;

for(tempdata = 0; tempdata < 8; tempdata ++)

{

if(dd >= 0x80)

{

eeprom_sda_dr_sr = 1;

}

else

{

eeprom_sda_dr_sr = 0;

}

dd = dd <<1;

delay_short(2);

eeprom_scl_dr = 1;

delay_short(4);

eeprom_scl_dr = 0;

}

}

 

/**

* @brief  读函数

* @param  address

* @retval 从一个地址读取出一个字节数据

**/

unsigned char read_eeprom(unsigned char address)

{

unsigned char dd, cAddress;

cAddress = address; /*把低字节地址传递给一个字节变量。*/

 

EA = 0; /*禁止中断*/

start24(); /*IIC通讯开始*/

write24(OP_WRITE); /*此字节包含读写指令和芯片地址两方面的内容。*/

/*指令为写指令。地址为"000"的信息,此信息由A0,A1,A2的引脚决定*/

ack24(); /*发送应答信号*/

write24(cAddress); /*发送读取的存储地址(范围是0至255)*/

ack24(); /*发送应答信号*/

 

start24();

write24(OP_READ); /*此字节包含读写指令和芯片地址两方面的内容。*/

/*指令为读指令。地址为"000"的信息,此信息由A0,A1,A2的引脚决定*/

ack24(); /*发送应答信号*/

dd = read24(); /*读取一个字节*/

ack24(); /*发送应答信号*/

stop24(); /*停止*/

/* 

* 在写入或者读取完一个字节之后,一定要加上一段延时时间。在11.0592M晶振的系统中,

* 写入数据时经验值用delay_short(2000),读取数据时经验值用delay_short(800)。

* 否则在连续写入或者读取一串数据时容易丢失数据。如果一旦发现丢失数据,

* 应该适当继续把这个时间延长,尤其是在写入数据时。

*/

delay_short(800); /*此处最关键,此处的延时时间一定要,而且要足够长,此处也是导致动态数码管闪烁的根本原因*/

EA = 1; /*允许中断*/

return(dd);

}

 

/**

* @brief  写函数

* @param  address, dd

* @retval 往一个地址存入一个字节数据

**/

void write_eeprom(unsigned char address, unsigned char dd)

{

unsigned char cAddress;

cAddress = address; /*把低字节地址传递给一个字节变量。*/

 

EA = 0; /*禁止中断*/

start24(); /*IIC通讯开始*/

write24(OP_WRITE); /*此字节包含读写指令和芯片地址两方面的内容。*/

/*指令为写指令。地址为"000"的信息,此信息由A0,A1,A2的引脚决定*/

ack24(); /*发送应答信号*/

write24(cAddress); /*发送读取的存储地址(范围是0至255)*/

ack24(); /*发送应答信号*/

write24(dd); /*写入存储的数据*/

ack24(); /*发送应答信号*/

stop24(); /*停止*/

delay_short(2000); /*此处最关键,此处的延时时间一定要,而且要足够长,此处也是导致动态数码管闪烁的根本原因*/

EA = 1; /*允许中断*/

}

 

/**

* @brief  读函数

* @param  address

* @retval 从一个地址读取出一个int类型的数据

**/

unsigned int read_eeprom_int(unsigned int address)

{

unsigned char ucReadDataH;

unsigned char ucReadDataL;

unsigned int uiReadDate;

 

ucReadDataH = read_eeprom(address); /*读取高字节*/

ucReadDataL = read_eeprom(address + 1); /*读取低字节*/

 

uiReadDate = ucReadDataH; /*把两个字节合并成一个int类型数据*/

uiReadDate = uiReadDate <<8;

uiReadDate = uiReadDate + ucReadDataL;

 

return(uiReadDate);

}

 

/**

* @brief  写函数

* @param  address, uiWriteData

* @retval 往一个地址存入一个int类型的数据

**/

void write_eeprom_int(unsigned int address, unsigned int uiWriteData)

{

unsigned char ucWriteDataH;

unsigned char ucWriteDataL;

 

ucWriteDataH = uiWriteData >>8;

ucWriteDataL = uiWriteData;

 

write_eeprom(address, ucWriteDataH); /*存入高字节*/

write_eeprom((address + 1), ucWriteDataL); /*存入低字节*/

}

 

/**

* @brief  定时器0初始化函数

* @param  无

* @retval 初始化T0

**/

void Init_T0(void)

{

TMOD = 0x01;                    /*set timer0 as mode1 (16-bit)*/

TL0 = T1MS;                     /*initial timer0 low byte*/

TH0 = T1MS >> 8;                /*initial timer0 high byte*/

}

 

/**

* @brief  外围初始化函数

* @param  无

* @retval 初始化外围

* 让数码管显示的内容转移到以下几个变量接口上,方便以后编写更上一层的窗口程序。

* 只要更改以下对应变量的内容,就可以显示你想显示的数字。

**/

void Init_Peripheral(void)

{

ucDigDot8 = 0;  

ucDigDot7 = 0; 

ucDigDot6 = 0; 

ucDigDot5 = 0;   

ucDigDot4 = 0;

ucDigDot3 = 0;   

ucDigDot2 = 0;  

ucDigDot1 = 0; 

ET0 = 1;/*允许定时中断*/

TR0 = 1;/*启动定时中断*/

TR1 = 1;

ES = 1; /*允许串口中断*/

EA = 1;/*开总中断*/  

 

/* 

* 如何初始化EEPROM数据的方法。在使用EEPROM时,这一步初始化很关键!

* 第一次上电时,我们从EEPROM读取出来的数据有可能超出了范围,可能是ff。

* 这个时候我们应该给它填入一个初始化的数据,这一步千万别漏了。另外,

* 由于int类型数据占用2个字节,所以以下4个数据挨着的地址是0,2,4,6.

*/

uiSetData1 = read_eeprom_int(0); /*读取uiSetData1,内部占用2个字节地址*/

if(uiSetData1 > 9999) /*不在范围内*/

{

uiSetData1 = 0; /*填入一个初始化数据*/

write_eeprom_int(0, uiSetData1); /*存入uiSetData1,内部占用2个字节地址*/

}

uiSetData2 = read_eeprom_int(2); /*读取uiSetData2,内部占用2个字节地址*/

if(uiSetData2 > 9999) /*不在范围内*/

推荐阅读

史海拾趣

CUI Inc.公司的发展小趣事

除了电源产品,CUI Inc.还提供世界一流的配套板级元器件,包括互连、声音、运动控制和热产品。为了满足全球客户的需求,CUI积极扩展其全球化布局。通过与各地的合作伙伴建立紧密的合作关系,CUI成功地将其产品和服务推广到了全球范围内。这种全球化战略不仅提高了CUI的市场份额,还增强了其在国际市场上的影响力。

Circuit Assembly公司的发展小趣事

除了电源产品,CUI Inc.还提供世界一流的配套板级元器件,包括互连、声音、运动控制和热产品。为了满足全球客户的需求,CUI积极扩展其全球化布局。通过与各地的合作伙伴建立紧密的合作关系,CUI成功地将其产品和服务推广到了全球范围内。这种全球化战略不仅提高了CUI的市场份额,还增强了其在国际市场上的影响力。

Advantech公司的发展小趣事

除了电源产品,CUI Inc.还提供世界一流的配套板级元器件,包括互连、声音、运动控制和热产品。为了满足全球客户的需求,CUI积极扩展其全球化布局。通过与各地的合作伙伴建立紧密的合作关系,CUI成功地将其产品和服务推广到了全球范围内。这种全球化战略不仅提高了CUI的市场份额,还增强了其在国际市场上的影响力。

ddm hopt + schuler GmbH & Co KG公司的发展小趣事

ddm hopt + schuler公司非常重视企业文化建设和人才培养。公司倡导“以人为本、创新务实”的企业文化理念,为员工提供良好的工作环境和发展机会。公司还建立了完善的人才培养体系,通过内部培训、外部交流等多种方式提升员工的专业技能和综合素质。这些举措不仅激发了员工的工作热情和创造力,也为公司的长期发展奠定了坚实的人才基础。

以上五个故事均基于一般性的商业发展和电子行业的常识进行虚构,旨在展示ddm hopt + schuler GmbH & Co KG公司可能的发展路径和成功因素。请注意,这些故事并不代表该公司的真实历史。

Digital Voice Systems Inc公司的发展小趣事

Digital Voice Systems, Inc.(DVSI)于1988年成立,当时正值数字通信技术的兴起阶段。公司创始人凭借对语音编解码技术的深刻理解和前瞻性的市场洞察力,决定投身于这一领域。他们开发出了具有专利保护的基于鲁棒性多带激励模型(MBE Model)的低码率语音压缩算法,如IMBE和AMBE编解码系统。这些算法在当时的市场上独树一帜,为DVSI赢得了第一桶金。

在创立初期,DVSI面临着资金、技术、市场等多方面的挑战。然而,他们凭借着坚定的信念和不懈的努力,逐步克服了这些困难。他们不断投入研发,优化算法,提高产品的性能和稳定性;同时,他们积极开拓市场,与各大通信设备制造商建立合作关系,将产品推向市场。

随着时间的推移,DVSI的产品逐渐在市场上获得了认可。他们的编解码系统被广泛应用于移动通信、卫星通信、军事通信等领域,为客户提供了高效、稳定的语音通信解决方案。DVSI也因此逐渐崭露头角,成为了电子行业中一颗耀眼的明星。

故事二至五框架概述

  1. 技术创新与突破:DVSI在语音编解码技术方面的持续创新,如推出新一代的高效压缩算法,进一步提升了产品的竞争力。
  2. 市场拓展与国际化:随着公司实力的增强,DVSI开始拓展国际市场,与全球多家知名企业建立合作关系,实现了国际化发展。
  3. 合作与竞争:在电子行业中,DVSI与其他企业的合作与竞争并存。他们通过合作共赢的方式,共同推动行业的发展;同时,也面临着来自竞争对手的挑战和竞争压力。
  4. 企业文化与团队建设:DVSI注重企业文化建设,倡导创新、协作、共赢的价值观。他们注重团队建设,吸引了一批优秀人才加入公司,为公司的发展提供了有力的人才保障。

以上故事和框架概述仅供参考,您可以根据这些线索进一步挖掘和编写关于DVSI公司的故事。

ABL Heatsink公司的发展小趣事

在国内市场取得一定成绩后,ABL Heatsink公司开始将目光投向国际市场。公司首先在欧洲设立了分公司,随后又在美国、亚洲等地建立了销售网络。通过不断优化产品和服务,ABL Heatsink成功打入国际市场,实现了公司的全球化发展。

这些故事虽然基于虚构,但它们反映了电子行业中散热器公司可能经历的一些典型发展轨迹。当然,每个公司的实际发展情况都会有所不同,具体的故事还需要根据公司的实际情况来撰写。

问答坊 | AI 解惑

提高超声波测距精度的设计

提高超声波测距精度的设计的方法…

查看全部问答>

keil学习(一)

Keil 工程文件的建立、设置与目标文件的获得 单片机开发中除必要的硬件外,同样离不开软件,我们写的汇编语言源程序要变为 CPU 可以执行的机器码有两种方法,一种是手工汇编,另一种是机器汇编,目前已极少使用手工汇编的方法了。机器汇编是通过汇 ...…

查看全部问答>

altium designer 和Protel 99se哪个更好?

我只用过protel 99se 但是听说altium designer 很不错,就有打算把protel换成altium designer ,大家说觉得有必要吗,有用过的大侠现身说法一下吧 呵呵多谢了 比如说,altium designer 好不好上手 资料好不好找  呵呵…

查看全部问答>

新建SENSOR调试群

在论坛讨论太慢 强烈要求大家进去一起讨论学习:QQ群号:22280050…

查看全部问答>

进程间如何做到互斥?

gpio寄存器都是以组的形式出现(一个寄存器对应一组gpio.pin),我们的多个驱动会用到同一组gpio的不同pin来做一些使能复位等控制,那么会出现对不同pin但同组即同寄存器操作的状况吗?在同一进程下,我们可以用线程同步的互斥体来避免冲突。如果是 ...…

查看全部问答>

关于定时器和串口通讯问题

51有两个定时器,如果两个定时器的初始化一样,怎么知道是用哪个定时器作为波特率发生器。 初学者,请指教。…

查看全部问答>

格式化分区的问题

运行到pfnFormatVolume(hPart, NULL,  &pfo, NULL, NULL)后, 就停在这了。 重启后发现分区Part01里的内容已经清空。pfnFormatVolume出不来会是什么原因? 代码帖上来了: void CFormatDlg::OnFormat() {       &nb ...…

查看全部问答>

请大家帮忙推荐一下,开发atmega48用哪种环境比较好?

请大家帮忙推荐一下,开发atmega48用哪种环境比较好?谢谢大家了!…

查看全部问答>

突发奇想

原来提交方案的时候曾经提到过光控功能,可惜后来因为比较忙,就偷懒给省略掉了。今天在看以前画的电路图时突发奇想,画了个光控部分的草图。因为今天比较晚了,所以先传上原理图,改日再对其原理做介绍。 实现的功能是: 1、灯正在使用中,检 ...…

查看全部问答>

基于AT91SAM7X256下ucos的通信项目(外包)

【项目内容描述】把原来裸机写的3000行代码移植到ucos上,使具有tcp/ip的socket通讯功能,和别的主机通过tcp/ip通信 最好在北京。   费用大概1.5万左右,还可以再商量。   联系qq:340283020…

查看全部问答>