system/btn.c

249 lines
5.6 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* @Author:
* @Date: 2023-07-04 08:25:56
* @LastEditors: xxx
* @LastEditTime: 2023-08-25 11:13:52
* @Description:一个小巧简单易用的事件驱动型按键驱动模块,可无限量扩展按键,按键事件的回调异步处理方式可以简化你的程序结构,去除冗余的按键处理硬编码,让你的按键业务逻辑更清晰
* email:
* Copyright (c) 2023 by xxx, All Rights Reserved.
*/
#include "btn.h"
#define EVENT_CB(ev) \
if (handle->cb[ev]) \
handle->cb[ev]((Button *)handle)
// button handle list head.
static struct Button *head_handle = NULL;
/**
* @brief 初始化按钮结构体句柄。
* @param handle: 按钮句柄结构体。
* @param pin_level: 读取按钮连接的HAL GPIOLevel。
* @param active_level: 按下按钮的GPIOLevel。
* @param button_id: 按钮ID。
* @retval 无
*/
void button_init(struct Button *handle, uint8_t (*pin_level)(uint8_t), active_level_e active_level, uint8_t button_id, uint8_t button_id_reverse)
{
#ifdef STM32
osel_memset((uint8_t *)handle, 0, sizeof(struct Button));
#else
memset(handle, 0, sizeof(struct Button));
#endif
handle->event = (uint8_t)NONE_PRESS;
handle->hal_button_Level = pin_level;
handle->button_level = handle->hal_button_Level(button_id);
handle->active_level = (uint8_t)active_level;
handle->button_id = button_id;
handle->button_id_reverse = button_id_reverse;
}
/**
* @brief 为按钮添加事件回调函数。
* @param handle: 按钮句柄结构体。
* @param event: 触发事件类型。
* @param cb: 回调函数。
* @retval 无
*/
void button_attach(struct Button *handle, PressEvent event, BtnCallback cb)
{
handle->cb[event] = cb;
}
/**
* @brief 查询按钮发生的事件。
* @param handle: 按钮句柄结构体。
* @retval 按钮事件。
*/
PressEvent get_button_event(struct Button *handle)
{
return (PressEvent)(handle->event);
}
/**
* @brief 按钮驱动核心函数,驱动状态机。
* @param handle: 按钮句柄结构体。
* @retval 无
*/
void button_handler(struct Button *handle)
{
uint8_t read_gpio_level = handle->hal_button_Level(handle->button_id);
// 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)
{ // start press down
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)
{ // released press up
handle->event = (uint8_t)PRESS_UP;
EVENT_CB(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 = 5;
}
break;
case 2:
if (handle->button_level == handle->active_level)
{ // press down again
handle->event = (uint8_t)PRESS_DOWN;
EVENT_CB(PRESS_DOWN);
handle->repeat++;
EVENT_CB(PRESS_REPEAT); // repeat hit
handle->ticks = 0;
handle->state = 3;
}
else if (handle->ticks > SHORT_TICKS)
{ // released timeout
if (handle->repeat == 1)
{
handle->event = (uint8_t)SINGLE_CLICK;
EVENT_CB(SINGLE_CLICK);
}
else if (handle->repeat == 2)
{
handle->event = (uint8_t)DOUBLE_CLICK;
EVENT_CB(DOUBLE_CLICK); // repeat hit
}
handle->state = 0;
}
break;
case 3:
if (handle->button_level != handle->active_level)
{ // released press up
handle->event = (uint8_t)PRESS_UP;
EVENT_CB(PRESS_UP);
if (handle->ticks < SHORT_TICKS)
{
handle->ticks = 0;
handle->state = 2; // repeat press
}
else
{
handle->state = 0;
}
}
else if (handle->ticks > SHORT_TICKS)
{ // long press up
handle->state = 0;
}
break;
case 5:
if (handle->button_level == handle->active_level)
{
// continue hold trigger
handle->event = (uint8_t)LONG_PRESS_HOLD;
EVENT_CB(LONG_PRESS_HOLD);
}
else
{ // releasd
handle->event = (uint8_t)PRESS_UP;
EVENT_CB(PRESS_UP);
handle->state = 0; // reset
}
break;
default:
handle->state = 0; // reset
break;
}
}
/**
* @brief 启动按钮工作,将句柄添加到工作队列中。
* @param handle: 目标句柄结构体。
* @retval 0: 成功。-1: 已存在。
*/
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;
head_handle = handle;
return 0;
}
/**
* @brief 停止按钮工作,从工作队列中移除句柄。
* @param handle: 目标句柄结构体。
* @retval None
*/
void button_stop(struct Button *handle)
{
struct Button **curr;
for (curr = &head_handle; *curr;)
{
struct Button *entry = *curr;
if (entry == handle)
{
*curr = entry->next;
// free(entry);
return;
}
else
curr = &entry->next;
}
}
/**
* @brief 后台计时定时器重复调用间隔为5ms。
* @param None.
* @retval None
*/
void button_ticks()
{
struct Button *target;
for (target = head_handle; target; target = target->next)
{
button_handler(target);
}
}