[资料] 吴坚鸿单片机程序风格赏析(连载)

    [复制链接]

技术达人

发表于 2012-11-7 08:06:34   449003 查看 3118 回复 只看该作者 倒序浏览
分享
本帖最后由 jianhong_wu 于 2015-2-20 15:53 编辑

        我最近新开了一个连载的技术贴《从业将近十年!手把手教你单片机程序框架(连载)》 希望大家关注。

(一)按键行列扫描与蜂鸣器:http://bbs.elecfans.com/forum.ph ... 5319&fromuid=220353
第二节:独立按键扫描与蜂鸣器:http://bbs.elecfans.com/forum.ph ... 9622&fromuid=220353
第三节:AD按键扫描与蜂鸣器:http://bbs.elecfans.com/forum.ph ... 1711&fromuid=220353
第四节:“鸿哥三宝”之74HC165(按键扫描篇):http://bbs.elecfans.com/forum.ph ... 9196&fromuid=220353
第五节:“鸿哥三宝”之74HC595(继电器驱动篇):http://bbs.elecfans.com/forum.ph ... 3364&fromuid=220353
第六节:单串口通讯之如何接住别人丢过来的一堆数据:http://bbs.elecfans.com/forum.ph ... 5201&fromuid=220353
第七节:安全可靠的串口通讯协议之校验:http://bbs.elecfans.com/forum.ph ... 2285&fromuid=220353
第八节:串口通讯之如何提取数据尾:http://bbs.elecfans.com/forum.ph ... 8356&fromuid=220353
第九节:串口通讯之485通讯:http://bbs.elecfans.com/forum.ph ... 6062&fromuid=220353
第十节:鸿哥多文件编程套路的”十二招式”:http://bbs.elecfans.com/forum.ph ... 5236&fromuid=220353
第十一节:鸿哥单色液晶屏三大类定律(KS0107驱动12864屏):http://bbs.elecfans.com/forum.ph ... 9903&fromuid=220353

第十二节:液晶屏第一大类定律--纵向显示八个点(KS0107驱动19264屏):
   http://bbs.elecfans.com/forum.ph ... 6079&fromuid=220353

第十三节:液晶屏第一大类定律--纵向显示八个点(SED1520驱动12232屏):http://bbs.elecfans.com/forum.ph ... 3442&fromuid=220353

第十四节:液晶屏第二大类定律—横向显示八个点(T6963C驱动240128屏):http://bbs.elecfans.com/forum.ph ... 4039&fromuid=220353

第十五节:液晶屏第二大类定律—横向显示八个点(RA8835驱动320240屏):http://bbs.elecfans.com/forum.ph ... 9399&fromuid=220353

第十六节:液晶屏第三大类定律—任意位置显示一个点(彩色320240点阵屏):http://bbs.elecfans.com/forum.ph ... 3980&fromuid=220353

第十七节:液晶屏第三大类定律—任意位置显示一个点(HT1621驱动段码液晶屏):http://bbs.elecfans.com/forum.ph ... 6976&fromuid=220353

第十八节:ADS7843驱动的320240电阻型触摸屏:
  http://bbs.elecfans.com/forum.ph ... 7451&fromuid=220353

第十九节:FTC359A驱动的触摸按键:http://bbs.elecfans.com/forum.ph ... 7041&fromuid=220353

第二十节:按键操作液晶显示屏菜单的基本程序框架http://bbs.elecfans.com/forum.ph ... &page=30#pid1603620

第二十一节:纯IO口动态扫描数码管的驱动方式:http://bbs.elecfans.com/forum.ph ... 6685&fromuid=137188

第二十二节:利用74HC595辅助来动态扫描数码管:http://bbs.elecfans.com/forum.ph ... 5807&fromuid=137188

第二十三节:利用74HC595静态驱动数码管:http://bbs.elecfans.com/forum.ph ... 7744&fromuid=137188

第二十四节:两片联级的74HC595动态扫描8位数码管:http://bbs.elecfans.com/forum.ph ... 3724&fromuid=137188

第二十五节:专用集成芯片TM1639驱动8位数码管:http://bbs.elecfans.com/forum.ph ... 4628&fromuid=137188

第二十六节:按键操作数码管菜单的基本程序框架:http://bbs.elecfans.com/forum.ph ... 7963&fromuid=137188

