完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
硬件平台
软件平台 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); 1 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) 状态机处理,所以每个按键的状态彼此独立。 TIM_TimeBaseInitTypeDef 结构体内部成员 #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); //make the timer invoking the button_ticks() interval 5ms. //This function is implemented by yourself. __timer_start(button_ticks, 0, 5); while(1) { } } void BTN1_PRESS_DOWN_Handler(void* btn) { //do something... } void BTN1_PRESS_UP_Handler(void* btn) { //do something... } 软件部分 MAIN.C /** ****************************************************************************** * @file main.c * 实验平台:野火 F103-霸道 STM32 开发板 ****************************************************************************** * 第三方库文件导入 START THE FILE ****************************************************************************** */ //基本库 #include "stm32f10x.h" #include "bsp_SysTick.h" #include "bsp_usart.h" #include #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) //IO初始化 { GPIO_InitTypeDef GPIO_InitStructure; /*开启按键端口的时钟*/ RCC_APB2PeriphClockCmd(KEY1_GPIO_CLK|KEY2_GPIO_CLK,ENABLE); //选择按键KEY1的引脚 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); } //按键状态读取接口 按键输入模式 ReadInputDataBit uint8_t read_button1_GPIO() { return GPIO_ReadInputDataBit(KEY1_GPIO_PORT, KEY1_GPIO_PIN); } //按键1按下事件回调函数 void btn1_press_down_Handler(void* btn) { printf("---> key1 press down! <---rn"); } //按键1松开事件回调函数 void btn1_press_up_Handler(void* btn) { printf("***> key1 press up! <***rn"); } 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! <---rn"); break; case PRESS_UP: printf("***> key1 press up! <***rn"); break; case PRESS_REPEAT: printf("---> key1 press repeat! <---rn"); break; case SINGLE_CLICK: printf("---> key1 single click! <---rn"); break; case DOUBLE_CLICK: printf("***> key1 double click! <***rn"); break; case LONG_PRESS_START: printf("---> key1 long press start! <---rn"); break; case LONG_PRESS_HOLD: printf("***> key1 long press hold! <***rn"); break; } } /** * @brief HardWare_Iint * @param 无 * @retval 无 */ void HardWare_Iint(void) { SysTick_Init(); delay_init(); KEY_Init(); } /** * @brief 主函数 * @param 无 * @retval 无 */ int main(void) { /* 硬件端口初始化 */ HardWare_Iint(); printf("MultiButton Test...rn"); //初始化按键对象 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 /* * Copyright (c) 2016 Zibin Zheng * All rights reserved */ #include "multi_button.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 Initializes the button struct handle. * @param handle: the button handle strcut. * @param pin_level: read the HAL GPIO of the connet button level. * @param active_level: pressed GPIO level. * @retval None */ 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; } /** * @brief Attach the button event callback function. * @param handle: the button handle strcut. * @param event: trigger event type. * @param cb: callback function. * @retval None */ void button_attach(struct Button* handle, PressEvent event, BtnCallback cb) { handle->cb[event] = cb; } /** * @brief Inquire the button event happen. * @param handle: the button handle strcut. * @retval button event. */ PressEvent get_button_event(struct Button* handle) { return (PressEvent)(handle->event); } /** * @brief Button driver core function, driver state machine. * @param handle: the button handle strcut. * @retval None */ 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) { //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++; handle->event = (uint8_t)PRESS_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; } } 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; } } /** * @brief Start the button work, add the handle into work list. * @param handle: target handle strcut. * @retval 0: succeed. -1: already exist. */ 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 Stop the button work, remove the handle off work list. * @param handle: target handle strcut. * @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); } else curr = &entry->next; } } /** * @brief background ticks, timer repeat invoking interval 5ms. * @param None. * @retval None */ void button_ticks() { struct Button* target; for(target=head_handle; target; target=target->next) { button_handler(target); } } multi_button.h /* * Copyright (c) 2016 Zibin Zheng * All rights reserved */ #ifndef _MULTI_BUTTON_H_ #define _MULTI_BUTTON_H_ #include "stdint.h" #include "string.h" #include "stm32f10x.h" //According to your need to modify the constants. #define TICKS_INTERVAL 5 //ms #define DEBOUNCE_TICKS 3 //MAX 8 #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 /** ****************************************************************************** * @file bsp_usart.c * @version V1.0 * @date 2013-xx-xx * @brief 调试用的printf串口,重定向printf到串口 ****************************************************************************** * @attention * * 实验平台:野火 F103-霸道 STM32 开发板 * 论坛 :http://www.firebbs.cn * 淘宝 :https://fire-stm32.taobao.com * ****************************************************************************** */ #include "./usart/bsp_usart.h" /** * @brief USART GPIO 配置,工作参数配置 * @param 无 * @retval 无 */ void USART_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; // 打开串口GPIO的时钟 DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE); // 打开串口外设的时钟 DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE); // 将USART Tx的GPIO配置为推挽复用模式 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); // 将USART Rx的GPIO配置为浮空输入模式 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); } ///重定向c库函数printf到串口,重定向后可使用printf函数 int fputc(int ch, FILE *f) { /* 发送一个字节数据到串口 */ USART_SendData(DEBUG_USARTx, (uint8_t) ch); /* 等待发送完毕 */ while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_TXE) == RESET); return (ch); } ///重定向c库函数scanf到串口,重写向后可使用scanf、getchar等函数 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 /** * 串口宏定义,不同的串口挂载的总线和IO不一样,移植时需要修改这几个宏 * 1-修改总线时钟的宏,uart1挂载到apb2总线,其他uart挂载到apb1总线 * 2-修改GPIO的宏 */ // 串口1-USART1 #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 /* __USART_H */ 简单效果图片 |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1885 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1663 浏览 1 评论
1149 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
763 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1720 浏览 2 评论
1965浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
790浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
616浏览 3评论
631浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
594浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2025-1-14 14:21 , Processed in 0.738759 second(s), Total 41, Slave 36 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号