CH582使用MultiButton

参考:

MultiButton

MultiButton | 一个小巧简单易用的事件驱动型按键驱动模块

B站视频-第17讲:STM32项目经验分享:MultiButton按键 1/5

感谢开源项目和其他作者的分享,本文为新手CH582F裸机移植MultiButton按键库的过程记录。

0. MultiButton

MultiButton 是一个小巧简单易用的事件驱动型按键驱动模块,可无限量扩展按键,按键事件的回调异步处理方式可以简化你的程序结构,去除冗余的按键处理硬编码,让你的按键业务逻辑更清晰。

下载源码MultiButton

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  button_id;
	uint8_t  (*hal_button_Level)(uint8_t  button_id_);
	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_PRESS_START达到长按时间阈值时触发一次
LONG_PRESS_HOLD长按期间一直触发

1. 新建工程

这里我是在SysTick工程的基础上修改的,CH582使用SysTick滴答定时器

添加MultiButton源码到裸机工程中,并添加头文件路径:

在这里插入图片描述

2. 简单使用MultiButton

我这里是在SysTick_Handler()中每隔5ms调用一次按键后台处理函数button_ticks()的:

这里要注意!CH582官方库中的GPIOx_ReadPortPin()函数的返回值是32位的,而button_initpin_level函数的返回值需要是uint8_t类型的。

// CH57x_gpio.h

/**
 * @brief   GPIOA端口引脚状态,0-引脚低电平,(!0)-引脚高电平
 *
 * @param   pin     - PA0-PA15
 *
 * @return  GPIOA端口引脚状态
 */
#define GPIOA_ReadPortPin(pin)    (R32_PA_PIN & (pin))

/**
 * @brief   GPIOB端口引脚状态,0-引脚低电平,(!0)-引脚高电平
 *
 * @param   pin     - PB0-PB23
 *
 * @return  GPIOB端口引脚状态
 */
#define GPIOB_ReadPortPin(pin)    (R32_PB_PIN & (pin))

需要将返回值做个转换,return (GPIOB_ReadPortPin(GPIO_Pin_13) == 0 ? 0 : 1);

/**
  * @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.
  * @param  button_id: the button id.
  * @retval None
  */
void button_init(struct Button* handle, uint8_t(*pin_level)(uint8_t), uint8_t active_level, uint8_t button_id)
{
	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(button_id);
	handle->active_level = active_level;
	handle->button_id = button_id;
}

main.c如下:

/*
 * @Author       : stark1898y gg1658608470@gmail.com
 * @Date         : 2022-11-26 09:54:23
 * @LastEditors  : stark1898y gg1658608470@gmail.com
 * @LastEditTime : 2022-11-26 12:47:25
 * @FilePath     : \CH582F_MultiButton\src\Main.c
 * @Description  : CH582F使用MultiButton基于SysTick
 *
 * Copyright (c) 2022 by stark1898y gg1658608470@gmail.com, All Rights Reserved.
 */
#include "CH58x_common.h"
#include "multi_button.h"

uint8_t TxBuff[] = "This is a tx exam\r\n";
uint8_t RxBuff[100];
uint8_t trigB;

// 设定嘀嗒时间 5 ms
// 这里是在SysTick_Handler()中每隔5ms调用一次按键后台处理函数button_ticks();
#define SYSTICK_INTERVAL (5)

// SysTick完成一次计时中断的标志
volatile uint8_t systick_flag = 0;

#define BUTTON_1_ID     1

//先申请一个按键结构
struct Button button1;

/**
 * @description: 按键读取GPIO电平
 * @param {uint8_t} button_id   自己定义的按键id
 * @return {uint8_t}            0-引脚低电平,(!0)-引脚高电平
 */
uint8_t Button_ReadPin(uint8_t button_id)
{
    // you can share the GPIO read function with multiple Buttons
	switch(button_id)
	{
		case BUTTON_1_ID:
		    return (GPIOB_ReadPortPin(GPIO_Pin_13) == 0 ? 0 : 1);
			break;

		default:
			return 0;
			break;
	}
}

/**
 * @description: 按键1单击回调函数
 * @param {void*} btn
 * @return {void}
 */
void Button1_SingleClickHandler(void* btn)
{
    printf("Button1 SingleClick!\r\n");
}

/**
 * @description: 按键1双击回调函数
 * @param {void*} btn
 * @return {void}
 */