第二十七节:时间篇--单片机定时中断实现倒计时:http://bbs.elecfans.com/forum.ph ... 9927&fromuid=137188

第二十八节:时间篇--在主函数while(1)循环里实现倒计时:http://bbs.elecfans.com/forum.ph ... 4427&fromuid=137188

第二十九节:时间篇--利用时钟芯片DS1302实现时钟功能:http://bbs.elecfans.com/forum.php?mod=redirect&goto=findpost&ptid=288846&pid=1809183&fromuid=625560

第三十节:时间篇--利用时钟芯片DS12C887实现时钟功能:http://bbs.elecfans.com/forum.php?mod=redirect&goto=findpost&ptid=288846&pid=1842469&fromuid=625560

第三十一节:时间篇--结合CPLD芯片实现超高精度的计时:http://bbs.elecfans.com/forum.php?mod=redirect&goto=findpost&ptid=288846&pid=1872897&fromuid=625560

第三十二节:温度篇--利用热敏电阻来采集温度http://bbs.elecfans.com/forum.ph ... 4613&fromuid=137188

第三十三节:温度篇--利用MAX6675和K型热电偶来采集超高温度:http://bbs.elecfans.com/forum.ph ... 5358&fromuid=137188

第三十四节:温度篇--利用DS18B20来采集温度:http://bbs.elecfans.com/forum.ph ... 7744&fromuid=137188

第三十五节:温湿度篇--利用DHT11来采集温湿度:http://bbs.elecfans.com/forum.ph ... 8196&fromuid=137188

第三十六节:EPROM篇--利用单片机内部自带EPROM存储数据:http://bbs.elecfans.com/forum.ph ... 3361&fromuid=137188

第三十七节:EEPROM篇--利用AT24C02存储数据:http://bbs.elecfans.com/forum.ph ... 8332&fromuid=137188

第三十八节:EEPROM篇--利用AT24C64存储数据:http://bbs.elecfans.com/forum.ph ... 8995&fromuid=137188

第三十九节:EEPROM篇--利用24LC1025存储数据:http://bbs.elecfans.com/forum.ph ... 9046&fromuid=137188

第四十节:EEPROM篇--利用93C46存储数据:http://bbs.elecfans.com/forum.ph ... 7693&fromuid=137188

第四十一节:FLASH篇--利用SST25VF032B存储批量数据:http://bbs.elecfans.com/forum.ph ... 2764&fromuid=137188

第四十二节:算术篇---大数据的加法运算:http://bbs.elecfans.com/forum.ph ... 4543&fromuid=137188

第四十三节:算术篇---大数据的减法运算 http://bbs.elecfans.com/forum.ph ... 0448&fromuid=137188

第四十四节:算术篇---大数据的乘法运算 http://bbs.elecfans.com/forum.ph ... 4209&fromuid=137188

第四十五节:算术篇---大数据的除法运算  http://bbs.elecfans.com/forum.ph ... 8938&fromuid=137188

第四十六节:单片机中的战斗机switch---利用主函数循环实现LED灯闪烁http://bbs.elecfans.com/forum.ph ... 3781&fromuid=137188

第四十七节:单片机中的战斗机switch---利用定时中断实现LED闪烁http://bbs.elecfans.com/forum.ph ... 5042&fromuid=137188


第四十八节:单片机中的战斗机switch---利用主函数循环实现跑马灯 http://bbs.elecfans.com/forum.ph ... 2253&fromuid=137188

第四十九节:单片机中的战斗机switch---利用定时中断实现跑马灯 http://bbs.elecfans.com/forum.ph ... 3930&fromuid=137188

第五十节:单片机中的战斗机switch---多任务并行处理两路独立的跑马灯 http://bbs.elecfans.com/forum.ph ... 4065&fromuid=137188

第五十一节:单片机中的战斗机switch---按键设置跑马灯的速度http://bbs.elecfans.com/forum.ph ... 5245&fromuid=137188

第五十二节:单片机中的战斗机switch---按键设置跑马灯的方向 http://bbs.elecfans.com/forum.ph ... 5329&fromuid=137188

第五十三节:单片机中的战斗机switch---按键控制跑马灯的启动,停止和暂停 http://bbs.elecfans.com/forum.ph ... 4980&fromuid=137188

