【嵌入式】使用MultiButton开源库驱动按键并控制多级界面切换

这篇具有很好参考价值的文章主要介绍了【嵌入式】使用MultiButton开源库驱动按键并控制多级界面切换。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

一 背景说明

二 参考资料

三 MultiButton开源库移植

四 设计实现--驱动按键

五 设计实现--界面处理


一 背景说明

        需要做一个通过不同按键控制多级界面切换以及界面动作的程序。

        查阅相关资料,发现网上大多数的应用都比较繁琐,且对于多级界面的切换逻辑可读性较差。所幸找到一篇使用开源库 MultiButton 来驱动按键,并控制多级界面切换的博文。按图索骥实现了预期的需求。

         开源库 MultiButton 是一个小巧简单易用的事件驱动型按键驱动模块,作者 0x1abin。这个项目非常精简,只有两个文件,可无限量扩展按键,按键事件的回调异步处理方式可以简化程序结构,去除冗余的按键处理硬编码,让你的按键业务逻辑更清晰。

        MultiButton 支持如下的按钮事件:

【嵌入式】使用MultiButton开源库驱动按键并控制多级界面切换,开源,嵌入式硬件,单片机,stm32

        MultiButton的状态机如下:

【嵌入式】使用MultiButton开源库驱动按键并控制多级界面切换,开源,嵌入式硬件,单片机,stm32

二 参考资料

        【1】MultiButton开源库:mirrors / 0x1abin / MultiButton · GitCode

        【2】MultiButton博文:MultiButton | 一个小巧简单易用的事件驱动型按键驱动模块-CSDN博客

        【3】MultiTimer开源库:mirrors / 0x1abin / MultiTimer · GitCode

        【4】MultiTimer博文:【嵌入式开源库】MultiTimer 的使用,一款可无限扩展的软件定时器_multi_timer-CSDN博客

        【5】MultiButton+MultiTimer+菜单操作博文:开源按键组件MultiButton支持菜单操作(事件驱动型)-阿里云开发者社区

        【注】:我下面的实现没有用到MultiTimer,仅单列出来备查。

三 MultiButton开源库移植

        从上面的开源库或者github下载该开源库,主要用到就两个文件 multi_button.c/multi_button.h 。将这两个文件直接添加到自己的工程中,并关联头文件。

【嵌入式】使用MultiButton开源库驱动按键并控制多级界面切换,开源,嵌入式硬件,单片机,stm32

        到这边编译应该没有问题。

四 设计实现--驱动按键

        【1】首先初始化自己用到的几个按键GPIO口:

void KNOB_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);

    GPIO_InitStructure.GPIO_Pin  = KNOB_1_PIN | KNOB_2_PIN | KNOB_3_PIN | KNOB_4_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;   //设置成上拉输入
    GPIO_Init(KNOB_PORT, &GPIO_InitStructure);
}

        【2】由于这边用到了四个按键,申请四个按键结构:

struct Button knob_1;
struct Button knob_2;
struct Button knob_3;
struct Button knob_4;

        【3】编写回调函数,绑定按键的GPIO电平读取接口:

u8 knobRead(u8 button_id)
{
	switch(button_id)
	{
		case 0:
			return GPIO_ReadInputDataBit(KNOB_PORT,KNOB_1_PIN);
		case 1:
			return GPIO_ReadInputDataBit(KNOB_PORT,KNOB_2_PIN);
        case 2:
            return GPIO_ReadInputDataBit(KNOB_PORT,KNOB_3_PIN);
        case 3:
            return GPIO_ReadInputDataBit(KNOB_PORT,KNOB_4_PIN);
		default:
			return 0;
	}
}

        【4】关联 MultiButton ,使用上面的按键结构以及回调函数初始化按键对象:

button_init(&knob_1, knobRead, 0, 0);
button_init(&knob_2, knobRead, 0, 1);
button_init(&knob_3, knobRead, 0, 2);
button_init(&knob_4, knobRead, 0, 3);

        【5】注册按键事件(根据实际需要注册按键事件,不必一次性全注册,我这边只用到点按和长按,所以只注册了 SINGLE_CLICK 和 LONG_PRESS_START 两个事件)。

                其中的回调函数 knobCallback_1/2/3/4 先空着,后面需要结合界面切换来实现:

button_attach(&knob_1, SINGLE_CLICK,     knobCallback_1);
button_attach(&knob_1, LONG_PRESS_START, knobCallback_1);
button_attach(&knob_2, SINGLE_CLICK,     knobCallback_2);
button_attach(&knob_2, LONG_PRESS_START, knobCallback_2);
button_attach(&knob_3, SINGLE_CLICK,     knobCallback_3);
button_attach(&knob_3, LONG_PRESS_START, knobCallback_3);
button_attach(&knob_4, SINGLE_CLICK,     knobCallback_4);
button_attach(&knob_4, LONG_PRESS_START, knobCallback_4);

        【6】启动按键:

