按键处理 C

    看过不少按键方面的写法,都写的比较实用,但没有发现一个写的比较全面的,即包含各个按键,长按,短按,同时按,按住不放各个功能都含一起的C程序,刚好项目需要用到按键,所以自己花了点时间写了一个,功能上基本包含上面所说的,但缺少连击功能,按住不放功能不全,即只有一个按键可以拥有按住不放的功能;

    具体开发环境如下:

    使用的是STM32F2XX芯片,采用IAR编译环境,以下具体解说代码:

1、首先变量参数,先定义按键引脚,使用6个按键

#define BUTTONn                          6
#define QUEUE_BUTTON_LENGH               4
/*UP BUTTON EXTI*/
#define BUTTON_PIN_UP                     GPIO_Pin_0
#define BUTTON_UP_PORT           GPIOC
#define BUTTON_UP_CLK           RCC_AHB1Periph_GPIOC
#define BUTTON_UP_LINE           EXTI_Line0
#define BUTTON_EXTI_UP_PORT_SOURCE       EXTI_PortSourceGPIOC
#define BUTTON_EXTI_UP_PIN_SOURCE       EXTI_PinSource0

#define BUTTON_EXTI_UP_IRQn           EXTI0_IRQn 


/*DOWN BUTTON EXTI*/
#define BUTTON_PIN_DOWN                     GPIO_Pin_1
#define BUTTON_DOWN_PORT           GPIOC
#define BUTTON_DOWN_CLK           RCC_AHB1Periph_GPIOC
#define BUTTON_DOWN_LINE           EXTI_Line1
#define BUTTON_EXTI_DOWN_PORT_SOURCE       EXTI_PortSourceGPIOC
#define BUTTON_EXTI_DOWN_PIN_SOURCE       EXTI_PinSource1

#define BUTTON_EXTI_DOWN_IRQn           EXTI1_IRQn 


/*LEFT BUTTON EXTI*/
#define BUTTON_PIN_LEFT                     GPIO_Pin_2
#define BUTTON_LEFT_PORT           GPIOC
#define BUTTON_LEFT_CLK           RCC_AHB1Periph_GPIOC
#define BUTTON_LEFT_LINE           EXTI_Line2
#define BUTTON_EXTI_LEFT_PORT_SOURCE       EXTI_PortSourceGPIOC
#define BUTTON_EXTI_LEFT_PIN_SOURCE       EXTI_PinSource2

#define BUTTON_EXTI_LEFT_IRQn           EXTI2_IRQn 


/*RIGHT BUTTON EXTI*/
#define BUTTON_PIN_RIGHT                    GPIO_Pin_3
#define BUTTON_RIGHT_PORT           GPIOC
#define BUTTON_RIGHT_CLK           RCC_AHB1Periph_GPIOC
#define BUTTON_RIGHT_LINE           EXTI_Line3
#define BUTTON_EXTI_RIGHT_PORT_SOURCE       EXTI_PortSourceGPIOC
#define BUTTON_EXTI_RIGHT_PIN_SOURCE       EXTI_PinSource3

#define BUTTON_EXTI_RIGHT_IRQn           EXTI3_IRQn 


/*CANCEL BUTTON EXTI*/
#define BUTTON_PIN_CANCEL                   GPIO_Pin_4
#define BUTTON_CANCEL_PORT           GPIOC
#define BUTTON_CANCEL_CLK           RCC_AHB1Periph_GPIOC
#define BUTTON_CANCEL_LINE           EXTI_Line4
#define BUTTON_EXTI_CANCEL_PORT_SOURCE      EXTI_PortSourceGPIOC
#define BUTTON_EXTI_CANCEL_PIN_SOURCE       EXTI_PinSource4

#define BUTTON_EXTI_CANCEL_IRQn           EXTI4_IRQn 


/*CONFIRM BUTTON EXTI*/
#define BUTTON_PIN_CONF                     GPIO_Pin_5
#define BUTTON_CONF_PORT           GPIOC
#define BUTTON_CONF_CLK           RCC_AHB1Periph_GPIOC
#define BUTTON_CONF_LINE           EXTI_Line5
#define BUTTON_EXTI_CONF_PORT_SOURCE       EXTI_PortSourceGPIOC
#define BUTTON_EXTI_CONF_PIN_SOURCE       EXTI_PinSource5