第五十四节:单片机中的战斗机switch---工业自动化设备的运动控制 http://bbs.elecfans.com/forum.ph ... 0918&fromuid=137188

第五十五节:按键的花样技巧----双击按键的触发   http://bbs.elecfans.com/forum.ph ... page=124#pid2365581




点评

给力!  发表于 2014-4-3 22:19

评分

参与人数 5威望 +14 +24 积分 +104 收起 理由
Arthur_L + 5 资源分享达人,加分鼓励!
电子打扰呦 + 1 + 1 您的付出是论坛的动力,感谢您一直支持!.
ksd + 2 + 2 简单易懂,谢谢分享,风格很适合我的学习。.
baigame + 1 + 1
86hupeng + 10 + 20 + 99 您的付出是论坛的动力,感谢您一直支持!.

查看全部评分

工程师

发表于 2013-5-17 21:59:09  
第二节 独立按键与蜂鸣器
C51平台移植(测试通过)。
谢谢鸿哥这些不错的帖子,在移植的过程中学到了很多,有进一步的认识,也了解了项目中需要注意的问题,学到了一些思想,其实编程就是一种思想。比如,矩阵按键,分段扫描,很实用,满足基本的实时性要求。

如有bug,欢迎指正!
independent_key_buzzer.zip (53.03 KB, 下载次数: 384)

点评

我喜欢你的风格。更易读了  发表于 2013-5-22 10:15
回复

点赞 举报

技术员

发表于 2012-11-7 08:37:42  
东西呢 看看 期待
回复

点赞 1 举报

助理工程师

发表于 2012-11-7 09:08:18  
撒花~~~
回复

点赞 举报

资深工程师

发表于 2012-11-7 10:51:03  
赏析呢
回复

点赞 1 举报

技术达人

发表于 2012-11-7 11:55:07    楼主|
本帖最后由 jianhong_wu 于 2013-4-22 19:48 编辑

(一)按键行列扫描与蜂鸣器
(1)技术体会:在行列式扫描结构的薄膜按键里,干扰很大,按键扫描程序非常讲究,尤其是去抖动的处理。
(2)功能需求:每按一个按键,蜂鸣器就响一次。
(3)硬件原理:
(a)用4个IO来做2X2按键行列扫描,其中作为输入的2个IO口必须接上拉电阻20K左右。
(b)用1个IO经过8050三极管来驱动有源蜂鸣器,有源蜂鸣器通电就一直响,断电就停止。而无源蜂鸣器是要靠断断续续的开关信号来驱动才能响,就是要频率来驱动。
(4)源码适合的单片机:PIC18F4620,晶振为22.1184MHz
(5)源代码讲解如下:
#include<pic18.h>         //包含芯片相关头文件

//补充说明:吴坚鸿程序风格是这样的,凡是输出IO后缀都是_dr,凡是输入的//IO后缀都//是_sr

#define  beep_dr  LATA1  //蜂鸣器输出

#define key_dr1 LATB3  //2X2按键行输出
#define key_dr2 LATB4 //2X2按键行输出

#define key_sr1 RB6    //2X2按键行输入
#define key_sr2 RB7   //2X2按键行输入

//补充说明:吴坚鸿程序风格是这样的,凡是做延时计数阀值的常量
//前缀都用cnt_表示。
#define cnt_delay_cnt1   25   //按键去抖动延时阀值
#define cnt_delay_cnt2   5    //按键行输出信号稳定的小延时阀值

#define cnt_voice_time   60  //蜂鸣器响的声音长短的延时阀值
void delay1(unsigned int de)  ;//小延时程序,时间不宜太长,因为内部没有喂看门狗

//补充说明:吴坚鸿程序风格是这样的,凡是按键扫描函数都放在定时中
//断里,凡是按键服务程序都是放在main函数循环里。有人说不应该把子程序//放在中断里,别听他们,信鸿哥无坎坷。
void key_scan();                         //按键扫描函数,放在定时中断里
void key_service();                            //按键服务函数,放在main函数循环里


//补充说明:吴坚鸿程序风格是这样的,凡是switch()语句括号里面的变量名
//后缀都用_step表示。

