1、 数码之家-我的数字生活,分享创意无限!http:/ 吴坚鸿吴坚鸿 数码之家-我的数字生活,分享创意无限!http:/ 90 个章节,着实耗费了吴老师很多心血。这里同样是为了方便大家观看帖子内容而做的资料整理,同样电子资料附带程序代码,需要代码的朋友也可在老师的原帖里代码结尾可以直接点击复制,也请大家多多支持吴坚鸿老师。原文地址:从业将近十年!手把手教你单片机程序框架(连载)http:/ ID:jianhong_wu 本电子书由数码之家会员 zzc_qyc 整理,为了方便大家阅读,制作成 PDF 模式,每一章节都有标签,能够快速找到你所需要的章节。数码之家-我的数字生活,分享创意无限!http
2、:/ 第一节:吴坚鸿谈初学单片机的误区.6 第二节:delay()延时实现 LED 灯的闪烁.7 第三节:累计主循环次数使 LED 灯闪烁.11 第四节:累计定时中断次数使 LED 灯闪烁.14 第五节:蜂鸣器的驱动程序.19 第六节:在主函数中利用累计主循环次数来实现独立按键的检测.24 第七节:在主函数中利用累计定时中断的次数来实现独立按键的检测.29 第八节:在定时中断函数里执行独立按键的扫描程序.35 第九节:独立按键的双击按键触发.40 第十节:两个独立按键的组合按键触发.46 第十一节:同一个按键短按与长按的区别触发.51 第十二节:按住一个独立按键不松手的连续步进触发.57 第十
3、三节:按住一个独立按键不松手的加速匀速触发.64 第十四节:矩阵键盘的单个触发.73 第十五节:矩阵键盘单个触发的压缩代码编程.86 第十六节:矩阵键盘的组合按键触发.95 第十七节:两片联级 74HC595 驱动 16 个 LED 灯的基本驱动程序.111 第十八节:把 74HC595 驱动程序翻译成类似单片机 IO 口直接驱动的方式.116 第十九节:依次逐个点亮 LED 之后,再依次逐个熄灭 LED 的跑马灯程序.126 第二十节:依次逐个亮灯并且每次只能亮一个灯的跑马灯程序.138 第二十一节:多任务并行处理两路跑马灯.149 第二十二节:独立按键控制跑马灯的方向.164 第二十三节:
4、独立按键控制跑马灯的速度.179 第二十四节:独立按键控制跑马灯的启动和暂停.195 第二十五节:用 LED 灯和按键来模拟工业自动化设备的运动控制.212 第二十六节:在主函数 while 循环中驱动数码管的动态扫描程序.227 第二十七节:在定时中断里动态扫描数码管的程序.236 第二十八节:数码管通过切换窗口来设置参数.245 第二十九节:数码管通过切换窗口来设置参数,并且不显示为 0 的高位.260 第三十节:数码管通过闪烁来设置数据.277 第三十一节:数码管通过一二级菜单来设置数据的综合程序.298 第三十二节:数码管中的倒计时程序.321 第三十三节:能设置速度档位的数码管倒计时
5、程序.335 第三十四节:在数码管中实现 iphone4S 开机密码锁的程序.353 第三十五节:带数码管显示的象棋比赛专用计时器.372 第三十六节:带数码管显示的加法简易计算器.397 第三十七节:数码管作为仪表盘显示跑马灯的方向,速度和运行状态.417 第三十八节:判断数据尾来接收一串数据的串口通用程序框架.441 第三十九节:判断数据头来接收一串数据的串口通用程序框架.446 数码之家-我的数字生活,分享创意无限!http:/ 第四十一节:在串口接收中断里即时解析数据头的特殊程序框架.459 第四十二节:通过串口用 delay 延时方式发送一串数据.466 第四十三节:通过串口用计数延
6、时方式发送一串数据.473 第四十四节:从机的串口收发综合程序框架.486 第四十五节:主机的串口收发综合程序框架.512 第四十六节:利用 AT24C02 进行掉电后的数据保存.539 第四十七节:操作 AT24C02 时,利用“一气呵成的定时器延时”改善数码管的闪烁现象.563 第四十八节:利用 DS1302 做一个实时时钟.588 第四十九节:利用 DS18B20 做一个温控器.620 第五十节:利用 ADC0832 采集电压信号,用平均法和区间法进行软件滤波处理.637 第五十一节:利用 ADC0832 采集电压信号,用连续 N 次一致性的方法进行滤波处理.650 第五十二节:程序后续
7、升级修改的利器,return 语句鲜为人知的用法.662 第五十三节:指针的第一大好处,让一个函数可以封装多个相当于 return 语句返回的参数.668 第五十四节:指针的第二大好处,指针作为数组在函数中的输入接口.677 第五十五节:指针的第三大好处,指针作为数组在函数中的输出接口.685 第五十六节:指针的第四大好处,指针作为数组在函数中的输入输出接口.694 第五十七节:为指针加上紧箍咒 const,避免意外修改了只做输入接口的数据.702 第五十八节:指针的第五大好处,指针在众多数组中的中转站作用.711 第五十九节:串口程序第 40,44,45 节中存在一个 bug,特此紧急公告.
8、720 第六十节:用关中断和互斥量来保护多线程共享的全局变量.721 第六十一节:组合 BCD 码,非组合 BCD 码,以及数值三者之间的相互转换和关系.725 第六十二节:大数据的加法运算.738 第六十三节:大数据的减法运算.751 第六十四节:大数据的乘法运算.765 第六十五节:大数据的除法运算.780 第六十六节:单片机外部中断的基础.806 第六十七节:利用外部中断实现模拟串口数据的收发.815 第六十八节:单片机 C 语言的多文件编程技巧.822 第六十九节:使用 static 关键字可以减少全局变量的使用.830 第七十节:深入讲解液晶屏的构字过程.835 第七十一节:液晶屏的
9、字符,16 点阵,24 点阵和 32 点阵的显示程序.841 第七十二节:在液晶屏中把字体顺时针旋转 90 度显示的算法程序.847 第七十三节:在液晶屏中把字体镜像显示的算法程序.855 第七十四节:在液晶屏中让字体可以跨区域无缝对接显示的算法程序.862 第七十五节:在 12864 液晶屏中让字体以 1 个点阵为单位进行移动显示的算法程序.869 第七十六节:如何把一个任意数值的变量显示在液晶屏上.880 第七十七节:在 1 个窗口里通过移动光标来设置不同参数的液晶屏菜单程序.891 第七十八节:在多个窗口里通过移动光标来设置不同参数的液晶屏菜单程序.916 第七十九节:通过主菜单移动光标
10、来进入子菜单窗口的液晶屏程序.949 第八十节:调用液晶屏内部字库来显示汉字或字符的坐标体系和本质.984 第八十一节:液晶屏显示串口发送过来的任意汉字和字符.990 第八十二节:如何通过调用液晶屏内部字库把一个任意数值的变量显示出来.1001 第八十三节:矩阵键盘输入任意数字或小数点的液晶屏显示程序.1009 数码之家-我的数字生活,分享创意无限!http:/ BCD 码数组转换成数值的液晶屏显示程序.1033 第八十五节:实时同步把加减按键输入的数值转换成 BCD 码数组的液晶屏显示程序.1061 第八十六节:数字键盘与液晶菜单的综合程序.1094 第八十七节:郑文显捐赠的工控项目源代码.
11、1133 第八十八节:电子称连续不断从串口对外发送数据,单片机靠关键字快速截取有效数据串.1160 第八十九节:用单片机内部定时器做一个时钟.1173 第九十节:针对行程开关感应器,分享一种既能及时响应,又能抗干扰处理的识别思路.1201 数码之家-我的数字生活,分享创意无限!http:/ 语言很难学?你不用学指针,你不用学带形参的函数,你不用学结构体,你不用学宏定义,你不用学文件操作,你也不用死记繁琐的数据类型。你只要会:5 条指令语句 switch 语句,if else 语句,while 语句,for 语句,=赋值语句。7 个运算符+,-,*,/,|,&,!。4 个逻辑关系符|,&,!=,
12、=.3 个数据类型 unsigned char,unsigned int,unsigned long。3 个进制相互转化,二进制,十六进制,十进制。1 个 void 函数。1 个一维数组 code(或 const)unsigned char array。那么世界上任何一种逻辑功能的单片机软件你都能做出来。鸿哥我当年刚毕业出来工作的时候才知道可以用 C 语言开发单片机,一开始只用 if 语句就把项目做出来了,没有用指针,没有用带形参的函数等复杂的功能。再到后来才慢慢开始用 C 语言其他的高级功能,但是我发现 C语言其他的高级功能,本质上都是用我前面列举出来的最基本功能集合而成,只是书写更加简单方
13、便了一点,编译后的机器码都大同小异。所以不会指针等高级功能你不用自卑,恰恰相反,当你会最简单的几个语句,就把这些高级功能的程序都做出来了,你才发现你对底层了解得更加透切,再学那些高级功能轻而易举。当你裸机跑的程序都能够协调得很好的时候,你才发现所谓高深的操作系统也不过如此,只要给你时间和金钱你也可以写个操作系统来玩玩。(4)很难记住精确时间的计算公式?经常看到时间公式等于晶振,时钟周期,执行指令次数他们之间的乘除关系式。鸿哥我认为这些都是浮云,不用纠结也不用去记,大概了解一下就可以了。不管你对公式掌握得有多精确,你都不可能做出非常精确的时间。想用单片机做一个非常精确的时间这种想法一开始就是错的
14、,不可能的。真想做一个比较精确的时间,应该用外围时钟芯片或者 FPGA 和 CPLD,而不是单片机。(5)很难记住繁杂的各种通信协议?什么 IIC,SPI,232 串口通讯,CAN,USB 等等。这些都是浮云,你不用记那么多,你只要理解两种通讯方式就够了,那就是串行通讯方式和并行通讯方式。不管世界上有多少种通讯协议,物理世界上只有这两种通讯方式,其他各种名称的通讯协议都基于此两种方式演变而来。(6)很难写短小精悍的程序?初学者不要纠结于此。做项目开发,程序容量不是刻意追求的目标,程序多一点少一点没关系,现在大容量的单片机品种非常多,容量不会是寸土寸金的事情,我们更加要关注程序的运行效率,可读性
15、和可修改性。既然鸿哥列出了那么多误区,那么什么才是初学者关注的核心?预知详情,请听下回分解-delay()延时实现 LED 灯的闪烁。数码之家-我的数字生活,分享创意无限!http:/ LED 灯的闪烁。灯的闪烁。开场白:上一节鸿哥列出了初学者六大误区,到底什么才是初学者关注的核心?那就是裸机奔跑的程序结构。一个好的程序结构,本身就是一个微型的多任务操作系统。鸿哥教给大家的就是如何编写这个简单的操作系统。在 main函数循环中用 switch 语句实现多任务并行处理的任务切换,再外加一个定时器中断,这两者的结合就是鸿哥多年来所有实战项目的核心。鸿哥的程序结构看似简单,实际上就是那么简单。大家不
16、用着急,本篇连载文章现在才正式开始,这一节我要教会大家两个知识点:第一点:鸿哥首次提出的“三区一线”理论。此理论把程序代码分成三个区,一个延时分割线。第二点:delay()延时的用途。(1)硬件平台:基于朱兆祺 51 单片机学习板。(2)实现功能:让一个 LED 闪烁。(3)源代码讲解如下:#include REG52.H void initial_myself();void initial_peripheral();void delay_short(unsigned int uiDelayshort);void delay_long(unsigned int uiDelaylong);voi
17、d led_flicker();/*注释一:*吴坚鸿个人的命名风格:凡是输出后缀都是_dr,凡是输入后缀都是_sr。*dr 代表 drive 驱动,sr 代表 sensor 感应器*/sbit led_dr=P35;void main()/学习要点:深刻理解鸿哥首次提出的三区一线理论 /*注释二:*initial_myself()函数属于鸿哥三区一线理论的第一区,*专门用来初始化单片机自己的寄存器以及个别外围要求响应速度快的输出设备,*防止刚上电之后,由于输出 IO 口电平状态不确定而导致外围设备误动作,*比如继电器的误动作等等。*/initial_myself();/*注释三:数码之家-我的
18、数字生活,分享创意无限!http:/ delay_long()延时函数属于第一区与第二区的分割线,*延时时间一般是 0.3 秒到 2 秒之间,等待外围芯片和模块上电稳定。*比如液晶模块,AT24C02 存储芯片,DS1302 时钟芯片,*这类芯片有个特点,一般都是跟单片机进行串口或并口通讯的,*并且不要求上电立即处理的。*/delay_long(100);/*注释四:*initial_peripheral()函数属于鸿哥三区一线理论的第二区,*专门用来初始化不要求上电立即处理的外围芯片和模块.*比如液晶模块,AT24C02 存储芯片,DS1302 时钟芯片。*本程序基于朱兆祺 51 单片机学习
19、板。*/initial_peripheral();/*注释五:*while(1)主函数循环区属于鸿哥三区一线理论的第三区,*专门用来编写被循环扫描到的非中断应用程序*/while(1)led_flicker();/LED 闪烁应用程序 void led_flicker()/LED 闪烁应用程序 led_dr=1;/LED 亮 delay_short(50000);/延时 50000 个空指令的时间 /*注释六:*delay_long(100)延时 50000 个空指令的时间,因为内嵌了一个 500 次的 for 循环*/led_dr=0;/LED 灭 delay_long(100);/延时 5
20、0000 个空指令的时间 /*注释七:*delay_short(unsigned int uiDelayShort)是小延时函数,*专门用在时序驱动的小延时,一般 uiDelayShort 的数值取 10 左右,*最大一般也不超过 100.本例为了解释此函数的特点,取值范围超过 100。数码之家-我的数字生活,分享创意无限!http:/ 数值*的大小就代表里面执行了多少条空指令的时间。数值越大,延时越长。*时间精度不要刻意去计算,感觉差不多就行。*/void delay_short(unsigned int uiDelayShort)unsigned int i;for(i=0;iuiDela
21、yShort;i+);/一个分号相当于执行一条空语句 /*注释八:*delay_long(unsigned int uiDelayLong)是大延时函数,*专门用在上电初始化的大延时,*此函数的特点是能实现比较长时间的延时,细分度取决于内嵌 for 循环的次数,*uiDelayLong 的数值的大小就代表里面执行了多少次 500 条空指令的时间。*数值越大,延时越长。时间精度不要刻意去计算,感觉差不多就行。*/void delay_long(unsigned int uiDelayLong)unsigned int i;unsigned int j;for(i=0;iuiDelayLong;i
22、+)for(j=0;j=const_time_level)/时间到 uiTimeCnt=0;/时间计数器清零 led_dr=1;/让 LED 亮 ucLedStep=1;/切换到下一个步骤 break;case 1:uiTimeCnt+;/累加循环次数,if(uiTimeCnt=const_time_level)/时间到 uiTimeCnt=0;/时间计数器清零 led_dr=0;/让 LED 灭 ucLedStep=0;/返回到上一个步骤 break;void delay_long(unsigned int uiDelayLong)unsigned int i;unsigned int j;
23、for(i=0;iuiDelayLong;i+)for(j=0;j=const_time_level)/时间到 /*注释二:*ET0=0;uiTimeCnt=0;ET0=1;-在清零 uiTimeCnt 之前,为什么要先禁止定时中断?*因为 uiTimeCnt 是 unsigned int 类型,本质上是由两个字节组成。*在 C 语言中 uiTimeCnt=0 看似一条指令,实际上经过编译之后它不只一条汇编指令。*由于定时中断函数里也对这个变量进行累加操作,如果不禁止定时中断,*那么 uiTimeCnt 这个变量在 main()函数中还没被完全清零的时候,如果这个时候*突然来一个定时中断,并且
24、在中断里又更改了此变量,这种情况在某些要求高的*项目上会是一个不容易察觉的漏洞,为项目带来隐患。当然,大部分的普通项目,*都可以不用那么严格,可以不用禁止定时中断。在这里只是提醒各位初学者有这种情况。*/ET0=0;/禁止定时中断 uiTimeCnt=0;/时间计数器清零 ET0=1;/开启定时中断 led_dr=1;/让 LED 亮 ucLedStep=1;/切换到下一个步骤 break;case 1:if(uiTimeCnt=const_time_level)/时间到 ET0=0;/禁止定时中断 数码之家-我的数字生活,分享创意无限!http:/ ET0=1;/开启定时中断 led_dr=
25、0;/让 LED 灭 ucLedStep=0;/返回到上一个步骤 break;/*注释三:*C51 的中断函数格式如下:*void 函数名()interrupt 中断号*中断程序内容*函数名可以随便取,只要不是编译器已经征用的关键字。*这里最关键的是中断号,不同的中断号代表不同类型的中断。*定时中断的中断号是 1.至于其它中断的中断号,大家可以查找*相关书籍和资料。大家进入中断时,必须先清除中断标志,并且*关闭中断,然后再写代码,最后出来时,记得重装初始值,并且*打开中断。*/void T0_time()interrupt 1 TF0=0;/清除中断标志 TR0=0;/关中断 if(uiTim
26、eCnt0 xffff)/设定这个条件,防止 uiTimeCnt 超范围。uiTimeCnt+;/累加定时中断的次数,TH0=0 xf8;/重装初始值(65535-2000)=63535=0 xf82f TL0=0 x2f;TR0=1;/开中断 void delay_long(unsigned int uiDelayLong)unsigned int i;数码之家-我的数字生活,分享创意无限!http:/ int j;for(i=0;iuiDelayLong;i+)for(j=0;j=const_time_05s)/时间到 uiTimeLedCnt=0;/时间计数器清零 led_dr=1;/让
27、 LED 亮 ucLedStep=1;/切换到下一个步骤 数码之家-我的数字生活,分享创意无限!http:/ 1:if(uiTimeLedCnt=const_time_05s)/时间到 uiTimeLedCnt=0;/时间计数器清零 led_dr=0;/让 LED 灭 ucLedStep=0;/返回到上一个步骤 break;void alarm_run()/第三区 报警器的应用程序 switch(ucAlarmStep)case 0:if(uiTimeAlarmCnt=const_time_3s)/时间到 uiTimeAlarmCnt=0;/时间计数器清零/*注释二:*只要变量 uiVoice
28、Cnt 不为 0,蜂鸣器就会在定时中断函数里启动鸣叫,并且自减 uiVoiceCnt*直到 uiVoiceCnt 为 0 时才停止鸣叫。因此控制 uiVoiceCnt 变量的大小就是控制声音的长短。*/uiVoiceCnt=const_voice_short;/蜂鸣器短叫 ucAlarmStep=1;/切换到下一个步骤 break;case 1:if(uiTimeAlarmCnt=const_time_6s)/时间到 uiTimeAlarmCnt=0;/时间计数器清零 uiVoiceCnt=const_voice_long;/蜂鸣器长叫 ucAlarmStep=0;/返回到上一个步骤 brea
29、k;void T0_time()interrupt 1 数码之家-我的数字生活,分享创意无限!http:/ TR0=0;/关中断 if(uiTimeLedCnt0 xffff)/设定这个条件,防止 uiTimeLedCnt 超范围。uiTimeLedCnt+;/LED 灯的时间计数器,累加定时中断的次数,if(uiTimeAlarmCnt0 xffff)/设定这个条件,防止 uiTimeAlarmCnt 超范围。uiTimeAlarmCnt+;/报警的时间计数器,累加定时中断的次数,/*注释三:*为什么不把驱动蜂鸣器这段代码放到 main 函数的循环里去?*因为放在定时中断里,能保证蜂鸣器的声
30、音长度是一致的,*如果放在 main 循环里,声音的长度就有可能受到某些必须*一气呵成的任务干扰,得不到及时响应,影响声音长度的一致性。*/if(uiVoiceCnt!=0)uiVoiceCnt-;/每次进入定时中断都自减 1,直到等于零为止。才停止鸣叫 beep_dr=0;/蜂鸣器是 PNP 三极管控制,低电平就开始鸣叫。else ;/此处多加一个空指令,想维持跟 if 括号语句的数量对称,都是两条指令。不加也可以。beep_dr=1;/蜂鸣器是 PNP 三极管控制,高电平就停止鸣叫。TH0=0 xf8;/重装初始值(65535-2000)=63535=0 xf82f TL0=0 x2f;T
31、R0=1;/开中断 void delay_long(unsigned int uiDelayLong)unsigned int i;数码之家-我的数字生活,分享创意无限!http:/ int j;for(i=0;iuiDelayLong;i+)for(j=0;jconst_key_time1)uiKeyTimeCnt1=0;ucKeyLock1=1;/自锁按键置位,避免一直触发 ucKeySec=1;/触发 1 号键 if(key_sr2=1)ucKeyLock2=0;uiKeyTimeCnt2=0;else if(ucKeyLock2=0)+uiKeyTimeCnt2;if(uiKeyTim
32、eCnt2const_key_time2)uiKeyTimeCnt2=0;ucKeyLock2=1;ucKeySec=2;/触发 2 号键 void key_service()/第三区 按键服务的应用程序 switch(ucKeySec)/按键服务状态切换 case 1:/1 号键 对应朱兆祺学习板的 S1 键 uiVoiceCnt=const_voice_short;/按键声音触发,滴一声就停。ucKeySec=0;/响应按键服务处理程序后,按键编号清零,避免一致触发 break;case 2:/2 号键 对应朱兆祺学习板的 S5 键 uiVoiceCnt=const_voice_short
33、;/按键声音触发,滴一声就停。ucKeySec=0;/响应按键服务处理程序后,按键编号清零,避免一致触发 数码之家-我的数字生活,分享创意无限!http:/ T0_time()interrupt 1 TF0=0;/清除中断标志 TR0=0;/关中断 if(uiVoiceCnt!=0)uiVoiceCnt-;/每次进入定时中断都自减 1,直到等于零为止。才停止鸣叫 beep_dr=0;/蜂鸣器是 PNP 三极管控制,低电平就开始鸣叫。else ;/此处多加一个空指令,想维持跟 if 括号语句的数量对称,都是两条指令。不加也可以。beep_dr=1;/蜂鸣器是 PNP 三极管控制,高电平就停止鸣叫
34、。TH0=0 xf8;/重装初始值(65535-2000)=63535=0 xf82f TL0=0 x2f;TR0=1;/开中断 void delay_long(unsigned int uiDelayLong)unsigned int i;unsigned int j;for(i=0;iuiDelayLong;i+)for(j=0;jconst_key_time1)ucKeyStartFlag1=0;/停止计数器 uiKeyTimeCnt1=0;ucKeyLock1=1;/自锁按键置位,避免一直触发 ucKeySec=1;/触发 1 号键 if(key_sr2=1)ucKeyLock2=0;
35、ucKeyStartFlag2=0;/停止计数器 uiKeyTimeCnt2=0;else if(ucKeyLock2=0)ucKeyStartFlag2=1;/启动计数器 if(uiKeyTimeCnt2const_key_time2)ucKeyStartFlag2=0;/停止计数器 uiKeyTimeCnt2=0;ucKeyLock2=1;ucKeySec=2;/触发 2 号键 void key_service()/第三区 按键服务的应用程序 switch(ucKeySec)/按键服务状态切换 case 1:/1 号键 对应朱兆祺学习板的 S1 键 uiVoiceCnt=const_voi
36、ce_short;/按键声音触发,滴一声就停。ucKeySec=0;/响应按键服务处理程序后,按键编号清零,避免一致触发 数码之家-我的数字生活,分享创意无限!http:/ 2:/2 号键 对应朱兆祺学习板的 S5 键 uiVoiceCnt=const_voice_short;/按键声音触发,滴一声就停。ucKeySec=0;/响应按键服务处理程序后,按键编号清零,避免一致触发 break;void T0_time()interrupt 1 TF0=0;/清除中断标志 TR0=0;/关中断 if(ucKeyStartFlag1=1)/启动计数器 if(uiKeyTimeCnt10 xffff)
37、/防止计数器超范围 uiKeyTimeCnt1+;if(ucKeyStartFlag2=1)/启动计数器 if(uiKeyTimeCnt20 xffff)/防止计数器超范围 uiKeyTimeCnt2+;if(uiVoiceCnt!=0)uiVoiceCnt-;/每次进入定时中断都自减 1,直到等于零为止。才停止鸣叫 beep_dr=0;/蜂鸣器是 PNP 三极管控制,低电平就开始鸣叫。else ;/此处多加一个空指令,想维持跟 if 括号语句的数量对称,都是两条指令。不加也可以。beep_dr=1;/蜂鸣器是 PNP 三极管控制,高电平就停止鸣叫。数码之家-我的数字生活,分享创意无限!htt
38、p:/ TL0=0 x2f;TR0=1;/开中断 void delay_long(unsigned int uiDelayLong)unsigned int i;unsigned int j;for(i=0;iuiDelayLong;i+)for(j=0;jconst_key_time1)uiKeyTimeCnt1=0;ucKeyLock1=1;/自锁按键置位,避免一直触发 ucKeySec=1;/触发 1 号键 if(key_sr2=1)ucKeyLock2=0;uiKeyTimeCnt2=0;else if(ucKeyLock2=0)uiKeyTimeCnt2+;/累加定时中断次数 if(
39、uiKeyTimeCnt2const_key_time2)uiKeyTimeCnt2=0;ucKeyLock2=1;ucKeySec=2;/触发 2 号键 void key_service()/第三区 按键服务的应用程序 switch(ucKeySec)/按键服务状态切换 case 1:/1 号键 对应朱兆祺学习板的 S1 键 uiVoiceCnt=const_voice_short;/按键声音触发,滴一声就停。ucKeySec=0;/响应按键服务处理程序后,按键编号清零,避免一致触发 break;case 2:/2 号键 对应朱兆祺学习板的 S5 键 uiVoiceCnt=const_voi
40、ce_short;/按键声音触发,滴一声就停。ucKeySec=0;/响应按键服务处理程序后,按键编号清零,避免一致触发 break;数码之家-我的数字生活,分享创意无限!http:/ T0_time()interrupt 1 TF0=0;/清除中断标志 TR0=0;/关中断 key_scan();/按键扫描函数 if(uiVoiceCnt!=0)uiVoiceCnt-;/每次进入定时中断都自减 1,直到等于零为止。才停止鸣叫 beep_dr=0;/蜂鸣器是 PNP 三极管控制,低电平就开始鸣叫。else ;/此处多加一个空指令,想维持跟 if 括号语句的数量对称,都是两条指令。不加也可以。b
41、eep_dr=1;/蜂鸣器是 PNP 三极管控制,高电平就停止鸣叫。TH0=0 xf8;/重装初始值(65535-2000)=63535=0 xf82f TL0=0 x2f;TR0=1;/开中断 void delay_long(unsigned int uiDelayLong)unsigned int i;unsigned int j;for(i=0;iuiDelayLong;i+)for(j=0;j0)/之前已经有按键触发过一次,再来一次就构成双击 uiKeyIntervalCnt1+;/按键间隔的时间计数器累加 if(uiKeyIntervalCnt1const_interval_time
42、1)/超过最大允许的间隔时间 uiKeyIntervalCnt1=0;/时间计数器清零 ucKeyTouchCnt1=0;/清零按键的按下的次数 else if(ucKeyLock1=0)/有按键按下,且是第一次被按下 uiKeyTimeCnt1+;/累加定时中断次数 if(uiKeyTimeCnt1const_key_time1)uiKeyTimeCnt1=0;ucKeyLock1=1;/自锁按键置位,避免一直触发 uiKeyIntervalCnt1=0;/按键有效间隔的时间计数器清零 ucKeyTouchCnt1+;if(ucKeyTouchCnt11)/连续被按了两次以上 ucKeyTo
43、uchCnt1=0;/统计按键次数清零 ucKeySec=1;/触发 1 号键 if(key_sr2=1)数码之家-我的数字生活,分享创意无限!http:/ if(uiKeyIntervalCnt2const_interval_time2)/超过最大允许的间隔时间 uiKeyIntervalCnt2=0;/时间计数器清零 ucKeyTouchCnt2=0;/清零按键的按下的次数 else if(ucKeyLock2=0)uiKeyTimeCnt2+;/累加定时中断次数 if(uiKeyTimeCnt2const_key_time2)uiKeyTimeCnt2=0;ucKeyLock2=1;ui
44、KeyIntervalCnt2=0;/按键有效间隔的时间计数器清零 ucKeyTouchCnt2+;if(ucKeyTouchCnt21)/连续被按了两次以上 ucKeyTouchCnt2=0;/统计按键次数清零 ucKeySec=2;/触发 2 号键 void key_service()/第三区 按键服务的应用程序 switch(ucKeySec)/按键服务状态切换 case 1:/1 号键 双击 对应朱兆祺学习板的 S1 键 uiVoiceCnt=const_voice_short;/按键声音触发,滴一声就停。ucKeySec=0;/响应按键服务处理程序后,按键编号清零,避免一致触发 br
45、eak;case 2:/2 号键 双击 对应朱兆祺学习板的 S5 键 数码之家-我的数字生活,分享创意无限!http:/ break;void T0_time()interrupt 1 TF0=0;/清除中断标志 TR0=0;/关中断 key_scan();/按键扫描函数 if(uiVoiceCnt!=0)uiVoiceCnt-;/每次进入定时中断都自减 1,直到等于零为止。才停止鸣叫 beep_dr=0;/蜂鸣器是 PNP 三极管控制,低电平就开始鸣叫。else ;/此处多加一个空指令,想维持跟 if 括号语句的数量对称,都是两条指令。不加也可以。beep_dr=1;/蜂鸣器是 PNP 三极
46、管控制,高电平就停止鸣叫。TH0=0 xf8;/重装初始值(65535-2000)=63535=0 xf82f TL0=0 x2f;TR0=1;/开中断 void delay_long(unsigned int uiDelayLong)unsigned int i;unsigned int j;for(i=0;iuiDelayLong;i+)for(j=0;jconst_key_time12)uiKeyTimeCnt12=0;ucKeyLock12=1;/自锁按键置位,避免一直触发 数码之家-我的数字生活,分享创意无限!http:/ 1 号键 void key_service()/第三区 按键
47、服务的应用程序 switch(ucKeySec)/按键服务状态切换 case 1:/1 号键 组合按键 对应朱兆祺学习板的 S1 键和 S5 键 uiVoiceCnt=const_voice_short;/按键声音触发,滴一声就停。ucKeySec=0;/响应按键服务处理程序后,按键编号清零,避免一致触发 break;void T0_time()interrupt 1 TF0=0;/清除中断标志 TR0=0;/关中断 key_scan();/按键扫描函数 if(uiVoiceCnt!=0)uiVoiceCnt-;/每次进入定时中断都自减 1,直到等于零为止。才停止鸣叫 beep_dr=0;/蜂
48、鸣器是 PNP 三极管控制,低电平就开始鸣叫。else ;/此处多加一个空指令,想维持跟 if 括号语句的数量对称,都是两条指令。不加也可以。beep_dr=1;/蜂鸣器是 PNP 三极管控制,高电平就停止鸣叫。数码之家-我的数字生活,分享创意无限!http:/ TL0=0 x2f;TR0=1;/开中断 void delay_long(unsigned int uiDelayLong)unsigned int i;unsigned int j;for(i=0;iuiDelayLong;i+)for(j=0;jconst_key_time_short1)ucShortTouchFlag1=1;/
49、激活按键短按的有效标志 if(uiKeyTimeCnt1const_key_time_long1)ucShortTouchFlag1=0;/清除按键短按的有效标志 uiKeyTimeCnt1=0;ucKeyLock1=1;/自锁按键置位,避免一直触发 ucKeySec=2;/触发 1 号键的长按 if(key_sr2=1)/IO 是高电平,说明两个按键没有全部被按下,这时要及时清零一些标志位 ucKeyLock2=0;/按键自锁标志清零 uiKeyTimeCnt2=0;/按键去抖动延时计数器清零,此行非常巧妙,是我实战中摸索出来的。if(ucShortTouchFlag2=1)/短按触发标志
50、ucShortTouchFlag2=0;ucKeySec=3;/触发 2 号键的短按 else if(ucKeyLock2=0)/有按键按下,且是第一次被按下 uiKeyTimeCnt2+;/累加定时中断次数 数码之家-我的数字生活,分享创意无限!http:/ if(uiKeyTimeCnt2const_key_time_long2)ucShortTouchFlag2=0;/清除按键短按的有效标志 uiKeyTimeCnt2=0;ucKeyLock2=1;/自锁按键置位,避免一直触发 ucKeySec=4;/触发 2 号键的长按 void key_service()/第三区 按键服务的应用程序