野火F1开发板STM32案例-MultiButton移植
硬件平台
野火STM32F103ZET6 霸道V2开发板正点原子F1系列开发板
软件平台
Keil MDK 5.31串口调试助手
MultiButton
简介
开源项目 MultiButton,一个小巧简单易用的事件驱动型按键驱动模块,作者 0x1abin。 这个项目非常精简,只有两个文件,可无限量扩展按键,按键事件的回调异步处理方式可以简化程序结构,去除冗余的按键处理硬编码,让你的按键业务逻辑更清晰。 MultiButton 是一个小巧简单易用的事件驱动型按键驱动模块,可无限量扩展按键,按键事件的回调异步处理方式可以简化你的程序结构,去除冗余的按键处理硬编码,让你的按键业务逻辑更清晰。
使用方法
1.先申请一个按键结构
struct Button button1
;
2.初始化按键对象,绑定按键的GPIO电平读取接口read_button_pin() ,后一个参数设置有效触发电平
button_init(&button1
, read_button_pin
, 0);
3.注册按键事件
button_attach(&button1, SINGLE_CLICK, Callback_SINGLE_CLICK_Handler);
button_attach(&button1, DOUBLE_CLICK, Callback_DOUBLE_Click_Handler);
...
4.启动按键
button_start(&button1
);
5.设置一个5ms间隔的定时器循环调用后台处理函数
while(1) {
...
if(timer_ticks
== 5) {
timer_ticks
= 0;
button_ticks();
}
}
特性
MultiButton 使用C语言实现,基于面向对象方式设计思路,每个按键对象单独用一份数据结构管理:
struct Button
{
uint16_t ticks
;
uint8_t repeat
: 4;
uint8_t event
: 4;
uint8_t state
: 3;
uint8_t debounce_cnt
: 3;
uint8_t active_level
: 1;
uint8_t button_level
: 1;
uint8_t
(*hal_button_Level
)(void);
BtnCallback cb
[number_of_event
];
struct Button
* next
;
};
这样每个按键使用单向链表相连,依次进入 button_handler(struct Button* handle) 状态机处理,所以每个按键的状态彼此独立。
按键事件
事件说明
PRESS_DOWN按键按下,每次按下都触发PRESS_UP按键弹起,每次松开都触发PRESS_REPEAT重复按下触发,变量repeat计数连击次数SINGLE_CLICK单击按键事件DOUBLE_CLICK双击按键事件LONG_RRESS_START达到长按时间阈值时触发一次LONG_PRESS_HOLD长按期间一直触发
官方Examples
#include "button.h"
struct Button btn1
;
int read_button1_GPIO()
{
return HAL_GPIO_ReadPin(B1_GPIO_Port
, B1_Pin
);
}
int main()
{
button_init(&btn1
, read_button1_GPIO
, 0);
button_attach(&btn1
, PRESS_DOWN
, BTN1_PRESS_DOWN_Handler
);
button_attach(&btn1
, PRESS_UP
, BTN1_PRESS_UP_Handler
);
button_attach(&btn1
, PRESS_REPEAT
, BTN1_PRESS_REPEAT_Handler
);
button_attach(&btn1
, SINGLE_CLICK
, BTN1_SINGLE_Click_Handler
);
button_attach(&btn1
, DOUBLE_CLICK
, BTN1_DOUBLE_Click_Handler
);
button_attach(&btn1
, LONG_RRESS_START
, BTN1_LONG_RRESS_START_Handler
);
button_attach(&btn2
, LONG_PRESS_HOLD
, BTN1_LONG_PRESS_HOLD_Handler
);
button_start(&btn1
);
__timer_start(button_ticks
, 0, 5);
while(1)
{
}
}
void BTN1_PRESS_DOWN_Handler(void* btn
)
{
}
void BTN1_PRESS_UP_Handler(void* btn
)
{
}
野火F1开发板STM32案例-MultiButton移植
MAIN.C
#include "stm32f10x.h"
#include "bsp_SysTick.h"
#include "bsp_usart.h"
#include <stdio.h>
#include "delay.h"
#include "multi_button.h"
struct Button button1
;
#define KEY1_GPIO_CLK RCC_APB2Periph_GPIOA
#define KEY1_GPIO_PORT GPIOA
#define KEY1_GPIO_PIN GPIO_Pin_0
#define KEY2_GPIO_CLK RCC_APB2Periph_GPIOC
#define KEY2_GPIO_PORT GPIOC
#define KEY2_GPIO_PIN GPIO_Pin_13
void KEY_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure
;
RCC_APB2PeriphClockCmd(KEY1_GPIO_CLK
|KEY2_GPIO_CLK
,ENABLE
);
GPIO_InitStructure
.GPIO_Pin
= KEY1_GPIO_PIN
;
GPIO_InitStructure
.GPIO_Mode
= GPIO_Mode_IN_FLOATING
;
GPIO_Init(KEY1_GPIO_PORT
, &GPIO_InitStructure
);
GPIO_InitStructure
.GPIO_Pin
= KEY2_GPIO_PIN
;
GPIO_InitStructure
.GPIO_Mode
= GPIO_Mode_IN_FLOATING
;
GPIO_Init(KEY2_GPIO_PORT
, &GPIO_InitStructure
);
}
uint8_t
read_button1_GPIO()
{
return GPIO_ReadInputDataBit(KEY1_GPIO_PORT
, KEY1_GPIO_PIN
);
}
void btn1_press_down_Handler(void* btn
)
{
printf("---> key1 press down! <---\r\n");
}
void btn1_press_up_Handler(void* btn
)
{
printf("***> key1 press up! <***\r\n");
}
void button_callback(void *button
)
{
uint32_t btn_event_val
;
btn_event_val
= get_button_event((struct Button
*)button
);
switch(btn_event_val
)
{
case PRESS_DOWN
:
printf("---> key1 press down! <---\r\n");
break;
case PRESS_UP
:
printf("***> key1 press up! <***\r\n");
break;
case PRESS_REPEAT
:
printf("---> key1 press repeat! <---\r\n");
break;
case SINGLE_CLICK
:
printf("---> key1 single click! <---\r\n");
break;
case DOUBLE_CLICK
:
printf("***> key1 double click! <***\r\n");
break;
case LONG_PRESS_START
:
printf("---> key1 long press start! <---\r\n");
break;
case LONG_PRESS_HOLD
:
printf("***> key1 long press hold! <***\r\n");
break;
}
}
void HardWare_Iint(void)
{
SysTick_Init();
delay_init();
KEY_Init();
}
int main(void)
{
HardWare_Iint();
printf("MultiButton Test...\r\n");
button_init(&button1
, read_button1_GPIO
, 0);
button_attach(&button1
, PRESS_DOWN
, button_callback
);
button_attach(&button1
, PRESS_UP
, button_callback
);
button_start(&button1
);
while(1)
{
button_ticks();
delay_ms(5);
}
}
multi_button.c
#include "multi_button.h"
#define EVENT_CB(ev) if(handle->cb[ev])handle->cb[ev]((Button*)handle)
static struct Button
* head_handle
= NULL;
void button_init(struct Button
* handle
, uint8_t(*pin_level
)(), 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
;
}
PressEvent
get_button_event(struct Button
* handle
)
{
return (PressEvent
)(handle
->event
);
}
void button_handler(struct Button
* handle
)
{
uint8_t read_gpio_level
= handle
->hal_button_Level();
if((handle
->state
) > 0) handle
->ticks
++;
if(read_gpio_level
!= handle
->button_level
) {
if(++(handle
->debounce_cnt
) >= DEBOUNCE_TICKS
) {
handle
->button_level
= read_gpio_level
;
handle
->debounce_cnt
= 0;
}
} else {
handle
->debounce_cnt
= 0;
}
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
)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
) {
handle
->event
= (uint8_t
)PRESS_DOWN
;
EVENT_CB(PRESS_DOWN
);
handle
->repeat
++;
handle
->event
= (uint8_t
)PRESS_REPEAT
;
EVENT_CB(PRESS_REPEAT
);
handle
->ticks
= 0;
handle
->state
= 3;
} else if(handle
->ticks
> SHORT_TICKS
) {
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
);
}
handle
->state
= 0;
}
break;
case 3:
if(handle
->button_level
!= handle
->active_level
) {
handle
->event
= (uint8_t
)PRESS_UP
;
EVENT_CB(PRESS_UP
);
if(handle
->ticks
< SHORT_TICKS
) {
handle
->ticks
= 0;
handle
->state
= 2;
} else {
handle
->state
= 0;
}
}
break;
case 5:
if(handle
->button_level
== handle
->active_level
) {
handle
->event
= (uint8_t
)LONG_PRESS_HOLD
;
EVENT_CB(LONG_PRESS_HOLD
);
} else {
handle
->event
= (uint8_t
)PRESS_UP
;
EVENT_CB(PRESS_UP
);
handle
->state
= 0;
}
break;
}
}
int button_start(struct Button
* handle
)
{
struct Button
* target
= head_handle
;
while(target
) {
if(target
== handle
) return -1;
target
= target
->next
;
}
handle
->next
= head_handle
;
head_handle
= handle
;
return 0;
}
void button_stop(struct Button
* handle
)
{
struct Button
** curr
;
for(curr
= &head_handle
; *curr
; ) {
struct Button
* entry
= *curr
;
if (entry
== handle
) {
*curr
= entry
->next
;
} else
curr
= &entry
->next
;
}
}
void button_ticks()
{
struct Button
* target
;
for(target
=head_handle
; target
; target
=target
->next
) {
button_handler(target
);
}
}
multi_button.h
#ifndef _MULTI_BUTTON_H_
#define _MULTI_BUTTON_H_
#include "stdint.h"
#include "string.h"
#include "stm32f10x.h"
#define TICKS_INTERVAL 5
#define DEBOUNCE_TICKS 3
#define SHORT_TICKS (300 /TICKS_INTERVAL)
#define LONG_TICKS (1000 /TICKS_INTERVAL)
typedef void (*BtnCallback
)(void*);
typedef enum {
PRESS_DOWN
= 0,
PRESS_UP
,
PRESS_REPEAT
,
SINGLE_CLICK
,
DOUBLE_CLICK
,
LONG_PRESS_START
,
LONG_PRESS_HOLD
,
number_of_event
,
NONE_PRESS
}PressEvent
;
typedef struct Button
{
uint16_t ticks
;
uint8_t repeat
: 4;
uint8_t event
: 4;
uint8_t state
: 3;
uint8_t debounce_cnt
: 3;
uint8_t active_level
: 1;
uint8_t button_level
: 1;
uint8_t
(*hal_button_Level
)(void);
BtnCallback cb
[number_of_event
];
struct Button
* next
;
}Button
;
#ifdef __cplusplus
extern "C" {
#endif
void button_init(struct Button
* handle
, uint8_t(*pin_level
)(), uint8_t active_level
);
void button_attach(struct Button
* handle
, PressEvent event
, BtnCallback cb
);
PressEvent
get_button_event(struct Button
* handle
);
int button_start(struct Button
* handle
);
void button_stop(struct Button
* handle
);
void button_ticks(void);
#ifdef __cplusplus
}
#endif
#endif
bsp_usart.c
#include "./usart/bsp_usart.h"
void USART_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure
;
USART_InitTypeDef USART_InitStructure
;
DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK
, ENABLE
);
DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK
, ENABLE
);
GPIO_InitStructure
.GPIO_Pin
= DEBUG_USART_TX_GPIO_PIN
;
GPIO_InitStructure
.GPIO_Mode
= GPIO_Mode_AF_PP
;
GPIO_InitStructure
.GPIO_Speed
= GPIO_Speed_50MHz
;
GPIO_Init(DEBUG_USART_TX_GPIO_PORT
, &GPIO_InitStructure
);
GPIO_InitStructure
.GPIO_Pin
= DEBUG_USART_RX_GPIO_PIN
;
GPIO_InitStructure
.GPIO_Mode
= GPIO_Mode_IN_FLOATING
;
GPIO_Init(DEBUG_USART_RX_GPIO_PORT
, &GPIO_InitStructure
);
USART_InitStructure
.USART_BaudRate
= DEBUG_USART_BAUDRATE
;
USART_InitStructure
.USART_WordLength
= USART_WordLength_8b
;
USART_InitStructure
.USART_StopBits
= USART_StopBits_1
;
USART_InitStructure
.USART_Parity
= USART_Parity_No
;
USART_InitStructure
.USART_HardwareFlowControl
= USART_HardwareFlowControl_None
;
USART_InitStructure
.USART_Mode
= USART_Mode_Rx
| USART_Mode_Tx
;
USART_Init(DEBUG_USARTx
, &USART_InitStructure
);
USART_Cmd(DEBUG_USARTx
, ENABLE
);
}
int fputc(int ch
, FILE
*f
)
{
USART_SendData(DEBUG_USARTx
, (uint8_t
) ch
);
while (USART_GetFlagStatus(DEBUG_USARTx
, USART_FLAG_TXE
) == RESET
);
return (ch
);
}
int fgetc(FILE
*f
)
{
while (USART_GetFlagStatus(DEBUG_USARTx
, USART_FLAG_RXNE
) == RESET
);
return (int)USART_ReceiveData(DEBUG_USARTx
);
}
bsp_usart.h
#ifndef __USART_H
#define __USART_H
#include "stm32f10x.h"
#include <stdio.h>
#define DEBUG_USARTx USART1
#define DEBUG_USART_CLK RCC_APB2Periph_USART1
#define DEBUG_USART_APBxClkCmd RCC_APB2PeriphClockCmd
#define DEBUG_USART_BAUDRATE 115200
void USART_Config(void);
#endif
串口打印效果