unsigned char key_step=1;      //按键扫描步骤变量,在switch()语句的括号里
//补充说明:吴坚鸿程序风格是这样的,凡是按键或者感应输入的自锁变量名
//后缀都用_lock表示。
unsigned char key_lock1=0;   //按键自锁标志
//补充说明:吴坚鸿程序风格是这样的,凡是计数器延时的变量
//后缀都用_cnt表示。
unsigned int  delay_cnt1=0;     //延时计数器的变量
unsigned int  delay_cnt2=0;     //延时计数器的变量
unsigned int voice_time_cnt;        //蜂鸣器响的声音长短的计数延时

//补充说明:吴坚鸿程序风格是这样的,凡是做类型的变量的分类
//后缀都用_sec表示。
Unsigned char key_sec=0;  //哪个按键被触发

//主程序
main()
{
ADCON0=0x00;  
ADCON1=0x0f;                               //全部为数字信号
    ADCON2=0xa1;                               //右对齐
    RBPU=0;                                      //上拉电阻
    SSPEN=0;                                    //决定RA5不作为串口

    TRISB3=0;            //配置按键行扫描IO为输出
    TRISB4=0;           //配置按键行扫描IO为输出
    TRISB6=1;           //配置按键列扫描IO为输入
    TRISB7=1;           //配置按键列扫描IO为输入


    T1CON=0x24;     //定时器中断配置
    TMR1H=0xF5;
    TMR1L=0x5F;
    TMR1IF=0;
    TMR1IE=1;
    TMR1ON=1;
    TMR1IE=1;
//补充说明,以上的内容为寄存器配置,每种不同的单片机会有点差异,
//大家不用过度关注以上寄存器的配置,只要知道有这么一回事即可

    beep_dr=0;                               //关蜂鸣器,上电初始化IO

   while(1)   
   {
                     CLRWDT(); //喂看门狗,大家不用过度关注此行
                key_service();        //按键服务
}

}
      

void key_scan()                                //按键扫描函数
{  
    //补充说明:如果中断一次就把所有的按键都扫描完,中断占用的时间片就会太多,势//必会影响main函数里其他子程序的运行,为了避免一口气把所//的按键都扫描完,此
//处用switch语句把4个按键分成2等分,一次中断只扫描2个按键
switch(key_step)    //按键扫描步骤,
{
        case 1:   //扫描1号键,2号键
                key_dr1=0;      //按键行扫描输出第一行低电平
                key_dr2=1;
                delay_cnt2=0;  //延时计数器清零
                key_step++;     //切换到下一个运行步骤
                break;
        case 2:
                delay_cnt2++;
                if(delay_cnt2>cnt_delay_cnt2) //小延时,但不是去抖动延时,替代一直受网友争议的delay1(40)   
                {
                       delay_cnt2=0;
                       key_step++;     //切换到下一个运行步骤
                }
                break;
        case 3:
                if(key_sr1==1&&key_sr2==1)
                {   //如果没有按键按下,则2个IO输入都是高电平
                        key_step++; //如果没有按键按下,下一个中断扫描下2个
//按键
key_lock1=0;  //按键自锁标志清零
                         delay_cnt1=0; //按键去抖动延时计数器清零,此行非常巧妙        

                  }
Else   if(key_sr1==0&&key_sr2==1&&key_lock1==0)
                 {     // key_lock1按键自锁,避免按键一直触发,下降沿有效
                        ++delay_cnt1;  //延时计数器
//补充说明:有按键触发之后,不要马上响应,要延时一段时间去抖动,此处本人设计非常
//巧妙,很多人仅仅知道按键延时的时候要保证还能去处理别的程序,这样是还不够的,
//在延时去抖动的时候,还必须要监控延时这段时间里,按键IO输入口是否会由于受到某//种干扰突然由低变成高,如果一旦变成高,那么延时计数器delay_cnt1必须重新清零
//我当年就是因为这样处理,把卖给富士康100台受干扰死机的设备修好了,老板马上
//给我加薪1000元。
                        if(delay_cnt1>cnt_delay_cnt1)  //延时计数器超过一定的数值
                        {
                             delay_cnt1=0;
key_lock1=1;  //自锁按键置位,避免一直触发,只有松开按键,
//此标志位才会被清零
                                       key_sec=1;         //触发1号键
                        }
                                       
                }
                else if(key_sr1==1&&key_sr2==0&&key_lock1==0)
                {
                        ++delay_cnt1;
                         if(delay_cnt1>cnt_delay_cnt1)
                         {
                                 delay_cnt1=0;
key_lock1=1;  //自锁按键置位,避免一直触发
                                          key_sec=2;         //触发2号键
                                                
                        }        
                     }
             break;
        
        case 4:       //扫描//扫描3号键,4号键
                key_dr1=1;     
                key_dr2=0;   //按键行扫描输出第二行低电平
                delay_cnt2=0;  //延时计数器清零
                key_step++;     //切换到下一个运行步骤
                break;
        case 5:
                delay_cnt2++;
                if(delay_cnt2>cnt_delay_cnt2) //小延时,但不是去抖动延时,替代一直受网友争议的delay1(40)   
                {
                       delay_cnt2=0;
                       key_step++;     //切换到下一个运行步骤
                }
                break;
        case 6:
                if(key_sr1==1&&key_sr2==1)
                {   
                        key_step++;
key_lock1=0;  
                         delay_cnt1=0;

                  }
Else   if(key_sr1==0&&key_sr2==1&&key_lock1==0)
                 {     
                        ++delay_cnt1;
                        if(delay_cnt1>cnt_delay_cnt1)
                        {
                             delay_cnt1=0;
key_lock1=1;  
                                       key_sec=3;         //触发3号键
                        }
                                       
                }
                else if(key_sr1==1&&key_sr2==0&&key_lock1==0)
                {
                        ++delay_cnt1;
                         if(delay_cnt1>cnt_delay_cnt1)
                         {
                                 delay_cnt1=0;
key_lock1=1;  //自锁按键置位,避免一直触发
                                          key_sec=4;         //触发4号键
                                                
                        }        
                     }
             break;
     }
if(key_step>6)   //第1组按键与第2组按键反复轮流扫描
{
        key_step=1;        
}
}

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;        
                case 4://4号键

                                 voice_time_cnt= cnt_voice_time;    //蜂鸣器响“滴”一声就停
                        key_sec=0;   //相应完按键处理程序之后,把按键选择变量清零,
