打印
[PIC®/AVR®/dsPIC®产品]

还是想弱弱的问一个基础的问题

[复制链接]
1183|5
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
liudanwei|  楼主 | 2013-9-12 16:40 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
求各位大神给我介绍一下,你们在编一个独立按键的时候都是怎么编写的么?不要delay延时的那种。
沙发
NE5532| | 2013-9-14 16:11 | 只看该作者
只能回答楼主下面的话:根据需求编写。

楼主不要把自己局限在一个点上,这样写不出程序的。

使用特权

评论回复
板凳
yewuyi| | 2013-9-14 17:05 | 只看该作者
ms级定时中断,一般1~20ms,中断后扫描按键信号,一般扫描一次最多大约数十us,扫描到键值后退出中断,根据键值进行随后的处理。

使用特权

评论回复
地板
Rain_King| | 2013-9-15 10:36 | 只看该作者
楼上回答是正确的,可以用中断扫描按键,我给你发一个大神写的程序代码吧,他写的是个框架,我个人觉得不错..........代码在楼下,好好看....

使用特权

评论回复
5
Rain_King| | 2013-9-15 10:37 | 只看该作者
/*第一节:按键行列扫描与蜂鸣器
(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;  //哪个按键被触发

//主程序
void 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_delay2) //小延时,但不是去抖动延时,替代一直受网友争议的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_delay2) //小延时,但不是去抖动延时,替代一直受网友争议的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元。(未完待续下一节)*/

使用特权

评论回复
6
Rain_King| | 2013-9-15 10:58 | 只看该作者
这个是吴坚鸿写的课程之一,我觉得还不错,你可以去查找一下他写程序的风格,仅作参考.....我不能给他写程序的链接.....自己找...

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

27

主题

132

帖子

2

粉丝