不用延时去抖的按键程序

发布者:SparklingSoul最新更新时间:2016-08-18 来源: eefocus关键字:延时去抖  按键程序 手机看文章 扫描二维码
随时随地手机看文章
今天完成一个很好的按键程序,一般教科书上写的都是延时去抖,那样一旦进入按键扫描子程序就不得不等待延时,这样会影响cpu做其他的事。一种高效的按键扫描方法是用定时扫描。把所有程序都附在下面,希望大家能看懂,也请多多指教。本程序的主要功能是采样定时扫描的高效按键扫描程序设置电压电流的值,实时显示在lcd1602上,在1602上可左移、右移,数值加减,以及设置/确认(复用键),还有一个短路键。

//主函数 main

#include
#include "key.h"
#include "lcd1602.h"

uchar Key_Flag=0;
uchar Keyval = 0;
uchar Respond_Key_Flag=0;      //响应键
uchar Set_Confirm_Flag=0;      //设设置/确认标志

uchar Volt_buf[5]={'1','2','.','2','6'};                                     
uchar Curr_buf[5]={'0','5','.','5','4'};

void Init_port();
void Init_SysClk();
void Init_TimerB();
void key_process(); 
//////////////////主函数///////////////
void main()
{
   WDTCTL = WDTPW + WDTHOLD;                 //关闭看门狗
   Init_port();
   Init_LCD() ;
   Init_SysClk();
   Init_TimerB();
   _EINT();
   
   Disp1Char(0,0,'U') ;
   Disp1Char(1,0,':') ;
   DispNChar(3,0, 5,Volt_buf);
   Disp1Char(0x0F,0,'V') ;
   
   Disp1Char(0,1,'I') ;
   Disp1Char(1,1,':') ;
   DispNChar(3,1, 5,Curr_buf);
   Disp1Char(0x0F,1,'A') ;
   
   
   
   while(1)
   {    
     key_process(); 
     
     
   }

}
/*******************************************
函数名称:key_process
功    能:根据按键值执行任务
参    数:无
返回值  :无
********************************************/
void key_process()
{
      static uchar x=3;
      uchar keyvalue;
      static uchar key_one_flag=0;
      
      keyvalue = Key_Scan();
      Keyval = 0;
      if(Set_Confirm_Flag == 1)
      {  
                if(keyvalue == 1)               //设置/确认键
                {  
                    key_one_flag ^= 1;
                    if(key_one_flag==1)
                    {
                        write_cmd(0x0f);  //打开显示屏,显示光标,光标所在位置的字符闪烁
                        LocateXY(3,0);    //向液晶写入显示字符位置的坐标信息  
                    }
                    else 
                    {
                      write_cmd(0x0c);//开显示, 关光标,光标所在位置的字符不闪烁 
                      Set_Confirm_Flag =0;
                    }
                }
                                    
                if((keyvalue == 2)&&( Respond_Key_Flag==1))      //短路键
                {
                  
                  Respond_Key_Flag=0;
                  Disp1Char(3,0,'Z') ;
                  //TACCR0 = 65535;                   //PWM占空比设置为百分之百
                }
            
      
                if((keyvalue == 3)&&( Respond_Key_Flag==1))      //左移
                { 
                  
                  Respond_Key_Flag=0;
                  if(x >3) 
                  {
                    --x; 
                    if(x<8)
                      LocateXY(x,0);
                    else
                      LocateXY(x-5,1);  
                  }
                  else 
                  {
                    x=12;
                    LocateXY(x-5,1);
                  }        
                }
                
                
     
                if((keyvalue == 4)&&( Respond_Key_Flag==1))      //右移
                {
                  Respond_Key_Flag=0; 
                  
                  if(x < 12) 
                  {
                    ++x; 
                    if(x<8)
                      LocateXY(x,0);
                    else
                      LocateXY(x-5,1);  
                  }
                  else 
                  {
                    x=3;
                    LocateXY(x,0);
                  }        
                  
                }
                
      
      
      
                 if((keyvalue == 5)&&( Respond_Key_Flag==1))      //数值"+"键
                  {
                    Respond_Key_Flag=0; 
                    
                    if(x<3 || x>12)
                    { 
                      write_data(' ') ;
                    }
                    else if(x==5||x==10) 
                       {
                          write_data('.') ; 
                          if(x==5)
                            LocateXY(x,0);
                          if(x==10)
                            LocateXY(x-5,1);
                            
                       }
                     else
                       {
                           if((x>=3)&&(x<=7))
                           {
                              if(Volt_buf[x-3]<'9')
                               Volt_buf[x-3] += 1;
                               else Volt_buf[x-3]='0';
                               Disp1Char(x,0,Volt_buf[x-3]) ;
                               LocateXY(x,0);
                           }
                           else
                             {
                               if(Curr_buf[x-8]<'9')
                               Curr_buf[x-8] += 1;
                               else Curr_buf[x-8]='0';
                               Disp1Char(x-5,1,Curr_buf[x-8]) ;
                               LocateXY(x-5,1);
                           }                                                             
                       }
                    } 
       
           
        
                    
                     if((keyvalue == 6)&&( Respond_Key_Flag==1))    //数值"-"键
                      {
                        Respond_Key_Flag=0; 
                          
                          if(x<3 || x>12)
                          { 
                            write_data(' ') ;
                          }
                          else if(x==5||x==10) 
                             {
                                write_data('.') ; 
                                if(x==5)
                                  LocateXY(x,0);
                                if(x==10)
                                  LocateXY(x-5,1);
                                  
                             }
                           else
                             {
                                 if((x>=3)&&(x<=7))
                                 {
                                    if(Volt_buf[x-3]>'0')
                                     Volt_buf[x-3] -= 1;
                                     else Volt_buf[x-3]='9';
                                     Disp1Char(x,0,Volt_buf[x-3]) ;
                                     LocateXY(x,0);
                                 }
                                 else
                                   {
                                     if(Curr_buf[x-8]>'0')
                                     Curr_buf[x-8] -= 1;
                                     else Curr_buf[x-8]='9';
                                     Disp1Char(x-5,1,Curr_buf[x-8]) ;
                                     LocateXY(x-5,1);
                                 }                                                             
                             }
                      }
                
      }

    
}