//避免一直触发
                        break;        



                        
        }               
}




//中断
void interrupt timer1rbint(void)
{
    if(TMR1IE==1&&TMR1IF==1)    //定时中断
        {
         
               TMR1IF=0;     //定时中断标志位关闭
                TMR1ON=0;    //定时中断开关关闭

                 key_scan();                    //按键扫描函数
      if(voice_time_cnt)                       //控制蜂鸣器声音的长短
                 {
                        beep_dr=1;         //蜂鸣器响
                      --voice_time_cnt;        //蜂鸣器响的声音长短的计数延时
                 }
                else
                {
beep_dr=0;      //蜂鸣器停止
                }
      TMR1H=0xF5;     //重新设置定时时间间隔
      TMR1L=0x5F;
      TMR1ON=1;        //定时中断开关打开
    }
}




void delay1(unsigned int de)
{
        unsigned int t;
        for(t=0;t<de;t++);
}


(6)小结:
  以上是我常用的编程结构。后续我做的所有项目基本上是这样一种编程结构。这一节技术上要特别重视按键扫描。有按键触发之后,不要马上响应,要延时一段时间去抖动,此处本人设计非常巧妙,很多人仅仅知道按键延时的时候要保证还能去处理别的程序,这样是还不够的,在延时去抖动的时候,还必须要监控延时这段时间里,按键IO输入口是否会由于受到某种干扰突然由低变成高,如果一旦变成高,那么延时计数器delay_cnt1必须重新清零,我当年就是因为这样处理,把卖给富士康100台受干扰死机的设备修好了,老板马上给我加薪1000元。(未完待续下一节)

点评

1177楼  发表于 2013-5-11 23:42
51平台的移植:http://bbs.elecfans.com/forum.php?mod=viewthread&tid=288846&pid=1846645&page=59&extra=#pid1846645  发表于 2013-5-11 23:40
回复

点赞 0 举报

助理工程师

发表于 2012-11-7 12:03:42  

!
回复

点赞 举报

技术员

发表于 2012-11-7 14:16:56  
回复

点赞 举报

技术员

发表于 2012-11-7 18:31:29  
期待下文

点评

www_xiushu.com/ 蛮荒记无弹窗  发表于 2013-5-25 11:52
回复

