相信很多刚接触STM32的程序员来说,都会选择一种开发板来学习,而开发板给出的例程也主要是针对初学者,提供一种简单的按键处理方法,让大家更容易理解。但在相对复杂的工程中,这种简单的扫面实现的按键程序有时会显得力不从心。今天给大家分享一段项目中实际应用的按键处理程序。
前端时间在做一个项目时用到了相对复杂的按键处理,于是找到了基于状态机实现按键处理的multi_button开源软件,结合自己的应用对其进行了改编,实现了按键长按、短按、开关状态检测等,使用起来非常方便。
该按键程序的核心是状态机处理函数,对程序中的各个按键(在button init 中声明)进行管理,定期检测按键状态,当发生松开、按下动作时,根据按键状态变化,生成不同的事件,可在回调函数中执行需要的应用。
状态图如下
按键状态机需要定时调用,一般调用时间间隔为5mS,代码如下:
void button_handler(struct button* handle)
{
uint8_t read_gpio_level = handle->hal_button_Level();
//ticks counter working..
if((handle->state) > 0)
{
handle->ticks++;
}
/*------------button debounce handle---------------*/
if(read_gpio_level != handle->button_level)
{
//not equal to prev one
//continue read 3 times same new level change
if(++(handle->debounce_cnt) >= DEBOUNCE_TICKS)
{
handle->button_level = read_gpio_level;
handle->debounce_cnt = 0;
}
}
else
{
// leved not change ,counter reset.
handle->debounce_cnt = 0;
}
/*-----------------State machine-------------------*/
switch (handle->state)
{
case 0: //释放状态
if(handle->button_level == handle->active_level)
{
handle->event = (uint8_t)PRESS_DOWN;
EVENT_CB(PRESS_DOWN);
handle->ticks = 0;
//handle->repeat = 1;
handle->state = 1;
}
else
{
handle->event = (uint8_t)NONE_PRESS;
}
break;
case 1: //短按状态
if(handle->button_level != handle->active_level)
{
handle->event = (uint8_t)SHORT_PRESS_UP;
EVENT_CB(SHORT_PRESS_UP);
handle->ticks = 0;
handle->state = 2;
}
else if(handle->ticks > LONG_TICKS)
{
handle->event = (uint8_t)LONG_PRESS_START;
EVENT_CB(LONG_PRESS_START);
handle->state = 2;
}
break;
case 2: //长按状态
if(handle->button_level == handle->active_level)
{
handle->event = (uint8_t)LONG_PRESS_HOLD;
if (handle->ticks % LONG_HOLD_CYC == 0)
{
EVENT_CB(LONG_PRESS_HOLD);
}
}
else
{
handle->event = (uint8_t)LONG_PRESS_UP;
EVENT_CB(LONG_PRESS_UP);
handle->state = 0;
}
break;
}
}
按键初始化函数,
void button_init(struct button* handle, uint8_t(*pin_level)(void), uint8_t active_level)
{
memset(handle, 0, sizeof(struct button));
handle->event = (uint8_t)NONE_PRESS;
handle->hal_button_Level = pin_level;
handle->button_level = handle->hal_button_Level();
handle->active_level = active_level;
}
将按键事件添加到状态机事件列表中
void button_attach(struct button* handle, PressEvent event, BtnCallback cb)
{
handle->cb[event] = cb;
}
5.启用按键,供状态机管理
int button_start(struct button* handle)
{
struct button* target = head_handle;
while(target) //遍历所有节点,避免重复插入
{
if(target == handle)
{
return -1; //already exist.
}
target = target->next; //遍历到最后一个节点后,跳出循环
}
handle->next = head_handle; //新节点next指向上一个节点,第一个插入的节点next = NULL,时钟为尾
head_handle = handle; //自己变成头
return 0;
}
按键状态机工作的滴答时钟函数
void button_ticks(void)
{
struct button* target;
for(target = head_handle; target != NULL; target = target->next) //遍历所有节点
{
button_handler(target);
}
}
总结,以上是一些主要函数的说明,详细的源码可从博主的博客中下载。感兴趣的可持续关注~