看过不少按键方面的写法,都写的比较实用,但没有发现一个写的比较全面的,即包含各个按键,长按,短按,同时按,按住不放各个功能都含一起的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
#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
#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
#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
#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
#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、不能太占时间,毕竟大部份是在中断函数里处理;
结束 。
最后求回复,求高手帮我简化这个程序,个人感觉有些繁琐,变量太多;