点赞 1 举报

技术员

发表于 2012-11-7 18:52:56  

点评

步步惊情 http://www.kk321.cc/dalu/20472/ 爱情公寓4 http://www.kk321.cc/dalu/20471/  发表于 2013-12-28 23:55
回复

点赞 举报

助理工程师

发表于 2012-11-7 19:45:53  
信鸿哥无坎坷。。。
顶!!

点评

造化之门www.aoswx.com/read/4901/index.html 儒道至圣www.gmwxw.com/read/4533/index.html  发表于 2014-10-18 16:44
回复

点赞 0 举报

助理工程师

发表于 2012-11-7 20:13:33  
回复

点赞 0 举报

助理工程师

发表于 2012-11-7 20:16:42  
{:1:}
回复

点赞 1 举报

技术员

发表于 2012-11-7 20:17:28  
看看看
回复

点赞 举报

实习生

发表于 2012-11-7 21:51:01  
感谢楼主的无私分享,但是我没有理出“程序框架”。请楼主针对“程序框架”多做一些总结性的概括。
回复

点赞 举报

技术达人

发表于 2012-11-7 22:24:18    楼主|
349568117 发表于 2012-11-7 21:51
感谢楼主的无私分享,但是我没有理出“程序框架”。请楼主针对“程序框架”多做一些总结性的概括。
...

你坚持跟踪下去,等你多看我几节课时,你就会发现,我所有的程序都很有规律。不要着急,好戏还在后头。我会通过很多小项目的源代码讲解,来深入浅出,让鸿哥的程序风格水落石出。
回复

点赞 0 举报

助理工程师

发表于 2012-11-8 08:39:43  
期待中!

点评

00000000000000000  发表于 2013-8-30 21:16
跟鸿哥学习!!!  发表于 2013-7-10 21:10
回复

点赞 举报

技术达人

发表于 2012-11-8 13:01:04    楼主|
本帖最后由 jianhong_wu 于 2013-1-10 12:04 编辑

第二节:独立按键扫描与蜂鸣器
(1)学习目标:利用上一节的程序框架,把按键行列扫描方式改成独立按键扫描方式。通过这节的练习,加深熟悉吴坚鸿的程序框架,以及独立按键扫描的编程方式。
(2)功能需求:每按一个按键,蜂鸣器就响一次。
(3)硬件原理:
(a)用4个IO来做独立按键扫描, 4个IO口都必须接上拉电阻20K左右。
(b)用1个IO经过8050三极管来驱动有源蜂鸣器,有源蜂鸣器通电就一直响,断电就停止。
(4)源码适合的单片机:PIC18F4620,晶振为22.1184MHz
(5)源代码讲解如下:
#include<pic18.h>         //包含芯片相关头文件

//补充说明:吴坚鸿程序风格是这样的,凡是输出IO后缀都是_dr,凡是输入的//IO后缀都//是_sr

#define  beep_dr  LATA1  //蜂鸣器输出


#define key_sr1 RB6    //独立按键输入
#define key_sr2 RB7   //独立按键输入

#define key_sr3 RB3  //独立按键输入
#define key_sr4 RB4 //独立按键输入


//补充说明:吴坚鸿程序风格是这样的,凡是做延时计数阀值的常量
//前缀都用cnt_表示。凡是延时常量,都应该根据上机实际情况来调整出最佳的数值
#define cnt_delay_cnt1   25   //按键去抖动延时阀值
#define cnt_voice_time   60  //蜂鸣器响的声音长短的延时阀值

//补充说明:吴坚鸿程序风格是这样的,凡是按键扫描函数都放在定时中
//断里,凡是按键服务程序都是放在main函数循环里。有人说不应该把子程序//放在中断里,别听他们,信鸿哥无坎坷。
void key_scan();                         //按键扫描函数,放在定时中断里
void key_service();                            //按键服务函数,放在main函数循环里


//补充说明:吴坚鸿程序风格是这样的,凡是switch()语句括号里面的变量名
//后缀都用_step表示。

unsigned char key_step=1;      //按键扫描步骤变量,在switch()语句的括号里
//补充说明:吴坚鸿程序风格是这样的,凡是按键或者感应输入的自锁变量名
//后缀都用_lock表示。
unsigned char key_lock1=0;   //按键自锁标志
unsigned char key_lock2=0;   //按键自锁标志
unsigned char key_lock3=0;   //按键自锁标志
unsigned char key_lock4=0;   //按键自锁标志