/*============================================
函数名称:Init_port
功    能:初始化I/O端口
参    数:无       
返回值  :无
==============================================*/
void Init_port()
{
    /*下面六行程序关闭所有的IO口*/
    P1DIR = 0XFF;P1OUT = 0XFF;
    P2DIR = 0XFF;P2OUT = 0XFF;
    P3DIR = 0XFF;P3OUT = 0XFF;
    P4DIR = 0XFF;P4OUT = 0XFF;
    P5DIR = 0XFF;P5OUT = 0XFF;
    P6DIR = 0XFF;P6OUT = 0XFF;
    P6DIR |= BIT2;P6OUT |= BIT2;      //关闭电平转换
     //定义液晶端口
    
    P4DIR = 0XFF;                     //数据口
    P4OUT = 0;
    //定义按键端口
    P1DIR =BIT6 + BIT7;               //设置P1.0~P.3为输入状态,P.7为输出
   
    
}
/*============================================
函数名称:Init_SysClk
功    能:初始化系统时钟
参    数:无       
返回值  :无
==============================================*/

void Init_SysClk()
 {
 
  unsigned int i;
   
   BCSCTL1 &= ~XT2OFF;                       // XT2= HF XTAL

  do
  {
  IFG1 &= ~OFIFG;                           // Clear OSCFault flag
  for (i = 0xFF; i > 0; i--);               // Time for flag to set
  }
  while ((IFG1 & OFIFG));                   // OSCFault flag still set?

  BCSCTL2 |= SELM_2;                        // MCLK= XT2 (safe),分频椅子为1
  BCSCTL2 |= SELS;                          //SMCLK的时钟源为TX2CLK,分频因子为1
  
 }