button_start(&knob_1);
button_start(&knob_2);
button_start(&knob_3);
button_start(&knob_4);

        【7】将上面【4】【5】【6】的三个步骤整个成一个按键注册接口:

void KNOB_Reg(void)
{
    button_init(&knob_1, knobRead, 0, 0);
    button_init(&knob_2, knobRead, 0, 1);
    button_init(&knob_3, knobRead, 0, 2);
    button_init(&knob_4, knobRead, 0, 3);

    button_attach(&knob_1, SINGLE_CLICK,     knobCallback_1);
    button_attach(&knob_1, LONG_PRESS_START, knobCallback_1);
    button_attach(&knob_2, SINGLE_CLICK,     knobCallback_2);
    button_attach(&knob_2, LONG_PRESS_START, knobCallback_2);
    button_attach(&knob_3, SINGLE_CLICK,     knobCallback_3);
    button_attach(&knob_3, LONG_PRESS_START, knobCallback_3);
    button_attach(&knob_4, SINGLE_CLICK,     knobCallback_4);
    button_attach(&knob_4, LONG_PRESS_START, knobCallback_4);

    button_start(&knob_1);
    button_start(&knob_2);
    button_start(&knob_3);
    button_start(&knob_4);
}

        【8】至此,按键驱动还不能生效,还需要添加一个心跳,一般采用5ms间隔定时器来循环调用这个心跳函数,定时器相关函数如下:

//Timer14 5ms定时器
#define TIMER14_ARR  (500-1)
#define TIMER14_PSC  (960-1)

void Timer14_Config(void)
{
    TIM_TimeBaseInitTypeDef TIM_StructInit;
    NVIC_InitTypeDef NVIC_InitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM14, ENABLE);//使能定时器时钟

    //定时器基础配置
    TIM_StructInit.TIM_Period = TIMER14_ARR;            //自动重装值
    TIM_StructInit.TIM_Prescaler = TIMER14_PSC;         //预分频系数
    TIM_StructInit.TIM_ClockDivision = TIM_CKD_DIV1;    //时钟分频
    TIM_StructInit.TIM_CounterMode = TIM_CounterMode_Up;//向上计数
    TIM_StructInit.TIM_RepetitionCounter = 0;           //不重复计数
    TIM_TimeBaseInit(TIM14, &TIM_StructInit);
    
    //NVIC中断配置
    NVIC_InitStructure.NVIC_IRQChannel = TIM14_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPriority = 3;     //数字越小优先级越高
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
    
    TIM_ClearFlag(TIM14, TIM_FLAG_Update);
    TIM_ITConfig(TIM14, TIM_IT_Update, ENABLE);          //使能更新中断
    TIM_Cmd(TIM14, ENABLE);
}

extern void button_ticks(void);
void TIM14_IRQHandler(void)
{
    if(TIM_GetITStatus(TIM14, TIM_IT_Update) != RESET)
    {
        button_ticks();     //旋钮驱动心跳
        TIM_ClearITPendingBit(TIM14, TIM_IT_Update);
    }
}

        【9】在主函数的初始化中加上上面几个接口:

void main(void)
{
    //定时器初始化
    Timer14_Config();
    //旋钮初始化/注册
    KNOB_Init();
    KNOB_Reg();

    while(1)
    {
        //...
    }
}

        至此,MultiButton 开源库移植完毕,并将所用的四个按钮关联到 MultiButton ,按键事件待扩展。

五 设计实现--界面处理

        【1】新建头文件,新增界面相关的结构体定义等:

typedef enum tagMenuTree    //菜单树
{
    MENU_MAIN = 0,
    MEUN_LOG
}MENU_TREE;

typedef enum tagEventCode   //事件值
{
    NULL_EVENT = 0,
    KNOB_1_SHORT = 1,
    KNOB_1_LONG  = 2,
    KNOB_2_SHORT = 3,
    KNOB_2_LONG = 4,
    KNOB_3_SHORT = 5,
    KNOB_3_LONG = 6,
    KNOB_4_SHORT = 7,
    KNOB_4_LONG = 8
}EVENT_CODE;

typedef struct tagMenuInfo  //界面信息
{
    u8 cur_page;    //正在执行的界面
    u8 knb_evnt;    //当前触发的事件
}MENU_INFO;
extern MENU_INFO menu;


