1. bread88lifa利发国际娱乐论坛/
  2. 嵌入式论坛/
    1. 电子论坛综合区
    2. 嵌入式论坛
    3. 电源技术论坛
    4. 硬件设计论坛
    5. 测试测量论坛
    6. 检测技术与质量
    7. EDA设计论坛
    8. 综合技术与应用
    9. 开源硬件
    10. IC设计论坛
    11. 消费电子论坛
    12. 无线通信论坛
    13. 个人版区
    14. 厂商专区
    15. 论坛服务区
    16. 高校联盟
    17. 供求信息发布
  3. PIC单片机论坛
    1. ARM技术论坛
    2. Android论坛
    3. Linux论坛
    4. 单片机/MCU论坛
    5. MSP430技术论坛
    6. FPGA|CPLD|ASIC论坛
    7. STM32/STM8技术论坛
    8. NXP MCU 技术论坛
    9. DSP论坛
    10. 瑞萨单片机论坛
    11. 嵌入式系统论坛
  4. / 吴坚鸿单片机程序风格赏析(连载)
    关闭提示

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

    [复制链接]
技术达人
发表于 2012-11-7 08:06:34   466640 查看 3124 回复 只看该作者 倒序浏览
分享
本帖最后由 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,欢迎指正!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册
x

点评

我喜欢你的风格。更易读了  发表于 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 举报
高级模式
您需要登录后才可以回帖 登录 | 注册
关闭

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

快速回复 返回顶部 返回列表
  1. time最新主题
  2. recommend推荐主题
  3. hot热门主题
  4. post 我的帖子
-

推荐专区

技术干货集中营

专家问答

方案交易

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

工程师杂谈

项目|工程师创意

招聘|求职}工程师职场

论坛电子赛事

社区活动专版

发烧友活动

-

嵌入式论坛

ARM技术论坛

Android论坛

Linux论坛

单片机/MCU论坛

MSP430技术论坛

FPGA|CPLD|ASIC论坛

STM32/STM8技术论坛

NXP MCU 技术论坛

PIC单片机论坛

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论坛

音视/视频/机顶盒论坛

-

电子论坛综合区

聚丰众筹官方社区

新人报道区

聚丰供应链

-

论坛服务区

-

供求信息发布

供需广告

电子展览展会专区

芯片求购|供应发布区

88lifa利发国际娱乐

百度360搜索搜狗搜索