完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
|
|
|
|
本帖最后由 jianhong_wu 于 2012-12-24 03:31 编辑
第三节:AD按键扫描与蜂鸣器 (1) 开场白: AD按键可以实现一个IO口驱动几个按键的目的,以鸿哥的经验,一个IO口控制的按键数量最好不要超过3个。从实际应用来考虑,独立按键的方式最好,其次是行列按键,最次是AD按键。 (2)功能需求:每按一个按键,蜂鸣器就响一次。 (3)硬件原理: (a)用4个电阻竖着串联起来,最上端接5V,最下端接地。从上往下,最上端接5V的算第一个节点,最下端接地算最后一个节点,共5个节点。用一个带AD的IO口连接到第2个节点上,此节点上连接第一个按键,按键的另一端接地。以此方式,第二个按键连接到第3个节点,第三个按键连接到第4个节点。这4个电阻的目的主要是用来分压,靠不同的电压来识别不同的按键。 (b)用1个IO经过8050三极管来驱动有源蜂鸣器,有源蜂鸣器通电就一直响,断电就停止。 (4)源码适合的单片机:PIC16f73,晶振为4MHz (5)源代码讲解如下: #include //补充说明:吴坚鸿程序风格是这样的,凡是输出IO后缀都是_dr,凡是输入的//IO后缀都//是_sr #define beep_dr RB0 //蜂鸣器输出 //补充说明:吴坚鸿程序风格是这样的,凡是做延时计数阀值的常量 //前缀都用cnt_表示。凡是延时常量,都应该根据上机实际情况来调整出最佳的数值 #define cnt_delay_cnt1 40 //按键去抖动延时阀值 #define cnt_voice_time 75 //蜂鸣器响的声音长短的延时阀值 #define cnt_key_nc 185 //没有按键按下时电压对应的AD数值 #define cnt_key1_up 185 //1号按键按下时电压对应的AD数值上限 #define cnt_key2_down 117 //2号按键按下时电压对应的AD数值下限 #define cnt_key2_up 137 //2号按键按下时电压对应的AD数值上限 #define cnt_key3_down 160 //3号按键按下时电压对应的AD数值下限 #define cnt_key3_up 175 //3号按键按下时电压对应的AD数值上限 //补充说明:吴坚鸿程序风格是这样的,凡是按键扫描函数都放在定时中 //断里,凡是按键服务程序都是放在main函数循环里。有人说不应该把子程序//放在中断里,别听他们,信鸿哥无坎坷。 void key_scan(); //按键扫描函数,放在定时中断里 void key_service(); //按键服务函数,放在main函数循环里 void ad_samping(); //AD采样, 放在main函数循环里 void delay100(); //小延时 //补充说明:吴坚鸿程序风格是这样的,凡是switch()语句括号里面的变量名 //后缀都用_step表示。 unsigned char ad_step=0; //AD扫描步骤变量, //补充说明:吴坚鸿程序风格是这样的,凡是按键或者感应输入的自锁变量名 //后缀都用_lock表示。 unsigned char key_lock1=0; //按键自锁标志 //补充说明:吴坚鸿程序风格是这样的,凡是计数器延时的变量 //后缀都用_cnt表示。 unsigned int delay_cnt1=0; //延时计数器的变量 unsigned int voice_time_cnt; //蜂鸣器响的声音长短的计数延时 //补充说明:吴坚鸿程序风格是这样的,凡是做类型的变量的分类 //后缀都用_sec表示。 Unsigned char key_sec=0; //哪个按键被触发 //补充说明:吴坚鸿程序风格是这样的,凡是只有两种状态(0或者1)的变量, //后缀都用_flag表示。 Unsigned char AD_Flag=0; //用来指示单片机内部硬件AD处理完成的标志 //跟AD有关的变量 Unsigned char key_value=0; //跟电压成比例关系的AD数值 //主程序 main() { ADCON0=0x40; //设置AD模式 ADCON1=0x04; // AD输入通道 TRISA0=1; TRISA1=1; TRISA3=1; TRISB0=0; //配置蜂鸣器输出 T1CON=0x24;//定时中断 TMR1H=0xFE; TMR1L=0xEF; INTCON=0xC0; TMR1IF=0; TMR1IE=1; PIE1=0X00; PIE2=0X00; ADIF=0; //A/D转换中断允许 ADIE=1; //A/D转换中断允许 PEIE=1; //外围中断允许 GIE=1; TMR1ON=1; //开定时中断 //补充说明,以上的内容为寄存器配置,每种不同的单片机会有点差异, //大家不用过度关注以上寄存器的配置,只要知道有这么一回事即可 beep_dr=0; //关蜂鸣器,上电初始化IO while(1) { CLRWDT(); //喂看门狗,大家不用过度关注此行 ad_samping(); //AD采样 key_service(); //按键服务 } } void key_scan() //按键扫描函数 { if(key_value>cnt_key_nc) //空闲,没有按下 { key_lock1=0; //按键自锁标志清零 delay_cnt1=0; //按键去抖动延时计数器清零,此行非常巧妙 } else if(key_lock1==0) //有按键按下,且是第一次被按下 { if(key_value ++delay_cnt1; //延时计数器 If(delay_cnt1>cnt_delay_cnt1) { delay_cnt1=0; key_lock1=1; //自锁按键置位,避免一直触发 key_sec=1; //触发1号键 } } else if(key_value> cnt_key2_down&&key_value< cnt_key2_up) //K2按下 { ++delay_cnt1; //延时计数器 If(delay_cnt1>cnt_delay_cnt1) { delay_cnt1=0; key_lock1=1; //自锁按键置位,避免一直触发 key_sec=2; //触发2号键 } } else if(key_value> cnt_key3_down&&key_value< cnt_key3_up) //K3按下 { ++delay_cnt1; //延时计数器 If(delay_cnt1>cnt_delay_cnt1) { delay_cnt1=0; key_lock1=1; //自锁按键置位,避免一直触发 key_sec=3; //触发3号键 } } } void key_service() //按键服务函数 { switch(key_sec) //按键服务状态切换 { case 1:// 1号键 // 补充说明:voice_time_cnt只要不为0蜂鸣器就会响,中断里判断voice_time_cnt不为0 //时,会不断自减,一直到它为0时,自动把蜂鸣器关闭 voice_time_cnt= cnt_voice_time; //蜂鸣器响“滴”一声就停 key_sec=0; //相应完按键处理程序之后,把按键选择变量清零, //避免一直触发 break; case 2:// 2号键 voice_time_cnt= cnt_voice_time; //蜂鸣器响“滴”一声就停 key_sec=0; //相应完按键处理程序之后,把按键选择变量清零, //避免一直触发 break; case 3://3号键 voice_time_cnt= cnt_voice_time; //蜂鸣器响“滴”一声就停 key_sec=0; //相应完按键处理程序之后,把按键选择变量清零, //避免一直触发 break; } } void ad_samping() //AD采样函数,放在main循环里 { switch(ad_step) { case 0: ADCON0=0x59; //切换直流输入通道AD采样AN3 delay100(); //此处为无厘头一个。如果有两个以上的AD通道进行切换,必须把这//个延时加上,否则不好使。这个完全是我的实战经验,这样的事情我经常会遇到,这种 //事情完全靠经验,我当年第一次遇到的时候也被折腾了好久才发现。当然这个项目只有 //1个AD通道,所以也许可以不用。 ADIF=0; CLRWDT(); ADGO=1; //启动AD ad_step=1; //下一次循环进入下一个步骤,用switch来切换流程,一直是我 //的最爱,尤其是通道多的时候,它的优越性更加能发挥得淋漓尽致。 break; case 1: if(AD_Flag==1) // AD采样完成 { AD_Flag=0; key_value=ADRES; //采集按键电压的AD数值, ADIF=0; CLRWDT(); ADGO=1; //启动AD ad_step=0; //下次循环切换回最前面那个步骤,这种控制方式我最喜欢 } break; } } //小延时 void delay100() { unsigned char k; for(k=0;k<100;k++); } //中断 void interrupt timer1rbint(void) { if(ADIF==1&&ADIE==1) //AD转换完成中断 { TMR1IE=0; //禁止定时中断,避免两个中断相互扯淡 ADIF=0; //清除中断标志 AD_Flag=1; //置AD转换完成标志 TMR1IE=1; //允许定时中断 } if(TMR1IE==1&&TMR1IF==1) //定时中断 { ADIE=0; TMR1IF=0; TMR1ON=0; key_scan(); //按键扫描函数 if(voice_time_cnt) //控制蜂鸣器声音的长短 { beep_dr=1; //蜂鸣器响 --voice_time_cnt; //蜂鸣器响的声音长短的计数延时 } else { beep_dr=0; //蜂鸣器停止 } TMR1H=0xFF; TMR1L=0xC8; TMR1ON=1; ADIE=1; } } (6)小结: 有两路AD通道进行切换时,必须加一个小延时delay100(),否则会出现无厘头现象。“无厘头现象“是鸿哥发明的一个新词,专门用来表示那些莫名其妙的,用理论不好解释的现象。 (未完待续下一节) |
|
|
|
|
|
|
|
|
|
本帖最后由 jianhong_wu 于 2012-12-16 10:00 编辑
我真的是觉得这两个问题没有意义。我做那么多项目,真的是从来不会去在乎它。你只要知道设置哪些参数可以更改他频率的大小就可以了。你每改一次参数,都要上机测试一下符不符合你的感觉,当你感觉差不多的时候就行了,1S钟你肯定能感觉出来太长了,所以你就会把它改小了。没错,就是要靠感觉,不用精确计算。当你要做一个时间非常准的东西,你可以外接一个ds1302或者用CPLD自己做一个超级精准的时钟。如果客户一定要用单片机的定时器去做一个时钟,那么你可以通过修改比例系数的方式来校验时间,当然这个时候,定时中断里面处理的事情越少时间就越准。而我们实际应用来看,一般的时间都不要求特别精确的。这个只是我的经验,如果你非要自己去计算,那也可以,说明你比我更加科学,我是靠感觉的。 2012年12月16日鸿哥良心发现之后的补充: 记得我刚出第一节“按键行列扫描与蜂鸣器”的时候,网友“3htech ”曾经质疑过我在中断里直接用delay1(40)这个延时,昨天也有个朋友针对这个问题跟我讨论过,我觉得有必要在此多说明一下。在定时中断里用delay1(40)这种延时确实是兵家大忌。我这段程序是直接复制我当时做的一个项目的,因为这个项目是用在工控上的流程控制,对速度不敏感,而且delay1(40)这个延时还不是特别长,所以在这个项目上没有什么影响。为了不误人子弟,我还是决心把delay1(40)改成计数延时的方式,目前已经更改完毕。同时感谢网友“3htech ”的建议。 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{:soso__13585677261962887982_2:}顶下文
|
|
|
|
|
|
|
|
|
|
楼主,你在做工程中也是这样编代码的吗?好像你的代码可移植性不是那么的强,要是大些的工程就显得有点麻烦了,你没有将底层硬件彻底的与上层应用程序模块隔离开来,不知道你是怎么解决这个问题的?
|
|
|
|
|
|
本帖最后由 jianhong_wu 于 2012-11-10 09:17 编辑
我在工程中都是这样编代码的。我觉得单片机程序不存在移植性问题,因为单片机程序不存在跨平台一说,从某种意义上说,单片机的程序就是驱动程序的一种,属于硬件类,所以根本不存在硬件层跟用户层的分离问题,他们是混为一体的,人就是剑,剑就是人。我反倒认为单片机程序存在模式问题,好的模式就是一个好的套路,不管用户有什么需求,用什么单片机,我都可以用一种固定的套路来搭建程序框架。我现在跟大家介绍的,就是我多年来一直沿用的套路,这种套路结构清晰,坚如磐石,再大的工程都没问题,我曾经用这种结构写过64K的程序,好使得很。当然,我现在只是编了按键与蜂鸣器这样简单的程序,等我继续往后写的时候,你会越来越发现我的程序很有规律。 |
|
|
|
|
|
|
|
|
|
各位高手,打扰一下,现在做项目一般都用什么单片机,我只用过STC98C52,其他单片机没有接触过,听说每种单片机的编译环境不一样,烧录器不一样。你们做项目一般用什么单片机呢
|
|
|
|
|
|
|
|
|
|
只有小组成员才能发言,加入小组>>
求解外围电路实现的是4脚给持续低电平复位并正常工作,高电平不工作的原因
2076 浏览 1 评论
3486 浏览 3 评论
PIC1946程序有一个变量在运行过程中恢复初始值其他变量保持不变
2329 浏览 2 评论
2754 浏览 0 评论
PIC16F1825的RC5引脚,在主程序中操作无效,在中断中可以改变是为什么?
4012 浏览 5 评论
956浏览 0评论
用XC8编译PIC18F25K80时提示下面Error,求怎么解决这个问题
6343浏览 0评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-23 09:28 , Processed in 0.894583 second(s), Total 87, Slave 79 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号