/*============================================
函数名称:Init_TimerB
功    能:初始化定时器B,设定基准时间500us
参    数:无       
返回值  :无
==============================================*/
void  Init_TimerB()  
 {
    TBCTL |= TBSSEL1 +TBCLR; //MCLK ,8M
    TBCCTL0 = CCIE;
    TBCCR0 =4000;            //定时500us  
    TBCTL |= MC_1;           // Start Timer_B in up mode
 }


/*===========================================
函数名称:TimerB_ISR
功    能:设定时间间隔
参    数:无       
返回值  :无
=============================================*/
// Timer B0 interrupt service routine
#pragma vector=TIMERB0_VECTOR
__interrupt void TimerB_ISR (void)
{
    static uchar Flag_500us = 0;  
    Flag_500us ++;
     if(Flag_500us  ==20)
     {
         Key_Flag =1;          //定时10ms
         Flag_500us=0;
        // TBCCTL0 = ~CCIE;    //关中断,单步调试时用,不然一直在中断里
       
       
     }   
         
}


    

 

//按键头文件 key.h

#ifndef MSP430_KEY_PROCESS_H
#define MSP430_KEY_PROCESS_H
/***************************************************
程序功能:用定时扫描方式读取6个行列式按键的键值
****************************************************/
#include
#include "lcd1602.h"

#define keyin (P1IN & 0x0F)

uchar KEY_BUFFER ;
uchar KEY_BUFFER1 ;

uchar KEY_DATA = 0x00;
uchar Num=0;
extern uchar Key_Flag; 
extern uchar Keyval;
extern uchar Respond_Key_Flag;
extern uchar Set_Confirm_Flag;

/*******************************************
函数名称:Delay10us
功    能:延时约10us
参    数:无
返回值  :无
********************************************/
void Delay10us(void)

    uint i=10;
    while (i != 0)
    {
        i--;
    }
}
/*******************************************
函数名称:Key_Scan
功    能:扫描按键值
参    数:无
返回值  :Keyval
********************************************/
uchar Key_Scan()
{      
       
       if(Key_Flag ==1 )            //10ms扫描一次
       {                  
         Key_Flag =0;                
        //位处理,先用一个字节把八位按键的值读出来,然后再进行判断 
         P1OUT =0X70;
         Delay10us();                 //给硬件响应时间
         KEY_BUFFER =keyin;
         KEY_BUFFER =~KEY_BUFFER;
         KEY_BUFFER1 =KEY_BUFFER<<4;
       
         P1OUT =0XB0;
         Delay10us();
         KEY_BUFFER =keyin;
         KEY_BUFFER =~KEY_BUFFER;
         KEY_BUFFER &=0X0F;           //屏蔽高四位
         KEY_BUFFER1 |=KEY_BUFFER ;  
         
         if(KEY_BUFFER1 != KEY_DATA)  //首先判断是否有键按下
         {
            KEY_DATA =KEY_BUFFER1;    //读键值
            Num =0;
         }
         else                    //KEY_BUFFER1 = KEY_DATA的处理
         {
                Num ++; 
                         
                if(Num==4)     //Num=4,读4次有效,延时达到40ms 
                {               
                  if(KEY_DATA==0x10)      //判断键值
                     Keyval=1;
                  else if(KEY_DATA==0x20)
                     Keyval=2;
                  else if(KEY_DATA==0x40)
                     Keyval=3;
                  else if(KEY_DATA==0x80)
                     Keyval=4;
                  else if(KEY_DATA==0x01)
                     Keyval=5;
                  else if(KEY_DATA==0x02)
                     Keyval=6;
                  else Keyval=0;
                  
                }
                else                          //  Num!=4 的处理      
                {
                          
                          
                          if( KEY_DATA == 0x00)      //按键放开,允许响应
                          {
                            Num=0;
                            Respond_Key_Flag =1;     //按键响应标志位                      
                          }
                          else                       //按键没放开,连续按
                          {
                                  
                              if((KEY_DATA == 0x10)&&(Num >=100)) //1s,判断是否为设置/确认键
                              {                                 
                                  Num =0;
                                  Set_Confirm_Flag =1;                                   
                                 
                              }
                             
                                  
                          }
                            
                  }
   
         }
         
       }
         
return Keyval;

}
#endif




 

  //液晶1602头文件 lcd1602.h