void Button1_DoubleClickHandler(void* btn)
{
    printf("Button1 DoubleClick!\r\n");
}

/**
 * @description: 按键1长按开始回调函数
 * @param {void*} btn
 * @return {void}
 */
void Button1_LongStartHandler(void* btn)
{
    printf("Button1 LongStart!\r\n");
}

/*********************************************************************
 * @fn      main
 *
 * @brief   主函数
 *
 * @return  none
 */
int main()
{
    SetSysClock(CLK_SOURCE_PLL_60MHz);

    //  自动重新加载计数值,计数时钟60M,以1ms为例,参数是60000
    SysTick_Config( GetSysClock() / 1000 * SYSTICK_INTERVAL);  //设定嘀嗒时间1000ms

    /* 配置串口1:先配置IO口模式,再配置串口 */
    GPIOA_SetBits(GPIO_Pin_9);
    GPIOA_ModeCfg(GPIO_Pin_8, GPIO_ModeIN_PU);      // RXD-配置上拉输入
    GPIOA_ModeCfg(GPIO_Pin_9, GPIO_ModeOut_PP_5mA); // TXD-配置推挽输出,注意先让IO口输出高电平
    UART1_DefInit();

 // 中断方式:接收数据后发送出去
    UART1_ByteTrigCfg(UART_7BYTE_TRIG);
    trigB = 7;
    UART1_INTCfg(ENABLE, RB_IER_RECV_RDY | RB_IER_LINE_STAT);
    PFIC_EnableIRQ(UART1_IRQn);

    PRINT("GetSysClock: %d\r\n", GetSysClock());

    PRINT("sizeof GPIOB_ReadPortPin: %d\r\n", sizeof(GPIOB_ReadPortPin(GPIO_Pin_13)));

    // 按键管脚设置为上拉输入
    GPIOB_ModeCfg(GPIO_Pin_13, GPIO_ModeIN_PU);

    // 初始化button1对象,绑定按键的GPIO电平读取接口Button_ReadPin() ,第三个参数设置有效触发电平
    button_init(&button1, Button_ReadPin, 0, BUTTON_1_ID);
    // 注册Button1的事件回调函数
    button_attach(&button1, SINGLE_CLICK,     Button1_SingleClickHandler);
    button_attach(&button1, DOUBLE_CLICK,     Button1_DoubleClickHandler);
    button_attach(&button1, LONG_PRESS_START, Button1_LongStartHandler);
    // 启动button1
    button_start(&button1);

    while(1)
    {
//        if(GPIOB_ReadPortPin(GPIO_Pin_13) == 0)
//        {
//            DelayMs(20);
//            if(GPIOB_ReadPortPin(GPIO_Pin_13) == 0)
//            {
//                PRINT("KEY PRESS\r\n");
//            }
//            while(GPIOB_ReadPortPin(GPIO_Pin_13) == 0);
//            PRINT("KEY up\r\n");
//        }
    }
}

// SysTick中断函数
__INTERRUPT
__HIGH_CODE
void SysTick_Handler()
{
    systick_flag = 1;
    SysTick->SR = 0;    // 清除中断标志
    //每隔5ms调用一次按键后台处理函数
    button_ticks();
}

/*********************************************************************
 * @fn      UART1_IRQHandler
 *
 * @brief   UART1中断函数
 *
 * @return  none
 */
__INTERRUPT
__HIGH_CODE
void UART1_IRQHandler(void)
{
    volatile uint8_t i;

    switch(UART1_GetITFlag())
    {
        case UART_II_LINE_STAT: // 线路状态错误
        {
//            UART1_GetLinSTA();
            break;
        }

        case UART_II_RECV_RDY: // 数据达到设置触发点
            for(i = 0; i != trigB; i++)
            {
                RxBuff[i] = UART1_RecvByte();
                UART1_SendByte(RxBuff[i]);
            }
            break;

        case UART_II_RECV_TOUT: // 接收超时,暂时一帧数据接收完成
            i = UART1_RecvString(RxBuff);
            UART1_SendString(RxBuff, i);
            break;

        case UART_II_THR_EMPTY: // 发送缓存区空,可继续发送
            break;

        case UART_II_MODEM_CHG: // 只支持串口0
            break;

        default:
            break;
    }
}

测试效果如下:

在这里插入图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值