#define BUTTON_EXTI_CONF_IRQn           EXTI9_5_IRQn 

这里定义比较全,实际只采用的是轮询方式 ,即定时去查询按键状态,定时机制可用定时器中断或其它方式 ;

定义按键参数:

/******************************Start Button para*******************************/
typedef enum   //定义按键功能

BUTTON_UP = 0,
BUTTON_DOWN = 1,
BUTTON_LEFT = 2,
BUTTON_RIGHT = 3,
BUTTON_CANCEL = 4,
BUTTON_CONFIRM = 5,
BUTTON_MAX = 6,
} Button_TypeDef;


typedef enum   //定义按键使用方式
{  
  BUTTON_MODE_GPIO = 0,
  BUTTON_MODE_EXTI = 1
} ButtonMode_TypeDef;


typedef enum   //定义是否释放
{  
  BUTTON_RELEASE = 0,
  BUTTON_PRESS = 1
} Button_Press_s;


typedef enum    //定义按键类型

  BUTTON_NULL = 0,
  BUTTON_PRESS_SHORT = 1,
  BUTTON_PRESS_MIDDLE = 2,
  BUTTON_PRESS_LONG = 3,
  BUTTON_NO_RELEASE_LONG=4,
} Button_Press_type_s;
typedef struct{                    //按键发生时,的按键参数
uint16_t button_cnt;   //计数值
    Button_Press_s button_cur_state;
}button_para_s;                         

typedef struct{      //保存有按键时的个数,先保存
    uint8_t button_name[BUTTON_MAX]; //有按键发生时先保存按键名称,再去判别类型
    uint8_t button_press_cnt;                    //单次轮循环时按键发生个数
}cal_button_s;
typedef struct{
    Button_TypeDef button_name;           //保存按键发生时的按键名称
    Button_Press_type_s button_type;       //保存按键类型
bool button_flag;                           //预留,无用 
}button_local_s;


typedef struct{                                       //此即最后传递出去的按键参数:含按键个数,类型,即可多个同时按,也可单个按,长按,短按,按住不放
    button_local_s button_local[BUTTON_MAX];
uint8_t button_event_cnt;                //即按键发生的个数,判断同时按还是单个按
}button_valid_para_s;

2、下面的正式的按键程序:

cal_button_s press_s,release_s;
button_valid_para_s button_send_para;
uint8_t addr=0;
void Button_check(void)
{
uint8_t i,j;
Button_TypeDef pin_addr;
static portBASE_TYPE xHigherPriorityTaskWoken;//这是FREERTOS用的,不带系统可以不用
        xHigherPriorityTaskWoken = pdFALSE;

button_send_para.button_event_cnt=0;  //先清按键个数为0
for(i = 0;i < BUTTON_MAX; i++)            //先期扫描按键状态,并保存
{
pin_addr = (Button_TypeDef)i;
if(STM_Button_GetState(pin_addr) == RESET)//IO引脚状态,RESET=0即按住
{
if(button_para[i].button_cnt<1000)
button_para[i].button_cnt++; 
else
button_para[i].button_cnt=0;
if(button_para[i].button_cnt > 1)    //去抖
{
button_para[i].button_cur_state = BUTTON_PRESS;//记录6个按键状态
if(press_s.button_name[i] != i+1)  //防止按键错乱
{
press_s.button_name[i] = i+1;  
press_s.button_press_cnt ++;  //保存按住按键的个数
release_s.button_press_cnt = press_s.button_press_cnt;//按住按键个数与弹开相等,等待弹开,当所以弹开后即release_s.button_press_cnt = 0,按键有效
}
}
}
else
{
button_para[i].button_cur_state = BUTTON_RELEASE;
}
}
for(j = 0;j < BUTTON_MAX; j++) //处理
{
if(button_para[j].button_cur_state == BUTTON_PRESS)//还是按住,只能保存一个长按不放的按键
{
#if 1
/* Long press does not release the function 
 *There can only be one long press function
 *Ignore other short presses or long presses at the back
*/
if(button_para[j].button_cnt > 500)
{
addr = 0;
button_para[j].button_cnt=0;
button_send_para.button_local[addr].button_type = BUTTON_NO_RELEASE_LONG;
button_send_para.button_local[addr].button_name = (Button_TypeDef)j;
button_send_para.button_event_cnt++;
press_s.button_press_cnt = 1;
release_s.button_press_cnt = 0;
break;
}
#endif
}
else  //已弹开
{
if(button_para[j].button_cnt > 300)  //长按类型
{
button_para[j].button_cnt=0;  //清计数
button_send_para.button_local[addr].button_type = BUTTON_PRESS_LONG;//保存类型
button_send_para.button_local[addr++].button_name = (Button_TypeDef)j; //保存名称
//button_send_para.button_event_cnt+=1;
if(release_s.button_press_cnt != 0)
release_s.button_press_cnt -= 1;    //按键弹开计数减1
}
else if((button_para[j].button_cnt > 100)&&(button_para[j].button_cnt <= 300))//中按类型
{
button_para[j].button_cnt=0;
button_send_para.button_local[addr].button_type = BUTTON_PRESS_MIDDLE;
button_send_para.button_local[addr++].button_name = (Button_TypeDef)j;
//button_send_para.button_event_cnt+=1;
if(release_s.button_press_cnt != 0)
release_s.button_press_cnt -= 1;
}
if((button_para[j].button_cnt > 3)&&(button_para[j].button_cnt <= 100)) //短按类型
{
button_para[j].button_cnt=0;
button_send_para.button_local[addr].button_type = BUTTON_PRESS_SHORT;
button_send_para.button_local[addr++].button_name = (Button_TypeDef)j;
//button_send_para.button_event_cnt+=1;//Place this position with only a single key action:
//When multiple keys require multiple loop detection
if(release_s.button_press_cnt != 0)
release_s.button_press_cnt -= 1;
}
}

}
button_send_para.button_event_cnt = press_s.button_press_cnt;//Place this position with only a single key action: 
//When multiple keys require multiple loop detection
if((button_send_para.button_event_cnt != 0)&&(release_s.button_press_cnt == 0))//有按键事件,且所有按键都弹开,长按不弹开功能已在上面单独做;
{
release_s.button_press_cnt = 0;
press_s.button_press_cnt = 0;
addr=0;
for(i =0;i<BUTTON_MAX;i++)
{
button_para[i].button_cnt=0;
press_s.button_name[i]=0;
}
//xQueueSend(ButtonQueue,&button_send_para,xHigherPriorityTaskWoken);
if( uxQueueMessagesWaitingFromISR(ButtonQueue)<QUEUE_BUTTON_LENGH)//FREERTOS系统队列,无系统时可直接变量传递
xQueueSendFromISR(ButtonQueue,&button_send_para,&xHigherPriorityTaskWoken);
}
#if 0
if(xHigherPriorityTaskWoken == pdTRUE)
{
//portYIELD_FROM_ISR( xHigherPriorityTaskWoken );//Switch to highest pro task
portYIELD()( xHigherPriorityTaskWoken );//Switch to highest pro task
}
#endif

}

按键解析:

 if(button_valve.button_event_cnt != 0){

switch(button_valve.button_event_cnt)//几个按键
{
/********************** one button is valid **************************/
case 1://单个按键
{

switch (button_valve.button_local[0].button_name)//按键名称

                                {

                                    case 名称1:

                                     {

                                                swtich(button_valve.button_local[0].button_type)//按键类型

                                                 {

                                                        case 长按:

                                                        break;

                                                        case 短按:

                                                        break;

                                                        case 中按:

                                                        break;

                                                        default:

                                                        break;

                                                  }

                                    }

                                   break;

                                   case 名称2:

                                    break;

                                    '''

                        }

                        case 2: //2个按键同时按

                        {


                        }

}


此程序按键必先判定几个按键,因为按键保存一定是按保存在button_valve.button_local[0],button_valve.button_local[1]...

但不一定按顺序保存;


此按键程序难点在于,1、同时按键时确保去抖前后的按键顺序不乱;

                                2、不能太占时间,毕竟大部份是在中断函数里处理;

      结束 。


最后求回复,求高手帮我简化这个程序,个人感觉有些繁琐,变量太多;                   

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值