#ifndef MSP430_LCD1602_PROCESS_H
#define MSP430_LCD1602_PROCESS_H
//硬件连接    P4 数据口    P3.2---EN  P3.1---RW  P3.0----RS

#include"msp430x14x.h" 
typedef unsigned int uint;
typedef unsigned char uchar;
//定义MCU与LCD的接口
/**************宏定义***************/   
#define Busy     0x80
#define CtrlDir     P3DIR
#define CLR_RS P3OUT&=~BIT0    //RS = P3.0 
#define SET_RS P3OUT|=BIT0 
#define CLR_RW P3OUT&=~BIT1 //RW = P3.1
#define SET_RW P3OUT|=BIT1 
#define CLR_EN P3OUT&=~BIT2 //EN = P3.2 
#define SET_EN P3OUT|=BIT2  
/*******************************************
函数名称:Delay5ms
功    能:延时约5ms
参    数:无
返回值  :无
********************************************/
void Delay5ms(void)

    uint i=40000;
    while (i != 0)
    {
        i--;
    }
}
/*******************************************
函数名称:WaitForEnable
功    能:等待1602液晶完成内部操作
参    数:无
返回值  :无
********************************************/
void WaitForEnable(void) 
{
    P4DIR &= 0x00;  //将P4口切换为输入状态

    CLR_RS;
    SET_RW;
    _NOP();
    SET_EN; 
    _NOP();
    _NOP();
 
    while((P4IN & Busy)!=0);  //检测忙标志

    CLR_EN;

    P4DIR |= 0xFF;  //将P4口切换为输出状态

/*******************************************
函数名称:write_cmd
功    能:向液晶模块写入命令
参    数:com--命令,         
返回值  :无
********************************************/
void write_cmd(unsigned char com) 

    WaitForEnable();        //等待液晶不忙
    CLR_RS ;                //RS=0,RW=0,写指令
    CLR_RW;
    _NOP();
    P4OUT = com; 
    _NOP(); 
    SET_EN ;
    _NOP(); 
    _NOP();
    CLR_EN;
}

/*******************************************
函数名称:write_data
功    能:向液晶显示的当前地址写入显示数据
参    数:data--显示字符数据
返回值  :无
********************************************/ 
void write_data(unsigned char data) 
{  
    WaitForEnable();        //等待液晶不忙
    SET_RS;                 //RS=1,RW=0,写数据
    CLR_RW;
    _NOP();
    P4OUT = data; 
    _NOP(); 
    SET_EN ;                //产生负脉冲
    _NOP(); 
    _NOP();  
    CLR_EN ;             
}

/*============================================
函数名称:Init_LCD
功    能:初始化液晶
参    数:无
返回值  :无
==============================================*/
void Init_LCD() 

    CtrlDir |= 0x07;                 //控制线端口设为输出状态 
    P4DIR  = 0xFF;                   //数据端口设为输出状态
    
    write_cmd(0x38);                 //规定的复位操作
    Delay5ms();
    write_cmd(0x38);  
    Delay5ms();
    write_cmd(0x38);
    Delay5ms();
   
    write_cmd(0x38);//16×2显示,5×7点阵,8位数据接口  
    write_cmd(0x08);//关闭显示
    write_cmd(0x01);//清屏,数据指针=0,所有显示=0
    write_cmd(0x06);//读或写一个字符后地址指针加1 &光标加1,整屏显示不移动
    write_cmd(0x0c);//开显示, 关光标,光标所在位置的字符不闪烁    
     
}

/*******************************************
函数名称:LocateXY
功    能:向液晶写入显示字符位置的坐标信息
参    数:x--位置的列坐标
          y--位置的行坐标
返回值  :无
********************************************/
void LocateXY(uchar x,uchar y) 
{
    uchar temp;

    temp = x&0x0f;
    y &= 0x01;
    if(y)   temp |= 0x40;  //如果在第2行
    temp |= 0x80;

    write_cmd(temp);
}
/*******************************************
函数名称:Disp1Char
功    能:在某个位置显示一个字符
参    数:x--位置的列坐标
          y--位置的行坐标
          data--显示的字符数据
返回值  :无
********************************************/
void Disp1Char(uchar x,uchar y,uchar data) 
{
    LocateXY( x, y );   
    write_data( data );  
}