extern void Menu_Init(MENU_INFO *handle, u8 p_page, u8 p_evnt);
extern void Set_Menu(MENU_INFO *handle, u8 p_page);
extern u8 Get_Menu(MENU_INFO *handle);
extern void Set_Event_Code(MENU_INFO *handle, u8 p_evnt);
extern int Get_Event_Code(MENU_INFO *handle);
extern void Menu_Handler(MENU_INFO *handle);

        【2】新建源文件,新增界面相关的接口函数等:

/**************************************************************************
* 函数名称: Menu_Init
* 功能描述: 菜单初始化
**************************************************************************/
void Menu_Init(MENU_INFO *handle, u8 p_page, u8 p_evnt)
{
    memset(handle, 0, sizeof(MENU_INFO));
    handle->cur_page = p_page;
    handle->knb_evnt = p_evnt;
}

/**************************************************************************
* 函数名称: Set_Menu/Get_Menu
* 功能描述: 菜单跳转/获取当前菜单
**************************************************************************/
void Set_Menu(MENU_INFO *handle, u8 p_page)
{
    handle->cur_page = p_page;
}

u8 Get_Menu(MENU_INFO *handle)
{
    return handle->cur_page;
}

/**************************************************************************
* 函数名称: Set_Event_Code/Get_Event_Code
* 功能描述: 设置当前事件值/获取当前事件值
**************************************************************************/
void Set_Event_Code(MENU_INFO *handle, u8 p_evnt)
{
    handle->knb_evnt = p_evnt;
}

int Get_Event_Code(MENU_INFO *handle)
{
    return handle->knb_evnt;
}

        【3】结合上述菜单处理函数,关联“设计实现--驱动按键”中的【5】,完善 knobCallback_1/2/3/4 的实现。

                主要逻辑就是将按键的动作,通过回调,传递给菜单结构 menu (单列出knobCallback_1,其他按钮的回调一样实现):

void knobCallback_1(void *p_btn)
{
    u8 btn_event_val; 
    
    btn_event_val = get_button_event((struct Button *)p_btn); 
  
    switch(btn_event_val)
    {
        case SINGLE_CLICK:
            Set_Event_Code(&menu, KNOB_1_SHORT);
            break ;
        case LONG_PRESS_START:
            Set_Event_Code(&menu, KNOB_1_LONG);
            break ;
        default:
            break ;
    }
}

        【4】菜单处理函数 Menu_Handler 的实现:

void Menu_Handler(MENU_INFO *handle)
{
	switch(handle->cur_page)
	{
		case MENU_MAIN:
			menuMainHandle(handle->knb_evnt);
			break ;
		case MEUN_LOG:
			menuLogHandle(handle->knb_evnt);
			break ;
		default:
			break ;
	}
    Set_Event_Code(handle, NULL_EVENT);     //及时将事件清除,防止重复触发
}

        其中,menuMainHandle/menuLogHandle 就是每个界面的具体实现了:

void menuMainHandle(u8 p_evnt)
{
    cleanAll();  //清屏
    //主界面显示
    
    switch(p_evnt)
    {
        case KNOB_1_SHORT:
            break ;
        case KNOB_1_LONG:
            Set_Menu(&menu, MEUN_LOG);  //进入登录界面
            break ;
        default:
            break;
    }
}
void menuLogHandle(u8 p_evnt)
{
    cleanAll();  //清屏
    //登录界面的显示
    
    switch(p_evnt)
    {
        case KNOB_2_SHORT:
            break ;
        case KNOB_2_LONG:
            Set_Menu(&menu, MENU_MAIN);  //返回主界面
            break ;
        default:
            break;
    }
}

        【5】在主函数的初始化中加上上面界面初始化接口,同时界面处理函数置于主循环中执行:

void main(void)
{
    //定时器初始化
    Timer14_Config();
    //旋钮初始化/注册
    KNOB_Init();
    KNOB_Reg();
    //界面初始化
    Menu_Init(&menu, MENU_MAIN, NULL_EVENT);

    while(1)
    {
        Menu_Handler(&menu);  //界面处理函数
        
        LCD_Update();  //用缓存刷新屏幕
        
        //...
    }
}

        至此,完成了通过 MultiButton 开源库驱动按键并控制多级界面切换的工作。

        上述DEMO中,上电默认进入主界面,可以通过长按 knob_1 进入登陆界面。在登陆界面中,通过长按 knob_2 返回主界面(长按的时间可以在 multi_button.h 中设置)文章来源地址https://www.toymoban.com/news/detail-730065.html

