yang377156216 发表于 2022-2-22 15:25

【国民技术N32G457评测】五、移植小型嵌入式多按键库 MultiButton

本帖最后由 yang377156216 于 2022-2-22 15:25 编辑

<p><strong>【前言】</strong></p>

<p>在嵌入式软件开发中,通常需要独立按键支持多种功能,如:支持连击、双击、长按、组合连击、任意连击等等。之前使用过硬汉的按键检测程序,如果直接使用的话是没有问题,但是考虑到需要扩展多种按键组合以及多种功能还是有诸多不方便之处,到处搜罗了一遍网上的 FIFO Button ,最后找到个 开源好用的 &ldquo;MultiButton&rdquo; 库,打算移植到测评开发板上来。</p>

<p><strong>【MultiButton】</strong></p>

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

<p><strong>【MultiButton 使用方法】</strong></p>

<p>1.先申请一个按键结构</p>

<pre>
<code class="language-cpp">struct Button button1;</code></pre>

<p cid="n7" mdtype="paragraph">2.初始化按键对象,绑定按键的GPIO电平读取接口<strong>read_button_pin()</strong> ,后一个参数设置有效触发电平</p>

<pre>
<code class="language-cpp">button_init(&amp;button1, read_button_pin, 0);</code></pre>

<p cid="n9" mdtype="paragraph">3.注册按键事件</p>

<pre>
<code class="language-cpp">button_attach(&amp;button1, SINGLE_CLICK, Callback_SINGLE_CLICK_Handler);
button_attach(&amp;button1, DOUBLE_CLICK, Callback_DOUBLE_Click_Handler);
...</code></pre>

<p cid="n11" mdtype="paragraph">4.启动按键</p>

<pre>
<code class="language-cpp">button_start(&amp;button1);</code></pre>

<p cid="n13" mdtype="paragraph">5.设置一个5ms间隔的定时器循环调用后台处理函数</p>

<pre>
<code class="language-cpp">while(1) {
 ...
   if(timer_ticks == 5) {
       timer_ticks = 0;

       button_ticks();
 }
}</code></pre>

<p><strong>【MultiButton 特性】</strong></p>

<p cid="n16" mdtype="paragraph">MultiButton 使用C语言实现,基于面向对象方式设计思路,每个按键对象单独用一份数据结构管理:</p>

<pre>
<code class="language-cpp">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;
    struct Button* next;
};</code></pre>

<p cid="n18" mdtype="paragraph">这样每个按键使用单向链表相连,依次进入 button_handler(struct Button* handle) 状态机处理,所以每个按键的状态彼此独立。</p>

<p cid="n18" mdtype="paragraph"><strong>【MultiButton 按键事件】</strong></p>

<table align="center" border="1" cellpadding="1" cellspacing="1">
        <tbody>
                <tr>
                        <th>事件</th>
                        <th>说明</th>
                </tr>
                <tr>
                        <td>PRESS_DOWN</td>
                        <td>按键按下,每次按下都触发</td>
                </tr>
                <tr>
                        <td>PRESS_UP</td>
                        <td>按键弹起,每次松开都触发</td>
                </tr>
                <tr>
                        <td>PRESS_REPEAT</td>
                        <td>重复按下触发,变量repeat计数连击次数</td>
                </tr>
                <tr>
                        <td>SINGLE_CLICK</td>
                        <td>单击按键事件</td>
                </tr>
                <tr>
                        <td>DOUBLE_CLICK</td>
                        <td>双击按键事件</td>
                </tr>
                <tr>
                        <td>LONG_PRESS_START</td>
                        <td>达到长按时间阈值时触发一次</td>
                </tr>
                <tr>
                        <td>LONG_PRESS_HOLD</td>
                        <td>长按期间一直触发</td>
                </tr>
        </tbody>
</table>

<p>&nbsp;
<p><strong>【MultiButton 移植】</strong></p>
</p>

<p><strong>Step 1.&nbsp;</strong>获取到<a href="https://github.com/0x1abin/MultiButton" target="_blank"> <strong>MultiButton </strong>源码包</a>,往之前的模板工程中添加源码 ,且添加头文件路径:</p>

<p></p>

<p>&nbsp;<strong>Step 2.&nbsp;</strong>添加 bsp 文件 key.c 和 key.h ,主要实现 板载按键的初始化以及multi button 库的初始化,另外注册各个事件以及其实现,demo中实现了 SW1 的单击 、 SW2 的双击 、 SW3和Wakeup组合按键的长按3个事件,通过不同的 LED闪烁来直观感受各种按键功能。</p>

<pre>
<code class="language-cpp">struct Button btn1;
struct Button btn2;
struct Button btn3_wakeup;


/**
* @briefread wakeup pin value.
* @param
* @param
*/
uint8_t Read_ButtonWakeup_GPIO(void)
{
    return GPIO_ReadInputDataBit(KEY_WAKEUP_PORT, KEY_WAKEUP_INPUT_PIN);
}