/***********************************************
函数名称:DispStr
功    能:让液晶从某个位置起连续显示一个字符串
参    数:x--位置的列坐标
          y--位置的行坐标
          ptr--指向字符串存放位置的指针
返回值  :无
***********************************************/
void DispStr(uchar x,uchar y,uchar *ptr) 
{
    uchar *temp;
    uchar i,n = 0;
    
    temp = ptr;
    while(*ptr++ != '\0')   n++;    //计算字符串有效字符的个数
    
    for (i=0;i     {
        Disp1Char(x++,y,temp[i]);
        if (x == 0x0f)
        {
           x  = 0; 
           y ^= 1;
        }
    }
}

/*******************************************
函数名称:DispNchar
功    能:让液晶从某个位置起连续显示N个字符
参    数:x--位置的列坐标
          y--位置的行坐标
          n--字符个数
          ptr--指向字符存放位置的指针
返回值  :无
********************************************/
void DispNChar(uchar x,uchar y, uchar n,uchar *ptr) 
{
    uchar i;
    
    for (i=0;i     {
        Disp1Char(x++,y,ptr[i]);
        if (x == 0x0f)
        {
           x = 0; 
        y ^= 1;
        }
    }
}

#endif

 

第一次写的在实际使用时,发现不是很稳定,后经过两三天的修改,最终定型为以上代码,完全可以使用。

关键字:延时去抖  按键程序 引用地址:不用延时去抖的按键程序

上一篇:msp430f5418使用总结
下一篇:MSP430单片机指令周期小谈

推荐阅读最新更新时间:2024-03-16 15:06

51单片机定时闹钟程序(带按键)
电路图很简单,数码管是用2片573 接到p2口,通过p0.0 和P0.1锁存控制,详见单片机开发板上面的电路:http://www.51hei.com/f/51hei-5.pdf , 大家可以按里面的图接,只需要端口要改为本程序中的即可.可通过按键实现设置定时的时间.小时分秒设置等等. //////////////////包含头文件及宏定义////////////////////////////////////// #include reg52.h #include intrins.h #define uchar unsigned char #define uint unsigned int //////////////////
[单片机]
单片机按键程序
通常按键所用的开关都是机械弹性开关,当机械触点断开、闭合时,由于机械触点的弹性作用,一个按键开关在闭合时不会马上就稳定的接通,在断开时也不会一下子彻底断开,而是在闭合和断开的瞬间伴随了一连串的抖动,如图 8-10 所示。 图 8-10 按键抖动状态图 按键稳定闭合时间长短是由操作人员决定的,通常都会在 100ms 以上,刻意快速按的话能达到 40-50ms 左右,很难再低了。抖动时间是由按键的机械特性决定的,一般都会在 10ms以内,为了确保程序对按键的一次闭合或者一次断开只响应一次,必须进行按键的消抖处理。当检测到按键状态变化时,不是立即去响应动作,而是先等待闭合或断开稳定后再进行处理。按键消抖可分为硬件消抖和软件消抖。
[单片机]
单片机<font color='red'>按键</font>消<font color='red'>抖</font><font color='red'>程序</font>
关于独立按键扫描程序的思考(整合两种算法)
最近刚开始学51单片机编程。学到按键扫描,在网上看到Etual 2008年总结的《新型按键扫描》的博文,很有感触。关于按键扫描的介绍和应用可以参照原文,本文只是我对程序的一些心得和体会。原文的链接找不到了,给个转载的: http://www.ourdev.cn/bbs/bbs_content.jsp?bbs_sn=4308630&bbs_page_no=1&search_mode=4&search_text=atommann&bbs_id=9999 http://blog.21ic.com/user1/379/archives/2010/68144.html http://blog.ednchina.com/zp200
[单片机]
单片机单按键控制LED发光的程序
51单片机单按键控制LED发光的程序,就是用一个与P3_0引脚连接的按键控制P2_0相连LED的亮和灭两种状态。按一次按键灯亮,再按一次按键灯灭。再按一次又亮,再按一次灯又灭,本程序出自单片机网:http://www.51hei.com,经过测试已经可以成功运行。 #include "AT89X51.H" void main(void) // 主程序 { bit mark; //定义位变量 unsigned int n; //定义循环变量 while(1) //无限循环 { if (P3_0==0) //如果按键按下 { for(n=0;n 1000;n++); //延时一段时间,等待按键完全按下 mark=~mar
[单片机]
单片机独立按键控制LED流水灯电路图与源程序
这是我做的独立按键控制流水灯 每按一次独立按键点亮1个LED灯,计算按下按键次数。8个LED灯全亮后,再按按键全灭。回到最初状态重新开始计算按键次数。 电路原理图如下: 单片机源程序如下: #include reg51.h #include intrins.h typedef unsigned int uint; typedef unsigned char uchar; sbit k3=P3^5; uchar count,a; uchar sz ={0xfe,0xfc,0xf8,0xf0,0xe0,0xc0,0x80,0x00,0xff}; void delays(uint x) { uint i,
[单片机]
单片机独立<font color='red'>按键</font>控制LED流水灯电路图与源<font color='red'>程序</font>
矩阵键盘按键键值显示程序C语言程序设计
//矩阵式键盘按键值的数码管显示实验 #include reg52.h //包含51单片机寄存器定义的头文件 sbit P14=P1^4; //将P14位定义为P1.4引脚 sbit P15=P1^5; //将P15位定义为P1.5引脚 sbit P16=P1^6; //将P16位定义为P1.6引脚 sbit P17=P1^7; //将P17位定义为P1.7引脚 unsigned char code Tab ={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90}; //数字0~9的段码 unsigned char keyval; //定义变量储存按键值 /***
[单片机]
新型的按键扫描程序,仅三行程序
我在网上游逛了很久,也看过不少源程序了,没有发现这种按键处理办法的踪迹,所以,我将他共享出来,和广大同僚们共勉。我非常坚信这种按键处理办法的便捷和高效,你可以移植到任何一种嵌入式处理器上面,因为C语言强大的可移植性。 同时,这里面用到了一些分层的思想,在单片机当中也是相当有用的,也是本文的另外一个重点。 对于老鸟,我建议直接看那两个表达式,然后自己想想就会懂的了,也不需要听我后面的自吹自擂了,我可没有班门弄斧的意思,hoho~~但是对于新手,我建议将全文看完。因为这是实际项目中总结出来的经验,学校里面学不到的东西。 以下假设你懂C语言,因为纯粹的C语言描述,所以和处理器平台无关,你可以在MCS-51,AVR,PIC,甚至是ARM平台
[单片机]
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
  • ARM裸机篇--按键中断
    先看看GPOI的输入实验:按键电路图:GPF1管教的功能:EINT1要使用GPF1作为EINT1的功能时,只要将GPFCON的3:2位配置成10就可以了!GPF1先配 ...
  • 网上下的--ARM入门笔记
    简单的介绍打今天起菜鸟的ARM笔记算是开张了,也算给我的这些笔记找个存的地方。为什么要发布出来?也许是大家感兴趣的,其实这些笔记之所 ...
  • 学习ARM开发(23)
    三个任务准备与运行结果下来看看创建任务和任运的栈空间怎么样的,以及运行输出。Made in china by UCSDN(caijunsheng)Lichee 1 0 0 ...
  • 学习ARM开发(22)
    关闭中断与打开中断中断是一种高效的对话机制,但有时并不想程序运行的过程中中断运行,比如正在打印东西,但程序突然中断了,又让另外一个 ...
  • 学习ARM开发(21)
    先要声明任务指针,因为后面需要使用。 任务指针 volatile TASK_TCB* volatile g_pCurrentTask = NULL;volatile TASK_TCB* vol ...
  • 学习ARM开发(20)
  • 学习ARM开发(19)
  • 学习ARM开发(14)
  • 学习ARM开发(15)
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved