您的位置 首页 元件

第46节:使用AT24C02进行掉电后的数据保存

开场白:一个AT24C02可以存储256个字节,地址范围是(0至255)。利用AT24C02存储数据时,要教会大家六个知识点:第一个:单片机操作AT24C02的

开场白:

一个AT24C02能够存储256个字节,地址规模是(0至255)。运用AT24C02存储数据时,要教会咱们六个知识点:
榜首个:单片机操作AT24C02的通讯进程也便是IIC的通讯进程, IIC通讯进程是一个要求趁热打铁的通讯进程,中心不能被其它中止影响时序犯错,因而在整个通讯进程中应该先封闭总中止,完结之后再开中止。
第二个:在写入或许读取完一个字节之后,一定要加上一段延时时刻。在11.0592M晶振的体系中,写入数据时经验值用delay_short(2000),读取数据时经验值用delay_short(800)。不然在接连写入或许读取一串数据时简单丢掉数据。假如一旦发现丢掉数据,应该恰当持续把这个时刻延伸,尤其是在写入数据时。
第三个:怎么初始化EEPROM数据的办法。体系榜首次上电时,咱们从EEPROM读取出来的数据有或许超出了规模,或许是ff。这个时分咱们应该给它填入一个初始化的数据,这一步千万别漏了。
第四个:在时序中,发送ACK承认信号时,要记得把数据线eeprom_sda_dr_s设置为输入的状况。关于51单片机来说,只要把eeprom_sda_dr_s=1就能够。而关于PIC或许AVR单片机来说,它们都是带方向寄存器的,就不能直接eeprom_sda_dr_s=1,而要直接修正方向寄存器,把它设置为输入状况。在本驱动程序中,我没有对ACK信号进行犯错判别,因为我这么多年一向都是这样用也没呈现过什么问题。
第五个: 提示各位读者在硬件上应该留意的问题,单片机跟AT24C02通讯的2根IO口都要加上一个4.7K左右的上拉电阻。但凡在IIC通讯场合,都要加上拉电阻。AT24C02的WP引脚一定要接地,不然存不进数据。
第六个:旧版的朱兆祺51学习板在硬件上有一个bug,AT24C02的第8个引脚VCC悬空了!!!,读者记得把它飞线连接到5V电源处。新版的朱兆祺51学习板现已改正来了。
具体内容,请看源代码解说。

(1)硬件渠道:
依据朱兆祺51单片机学习板

(2)完结功用:

4个被更改后的参数断电后不丢掉,数据能够保存,断电再上电后仍是上一次最新被修正的数据。
显现和独立按键部分依据第29节的程序来改编,用朱兆祺51单片机学习板中的S1,S5,S9作为独立按键。
一共有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是切换窗口按键,按下此按键会顺次循环切换不同的窗口。
(3)源代码解说如下:
  1. #include “REG52.H”
  2. #define const_voice_short40 //蜂鸣器短叫的持续时刻
  3. #define const_key_time120 //按键去颤动延时的时刻
  4. #define const_key_time220 //按键去颤动延时的时刻
  5. #define const_key_time320 //按键去颤动延时的时刻
  6. void initial_myself(void);
  7. void initial_peripheral(void);
  8. void delay_short(unsigned int uiDelayShort);
  9. void delay_long(unsigned int uiDelaylong);
  10. //驱动数码管的74HC595
  11. void dig_hc595_drive(unsigned char ucDigStatusTemp16_09,unsigned char ucDigStatusTemp08_01);
  12. void display_drive(void); //显现数码管字模的驱动函数
  13. void display_service(void); //显现的窗口菜单服务程序
  14. //驱动LED的74HC595
  15. void hc595_drive(unsigned char ucLedStatusTemp16_09,unsigned char ucLedStatusTemp08_01);
  16. void start24(void);//开端位
  17. void ack24(void);//承认位
  18. void stop24(void);//中止位
  19. unsigned char read24(void);//读取一个字节的时序
  20. void write24(unsigned char dd); //发送一个字节的时序
  21. unsigned char read_eeprom(unsigned int address); //从一个地址读取出一个字节数据
  22. void write_eeprom(unsigned int address,unsigned char dd); //往一个地址存入一个字节数据
  23. unsigned int read_eeprom_int(unsigned int address); //从一个地址读取出一个int类型的数据
  24. void write_eeprom_int(unsigned int address,unsigned int uiWriteData); //往一个地址存入一个int类型的数据
  25. void T0_time(void);//守时中止函数
  26. void key_service(void); //按键服务的应用程序
  27. void key_scan(void);//按键扫描函数 放在守时中止里
  28. sbit key_sr1=P0^0; //对应朱兆祺学习板的S1键
  29. sbit key_sr2=P0^1; //对应朱兆祺学习板的S5键
  30. sbit key_sr3=P0^2; //对应朱兆祺学习板的S9键
  31. sbit key_gnd_dr=P0^4; //模仿独立按键的地GND,因而有必要一向输出低电平
  32. sbit beep_dr=P2^7; //蜂鸣器的驱动IO口
  33. sbit eeprom_scl_dr=P3^7; //时钟线
  34. sbit eeprom_sda_dr_sr=P3^6; //数据的输出线和输入线
  35. sbit dig_hc595_sh_dr=P2^0; //数码管的74HC595程序
  36. sbit dig_hc595_st_dr=P2^1;
  37. sbit dig_hc595_ds_dr=P2^2;
  38. sbit hc595_sh_dr=P2^3; //LED灯的74HC595程序
  39. sbit hc595_st_dr=P2^4;
  40. sbit hc595_ds_dr=P2^5;
  41. unsigned char ucKeySec=0; //被触发的按键编号
  42. unsigned intuiKeyTimeCnt1=0; //按键去颤动延时计数器
  43. unsigned char ucKeyLock1=0; //按键触发后自锁的变量标志
  44. unsigned intuiKeyTimeCnt2=0; //按键去颤动延时计数器
  45. unsigned char ucKeyLock2=0; //按键触发后自锁的变量标志
  46. unsigned intuiKeyTimeCnt3=0; //按键去颤动延时计数器
  47. unsigned char ucKeyLock3=0; //按键触发后自锁的变量标志
  48. unsigned intuiVoiceCnt=0;//蜂鸣器鸣叫的持续时刻计数器
  49. unsigned charucVoiceLock=0;//蜂鸣器鸣叫的原子锁
  50. unsigned char ucDigShow8;//第8位数码管要显现的内容
  51. unsigned char ucDigShow7;//第7位数码管要显现的内容
  52. unsigned char ucDigShow6;//第6位数码管要显现的内容
  53. unsigned char ucDigShow5;//第5位数码管要显现的内容
  54. unsigned char ucDigShow4;//第4位数码管要显现的内容
  55. unsigned char ucDigShow3;//第3位数码管要显现的内容
  56. unsigned char ucDigShow2;//第2位数码管要显现的内容
  57. unsigned char ucDigShow1;//第1位数码管要显现的内容
  58. unsigned char ucDigDot8;//数码管8的小数点是否显现的标志
  59. unsigned char ucDigDot7;//数码管7的小数点是否显现的标志
  60. unsigned char ucDigDot6;//数码管6的小数点是否显现的标志
  61. unsigned char ucDigDot5;//数码管5的小数点是否显现的标志
  62. unsigned char ucDigDot4;//数码管4的小数点是否显现的标志
  63. unsigned char ucDigDot3;//数码管3的小数点是否显现的标志
  64. unsigned char ucDigDot2;//数码管2的小数点是否显现的标志
  65. unsigned char ucDigDot1;//数码管1的小数点是否显现的标志
  66. unsigned char ucDigShowTemp=0; //暂时中心变量
  67. unsigned char ucDisplayDriveStep=1;//动态扫描数码管的进程变量
  68. unsigned char ucWd1Update=1; //窗口1更新显现标志
  69. unsigned char ucWd2Update=0; //窗口2更新显现标志
  70. unsigned char ucWd3Update=0; //窗口3更新显现标志
  71. unsigned char ucWd4Update=0; //窗口4更新显现标志
  72. unsigned char ucWd=1;//本程序的中心变量,窗口显现变量。类似于一级菜单的变量。代表显现不同的窗口。
  73. unsigned intuiSetData1=0;//本程序中需求被设置的参数1
  74. unsigned intuiSetData2=0;//本程序中需求被设置的参数2
  75. unsigned intuiSetData3=0;//本程序中需求被设置的参数3
  76. unsigned intuiSetData4=0;//本程序中需求被设置的参数4
  77. unsigned char ucTemp1=0;//中心过渡变量
  78. unsigned char ucTemp2=0;//中心过渡变量
  79. unsigned char ucTemp3=0;//中心过渡变量
  80. unsigned char ucTemp4=0;//中心过渡变量
  81. //依据原理图得出的共阴数码管字模表
  82. code unsigned char dig_table[]=
  83. {
  84. 0x3f,//0 序号0
  85. 0x06,//1 序号1
  86. 0x5b,//2 序号2
  87. 0x4f,//3 序号3
  88. 0x66,//4 序号4
  89. 0x6d,//5 序号5
  90. 0x7d,//6 序号6
  91. 0x07,//7 序号7
  92. 0x7f,//8 序号8
  93. 0x6f,//9 序号9
  94. 0x00,//无 序号10
  95. 0x40,//- 序号11
  96. 0x73,//P 序号12
  97. };
  98. void main()
  99. {
  100. initial_myself();
  101. delay_long(100);
  102. initial_peripheral();
  103. while(1)
  104. {
  105. key_service(); //按键服务的应用程序
  106. display_service(); //显现的窗口菜单服务程序
  107. }
  108. }
  109. //AT24C02驱动程序
  110. void start24(void)//开端位
  111. {
  112. eeprom_sda_dr_sr=1;
  113. eeprom_scl_dr=1;
  114. delay_short(15);
  115. eeprom_sda_dr_sr=0;
  116. delay_short(15);
  117. eeprom_scl_dr=0;
  118. }
  119. void ack24(void)//承认位时序
  120. {
  121. eeprom_sda_dr_sr=1; //51单片机在读取数据之前要先置一,表明数据输入
  122. eeprom_scl_dr=1;
  123. delay_short(15);
  124. eeprom_scl_dr=0;
  125. delay_short(15);
  126. //在本驱动程序中,我没有对ACK信号进行犯错判别,因为我这么多年一向都是这样用也没呈现过什么问题。
  127. //有爱好的朋友能够自己添加犯错判别,纷歧定非要按我的办法去做。
  128. }
  129. void stop24(void)//中止位
  130. {
  131. eeprom_sda_dr_sr=0;
  132. eeprom_scl_dr=1;
  133. delay_short(15);
  134. eeprom_sda_dr_sr=1;
  135. }
  136. unsigned char read24(void)//读取一个字节的时序
  137. {
  138. unsigned char outdata,tempdata;
  139. outdata=0;
  140. eeprom_sda_dr_sr=1; //51单片机的IO口在读取数据之前要先置一,表明数据输入
  141. delay_short(2);
  142. for(tempdata=0;tempdata<8;tempdata++)
  143. {
  144. eeprom_scl_dr=0;
  145. delay_short(2);
  146. eeprom_scl_dr=1;
  147. delay_short(2);
  148. outdata<<=1;
  149. if(eeprom_sda_dr_sr==1)outdata++;
  150. eeprom_sda_dr_sr=1; //51单片机的IO口在读取数据之前要先置一,表明数据输入
  151. delay_short(2);
  152. }
  153. return(outdata);
  154. }
  155. void write24(unsigned char dd) //发送一个字节的时序
  156. {
  157. unsigned char tempdata;
  158. for(tempdata=0;tempdata<8;tempdata++)
  159. {
  160. if(dd>=0x80)eeprom_sda_dr_sr=1;
  161. else eeprom_sda_dr_sr=0;
  162. dd<<=1;
  163. delay_short(2);
  164. eeprom_scl_dr=1;
  165. delay_short(4);
  166. eeprom_scl_dr=0;
  167. }
  168. }
  169. unsigned char read_eeprom(unsigned int address) //从一个地址读取出一个字节数据
  170. {
  171. unsigned char dd,cAddress;
  172. cAddress=address; //把低字节地址传递给一个字节变量。
  173. /* 注释一:
  174. * IIC通讯进程是一个要求趁热打铁的通讯进程,中心不能被其它中止影响时序犯错,因而
  175. * 在整个通讯进程中应该先封闭总中止,完结之后再开中止。可是,这样就会引起别的一个新
  176. * 问题,假如封闭总中止的时刻太长,会导致动态数码管不能及时均匀的扫描,在操作EEPROM时,
  177. * 数码管就会呈现闪耀的现象,处理这个问题最好的办法便是在做项目中尽量不要用动态扫描数码管
  178. * 的计划,应该用静态显现的计划。那么程序上还有没有改进的办法?有的,下一节我会讲这个问题
  179. * 的改进办法。
  180. */
  181. EA=0; //制止中止
  182. start24(); //IIC通讯开端
  183. write24(0xA0); //此字节包括读写指令和芯片地址两方面的内容。
  184. //指令为写指令。地址为”000″的信息,此信息由A0,A1,A2的引脚决议
  185. ack24(); //发送应对信号
  186. write24(cAddress); //发送读取的存储地址(规模是0至255)
  187. ack24(); //发送应对信号
  188. start24(); //开端
  189. write24(0xA1); //此字节包括读写指令和芯片地址两方面的内容。
  190. //指令为读指令。地址为”000″的信息,此信息由A0,A1,A2的引脚决议
  191. ack24(); //发送应对信号
  192. dd=read24(); //读取一个字节
  193. ack24(); //发送应对信号
  194. stop24();//中止
  195. /* 注释二:
  196. * 在写入或许读取完一个字节之后,一定要加上一段延时时刻。在11.0592M晶振的体系中,
  197. * 写入数据时经验值用delay_short(2000),读取数据时经验值用delay_short(800)。
  198. * 不然在接连写入或许读取一串数据时简单丢掉数据。假如一旦发现丢掉数据,
  199. * 应该恰当持续把这个时刻延伸,尤其是在写入数据时。
  200. */
  201. delay_short(800);//此处最要害,此处的延时时刻一定要,而且要满足长,此处也是导致动态数码管闪耀的根本原因
  202. EA=1; //答应中止
  203. return(dd);
  204. }
  205. void write_eeprom(unsigned int address,unsigned char dd) //往一个地址存入一个字节数据
  206. {
  207. unsigned char cAddress;
  208. cAddress=address; //把低字节地址传递给一个字节变量。
  209. EA=0; //制止中止
  210. start24(); //IIC通讯开端
  211. write24(0xA0); //此字节包括读写指令和芯片地址两方面的内容。
  212. //指令为写指令。地址为”000″的信息,此信息由A0,A1,A2的引脚决议
  213. ack24(); //发送应对信号
  214. write24(cAddress); //发送写入的存储地址(规模是0至255)
  215. ack24(); //发送应对信号
  216. write24(dd);//写入存储的数据
  217. ack24(); //发送应对信号
  218. stop24();//中止
  219. delay_short(2000);//此处最要害,此处的延时时刻一定要,而且要满足长,此处也是导致动态数码管闪耀的根本原因
  220. EA=1; //答应中止
  221. }
  222. unsigned int read_eeprom_int(unsigned int address) //从一个地址读取出一个int类型的数据
  223. {
  224. unsigned char ucReadDataH;
  225. unsigned char ucReadDataL;
  226. unsigned intuiReadDate;
  227. ucReadDataH=read_eeprom(address); //读取高字节
  228. ucReadDataL=read_eeprom(address+1);//读取低字节
  229. uiReadDate=ucReadDataH;//把两个字节合并成一个int类型数据
  230. uiReadDate=uiReadDate<<8;
  231. uiReadDate=uiReadDate+ucReadDataL;
  232. return uiReadDate;
  233. }
  234. void write_eeprom_int(unsigned int address,unsigned int uiWriteData) //往一个地址存入一个int类型的数据
  235. {
  236. unsigned char ucWriteDataH;
  237. unsigned char ucWriteDataL;
  238. ucWriteDataH=uiWriteData>>8;
  239. ucWriteDataL=uiWriteData;
  240. write_eeprom(address,ucWriteDataH); //存入高字节
  241. write_eeprom(address+1,ucWriteDataL); //存入低字节
  242. }
  243. void display_service(void) //显现的窗口菜单服务程序
  244. {
  245. switch(ucWd)//本程序的中心变量,窗口显现变量。类似于一级菜单的变量。代表显现不同的窗口。
  246. {
  247. case 1: //显现P–1窗口的数据
  248. if(ucWd1Update==1)//窗口1要悉数更新显现
  249. {
  250. ucWd1Update=0;//及时清零标志,防止一向进来扫描
  251. ucDigShow8=12;//第8位数码管显现P
  252. ucDigShow7=11;//第7位数码管显现-
  253. ucDigShow6=1; //第6位数码管显现1
  254. ucDigShow5=10;//第5位数码管显现无
  255. //先分化数据
  256. ucTemp4=uiSetData1/1000;
  257. ucTemp3=uiSetData1%1000/100;
  258. ucTemp2=uiSetData1%100/10;
  259. ucTemp1=uiSetData1%10;
  260. //再过渡需求显现的数据到缓冲变量里,让过渡的时刻越短越好
  261. if(uiSetData1<1000)
  262. {
  263. ucDigShow4=10;//假如小于1000,千位显现无
  264. }
  265. else
  266. {
  267. ucDigShow4=ucTemp4;//第4位数码管要显现的内容
  268. }
  269. if(uiSetData1<100)
  270. {
  271. ucDigShow3=10;//假如小于100,百位显现无
  272. }
  273. else
  274. {
  275. ucDigShow3=ucTemp3;//第3位数码管要显现的内容
  276. }
  277. if(uiSetData1<10)
  278. {
  279. ucDigShow2=10;//假如小于10,十位显现无
  280. }
  281. else
  282. {
  283. ucDigShow2=ucTemp2;//第2位数码管要显现的内容
  284. }
  285. ucDigShow1=ucTemp1;//第1位数码管要显现的内容
  286. }
  287. break;
  288. case 2://显现P–2窗口的数据
  289. if(ucWd2Update==1)//窗口2要悉数更新显现
  290. {
  291. ucWd2Update=0;//及时清零标志,防止一向进来扫描
  292. ucDigShow8=12;//第8位数码管显现P
  293. ucDigShow7=11;//第7位数码管显现-
  294. ucDigShow6=2;//第6位数码管显现2
  295. ucDigShow5=10; //第5位数码管显现无
  296. ucTemp4=uiSetData2/1000; //分化数据
  297. ucTemp3=uiSetData2%1000/100;
  298. ucTemp2=uiSetData2%100/10;
  299. ucTemp1=uiSetData2%10;
  300. if(uiSetData2<1000)
  301. {
  302. ucDigShow4=10;//假如小于1000,千位显现无
  303. }
  304. else
  305. {
  306. ucDigShow4=ucTemp4;//第4位数码管要显现的内容
  307. }
  308. if(uiSetData2<100)
  309. {
  310. ucDigShow3=10;//假如小于100,百位显现无
  311. }
  312. else
  313. {
  314. ucDigShow3=ucTemp3;//第3位数码管要显现的内容
  315. }
  316. if(uiSetData2<10)
  317. {
  318. ucDigShow2=10;//假如小于10,十位显现无
  319. }
  320. else
  321. {
  322. ucDigShow2=ucTemp2;//第2位数码管要显现的内容
  323. }
  324. ucDigShow1=ucTemp1;//第1位数码管要显现的内容
  325. }
  326. break;
  327. case 3://显现P–3窗口的数据
  328. if(ucWd3Update==1)//窗口3要悉数更新显现
  329. {
  330. ucWd3Update=0;//及时清零标志,防止一向进来扫描
  331. ucDigShow8=12;//第8位数码管显现P
  332. ucDigShow7=11;//第7位数码管显现-
  333. ucDigShow6=3;//第6位数码管显现3
  334. ucDigShow5=10; //第5位数码管显现无
  335. ucTemp4=uiSetData3/1000; //分化数据
  336. ucTemp3=uiSetData3%1000/100;
  337. ucTemp2=uiSetData3%100/10;
  338. ucTemp1=uiSetData3%10;
  339. if(uiSetData3<1000)
  340. {
  341. ucDigShow4=10;//假如小于1000,千位显现无
  342. }
  343. else
  344. {
  345. ucDigShow4=ucTemp4;//第4位数码管要显现的内容
  346. }
  347. if(uiSetData3<100)
  348. {
  349. ucDigShow3=10;//假如小于100,百位显现无
  350. }
  351. else
  352. {
  353. ucDigShow3=ucTemp3;//第3位数码管要显现的内容
  354. }
  355. if(uiSetData3<10)
  356. {
  357. ucDigShow2=10;//假如小于10,十位显现无
  358. }
  359. else
  360. {
  361. ucDigShow2=ucTemp2;//第2位数码管要显现的内容
  362. }
  363. ucDigShow1=ucTemp1;//第1位数码管要显现的内容
  364. }
  365. break;
  366. case 4://显现P–4窗口的数据
  367. if(ucWd4Update==1)//窗口4要悉数更新显现
  368. {
  369. ucWd4Update=0;//及时清零标志,防止一向进来扫描
  370. ucDigShow8=12;//第8位数码管显现P
  371. ucDigShow7=11;//第7位数码管显现-
  372. ucDigShow6=4;//第6位数码管显现4
  373. ucDigShow5=10; //第5位数码管显现无
  374. ucTemp4=uiSetData4/1000; //分化数据
  375. ucTemp3=uiSetData4%1000/100;
  376. ucTemp2=uiSetData4%100/10;
  377. ucTemp1=uiSetData4%10;
  378. if(uiSetData4<1000)
  379. {
  380. ucDigShow4=10;//假如小于1000,千位显现无
  381. }
  382. else
  383. {
  384. ucDigShow4=ucTemp4;//第4位数码管要显现的内容
  385. }
  386. if(uiSetData4<100)
  387. {
  388. ucDigShow3=10;//假如小于100,百位显现无
  389. }
  390. else
  391. {
  392. ucDigShow3=ucTemp3;//第3位数码管要显现的内容
  393. }
  394. if(uiSetData4<10)
  395. {
  396. ucDigShow2=10;//假如小于10,十位显现无
  397. }
  398. else
  399. {
  400. ucDigShow2=ucTemp2;//第2位数码管要显现的内容
  401. }
  402. ucDigShow1=ucTemp1;//第1位数码管要显现的内容
  403. }
  404. break;
  405. }
  406. }
  407. void key_scan(void)//按键扫描函数 放在守时中止里
  408. {
  409. if(key_sr1==1)//IO是高电平,阐明按键没有被按下,这时要及时清零一些标志位
  410. {
  411. ucKeyLock1=0; //按键自锁标志清零
  412. uiKeyTimeCnt1=0;//按键去颤动延时计数器清零,此行十分奇妙,是我实战中探索出来的。
  413. }
  414. else if(ucKeyLock1==0)//有按键按下,且是榜首次被按下
  415. {
  416. uiKeyTimeCnt1++; //累加守时中止次数
  417. if(uiKeyTimeCnt1>const_key_time1)
  418. {
  419. uiKeyTimeCnt1=0;
  420. ucKeyLock1=1;//自锁按键置位,防止一向触发
  421. ucKeySec=1; //触发1号键
  422. }
  423. }
  424. if(key_sr2==1)//IO是高电平,阐明按键没有被按下,这时要及时清零一些标志位
  425. {
  426. ucKeyLock2=0; //按键自锁标志清零
  427. uiKeyTimeCnt2=0;//按键去颤动延时计数器清零,此行十分奇妙,是我实战中探索出来的。
  428. }
  429. else if(ucKeyLock2==0)//有按键按下,且是榜首次被按下
  430. {
  431. uiKeyTimeCnt2++; //累加守时中止次数
  432. if(uiKeyTimeCnt2>const_key_time2)
  433. {
  434. uiKeyTimeCnt2=0;
  435. ucKeyLock2=1;//自锁按键置位,防止一向触发
  436. ucKeySec=2; //触发2号键
  437. }
  438. }
  439. if(key_sr3==1)//IO是高电平,阐明按键没有被按下,这时要及时清零一些标志位
  440. {
  441. ucKeyLock3=0; //按键自锁标志清零
  442. uiKeyTimeCnt3=0;//按键去颤动延时计数器清零,此行十分奇妙,是我实战中探索出来的。
  443. }
  444. else if(ucKeyLock3==0)//有按键按下,且是榜首次被按下
  445. {
  446. uiKeyTimeCnt3++; //累加守时中止次数
  447. if(uiKeyTimeCnt3>const_key_time3)
  448. {
  449. uiKeyTimeCnt3=0;
  450. ucKeyLock3=1;//自锁按键置位,防止一向触发
  451. ucKeySec=3; //触发3号键
  452. }
  453. }
  454. }
  455. void key_service(void) //按键服务的应用程序
  456. {
  457. switch(ucKeySec) //按键服务状况切换
  458. {
  459. case 1:// 加按键 对应朱兆祺学习板的S1键
  460. switch(ucWd)//在不同的窗口下,设置不同的参数
  461. {
  462. case 1:
  463. uiSetData1++;
  464. if(uiSetData1>9999) //最大值是9999
  465. {
  466. uiSetData1=9999;
  467. }
  468. write_eeprom_int(0,uiSetData1); //存入EEPROM 因为内部有延时函数,所以此处会引起数码管闪耀
  469. ucWd1Update=1;//窗口1更新显现
  470. break;
  471. case 2:
  472. uiSetData2++;
  473. if(uiSetData2>9999) //最大值是9999
  474. {
  475. uiSetData2=9999;
  476. }
  477. write_eeprom_int(2,uiSetData2); //存入EEPROM,因为内部有延时函数,所以此处会引起数码管闪耀
  478. ucWd2Update=1;//窗口2更新显现
  479. break;
  480. case 3:
  481. uiSetData3++;
  482. if(uiSetData3>9999) //最大值是9999
  483. {
  484. uiSetData3=9999;
  485. }
  486. write_eeprom_int(4,uiSetData3); //存入EEPROM,因为内部有延时函数,所以此处会引起数码管闪耀
  487. ucWd3Update=1;//窗口3更新显现
  488. break;
  489. case 4:
  490. uiSetData4++;
  491. if(uiSetData4>9999) //最大值是9999
  492. {
  493. uiSetData4=9999;
  494. }
  495. write_eeprom_int(6,uiSetData4); //存入EEPROM,因为内部有延时函数,所以此处会引起数码管闪耀
  496. ucWd4Update=1;//窗口4更新显现
  497. break;
  498. }
  499. ucVoiceLock=1;//原子锁加锁,维护主函数与中止函数的同享变量uiVoiceCnt
  500. uiVoiceCnt=const_voice_short; //按键声响触发,滴一声就停。
  501. ucVoiceLock=0;//原子锁解锁,维护主函数与中止函数的同享变量uiVoiceCnt
  502. ucKeySec=0;//呼应按键服务处理程序后,按键编号清零,防止共同触发
  503. break;
  504. case 2:// 减按键 对应朱兆祺学习板的S5键
  505. switch(ucWd)//在不同的窗口下,设置不同的参数
  506. {
  507. case 1:
  508. uiSetData1–;
  509. if(uiSetData1>9999)
  510. {
  511. uiSetData1=0;//最小值是0
  512. }
  513. write_eeprom_int(0,uiSetData1); //存入EEPROM,因为内部有延时函数,所以此处会引起数码管闪耀
  514. ucWd1Update=1;//窗口1更新显现
  515. break;
  516. case 2:
  517. uiSetData2–;
  518. if(uiSetData2>9999)
  519. {
  520. uiSetData2=0;//最小值是0
  521. }
  522. write_eeprom_int(2,uiSetData2); //存入EEPROM,因为内部有延时函数,所以此处会引起数码管闪耀
  523. ucWd2Update=1;//窗口2更新显现
  524. break;
  525. case 3:
  526. uiSetData3–;
  527. if(uiSetData3>9999)
  528. {
  529. uiSetData3=0;//最小值是0
  530. }
  531. write_eeprom_int(4,uiSetData3); //存入EEPROM,因为内部有延时函数,所以此处会引起数码管闪耀
  532. ucWd3Update=1;//窗口3更新显现
  533. break;
  534. case 4:
  535. uiSetData4–;
  536. if(uiSetData4>9999)
  537. {
  538. uiSetData4=0;//最小值是0
  539. }
  540. write_eeprom_int(6,uiSetData4); //存入EEPROM,因为内部有延时函数,所以此处会引起数码管闪耀
  541. ucWd4Update=1;//窗口4更新显现
  542. break;
  543. }
  544. ucVoiceLock=1;//原子锁加锁,维护主函数与中止函数的同享变量uiVoiceCnt
  545. uiVoiceCnt=const_voice_short; //按键声响触发,滴一声就停。
  546. ucVoiceLock=0;//原子锁解锁,维护主函数与中止函数的同享变量uiVoiceCnt
  547. ucKeySec=0;//呼应按键服务处理程序后,按键编号清零,防止共同触发
  548. break;
  549. case 3:// 切换窗口按键 对应朱兆祺学习板的S9键
  550. ucWd++;//切换窗口
  551. if(ucWd>4)
  552. {
  553. ucWd=1;
  554. }
  555. switch(ucWd)//在不同的窗口下,在不同的窗口下,更新显现不同的窗口
  556. {
  557. case 1:
  558. ucWd1Update=1;//窗口1更新显现
  559. break;
  560. case 2:
  561. ucWd2Update=1;//窗口2更新显现
  562. break;
  563. case 3:
  564. ucWd3Update=1;//窗口3更新显现
  565. break;
  566. case 4:
  567. ucWd4Update=1;//窗口4更新显现
  568. break;
  569. }
  570. ucVoiceLock=1;//原子锁加锁,维护主函数与中止函数的同享变量uiVoiceCnt
  571. uiVoiceCnt=const_voice_short; //按键声响触发,滴一声就停。
  572. ucVoiceLock=0;//原子锁解锁,维护主函数与中止函数的同享变量uiVoiceCnt
  573. ucKeySec=0;//呼应按键服务处理程序后,按键编号清零,防止共同触发
  574. break;
  575. }
  576. }
  577. void display_drive(void)
  578. {
  579. //以下程序,假如加一些数组和移位的元素,还能够紧缩容量。可是鸿哥寻求的不是容量,而是明晰的解说思路
  580. switch(ucDisplayDriveStep)
  581. {
  582. case 1://显现第1位
  583. ucDigShowTemp=dig_table[ucDigShow1];
  584. if(ucDigDot1==1)
  585. {
  586. ucDigShowTemp=ucDigShowTemp|0x80;//显现小数点
  587. }
  588. dig_hc595_drive(ucDigShowTemp,0xfe);
  589. break;
  590. case 2://显现第2位
  591. ucDigShowTemp=dig_table[ucDigShow2];
  592. if(ucDigDot2==1)
  593. {
  594. ucDigShowTemp=ucDigShowTemp|0x80;//显现小数点
  595. }
  596. dig_hc595_drive(ucDigShowTemp,0xfd);
  597. break;
  598. case 3://显现第3位
  599. ucDigShowTemp=dig_table[ucDigShow3];
  600. if(ucDigDot3==1)
  601. {
  602. ucDigShowTemp=ucDigShowTemp|0x80;//显现小数点
  603. }
  604. dig_hc595_drive(ucDigShowTemp,0xfb);
  605. break;
  606. case 4://显现第4位
  607. ucDigShowTemp=dig_table[ucDigShow4];
  608. if(ucDigDot4==1)
  609. {
  610. ucDigShowTemp=ucDigShowTemp|0x80;//显现小数点
  611. }
  612. dig_hc595_drive(ucDigShowTemp,0xf7);
  613. break;
  614. case 5://显现第5位
  615. ucDigShowTemp=dig_table[ucDigShow5];
  616. if(ucDigDot5==1)
  617. {
  618. ucDigShowTemp=ucDigShowTemp|0x80;//显现小数点
  619. }
  620. dig_hc595_drive(ucDigShowTemp,0xef);
  621. break;
  622. case 6://显现第6位
  623. ucDigShowTemp=dig_table[ucDigShow6];
  624. if(ucDigDot6==1)
  625. {
  626. ucDigShowTemp=ucDigShowTemp|0x80;//显现小数点
  627. }
  628. dig_hc595_drive(ucDigShowTemp,0xdf);
  629. break;
  630. case 7://显现第7位
  631. ucDigShowTemp=dig_table[ucDigShow7];
  632. if(ucDigDot7==1)
  633. {
  634. ucDigShowTemp=ucDigShowTemp|0x80;//显现小数点
  635. }
  636. dig_hc595_drive(ucDigShowTemp,0xbf);
  637. break;
  638. case 8://显现第8位
  639. ucDigShowTemp=dig_table[ucDigShow8];
  640. if(ucDigDot8==1)
  641. {
  642. ucDigShowTemp=ucDigShowTemp|0x80;//显现小数点
  643. }
  644. dig_hc595_drive(ucDigShowTemp,0x7f);
  645. break;
  646. }
  647. ucDisplayDriveStep++;
  648. if(ucDisplayDriveStep>8)//扫描完8个数码管后,从头从榜首个开端扫描
  649. {
  650. ucDisplayDriveStep=1;
  651. }
  652. }
  653. //数码管的74HC595驱动函数
  654. void dig_hc595_drive(unsigned char ucDigStatusTemp16_09,unsigned char ucDigStatusTemp08_01)
  655. {
  656. unsigned char i;
  657. unsigned char ucTempData;
  658. dig_hc595_sh_dr=0;
  659. dig_hc595_st_dr=0;
  660. ucTempData=ucDigStatusTemp16_09;//先送高8位
  661. for(i=0;i<8;i++)
  662. {
  663. if(ucTempData>=0x80)dig_hc595_ds_dr=1;
  664. else dig_hc595_ds_dr=0;
  665. dig_hc595_sh_dr=0; //SH引脚的上升沿把数据送入寄存器
  666. delay_short(1);
  667. dig_hc595_sh_dr=1;
  668. delay_short(1);
  669. ucTempData=ucTempData<<1;
  670. }
  671. ucTempData=ucDigStatusTemp08_01;//再先送低8位
  672. for(i=0;i<8;i++)
  673. {
  674. if(ucTempData>=0x80)dig_hc595_ds_dr=1;
  675. else dig_hc595_ds_dr=0;
  676. dig_hc595_sh_dr=0; //SH引脚的上升沿把数据送入寄存器
  677. delay_short(1);
  678. dig_hc595_sh_dr=1;
  679. delay_short(1);
  680. ucTempData=ucTempData<<1;
  681. }
  682. dig_hc595_st_dr=0;//ST引脚把两个寄存器的数据更新输出到74HC595的输出引脚上而且锁存起来
  683. delay_short(1);
  684. dig_hc595_st_dr=1;
  685. delay_short(1);
  686. dig_hc595_sh_dr=0; //拉低,抗干扰就增强
  687. dig_hc595_st_dr=0;
  688. dig_hc595_ds_dr=0;
  689. }
  690. //LED灯的74HC595驱动函数
  691. void hc595_drive(unsigned char ucLedStatusTemp16_09,unsigned char ucLedStatusTemp08_01)
  692. {
  693. unsigned char i;
  694. unsigned char ucTempData;
  695. hc595_sh_dr=0;
  696. hc595_st_dr=0;
  697. ucTempData=ucLedStatusTemp16_09;//先送高8位
  698. for(i=0;i<8;i++)
  699. {
  700. if(ucTempData>=0x80)hc595_ds_dr=1;
  701. else hc595_ds_dr=0;
  702. hc595_sh_dr=0; //SH引脚的上升沿把数据送入寄存器
  703. delay_short(1);
  704. hc595_sh_dr=1;
  705. delay_short(1);
  706. ucTempData=ucTempData<<1;
  707. }
  708. ucTempData=ucLedStatusTemp08_01;//再先送低8位
  709. for(i=0;i<8;i++)
  710. {
  711. if(ucTempData>=0x80)hc595_ds_dr=1;
  712. else hc595_ds_dr=0;
  713. hc595_sh_dr=0; //SH引脚的上升沿把数据送入寄存器
  714. delay_short(1);
  715. hc595_sh_dr=1;
  716. delay_short(1);
  717. ucTempData=ucTempData<<1;
  718. }
  719. hc595_st_dr=0;//ST引脚把两个寄存器的数据更新输出到74HC595的输出引脚上而且锁存起来
  720. delay_short(1);
  721. hc595_st_dr=1;
  722. delay_short(1);
  723. hc595_sh_dr=0; //拉低,抗干扰就增强
  724. hc595_st_dr=0;
  725. hc595_ds_dr=0;
  726. }
  727. void T0_time(void) interrupt 1 //守时中止
  728. {
  729. TF0=0;//铲除中止标志
  730. TR0=0; //关中止
  731. /* 注释三:
  732. * 此处多添加一个原子锁,作为中止与主函数同享数据的维护,实际上是学习了”红金龙吸味”关于原子锁的主张.
  733. */
  734. if(ucVoiceLock==0) //原子锁判别
  735. {
  736. if(uiVoiceCnt!=0)
  737. {
  738. uiVoiceCnt–; //每次进入守时中止都自减1,直到等于零中止。才中止鸣叫
  739. beep_dr=0;//蜂鸣器是PNP三极管操控,低电平就开端鸣叫。
  740. }
  741. else
  742. {
  743. ; //此处多加一个空指令,想保持跟if括号句子的数量对称,都是两条指令。不加也能够。
  744. beep_dr=1;//蜂鸣器是PNP三极管操控,高电平就中止鸣叫。
  745. }
  746. }
  747. key_scan(); //按键扫描函数
  748. display_drive();//数码管字模的驱动函数
  749. TH0=0xfe; //重装初始值(65535-500)=65035=0xfe0b
  750. TL0=0x0b;
  751. TR0=1;//开中止
  752. }
  753. void delay_short(unsigned int uiDelayShort)
  754. {
  755. unsigned int i;
  756. for(i=0;i
  757. {
  758. ; //一个分号相当于履行一条空句子
  759. }
  760. }
  761. void delay_long(unsigned int uiDelayLong)
  762. {
  763. unsigned int i;
  764. unsigned int j;
  765. for(i=0;i
  766. {
  767. for(j=0;j<500;j++)//内嵌循环的空指令数量
  768. {
  769. ; //一个分号相当于履行一条空句子
  770. }
  771. }
  772. }
  773. void initial_myself(void)//榜首区 初始化单片机
  774. {
  775. /* 注释四:
  776. * 矩阵键盘也能够做独立按键,条件是把某一根公共输出线输出低电平,
  777. * 模仿独立按键的触发地,本程序中,把key_gnd_dr输出低电平。
  778. * 朱兆祺51学习板的S1便是本程序中用到的一个独立按键。
  779. */
  780. key_gnd_dr=0; //模仿独立按键的地GND,因而有必要一向输出低电平
  781. beep_dr=1; //用PNP三极管操控蜂鸣器,输出高电平时不叫。
  782. hc595_drive(0x00,0x00);//封闭一切通过别的两个74HC595驱动的LED灯
  783. TMOD=0x01;//设置守时器0为工作办法1
  784. TH0=0xfe; //重装初始值(65535-500)=65035=0xfe0b
  785. TL0=0x0b;
  786. }
  787. void initial_peripheral(void) //第二区 初始化外围
  788. {
  789. ucDigDot8=0; //小数点悉数不显现
  790. ucDigDot7=0;
  791. ucDigDot6=0;
  792. ucDigDot5=0;
  793. ucDigDot4=0;
  794. ucDigDot3=0;
  795. ucDigDot2=0;
  796. ucDigDot1=0;
  797. EA=1; //开总中止
  798. ET0=1; //答应守时中止
  799. TR0=1; //发动守时中止
  800. /* 注释五:
  801. * 怎么初始化EEPROM数据的办法。在运用EEPROM时,这一步初始化很要害!
  802. * 榜首次上电时,咱们从EEPROM读取出来的数据有或许超出了规模,或许是ff。
  803. * 这个时分咱们应该给它填入一个初始化的数据,这一步千万别漏了。别的,
  804. * 因为int类型数据占用2个字节,所以以下4个数据挨着的地址是0,2,4,6.
  805. */
  806. uiSetData1=read_eeprom_int(0);//读取uiSetData1,内部占用2个字节地址
  807. if(uiSetData1>9999) //不在规模内
  808. {
  809. uiSetData1=0; //填入一个初始化数据
  810. write_eeprom_int(0,uiSetData1); //存入uiSetData1,内部占用2个字节地址
  811. }
  812. uiSetData2=read_eeprom_int(2);//读取uiSetData2,内部占用2个字节地址
  813. if(uiSetData2>9999)//不在规模内
  814. {
  815. uiSetData2=0;//填入一个初始化数据
  816. write_eeprom_int(2,uiSetData2); //存入uiSetData2,内部占用2个字节地址
  817. }
  818. uiSetData3=read_eeprom_int(4);//读取uiSetData3,内部占用2个字节地址
  819. if(uiSetData3>9999)//不在规模内
  820. {
  821. uiSetData3=0;//填入一个初始化数据
  822. write_eeprom_int(4,uiSetData3); //存入uiSetData3,内部占用2个字节地址
  823. }
  824. uiSetData4=read_eeprom_int(6);//读取uiSetData4,内部占用2个字节地址
  825. if(uiSetData4>9999)//不在规模内
  826. {
  827. uiSetData4=0;//填入一个初始化数据
  828. write_eeprom_int(6,uiSetData4); //存入uiSetData4,内部占用2个字节地址
  829. }
  830. }
总结陈词:
IIC通讯进程是一个要求趁热打铁的通讯进程,中心不能被其它中止影响时序犯错,因而,在整个通讯进程中应该先封闭总中止,完结之后再开中止。可是,这样就会引起别的一个新问题,假如封闭总中止的时刻太长,会导致动态数码管不能及时均匀的扫描,在按键更改参数,内部操作EEPROM时,数码管就会呈现时间短显着的闪耀现象,处理这个问题最好的办法便是在做项目中尽量不要用动态扫描数码管的计划,应该用静态显现的计划。那么在程序上还有没有改进这种现象的办法?当然有。欲知概况,请听下回分化—–操作AT24C02时,运用“趁热打铁的守时器办法”改进数码管的闪耀现象。

声明:本文内容来自网络转载或用户投稿,文章版权归原作者和原出处所有。文中观点,不代表本站立场。若有侵权请联系本站删除(kf@86ic.com)https://www.86ic.net/xinpin/yuanjian/262075.html

为您推荐

联系我们

联系我们

在线咨询: QQ交谈

邮箱: kf@86ic.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部