//补充说明:吴坚鸿程序风格是这样的,凡是计数器延时的变量
//后缀都用_cnt表示。
unsigned int  delay_cnt1=0;     //延时计数器的变量
unsigned int  delay_cnt2=0;     //延时计数器的变量
unsigned int  delay_cnt3=0;     //延时计数器的变量
unsigned int  delay_cnt4=0;     //延时计数器的变量
unsigned int voice_time_cnt;        //蜂鸣器响的声音长短的计数延时

//补充说明:吴坚鸿程序风格是这样的,凡是做类型的变量的分类
//后缀都用_sec表示。
Unsigned char key_sec=0;  //哪个按键被触发

//主程序
main()
{
ADCON0=0x00;  
ADCON1=0x0f;                               //全部为数字信号
    ADCON2=0xa1;                               //右对齐
    RBPU=0;                                      //上拉电阻
    SSPEN=0;                                    //决定RA5不作为串口

    TRISB3=1;            //配置独立按键IO为输入
    TRISB4=1;           //配置独立按键IO为输入
    TRISB6=1;           //配置独立按键IO为输入
    TRISB7=1;           //配置独立按键IO为输入


    T1CON=0x24;     //定时器中断配置
    TMR1H=0xF5;
    TMR1L=0x5F;
    TMR1IF=0;
    TMR1IE=1;
    TMR1ON=1;
    TMR1IE=1;
//补充说明,以上的内容为寄存器配置,每种不同的单片机会有点差异,
//大家不用过度关注以上寄存器的配置,只要知道有这么一回事即可

    beep_dr=0;                               //关蜂鸣器,上电初始化IO

   while(1)   
   {
                     CLRWDT(); //喂看门狗,大家不用过度关注此行
                key_service();        //按键服务
}

}
      

void key_scan()                                //按键扫描函数
{  
    //补充说明:因为独立按键扫描没有了行列按键扫描的delay1(40)延时,所以速度很
//快,可以一次中断扫描完所有的按键,不用switch(key_step)语句来分段
  if(key_sr1==1)    //IO是高电平,说明按键没有被按下,这时要及时清零一些标志位
  {
key_lock1=0;  //按键自锁标志清零
delay_cnt1=0; //按键去抖动延时计数器清零,此行非常巧妙        
}
else if(key_lock1==0)  //有按键按下,且是第一次被按下
{
++delay_cnt1;  //延时计数器
if(delay_cnt1>cnt_delay_cnt1)
{
delay_cnt1=0;
key_lock1=1;  //自锁按键置位,避免一直触发
                  key_sec=1;         //触发1号键
}
}

  if(key_sr2==1)    //IO是高电平,说明按键没有被按下,这时要及时清零一些标志位
  {
key_lock2=0;  //按键自锁标志清零
delay_cnt2=0; //按键去抖动延时计数器清零,此行非常巧妙        
}
else if(key_lock2==0)  //有按键按下,且是第一次被按下
{
++delay_cnt2;  //延时计数器
if(delay_cnt2>cnt_delay_cnt1)
{
delay_cnt2=0;
key_lock2=1;  //自锁按键置位,避免一直触发
                  key_sec=2;         //触发2号键
}
}


  if(key_sr3==1)    //IO是高电平,说明按键没有被按下,这时要及时清零一些标志位
  {
key_lock3=0;  //按键自锁标志清零
delay_cnt3=0; //按键去抖动延时计数器清零,此行非常巧妙        
}
else if(key_lock3==0)  //有按键按下,且是第一次被按下
{
++delay_cnt3;  //延时计数器
if(delay_cnt3>cnt_delay_cnt1)
{
delay_cnt3=0;
key_lock3=1;  //自锁按键置位,避免一直触发
                  key_sec=3;         //触发3号键
}
}


  if(key_sr4==1)    //IO是高电平,说明按键没有被按下,这时要及时清零一些标志位
  {
key_lock4=0;  //按键自锁标志清零
delay_cnt4=0; //按键去抖动延时计数器清零,此行非常巧妙        
}
else if(key_lock4==0)  //有按键按下,且是第一次被按下
{
++delay_cnt4;  //延时计数器
if(delay_cnt4>cnt_delay_cnt1)
{
delay_cnt4=0;
key_lock4=1;  //自锁按键置位,避免一直触发
                  key_sec=4;         //触发4号键
}
}

}

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;        
                case 4://4号键

                                 voice_time_cnt= cnt_voice_time;    //蜂鸣器响“滴”一声就停
                        key_sec=0;   //相应完按键处理程序之后,把按键选择变量清零,