到了这里,关于【嵌入式】使用MultiButton开源库驱动按键并控制多级界面切换的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • STM32之按键驱动的使用和自定义(MultiButton)

    Github地址 调整内容 将宏定义转换成配置结构体 头文件 源文件 驱动使用 执行循环逻辑 (在RTOS的定时器中执行) 测试结果

    2024年02月16日
    浏览(41)
  • 【速成】蓝桥杯嵌入式省一教程:(五)用按键和屏幕实现嵌入式交互系统

    一个完整的嵌入式系统,包括任务执行部分和人机交互部分。在前四节中,我们已经讲解了LED、LCD和按键,用这三者就能够实现一个人机交互系统,也即搭建整个嵌入式系统的框架。在后续,只要将各个功能加入到这个交互系统中,即可完成整个嵌入式系统的设计。因此,尽

    2024年02月09日
    浏览(66)
  • 速通蓝桥杯嵌入式省一教程:(五)用按键和屏幕实现嵌入式交互系统

    一个完整的嵌入式系统,包括任务执行部分和人机交互部分。在前四节中,我们已经讲解了LED、LCD和按键,用这三者就能够实现一个人机交互系统,也即搭建整个嵌入式系统的框架。在后续,只要将各个功能加入到这个交互系统中,即可完成整个嵌入式系统的设计。因此,尽

    2024年02月12日
    浏览(45)
  • 嵌入式51单片机04-矩阵按键系列

    一、矩阵按键基础知识 矩阵按键工作原理 : 逐行扫描 :通过高四位轮流输出低电平来对矩阵键盘进行扫描,当低四位接收到的数据不全为1的时候,说明有按键按下,然后通过判断低四位数据中哪一位为零来判断哪一个按键被按下。 逐列扫描 :通过低四位轮流输出低电平来

    2024年02月07日
    浏览(65)
  • 【嵌入式系统课设】单个按键控制LED灯

    合工大嵌入式系统课程设计 设计要求: 根据单个按键输入情况控制LED灯的不同显示效果。K1连续按下偶数次时,四个LED灯(LED1~LED4)按1秒(定时器中断实现)的间隔同时闪烁对应的次数,然后保持LED1和LED2常亮,LED3和LED4熄灭;K1连续按下奇数次时,四个灯按0.5秒(定时器中断实

    2024年02月12日
    浏览(47)
  • 嵌入式USB转RS232九针串口线驱动,及串口软件secureCRT的使用

    一般而言,嵌入式开发板使用串口来就监控后台。可以使用串口线连接开发板和电脑,对于没有串口的电脑来说就需要一根USB转串口线。 (有条件的这里个人推荐 绿联 的 比较稳定 ,当然其他的也可以,根据自身条件选择)                 它的芯片:PL2303            

    2024年02月07日
    浏览(78)
  • 驱动开发——嵌入式(驱动)软开基础(七)

    1 Linux驱动程序的功能是什么? (1)对设备初始化和释放。 (2)进行内核与硬件的数据交互。 (3)检测和处理设备出现的错误。 2. 内核程序中申请内存使用什么函数? 答案:kmalloc()、kzalloc()、vmalloc()。 解读: (1)void *kmalloc(size_t size, gfp_t flags); ①申请连续的物理内存,

    2024年02月06日
    浏览(99)
  • 驱动开发——嵌入式(驱动)软开基础(十)

    1. 64位的计算机有哪些优点? (1)可以进行更大范围的整数计算。 (2)可以支持更大的内存,虚拟内存空间大小一般为2^48(256TB)。64位的Linux一般使用48位表示虚拟内存空间地址,40位表示物理内存地址。 2. 中断分为哪两种? (1) 异步中断 :也叫 外部中断 ,由CPU外设产

    2024年02月06日
    浏览(59)
  • 嵌入式系统项目设计——基于LED、按键、蜂鸣器的打地鼠游戏(完整代码)

    内容摘自上学期嵌入式系统课程设计最终的实验报告,因为CSDN不支持上传word资料,所以我就摘录下来写进文章里了,希望可以帮助到你 1.1选题背景 打地鼠游戏是一种非常受小孩子欢迎的游戏,拥有数量极其广大的受众,所以它的市场前景非常广阔。当今社会虽然存在各种各

    2024年02月09日
    浏览(55)
  • 嵌入式内核及驱动开发高级

    仅devfs,导致开发不方便以及一些功能难以支持: 热插拔 不支持一些针对所有设备的统一操作(如电源管理) 不能自动mknod 用户查看不了设备信息 设备信息硬编码,导致驱动代码通用性差,即没有分离设备和驱动 uevent机制:sysfs + uevent + udevd(上层app) sysfs用途:(类似于

    2024年02月16日
    浏览(65)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包