您的位置 首页 产品

第89节:用单片机内部定时器做一个时钟

开场白:很多网友建议,为了方便初学者学习编程思路,我应该用单片机定时器做一个时钟程序供大家参考学习。其实我前面第48节就已经用ds1302

开场白:
许多网友主张,为了便利初学者学习编程思路,我应该用单片机守时器做一个时钟程序供咱们参阅学习。其实我前面第48节就现已用ds1302做了一个可以显现和更高时刻的时钟,这一节只需在第48节的源代码基础上,大的结构不必动,只需求把ds1302发生的时刻改成用守时中止发生的时刻就可以了,改动的当地十分小。可是为了让时刻的精度更高,终究有必要跟规范时刻进行校验,来批改体系中一秒钟需求多个守时中止的差错,这个差错决议了体系的时刻精度,其实这个校验办法我在前面许多章节上跟咱们介绍过了:
榜首步:在程序代码上先写入1秒钟大约需求200个守时中止。
第二步:把程序烧录进单片机后,上电开端测验,手上同步翻开手机里的秒表,当手机的规范时刻跑了780秒(这个规范时刻跑得越长校验精度越高),而此刻单片机只是跑了1632秒。那么终究得出1秒钟需求的守时中止次数是:const_time_1s=(200*1632)/780=418。
第三步:假如发现时钟仍是不太准,可以持续回来榜首步依据最新1秒钟的时刻是418次,多校验几回,来不断调整const_time_1s的数值,直到找到相对精度的时刻中止。
本体系仅供学习,精度不可能做得很好,由于影响时刻精度的要素还有守时中止的重装值,守时中止里边的代码尽量少,以及晶振等欠好操控的要素。所以鸿哥一向不推荐在实践项目顶用单片机的内部守时器做实时时钟,由于精度有限。真实想要精确的时钟时刻,仍是强烈主张大家用外部专用的时钟芯片或许用CPLD/FPGA来做。
详细内容,请看源代码解说。
(1硬件渠道.
依据朱兆祺51单片机学习板
(2)完成功用:
本程序有2两个窗口。
第1个窗口显现日期。显现格局“年-月-日”。留意中心有“-”分隔。
第2个窗口显现时刻。显现格局“时 分 秒”。留意中心没“-”,只需空格分隔。
体系上电后,默许显现第2个窗口,实时显现动态的“时 分 秒”时刻。此刻按下S13按键不松手就会切换到显现日期的第1个窗口。松手后主动切换回第2个显现动态时刻的窗口。
需求更改时刻的时分,长按S9按键不松手超越3秒后,体系将进入批改时刻的状况,切换到第1个日期窗口,而且显现“年”的两位数码管会闪耀,此刻可以按S1或许S5加减按键批改年的参数,批改完年后,持续短按S9按键,会切换到“月”的参数闪耀状况,只需顺次不断按下S9按键,就会顺次切换年,月,日,时,分,秒的参数闪耀状况,终究批改完秒的参数后,体系会主动把咱们批改设置的日期时刻一次性更改到守时中止函数内部的时刻变量,到达批改日期时刻的意图。
S13是电平改变按键,用来切换窗口的,专门用来检查当时日期。按下S13按键时显现日期窗口,松手后回来到显现实时时刻的窗口。
[size=10.5000pt](3)源代码解说如下:
  1. #include “REG52.H”
  2. #define const_dpy_time_half200//数码管闪耀时刻的半值
  3. #define const_dpy_time_all 400//数码管闪耀时刻的全值 必定要比const_dpy_time_half 大
  4. #define const_voice_short40 //蜂鸣器短叫的持续时刻
  5. #define const_key_time120 //按键去颤动延时的时刻
  6. #define const_key_time220 //按键去颤动延时的时刻
  7. #define const_key_time320 //按键去颤动延时的时刻
  8. #define const_key_time420 //按键去颤动延时的时刻
  9. #define const_key_time171200//长按超越3秒的时刻
  10. /* 注释一:
  11. * const_timer_1s这个是发生多少次守时中止才算1秒钟的规范。这个规范决议了时钟的精度。这个规范终究是需求校验的。
  12. * 那么是怎么查验的呢?依据咱们前面介绍的校验时刻办法:
  13. * 过程:
  14. * 榜首步:在程序代码上先写入1秒钟大约需求200个守时中止。
  15. * 第二步:把程序烧录进单片机后,上电开端测验,手上同步翻开手机里的秒表,当手机的规范时刻跑了780秒(这个规范时刻跑得越长校验精度越高),
  16. * 而此刻单片机只是跑了1632秒。那么终究得出1秒钟需求的守时中止次数是:const_time_1s=(200*1632)/780=418。
  17. * 第三步:假如发现时钟仍是不太准,可以持续回来榜首步依据最新1秒钟的时刻是418次,多校验几回。本体系仅供学习,精度不可能做得很好,由于
  18. * 影响时刻精度的要素还有守时中止的重装值,守时中止里边的代码尽量少,以及晶振等欠好操控的要素。所以鸿哥一向不推荐在实践项目中
  19. * 用单片机的内部守时器做实时时钟,由于精度有限。真实想要精确的时钟时刻,仍是强烈主张咱们用外部专用的时钟芯片或许用CPLD/FPGA来做。
  20. */
  21. //#define const_timer_1s200 //榜首次假定大约1秒的时刻需求200个守时中止
  22. #define const_timer_1s418//第2次校验后,终究选定大约1秒的时刻需求418个守时中止。假如发现时刻仍是禁绝,可以在此基础上持续校验来调整此数据。
  23. void initial_myself(void);
  24. void initial_peripheral(void);
  25. void delay_short(unsigned int uiDelayShort);
  26. void delay_long(unsigned int uiDelaylong);
  27. //驱动数码管的74HC595
  28. void dig_hc595_drive(unsigned char ucDigStatusTemp16_09,unsigned char ucDigStatusTemp08_01);
  29. void display_drive(void); //显现数码管字模的驱动函数
  30. void display_service(void); //显现的窗口菜单服务程序
  31. //驱动LED的74HC595
  32. void hc595_drive(unsigned char ucLedStatusTemp16_09,unsigned char ucLedStatusTemp08_01);
  33. void T0_time(void);//守时中止函数
  34. void key_service(void); //按键服务的应用程序
  35. void key_scan(void);//按键扫描函数 放在守时中止里
  36. void timer_sampling(void); //守时器采样程序,内部每秒钟收集更新一次
  37. unsigned char get_date(unsigned char ucYearTemp,unsigned char ucMonthTemp);//获取当时月份的最大天数
  38. //日调整 每个月份的日最大取值不同,有的最大28日,有的最大29日,有的最大30,有的最大31
  39. unsigned char date_adjust(unsigned char ucYearTemp,unsigned char ucMonthTemp,unsigned char ucDateTemp); //日调整
  40. sbit key_sr1=P0^0; //对应朱兆祺学习板的S1键
  41. sbit key_sr2=P0^1; //对应朱兆祺学习板的S5键
  42. sbit key_sr3=P0^2; //对应朱兆祺学习板的S9键
  43. sbit key_sr4=P0^3; //对应朱兆祺学习板的S13键
  44. sbit key_gnd_dr=P0^4; //模仿独立按键的地GND,因而有必要一向输出低电平
  45. sbit beep_dr=P2^7; //蜂鸣器的驱动IO口
  46. sbit dig_hc595_sh_dr=P2^0; //数码管的74HC595程序
  47. sbit dig_hc595_st_dr=P2^1;
  48. sbit dig_hc595_ds_dr=P2^2;
  49. sbit hc595_sh_dr=P2^3; //LED灯的74HC595程序
  50. sbit hc595_st_dr=P2^4;
  51. sbit hc595_ds_dr=P2^5;
  52. unsigned char ucKeySec=0; //被触发的按键编号
  53. unsigned intuiKeyTimeCnt1=0; //按键去颤动延时计数器
  54. unsigned char ucKeyLock1=0; //按键触发后自锁的变量标志
  55. unsigned intuiKeyTimeCnt2=0; //按键去颤动延时计数器
  56. unsigned char ucKeyLock2=0; //按键触发后自锁的变量标志
  57. unsigned intuiKeyTimeCnt3=0; //按键去颤动延时计数器
  58. unsigned char ucKeyLock3=0; //按键触发后自锁的变量标志
  59. unsigned int uiKey4Cnt1=0;//在软件滤波中,用到的变量
  60. unsigned int uiKey4Cnt2=0;
  61. unsigned char ucKey4Sr=1;//实时反映按键的电平状况
  62. unsigned char ucKey4SrRecord=0; //记载上一次按键的电平状况
  63. unsigned intuiVoiceCnt=0;//蜂鸣器鸣叫的持续时刻计数器
  64. unsigned charucVoiceLock=0;//蜂鸣器鸣叫的原子锁
  65. unsigned char ucDigShow8;//第8位数码管要显现的内容
  66. unsigned char ucDigShow7;//第7位数码管要显现的内容
  67. unsigned char ucDigShow6;//第6位数码管要显现的内容
  68. unsigned char ucDigShow5;//第5位数码管要显现的内容
  69. unsigned char ucDigShow4;//第4位数码管要显现的内容
  70. unsigned char ucDigShow3;//第3位数码管要显现的内容
  71. unsigned char ucDigShow2;//第2位数码管要显现的内容
  72. unsigned char ucDigShow1;//第1位数码管要显现的内容
  73. unsigned char ucDigDot8;//数码管8的小数点是否显现的标志
  74. unsigned char ucDigDot7;//数码管7的小数点是否显现的标志
  75. unsigned char ucDigDot6;//数码管6的小数点是否显现的标志
  76. unsigned char ucDigDot5;//数码管5的小数点是否显现的标志
  77. unsigned char ucDigDot4;//数码管4的小数点是否显现的标志
  78. unsigned char ucDigDot3;//数码管3的小数点是否显现的标志
  79. unsigned char ucDigDot2;//数码管2的小数点是否显现的标志
  80. unsigned char ucDigDot1;//数码管1的小数点是否显现的标志
  81. unsigned char ucDigShowTemp=0; //暂时中心变量
  82. unsigned char ucDisplayDriveStep=1;//动态扫描数码管的过程变量
  83. unsigned char ucWd=2;//本程序的中心变量,窗口显现变量。相似于一级菜单的变量。代表显现不同的窗口。
  84. unsigned char ucPart=0;//本程序的中心变量,部分显现变量。相似于二级菜单的变量。代表显现不同的部分。
  85. unsigned char ucWd1Update=0; //窗口1更新显现标志
  86. unsigned char ucWd2Update=1; //窗口2更新显现标志
  87. unsigned char ucWd1Part1Update=0;//在窗口1中,部分1的更新显现标志
  88. unsigned char ucWd1Part2Update=0; //在窗口1中,部分2的更新显现标志
  89. unsigned char ucWd1Part3Update=0; //在窗口1中,部分3的更新显现标志
  90. unsigned char ucWd2Part1Update=0;//在窗口2中,部分1的更新显现标志
  91. unsigned char ucWd2Part2Update=0; //在窗口2中,部分2的更新显现标志
  92. unsigned char ucWd2Part3Update=0; //在窗口2中,部分3的更新显现标志
  93. unsigned charucYear=15; //用来显现和设置的时刻变量
  94. unsigned charucMonth=1;
  95. unsigned charucDate=1;
  96. unsigned charucHour=12;
  97. unsigned charucMinute=0;
  98. unsigned charucSecond=0;
  99. unsigned int uiTimerCnt=0; //计时器的时基
  100. unsigned charucTimerYear=15; //在守时器内部时基发生的时刻变量
  101. unsigned charucTimerMonth=1;
  102. unsigned charucTimerDate=1;
  103. unsigned charucTimerHour=12;
  104. unsigned charucTimerMinute=0;
  105. unsigned charucTimerSecond=0;
  106. unsigned charucTimerDateMax=31; //当时月份的最大天数
  107. unsigned charucTimerUpdate=0; //守时器每1秒钟所发生的标志
  108. unsigned charucTimerStart=1;//是否翻开守时器内部时刻的标志,在本程序相当于原子锁的效果。
  109. unsigned char ucTemp1=0;//中心过渡变量
  110. unsigned char ucTemp2=0;//中心过渡变量
  111. unsigned char ucTemp4=0;//中心过渡变量
  112. unsigned char ucTemp5=0;//中心过渡变量
  113. unsigned char ucTemp7=0;//中心过渡变量
  114. unsigned char ucTemp8=0;//中心过渡变量
  115. unsigned char ucDelayTimerLock=0; //原子锁
  116. unsigned intuiDelayTimer=0;
  117. unsigned char ucDpyTimeLock=0; //原子锁
  118. unsigned intuiDpyTimeCnt=0;//数码管的闪耀计时器,放在守时中止里不断累加
  119. //依据原理图得出的共阴数码管字模表
  120. code unsigned char dig_table[]=
  121. {
  122. 0x3f,//0 序号0
  123. 0x06,//1 序号1
  124. 0x5b,//2 序号2
  125. 0x4f,//3 序号3
  126. 0x66,//4 序号4
  127. 0x6d,//5 序号5
  128. 0x7d,//6 序号6
  129. 0x07,//7 序号7
  130. 0x7f,//8 序号8
  131. 0x6f,//9 序号9
  132. 0x00,//无 序号10
  133. 0x40,//- 序号11
  134. 0x73,//P 序号12
  135. };
  136. void main()
  137. {
  138. initial_myself();
  139. delay_long(100);
  140. initial_peripheral();
  141. while(1)
  142. {
  143. key_service(); //按键服务的应用程序
  144. timer_sampling(); //守时器采样程序,内部每秒钟收集更新一次
  145. display_service(); //显现的窗口菜单服务程序
  146. }
  147. }
  148. /* 注释二:
  149. * 体系不必时时刻刻收集守时器的内部数据,每隔1秒钟的时刻更新收集一次就可以了。
  150. * 这个1秒钟的时刻是依据守时器内部ucTimerUpdate变量来判别。
  151. */
  152. void timer_sampling(void) //采样守时器的程序,内部每秒钟收集更新一次
  153. {
  154. if(ucPart==0)//当体系不是处于设置日期和时刻的情况下
  155. {
  156. if(ucTimerUpdate==1)//每隔1秒钟时刻就更新收集一次守时器的时刻数据
  157. {
  158. ucTimerUpdate=0;//及时清零,防止一向更新。
  159. ucYear=ucTimerYear; //读取守时器内部的年
  160. ucMonth=ucTimerMonth; //读取守时器内部的月
  161. ucDate=ucTimerDate;//读取守时器内部的日
  162. ucHour=ucTimerHour; //读取守时器内部的时
  163. ucMinute=ucTimerMinute;//读取守时器内部的分
  164. ucSecond=ucTimerSecond;//读取守时器内部的秒
  165. ucWd2Update=1; //窗口2更新显现时刻
  166. }
  167. }
  168. }
  169. /* 注释三:
  170. * 依据年份和月份来获取当时这个月的最大天数。每个月份的天数最大取值不同,有的最大28日,
  171. * 有的最大29日,有的最大30,有的最大31。
  172. */
  173. unsigned char get_date(unsigned char ucYearTemp,unsigned char ucMonthTemp)
  174. {
  175. unsigned char ucDayResult;
  176. unsigned int uiYearTemp;
  177. unsigned int uiYearYu;
  178. ucDayResult=31; //默许最大是31天,以下依据不同的年份和月份来决议是否需求批改这个值
  179. switch(ucMonthTemp)//依据不同的月份来获取当时月份天数的最大值
  180. {
  181. case 2://二月份要核算是否是闰年
  182. uiYearTemp=2000+ucYearTemp;
  183. uiYearYu=uiYearTemp%4;
  184. if(uiYearYu==0) //闰年
  185. {
  186. ucDayResult=29;
  187. }
  188. else
  189. {
  190. ucDayResult=28;
  191. }
  192. break;
  193. case 4:
  194. case 6:
  195. case 9:
  196. case 11:
  197. ucDayResult=30;
  198. break;
  199. }
  200. return ucDayResult;
  201. }
  202. //日调整 每个月份的日最大取值不同,有的最大28日,有的最大29日,有的最大30,有的最大31
  203. unsigned char date_adjust(unsigned char ucYearTemp,unsigned char ucMonthTemp,unsigned char ucDateTemp) //日调整
  204. {
  205. unsigned char ucDayResult;
  206. unsigned int uiYearTemp;
  207. unsigned int uiYearYu;
  208. ucDayResult=ucDateTemp;
  209. switch(ucMonthTemp)//依据不同的月份来批改不同的日最大值
  210. {
  211. case 2://二月份要核算是否是闰年
  212. uiYearTemp=2000+ucYearTemp;
  213. uiYearYu=uiYearTemp%4;
  214. if(uiYearYu==0) //闰年
  215. {
  216. if(ucDayResult>29)
  217. {
  218. ucDayResult=29;
  219. }
  220. }
  221. else
  222. {
  223. if(ucDayResult>28)
  224. {
  225. ucDayResult=28;
  226. }
  227. }
  228. break;
  229. case 4:
  230. case 6:
  231. case 9:
  232. case 11:
  233. if(ucDayResult>30)
  234. {
  235. ucDayResult=30;
  236. }
  237. break;
  238. }
  239. return ucDayResult;
  240. }
  241. void display_service(void) //显现的窗口菜单服务程序
  242. {
  243. switch(ucWd)//本程序的中心变量,窗口显现变量。相似于一级菜单的变量。代表显现不同的窗口。
  244. {
  245. case 1: //显现日期窗口的数据数据格局 NN-YY-RR 年-月-日
  246. if(ucWd1Update==1)//窗口1要悉数更新显现
  247. {
  248. ucWd1Update=0;//及时清零标志,防止一向进来扫描
  249. ucDigShow6=11;//显现一杠”-“
  250. ucDigShow3=11;//显现一杠”-“
  251. ucWd1Part1Update=1;//部分年更新显现
  252. ucWd1Part2Update=1;//部分月更新显现
  253. ucWd1Part3Update=1;//部分日更新显现
  254. }
  255. if(ucWd1Part1Update==1)//部分年更新显现
  256. {
  257. ucWd1Part1Update=0;
  258. ucTemp8=ucYear/10;//年
  259. ucTemp7=ucYear%10;
  260. ucDigShow8=ucTemp8; //数码管显现实践内容
  261. ucDigShow7=ucTemp7;
  262. }
  263. if(ucWd1Part2Update==1)//部分月更新显现
  264. {
  265. ucWd1Part2Update=0;
  266. ucTemp5=ucMonth/10;//月
  267. ucTemp4=ucMonth%10;
  268. ucDigShow5=ucTemp5; //数码管显现实践内容
  269. ucDigShow4=ucTemp4;
  270. }
  271. if(ucWd1Part3Update==1) //部分日更新显现
  272. {
  273. ucWd1Part3Update=0;
  274. ucTemp2=ucDate/10;//日
  275. ucTemp1=ucDate%10;
  276. ucDigShow2=ucTemp2; //数码管显现实践内容
  277. ucDigShow1=ucTemp1;
  278. }
  279. //数码管闪耀
  280. switch(ucPart)//相当于二级菜单,依据部分变量的值,使对应的参数发生闪耀的动态效果。
  281. {
  282. case 0://都不闪耀
  283. break;
  284. case 1://年参数闪耀
  285. if(uiDpyTimeCnt==const_dpy_time_half)
  286. {
  287. ucDigShow8=ucTemp8; //数码管显现实践内容
  288. ucDigShow7=ucTemp7;
  289. }
  290. else if(uiDpyTimeCnt>const_dpy_time_all) //const_dpy_time_all必定要比const_dpy_time_half 大
  291. {
  292. ucDpyTimeLock=1; //原子锁加锁
  293. uiDpyTimeCnt=0; //及时把闪耀记时器清零
  294. ucDpyTimeLock=0;//原子锁解锁
  295. ucDigShow8=10; //数码管显现空,什么都不显现
  296. ucDigShow7=10;
  297. }
  298. break;
  299. case 2: //月参数闪耀
  300. if(uiDpyTimeCnt==const_dpy_time_half)
  301. {
  302. ucDigShow5=ucTemp5; //数码管显现实践内容
  303. ucDigShow4=ucTemp4;
  304. }
  305. else if(uiDpyTimeCnt>const_dpy_time_all) //const_dpy_time_all必定要比const_dpy_time_half 大
  306. {
  307. ucDpyTimeLock=1; //原子锁加锁
  308. uiDpyTimeCnt=0; //及时把闪耀记时器清零
  309. ucDpyTimeLock=0;//原子锁解锁
  310. ucDigShow5=10; //数码管显现空,什么都不显现
  311. ucDigShow4=10;
  312. }
  313. break;
  314. case 3: //日参数闪耀
  315. if(uiDpyTimeCnt==const_dpy_time_half)
  316. {
  317. ucDigShow2=ucTemp2; //数码管显现实践内容
  318. ucDigShow1=ucTemp1;
  319. }
  320. else if(uiDpyTimeCnt>const_dpy_time_all) //const_dpy_time_all必定要比const_dpy_time_half 大
  321. {
  322. ucDpyTimeLock=1; //原子锁加锁
  323. uiDpyTimeCnt=0; //及时把闪耀记时器清零
  324. ucDpyTimeLock=0;//原子锁解锁
  325. ucDigShow2=10; //数码管显现空,什么都不显现
  326. ucDigShow1=10;
  327. }
  328. break;
  329. }
  330. break;
  331. case 2: //显现时刻窗口的数据数据格局 SS FF MM 时 分 秒
  332. if(ucWd2Update==1)//窗口2要悉数更新显现
  333. {
  334. ucWd2Update=0;//及时清零标志,防止一向进来扫描
  335. ucDigShow6=10;//显现空
  336. ucDigShow3=10;//显现空
  337. ucWd2Part3Update=1;//部分时更新显现
  338. ucWd2Part2Update=1;//部分分更新显现
  339. ucWd2Part1Update=1;//部分秒更新显现
  340. }
  341. if(ucWd2Part1Update==1)//部分时更新显现
  342. {
  343. ucWd2Part1Update=0;
  344. ucTemp8=ucHour/10;//时
  345. ucTemp7=ucHour%10;
  346. ucDigShow8=ucTemp8; //数码管显现实践内容
  347. ucDigShow7=ucTemp7;
  348. }
  349. if(ucWd2Part2Update==1)//部分分更新显现
  350. {
  351. ucWd2Part2Update=0;
  352. ucTemp5=ucMinute/10;//分
  353. ucTemp4=ucMinute%10;
  354. ucDigShow5=ucTemp5; //数码管显现实践内容
  355. ucDigShow4=ucTemp4;
  356. }
  357. if(ucWd2Part3Update==1) //部分秒更新显现
  358. {
  359. ucWd2Part3Update=0;
  360. ucTemp2=ucSecond/10;//秒
  361. ucTemp1=ucSecond%10;
  362. ucDigShow2=ucTemp2; //数码管显现实践内容
  363. ucDigShow1=ucTemp1;
  364. }
  365. //数码管闪耀
  366. switch(ucPart)//相当于二级菜单,依据部分变量的值,使对应的参数发生闪耀的动态效果。
  367. {
  368. case 0://都不闪耀
  369. break;
  370. case 1://时参数闪耀
  371. if(uiDpyTimeCnt==const_dpy_time_half)
  372. {
  373. ucDigShow8=ucTemp8; //数码管显现实践内容
  374. ucDigShow7=ucTemp7;
  375. }
  376. else if(uiDpyTimeCnt>const_dpy_time_all) //const_dpy_time_all必定要比const_dpy_time_half 大
  377. {
  378. ucDpyTimeLock=1; //原子锁加锁
  379. uiDpyTimeCnt=0; //及时把闪耀记时器清零
  380. ucDpyTimeLock=0;//原子锁解锁
  381. ucDigShow8=10; //数码管显现空,什么都不显现
  382. ucDigShow7=10;
  383. }
  384. break;
  385. case 2: //分参数闪耀
  386. if(uiDpyTimeCnt==const_dpy_time_half)
  387. {
  388. ucDigShow5=ucTemp5; //数码管显现实践内容
  389. ucDigShow4=ucTemp4;
  390. }
  391. else if(uiDpyTimeCnt>const_dpy_time_all) //const_dpy_time_all必定要比const_dpy_time_half 大
  392. {
  393. ucDpyTimeLock=1; //原子锁加锁
  394. uiDpyTimeCnt=0; //及时把闪耀记时器清零
  395. ucDpyTimeLock=0;//原子锁解锁
  396. ucDigShow5=10; //数码管显现空,什么都不显现
  397. ucDigShow4=10;
  398. }
  399. break;
  400. case 3: //秒参数闪耀
  401. if(uiDpyTimeCnt==const_dpy_time_half)
  402. {
  403. ucDigShow2=ucTemp2; //数码管显现实践内容
  404. ucDigShow1=ucTemp1;
  405. }
  406. else if(uiDpyTimeCnt>const_dpy_time_all) //const_dpy_time_all必定要比const_dpy_time_half 大
  407. {
  408. ucDpyTimeLock=1; //原子锁加锁
  409. uiDpyTimeCnt=0; //及时把闪耀记时器清零
  410. ucDpyTimeLock=0;//原子锁解锁
  411. ucDigShow2=10; //数码管显现空,什么都不显现
  412. ucDigShow1=10;
  413. }
  414. break;
  415. }
  416. break;
  417. }
  418. }
  419. void key_scan(void)//按键扫描函数 放在守时中止里
  420. {
  421. if(key_sr1==1)//IO是高电平,阐明按键没有被按下,这时要及时清零一些标志位
  422. {
  423. ucKeyLock1=0; //按键自锁标志清零
  424. uiKeyTimeCnt1=0;//按键去颤动延时计数器清零,此行十分奇妙,是我实战中探索出来的。
  425. }
  426. else if(ucKeyLock1==0)//有按键按下,且是榜首次被按下
  427. {
  428. uiKeyTimeCnt1++; //累加守时中止次数
  429. if(uiKeyTimeCnt1>const_key_time1)
  430. {
  431. uiKeyTimeCnt1=0;
  432. ucKeyLock1=1;//自锁按键置位,防止一向触发
  433. ucKeySec=1; //触发1号键
  434. }
  435. }
  436. if(key_sr2==1)//IO是高电平,阐明按键没有被按下,这时要及时清零一些标志位
  437. {
  438. ucKeyLock2=0; //按键自锁标志清零
  439. uiKeyTimeCnt2=0;//按键去颤动延时计数器清零,此行十分奇妙,是我实战中探索出来的。
  440. }
  441. else if(ucKeyLock2==0)//有按键按下,且是榜首次被按下
  442. {
  443. uiKeyTimeCnt2++; //累加守时中止次数
  444. if(uiKeyTimeCnt2>const_key_time2)
  445. {
  446. uiKeyTimeCnt2=0;
  447. ucKeyLock2=1;//自锁按键置位,防止一向触发
  448. ucKeySec=2; //触发2号键
  449. }
  450. }
  451. /* 注释四:
  452. * 留意,此处把一个按键的短按和长按的功用都完成了。
  453. */
  454. if(key_sr3==1)//IO是高电平,阐明按键没有被按下,这时要及时清零一些标志位
  455. {
  456. ucKeyLock3=0; //按键自锁标志清零
  457. uiKeyTimeCnt3=0;//按键去颤动延时计数器清零,此行十分奇妙,是我实战中探索出来的。
  458. }
  459. else if(ucKeyLock3==0)//有按键按下,且是榜首次被按下
  460. {
  461. uiKeyTimeCnt3++; //累加守时中止次数
  462. if(uiKeyTimeCnt3>const_key_time3)
  463. {
  464. uiKeyTimeCnt3=0;
  465. ucKeyLock3=1;//自锁按键置位,防止一向触发
  466. ucKeySec=3; //短按触发3号键
  467. }
  468. }
  469. else if(uiKeyTimeCnt3
  470. {
  471. uiKeyTimeCnt3++; //累加守时中止次数
  472. if(uiKeyTimeCnt3==const_key_time17)//等于3秒钟,触发17号长按按键
  473. {
  474. ucKeySec=17; //长按3秒触发17号键
  475. }
  476. }
  477. /* 注释五:
  478. * 留意,此处是电平按键的滤波抗干扰处理
  479. */
  480. if(key_sr4==1)//对应朱兆祺学习板的S13键
  481. {
  482. uiKey4Cnt1=0; //在软件滤波中,十分要害的句子!!!相似按键去颤动程序的及时清零
  483. uiKey4Cnt2++; //相似独立按键去颤动的软件抗干扰处理
  484. if(uiKey4Cnt2>const_key_time4)
  485. {
  486. uiKey4Cnt2=0;
  487. ucKey4Sr=1;//实时反映按键松手时的电平状况
  488. }
  489. }
  490. else
  491. {
  492. uiKey4Cnt2=0; //在软件滤波中,十分要害的句子!!!相似按键去颤动程序的及时清零
  493. uiKey4Cnt1++;
  494. if(uiKey4Cnt1>const_key_time4)
  495. {
  496. uiKey4Cnt1=0;
  497. ucKey4Sr=0;//实时反映按键按下时的电平状况
  498. }
  499. }
  500. }
  501. void key_service(void) //按键服务的应用程序
  502. {
  503. switch(ucKeySec) //按键服务状况切换
  504. {
  505. case 1:// 加按键 对应朱兆祺学习板的S1键
  506. switch(ucWd)//在不同的窗口下,设置不同的参数
  507. {
  508. case 1:
  509. switch(ucPart) //在不同的部分变量下,相当于二级菜单
  510. {
  511. case 1://年
  512. ucYear++;
  513. if(ucYear>99)
  514. {
  515. ucYear=99;
  516. }
  517. ucWd1Part1Update=1;//更新显现
  518. break;
  519. case 2: //月
  520. ucMonth++;
  521. if(ucMonth>12)
  522. {
  523. ucMonth=12;
  524. }
  525. ucWd1Part2Update=1;//更新显现
  526. break;
  527. case 3: //日
  528. ucDate++;
  529. if(ucDate>31)
  530. {
  531. ucDate=31;
  532. }
  533. ucWd1Part3Update=1;//更新显现
  534. break;
  535. }
  536. break;
  537. case 2:
  538. switch(ucPart) //在不同的部分变量下,相当于二级菜单
  539. {
  540. case 1://时
  541. ucHour++;
  542. if(ucHour>23)
  543. {
  544. ucHour=23;
  545. }
  546. ucWd2Part1Update=1;//更新显现
  547. break;
  548. case 2: //分
  549. ucMinute++;
  550. if(ucMinute>59)
  551. {
  552. ucMinute=59;
  553. }
  554. ucWd2Part2Update=1;//更新显现
  555. break;
  556. case 3: //秒
  557. ucSecond++;
  558. if(ucSecond>59)
  559. {
  560. ucSecond=59;
  561. }
  562. ucWd2Part3Update=1;//更新显现
  563. break;
  564. }
  565. break;
  566. }
  567. ucVoiceLock=1;//原子锁加锁,维护主函数与中止函数的同享变量uiVoiceCnt
  568. uiVoiceCnt=const_voice_short; //按键声响触发,滴一声就停。
  569. ucVoiceLock=0;//原子锁解锁,维护主函数与中止函数的同享变量uiVoiceCnt
  570. ucKeySec=0;//呼应按键服务处理程序后,按键编号清零,防止共同触发
  571. break;
  572. case 2:// 减按键 对应朱兆祺学习板的S5键
  573. switch(ucWd)//在不同的窗口下,设置不同的参数
  574. {
  575. case 1:
  576. switch(ucPart) //在不同的部分变量下,相当于二级菜单
  577. {
  578. case 1://年
  579. ucYear–;
  580. if(ucYear>99)
  581. {
  582. ucYear=0;
  583. }
  584. ucWd1Part1Update=1;//更新显现
  585. break;
  586. case 2: //月
  587. ucMonth–;
  588. if(ucMonth<1)
  589. {
  590. ucMonth=1;
  591. }
  592. ucWd1Part2Update=1;//更新显现
  593. break;
  594. case 3: //日
  595. ucDate–;
  596. if(ucDate<1)
  597. {
  598. ucDate=1;
  599. }
  600. ucWd1Part3Update=1;//更新显现
  601. break;
  602. }
  603. break;
  604. case 2:
  605. switch(ucPart) //在不同的部分变量下,相当于二级菜单
  606. {
  607. case 1://时
  608. ucHour–;
  609. if(ucHour>23)
  610. {
  611. ucHour=0;
  612. }
  613. ucWd2Part1Update=1;//更新显现
  614. break;
  615. case 2: //分
  616. ucMinute–;
  617. if(ucMinute>59)
  618. {
  619. ucMinute=0;
  620. }
  621. ucWd2Part2Update=1;//更新显现
  622. break;
  623. case 3: //秒
  624. ucSecond–;
  625. if(ucSecond>59)
  626. {
  627. ucSecond=0;
  628. }
  629. ucWd2Part3Update=1;//更新显现
  630. break;
  631. }
  632. break;
  633. }
  634. ucVoiceLock=1;//原子锁加锁,维护主函数与中止函数的同享变量uiVoiceCnt
  635. uiVoiceCnt=const_voice_short; //按键声响触发,滴一声就停。
  636. ucVoiceLock=0;//原子锁解锁,维护主函数与中止函数的同享变量uiVoiceCnt
  637. ucKeySec=0;//呼应按键服务处理程序后,按键编号清零,防止共同触发
  638. break;
  639. case 3://短按设置按键 对应朱兆祺学习板的S9键
  640. switch(ucWd)//在不同的窗口下,设置不同的参数
  641. {
  642. case 1:
  643. ucPart++;
  644. if(ucPart>3)
  645. {
  646. ucPart=1;
  647. ucWd=2; //切换到第二个窗口,设置时分秒
  648. ucWd2Update=1;//窗口2更新显现
  649. }
  650. ucWd1Update=1;//窗口1更新显现
  651. break;
  652. case 2:
  653. if(ucPart>0) //在窗口2的时分,要榜首次激活设置时刻,有必要是长按3秒才可以,这儿短按激活不了榜首次
  654. {
  655. ucPart++;
  656. if(ucPart>3)//设置时刻完毕
  657. {
  658. ucPart=0;
  659. /* 注释六:
  660. * 每个月份的天数最大值是不相同的,在写入ds1302时钟芯片内部数据前,应该做一次调整。
  661. * 有的月份最大28天,有的月份最大29天,有的月份最大30天,有的月份最大31天,
  662. */
  663. ucDate=date_adjust(ucYear,ucMonth,ucDate); //日调整 防止日的数值在某个月份超范围
  664. ucTimerStart=0;//封闭守时器的时刻。在更改守时器内部时刻数据时,先封闭它,相当于原子锁的加锁效果。
  665. ucTimerYear=ucYear;//把设置和显现的数据更改到守时器内部的时刻变量
  666. ucTimerMonth=ucMonth;
  667. ucTimerDate=ucDate;
  668. ucTimerHour=ucHour;
  669. ucTimerMinute=ucMinute;
  670. ucTimerSecond=ucSecond;
  671. ucTimerStart=1;//翻开守时器的时刻。在更改守时器内部时刻数据后,再翻开它,相当于原子锁的解锁效果。
  672. }
  673. ucWd2Update=1;//窗口2更新显现
  674. }
  675. break;
  676. }
  677. ucVoiceLock=1;//原子锁加锁,维护主函数与中止函数的同享变量uiVoiceCnt
  678. uiVoiceCnt=const_voice_short; //按键声响触发,滴一声就停。
  679. ucVoiceLock=0;//原子锁解锁,维护主函数与中止函数的同享变量uiVoiceCnt
  680. ucKeySec=0;//呼应按键服务处理程序后,按键编号清零,防止共同触发
  681. break;
  682. case 17://长按3秒设置按键 对应朱兆祺学习板的S9键
  683. switch(ucWd)//在不同的窗口下,设置不同的参数
  684. {
  685. case 2:
  686. if(ucPart==0) //处于非设置时刻的状况下,要榜首次激活设置时刻,有必要是长按3秒才可以
  687. {
  688. ucWd=1;
  689. ucPart=1;//进入到设置日期的状况下
  690. ucWd1Update=1;//窗口1更新显现
  691. }
  692. break;
  693. }
  694. ucVoiceLock=1;//原子锁加锁,维护主函数与中止函数的同享变量uiVoiceCnt
  695. uiVoiceCnt=const_voice_short; //按键声响触发,滴一声就停。
  696. ucVoiceLock=0;//原子锁解锁,维护主函数与中止函数的同享变量uiVoiceCnt
  697. ucKeySec=0;//呼应按键服务处理程序后,按键编号清零,防止共同触发
  698. break;
  699. }
  700. /* 注释七:
  701. * 留意,此处便是榜首次呈现的电平按键程序,跟以往的下降沿按键不相同。
  702. * ucKey4Sr是通过软件滤波处理后,直接反响IO口电平状况的变量.当电平发生
  703. * 改变时,就会切换到不同的显现界面,这儿多用了一个ucKey4SrRecord变量
  704. * 记载上一次的电平状况,是为了防止一向改写显现。
  705. */
  706. if(ucKey4Sr!=ucKey4SrRecord)//阐明S13的切换按键电平状况发生改变
  707. {
  708. ucKey4SrRecord=ucKey4Sr;//及时记载当时最新的按键电平状况防止一向进来触发
  709. if(ucKey4Sr==1) //松手后切换到显现时刻的窗口
  710. {
  711. ucWd=2; //显现时分秒的窗口
  712. ucPart=0;//进入到非设置时刻的状况下
  713. ucWd2Update=1;//窗口2更新显现
  714. }
  715. else//按下去切换到显现日期的窗口
  716. {
  717. ucWd=1; //显现年月日的窗口
  718. ucPart=0;//进入到非设置时刻的状况下
  719. ucWd1Update=1;//窗口1更新显现
  720. }
  721. }
  722. }
  723. void display_drive(void)
  724. {
  725. //以下程序,假如加一些数组和移位的元素,还可以紧缩容量。可是鸿哥寻求的不是容量,而是明晰的解说思路
  726. switch(ucDisplayDriveStep)
  727. {
  728. case 1://显现第1位
  729. ucDigShowTemp=dig_table[ucDigShow1];
  730. if(ucDigDot1==1)
  731. {
  732. ucDigShowTemp=ucDigShowTemp|0x80;//显现小数点
  733. }
  734. dig_hc595_drive(ucDigShowTemp,0xfe);
  735. break;
  736. case 2://显现第2位
  737. ucDigShowTemp=dig_table[ucDigShow2];
  738. if(ucDigDot2==1)
  739. {
  740. ucDigShowTemp=ucDigShowTemp|0x80;//显现小数点
  741. }
  742. dig_hc595_drive(ucDigShowTemp,0xfd);
  743. break;
  744. case 3://显现第3位
  745. ucDigShowTemp=dig_table[ucDigShow3];
  746. if(ucDigDot3==1)
  747. {
  748. ucDigShowTemp=ucDigShowTemp|0x80;//显现小数点
  749. }
  750. dig_hc595_drive(ucDigShowTemp,0xfb);
  751. break;
  752. case 4://显现第4位
  753. ucDigShowTemp=dig_table[ucDigShow4];
  754. if(ucDigDot4==1)
  755. {
  756. ucDigShowTemp=ucDigShowTemp|0x80;//显现小数点
  757. }
  758. dig_hc595_drive(ucDigShowTemp,0xf7);
  759. break;
  760. case 5://显现第5位
  761. ucDigShowTemp=dig_table[ucDigShow5];
  762. if(ucDigDot5==1)
  763. {
  764. ucDigShowTemp=ucDigShowTemp|0x80;//显现小数点
  765. }
  766. dig_hc595_drive(ucDigShowTemp,0xef);
  767. break;
  768. case 6://显现第6位
  769. ucDigShowTemp=dig_table[ucDigShow6];
  770. if(ucDigDot6==1)
  771. {
  772. ucDigShowTemp=ucDigShowTemp|0x80;//显现小数点
  773. }
  774. dig_hc595_drive(ucDigShowTemp,0xdf);
  775. break;
  776. case 7://显现第7位
  777. ucDigShowTemp=dig_table[ucDigShow7];
  778. if(ucDigDot7==1)
  779. {
  780. ucDigShowTemp=ucDigShowTemp|0x80;//显现小数点
  781. }
  782. dig_hc595_drive(ucDigShowTemp,0xbf);
  783. break;
  784. case 8://显现第8位
  785. ucDigShowTemp=dig_table[ucDigShow8];
  786. if(ucDigDot8==1)
  787. {
  788. ucDigShowTemp=ucDigShowTemp|0x80;//显现小数点
  789. }
  790. dig_hc595_drive(ucDigShowTemp,0x7f);
  791. break;
  792. }
  793. ucDisplayDriveStep++;
  794. if(ucDisplayDriveStep>8)//扫描完8个数码管后,从头从榜首个开端扫描
  795. {
  796. ucDisplayDriveStep=1;
  797. }
  798. }
  799. //数码管的74HC595驱动函数
  800. void dig_hc595_drive(unsigned char ucDigStatusTemp16_09,unsigned char ucDigStatusTemp08_01)
  801. {
  802. unsigned char i;
  803. unsigned char ucTempData;
  804. dig_hc595_sh_dr=0;
  805. dig_hc595_st_dr=0;
  806. ucTempData=ucDigStatusTemp16_09;//先送高8位
  807. for(i=0;i<8;i++)
  808. {
  809. if(ucTempData>=0x80)dig_hc595_ds_dr=1;
  810. else dig_hc595_ds_dr=0;
  811. dig_hc595_sh_dr=0; //SH引脚的上升沿把数据送入寄存器
  812. delay_short(1);
  813. dig_hc595_sh_dr=1;
  814. delay_short(1);
  815. ucTempData=ucTempData<<1;
  816. }
  817. ucTempData=ucDigStatusTemp08_01;//再先送低8位
  818. for(i=0;i<8;i++)
  819. {
  820. if(ucTempData>=0x80)dig_hc595_ds_dr=1;
  821. else dig_hc595_ds_dr=0;
  822. dig_hc595_sh_dr=0; //SH引脚的上升沿把数据送入寄存器
  823. delay_short(1);
  824. dig_hc595_sh_dr=1;
  825. delay_short(1);
  826. ucTempData=ucTempData<<1;
  827. }
  828. dig_hc595_st_dr=0;//ST引脚把两个寄存器的数据更新输出到74HC595的输出引脚上而且锁存起来
  829. delay_short(1);
  830. dig_hc595_st_dr=1;
  831. delay_short(1);
  832. dig_hc595_sh_dr=0; //拉低,抗干扰就增强
  833. dig_hc595_st_dr=0;
  834. dig_hc595_ds_dr=0;
  835. }
  836. //LED灯的74HC595驱动函数
  837. void hc595_drive(unsigned char ucLedStatusTemp16_09,unsigned char ucLedStatusTemp08_01)
  838. {
  839. unsigned char i;
  840. unsigned char ucTempData;
  841. hc595_sh_dr=0;
  842. hc595_st_dr=0;
  843. ucTempData=ucLedStatusTemp16_09;//先送高8位
  844. for(i=0;i<8;i++)
  845. {
  846. if(ucTempData>=0x80)hc595_ds_dr=1;
  847. else hc595_ds_dr=0;
  848. hc595_sh_dr=0; //SH引脚的上升沿把数据送入寄存器
  849. delay_short(1);
  850. hc595_sh_dr=1;
  851. delay_short(1);
  852. ucTempData=ucTempData<<1;
  853. }
  854. ucTempData=ucLedStatusTemp08_01;//再先送低8位
  855. for(i=0;i<8;i++)
  856. {
  857. if(ucTempData>=0x80)hc595_ds_dr=1;
  858. else hc595_ds_dr=0;
  859. hc595_sh_dr=0; //SH引脚的上升沿把数据送入寄存器
  860. delay_short(1);
  861. hc595_sh_dr=1;
  862. delay_short(1);
  863. ucTempData=ucTempData<<1;
  864. }
  865. hc595_st_dr=0;//ST引脚把两个寄存器的数据更新输出到74HC595的输出引脚上而且锁存起来
  866. delay_short(1);
  867. hc595_st_dr=1;
  868. delay_short(1);
  869. hc595_sh_dr=0; //拉低,抗干扰就增强
  870. hc595_st_dr=0;
  871. hc595_ds_dr=0;
  872. }
  873. void T0_time(void) interrupt 1 //守时中止
  874. {
  875. TF0=0;//铲除中止标志
  876. TR0=0; //关中止
  877. /* 注释八:
  878. * 以下是本节内容的中心程序,是守时器内部发生的时刻。const_timer_1s这个是发生多少次守时中止才
  879. * 算1秒钟的规范。这个规范决议了时钟的精度。这个规范终究是需求校验的。
  880. */
  881. if(ucTimerStart==1)//守时器的时刻现已翻开
  882. {
  883. uiTimerCnt++;//发生1秒钟的时基
  884. if(uiTimerCnt>=const_timer_1s) //一秒钟的时刻到。这个const_timer_1s详细数值终究需求校验得出。
  885. {
  886. uiTimerCnt=0; //清零为发生下一个1秒钟预备
  887. ucTimerUpdate=1; //守时器每1秒钟所发生的标志,告诉主函数及时更新收集时刻数据
  888. ucTimerSecond++; //秒时刻累加1
  889. if(ucTimerSecond>=60)
  890. {
  891. ucTimerSecond=0;
  892. ucTimerMinute++; //分时刻累加1
  893. if(ucTimerMinute>=60)
  894. {
  895. ucTimerMinute=0;
  896. ucTimerHour++;//小时的时刻累加1,为了防止if的嵌套过多,把小时的判别放到外面两层的if来持续判别
  897. }
  898. }
  899. if(ucTimerHour>=24)
  900. {
  901. ucTimerHour=0;
  902. ucTimerDate++; //天时刻累加1
  903. ucTimerDateMax=get_date(ucTimerYear,ucTimerMonth);//依据年和月获取当时月份的最大天数
  904. if(ucTimerDate>ucTimerDateMax)//
  905. {
  906. ucTimerDate=1; //每个月都是从1号开端
  907. ucTimerMonth++;//月时刻累加1
  908. if(ucTimerMonth>12)
  909. {
  910. ucTimerMonth=1; //每年从1月份开端
  911. ucTimerYear++; //年时刻累加1
  912. if(ucTimerYear>99) //本体系的最高有用年份是2099年
  913. {
  914. ucTimerYear=99;
  915. }
  916. }
  917. }
  918. }
  919. }
  920. }
  921. if(ucVoiceLock==0) //原子锁判别
  922. {
  923. if(uiVoiceCnt!=0)
  924. {
  925. uiVoiceCnt–; //每次进入守时中止都自减1,直到等于零中止。才中止鸣叫
  926. beep_dr=0;//蜂鸣器是PNP三极管操控,低电平就开端鸣叫。
  927. }
  928. else
  929. {
  930. ; //此处多加一个空指令,想保持跟if括号句子的数量对称,都是两条指令。不加也可以。
  931. beep_dr=1;//蜂鸣器是PNP三极管操控,高电平就中止鸣叫。
  932. }
  933. }
  934. if(ucDpyTimeLock==0) //原子锁判别
  935. {
  936. uiDpyTimeCnt++;//数码管的闪耀计时器
  937. }
  938. key_scan(); //按键扫描函数
  939. display_drive();//数码管字模的驱动函数
  940. TH0=0xfe; //重装初始值(65535-500)=65035=0xfe0b
  941. TL0=0x0b;
  942. TR0=1;//开中止
  943. }
  944. void delay_short(unsigned int uiDelayShort)
  945. {
  946. unsigned int i;
  947. for(i=0;i
  948. {
  949. ; //一个分号相当于履行一条空句子
  950. }
  951. }
  952. void delay_long(unsigned int uiDelayLong)
  953. {
  954. unsigned int i;
  955. unsigned int j;
  956. for(i=0;i
  957. {
  958. for(j=0;j<500;j++)//内嵌循环的空指令数量
  959. {
  960. ; //一个分号相当于履行一条空句子
  961. }
  962. }
  963. }
  964. void initial_myself(void)//榜首区 初始化单片机
  965. {
  966. key_gnd_dr=0; //模仿独立按键的地GND,因而有必要一向输出低电平
  967. beep_dr=1; //用PNP三极管操控蜂鸣器,输出高电平时不叫。
  968. hc595_drive(0x00,0x00);//封闭一切通过别的两个74HC595驱动的LED灯
  969. TMOD=0x01;//设置守时器0为工作方式1
  970. TH0=0xfe; //重装初始值(65535-500)=65035=0xfe0b
  971. TL0=0x0b;
  972. }
  973. void initial_peripheral(void) //第二区 初始化外围
  974. {
  975. ucDigDot8=0; //小数点悉数不显现
  976. ucDigDot7=0;
  977. ucDigDot6=0;
  978. ucDigDot5=0;
  979. ucDigDot4=0;
  980. ucDigDot3=0;
  981. ucDigDot2=0;
  982. ucDigDot1=0;
  983. EA=1; //开总中止
  984. ET0=1; //答应守时中止
  985. TR0=1; //发动守时中止
  986. }
总结陈词:
任何一个电子产品在投入出产的时分都要考虑到出产的测验,朱兆祺51单片机学习板在出产加工后也相同要进行测验。那么这个测验的程序怎么可以做到快速,全面,易用这三个要求呢?欲知概况,请听下回分解—–出产朱兆祺51学习板的从机自检测验程序源代码.。

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

为您推荐

联系我们

联系我们

在线咨询: QQ交谈

邮箱: kf@86ic.com

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

微信扫一扫关注我们

返回顶部