//避免一直触发
                        break;        



                        
        }               
}




//中断
void interrupt timer1rbint(void)
{
    if(TMR1IE==1&&TMR1IF==1)    //定时中断
        {
         
               TMR1IF=0;     //定时中断标志位关闭
                TMR1ON=0;    //定时中断开关关闭

                 key_scan();                    //按键扫描函数
      if(voice_time_cnt)                       //控制蜂鸣器声音的长短
                 {
                        beep_dr=1;         //蜂鸣器响
                      --voice_time_cnt;        //蜂鸣器响的声音长短的计数延时
                 }
                else
                {
beep_dr=0;      //蜂鸣器停止
                }
      TMR1H=0xF5;     //重新设置定时时间间隔
      TMR1L=0x5F;
      TMR1ON=1;        //定时中断开关打开
    }
}


(6)小结:
利用上一节的程序框架,把行列扫描改成独立按键扫描的速度非常快,体现了吴坚鸿程序框架的优越性。通过此节的学习,让大家加深熟悉吴坚鸿程序框架,同时也掌握了独立按键的编程方式。(未完待续下一节)

点评

C51平台移植,测试通过。第1241楼。 http://bbs.elecfans.com/forum.php?mod=viewthread&tid=288846&pid=1868244&page=63&extra=#pid1868244  发表于 2013-5-17 22:00
回复

点赞 3 举报

发表于 2012-11-8 13:20:04  
最好图文并茂
回复

点赞 1 举报

技术员

发表于 2012-11-8 13:31:04  
这个帖子有深度
程序都是实战性的
收藏起来慢慢研究!
回复

点赞 1 举报

技术员

发表于 2012-11-8 13:40:34  

点评

学习  发表于 2013-10-31 17:00
回复

点赞 1 举报

高级模式
您需要登录后才可以回帖 登录 | 注册

关闭

站长推荐 上一条 /7 下一条

快速回复 返回顶部 返回列表
-

推荐专区

技术干货集中营

专家问答

用户帮助┃咨询与建议┃版主议事

工程师杂谈

工程师创意

工程师职场

论坛电子赛事

社区活动专版

发烧友活动

-

嵌入式论坛

ARM技术论坛

Android论坛

Linux论坛

单片机/MCU论坛

FPGA|CPLD|ASIC论坛

DSP论坛

嵌入式系统论坛

-

电源技术论坛

电源技术论坛

无线充电技术

-

硬件设计论坛

PCB设计论坛

电路设计论坛

电子元器件论坛

控制|传感

总线技术|接口技术

-

测试测量论坛

LabVIEW论坛

Matlab论坛

测试测量技术专区

仪器仪表技术专区

-

EDA设计论坛

multisim论坛

PADS技术论坛

Protel|AD|DXP论坛

Allegro论坛

proteus论坛|仿真论坛

EasyEDA-中国人自已的EDA工具

Orcad论坛

-

综合技术与应用

电机控制

智能电网

光电及显示

参考设计中心

汽车电子技术论坛

医疗电子论坛

-

开源硬件

-

无线通信论坛

无线通信技术专区

天线|RF射频|微波|雷达技术

-

IC设计论坛

芯片测试与失效分析

Mixed Signal/SOC[数模混合芯片设计]

Analog/RF IC设计

设计与制造封装测试

-

厂商专区

TI论坛

TI Deyisupport社区

-

检测技术与质量

电磁兼容(EMC)设计与整改

安规知识论坛

检测与认证

-

消费电子论坛

手机技术论坛

平板电脑/mid论坛

音视/视频/机顶盒论坛

-

电子论坛综合区

聚丰众筹官方社区

新人报道区

聚丰供应链

-

论坛服务区

-

供求信息发布

供需广告

招聘┃求职发布区

电子展览展会专区