/**
* @briefread key1 pin value.
* @param
* @param
*/
uint8_t Read_ButtonSW1_GPIO(void)
{
    return GPIO_ReadInputDataBit(KEY_SW1_PORT, KEY_SW1_PIN);
}

/**
* @briefread key2 pin value.
* @param
* @param
*/
uint8_t Read_ButtonSW2_GPIO(void)
{
    return GPIO_ReadInputDataBit(KEY_SW2_PORT, KEY_SW2_PIN);
}

/**
* @briefread key3&amp;wakeup pin value.
* @param
* @param
*/
uint8_t Read_ButtonSW3_Wakeup_GPIO(void)
{
    if (!GPIO_ReadInputDataBit(KEY_SW3_PORT, KEY_SW3_PIN))
    {
      if (GPIO_ReadInputDataBit(KEY_WAKEUP_PORT, KEY_WAKEUP_INPUT_PIN))
            return 0;//active level is 0
    }
    return 1;//inactive level is 1
}

/**
* @briefSW1 btn single click event handler.
* @parambtn
* @param
*/
void BTN1_SINGLE_Click_Handler(void *btn)
{
    //do something...
    LedBlink(PORT_GROUP1, LED1_PIN);
}

/**
* @briefSW2 btn double click event handler.
* @parambtn
* @param
*/
void BTN2_DOUBLE_Click_Handler(void *btn)
{
    //do something...
    LedBlink(PORT_GROUP2, LED2_PIN);
}

/**
* @briefSW3 btn &amp; Wakeup btn long press event handler.
* @parambtn
* @param
*/
void BTN3_WAKEUP_LONG_PRESS_START_Handler(void *btn)
{
    //do something...
    LedBlink(PORT_GROUP2, LED3_PIN);
}

/**
* @briefConfigures key port.
* @param GPIOx x can be A to G to select the GPIO port.
* @param Pin This parameter can be GPIO_PIN_0~GPIO_PIN_15.
*/
void KeyInputInit(void)
{
    GPIO_InitType GPIO_InitStructure;

    RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_GPIOA, ENABLE);

    /*Configure the GPIO pin as input floating*/
    GPIO_InitStructure.Pin      = KEY_SW1_PIN | KEY_SW2_PIN | KEY_SW3_PIN;
    GPIO_InitStructure.GPIO_Mode= GPIO_Mode_IPU;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitPeripheral(KEY_SW1_PORT, &amp;GPIO_InitStructure);

    GPIO_InitStructure.Pin      = KEY_WAKEUP_INPUT_PIN;
    GPIO_InitStructure.GPIO_Mode= GPIO_Mode_IPD;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitPeripheral(KEY_WAKEUP_PORT, &amp;GPIO_InitStructure);

    /* MultiButton init */
    button_init(&amp;btn1, Read_ButtonSW1_GPIO, 0);
    button_init(&amp;btn2, Read_ButtonSW2_GPIO, 0);
    button_init(&amp;btn3_wakeup, Read_ButtonSW3_Wakeup_GPIO, 0);//btn3 &amp; wakeup 组合键

    button_attach(&amp;btn1, SINGLE_CLICK,   BTN1_SINGLE_Click_Handler);
    button_attach(&amp;btn2, DOUBLE_CLICK,   BTN2_DOUBLE_Click_Handler);
    button_attach(&amp;btn3_wakeup, LONG_PRESS_START, BTN3_WAKEUP_LONG_PRESS_START_Handler);

    button_start(&amp;btn1);
    button_start(&amp;btn2);
    button_start(&amp;btn3_wakeup);
}
</code></pre>

<p>&nbsp;<strong>Step 3.&nbsp;</strong>在main初始化时,调用按键初始化函数,并且注册按键轮询任务</p>

<pre>
<code class="language-cpp">/* Initialize sw1~sw3 and wakeup button */
    KeyInputInit();

mtmMain.Register(button_ticks,5);//每5ms轮询button tick一次</code></pre>

<p>&nbsp;<strong>Step 4.&nbsp;</strong>编译下载到开发板上,验证功能正常,可以实现 单击、双击以及组合按键的长按功能,该小型软件库也非常方便地可以移植到其它平台。</p>

<p>&nbsp;</p>

<p><strong>【评测后序】</strong></p>

<p>除了成功地完成了前面几篇测评任务,还验证了 USB和bxCAN 功能,这2大外设国民技术采用的策略非常明显,与 STM32 做得一样,且usbfs device 库也是使用的ST 的,所以比较稳定,上手也较为简单。本次开发板测评内容已全部结束,期待后续国民技术能带来更多优质国产MCU。<img height="48" src="https://bbs.eeworld.com.cn/static/editor/plugins/hkemoji/sticker/facebook/congra.gif" width="48" /></p>

<p>&nbsp;</p>

<p><br />
&nbsp;</p>
页: [1]
查看完整版本: 【国民技术N32G457评测】五、移植小型嵌入式多按键库 MultiButton