【致敬未来的攻城狮计划】--RA2E1 开发板测评(3)按键输入

2023-11-16

前言

(1)首先感谢 李肯前辈的活动,从而申请到了RA2L1开发板的测评。
(2)本文主要介绍按键输入的内容。
(3)学习本文需要准备的前提,
【致敬未来的攻城狮计划】--RA2E1 开发板测评(1)keil环境配置
【致敬未来的攻城狮计划】--RA2L1 开发板测评(2)LED闪烁
(4)本文主要介绍按键抖动问题,软硬件消抖方案,分析其中的优缺点。同时介绍如何进行模块移植。
(5)代码仓库: gitee仓库GitHub仓库

开发板按键原理图分析

我们根据下面原理图可知,按键被按下的时候为低电平,否则为高电平。

按键抖动问题

(1)我们所看到的按键基本都是这样的,按下电平IO输入为低电平,否则为高电平。
(2)但是由于,按键所用的开关为机械弹性开关,当机械触点断开、闭合时,由于机械触点的弹性作用,一个按键开关在闭合时不会马上稳定地接通,在断开时也不会一下子断开。因而在闭合及断开的瞬间均伴随有一连串的抖动。
(3)这样会有什么影响呢?很简单,当我们只按下了一下按键,而由于单片机程序执行速度很快,所以可能会识别到抖动的高电平,导致单片机以为我们按下了很多次按键。因此,我们需要进行按键消抖。
注意:下面这张图表示按键按下时候是低电平!!!

硬件消抖

(1)我比较喜欢进行硬件消抖,因为硬件能够处理的事情,就别扔给搞软件的了。野火的指南者的按键可以供我们的学习。
(2)首先,为了防止IO口没有内部下拉,所以使用一个外部下拉保证按键没有被按下的时候,电平不是未知的。
(3)我们在按键两端并联一个104的电容,能够让按键按下时候的抖动电平被电容消除,使得电平上升的更平缓。这样就能够有效的消除按键抖动。

软件消抖

(1)介绍完硬件消抖了,如何进行软件消抖呢?首先,我们需要知道按键抖动的大概时间为5ms~20ms。之后我们可以先判断按键是否被按下,如果发现第一次被按下,单片机延时20ms。然后第二次判断按键是否被按下,如果此次依旧被按下,说明按键的确被按下了。(听不明白可以看后面代码)

阻塞式消抖

(1)因为我还没介绍RA2E1的函数。所以这里以文字进行表示。
(2)阻塞式消抖优缺点:
缺点:阻塞式消抖会让程序堵死在while,对CPU的资源浪费,而且会让我们无法执行 其他程序
优点:如果我们按键按下需要执行的程序是记录按键按下次数,这样就不会导致我们误判明明只按下1次长时间不松手,而造成cpu认为按下多次。(如果按下按键为低电平,没有while,将会一直 执行程序
//判断是否为低电平(按键按下为低电平)
//延时20ms
//while判断是否为低电平(按键松手为高电平,如果松手不满足while条件,退出循环)
//判断是否为低电平(按键按下为低电平)
    //执行程序
//其他程序

非阻塞式消抖

优点:这种方法不会阻塞程序,如果我们按下按键不松手, 其他程序依旧可以跑。
缺点:如何我们需要的是记录按键按下的次数。因为人不可能将按键按下的时间精准控制在20ms之内。那么就会造成,我明明只按下了一次,而 执行程序部分却会执行多次。
//判断是否为低电平(按键按下为低电平)
//延时20ms
//判断是否为低电平(按键按下为低电平)
    //执行程序
//其他程序

程序编写

rasc配置

(1)首先为我们在 keil环境配置那一章获得了一个文件夹了,此时打开。
(2)因为我们在上一章节已经将rasc添加进入keil中了,所以打开右上角的Tools,RA Smart Configurator。
(3)根据原理图我们知道P004为按键,P502为LED1。所以此时我们需要配置这两个引脚。

keil程序编写

函数介绍

(1)关于下面程序编写阶段R_IOPORT_PinWrite()函数的第三个参数,我是传入的自定义变量i。原因是因为BSP_IO_LEVEL_HIGH和BSP_IO_LEVEL_LOW本质就是0和1。为了方便我们反转电平,所以我建立了一个变量i,利用'!'对i进行反转,从而实现反转电平。
(2)对于按键输入,我们需要读取IO电平。所以需要用到R_IOPORT_PinRead()函数,以下为函数介绍。
/*R_IOPORT_PinRead()用于读取引脚电平
 *函数原型
    *fsp_err_t R_IOPORT_PinRead (ioport_ctrl_t * const p_ctrl, bsp_io_port_pin_t pin, bsp_io_level_t * p_pin_value);
 *参数1:
    *固定为g_ioport_ctrl
 *参数2:
    *指定IO口,比如我们需要查看P004电平,就输入BSP_IO_PORT_00_PIN_04。如果是查看P411,就输入BSP_IO_PORT_04_PIN_11
 *参数3:
    返回的电平数值,这个需要自己定义。
*/

程序编写

四种按键输入的程序
(1) 我们需要注意一件事情,延时20ms是用于消抖。而阻塞式和非阻塞式者两种是按键扫描的方案,即使是硬件消抖也需要进行讨论!不过优缺点和软件消抖是一样的,唯一区别在于少了一个延时而已!
(2)以下为硬件消抖和软件消抖的方式。
/*************************************************/
/***************   阻塞式硬件消抖  *****************/
/*************************************************/
void hal_entry(void)
{
        bsp_io_level_t S1;
        uint8_t i = 0;
    /* TODO: add your own code here */
        while(1)
        {
            R_IOPORT_PinRead(&g_ioport_ctrl,BSP_IO_PORT_00_PIN_04,&S1);
            if(S1 == BSP_IO_LEVEL_LOW)
            {
                R_IOPORT_PinRead(&g_ioport_ctrl,BSP_IO_PORT_00_PIN_04,&S1);
                if(S1 == BSP_IO_LEVEL_LOW)
                {
                    while(S1 == BSP_IO_LEVEL_LOW)
                    {
                        R_IOPORT_PinRead(&g_ioport_ctrl,BSP_IO_PORT_00_PIN_04,&S1);
                    }
                    i=!i;
                    R_IOPORT_PinWrite(&g_ioport_ctrl,BSP_IO_PORT_05_PIN_02,i);
                }
            }
        }
    
#if BSP_TZ_SECURE_BUILD
    /* Enter non-secure code */
    R_BSP_NonSecureEnter();
#endif
}
/***************************************************/
/***************   非阻塞式硬件消抖  *****************/
/***************************************************/
void hal_entry(void)
{
        bsp_io_level_t S1;
        uint8_t i = 0;
    /* TODO: add your own code here */
        while(1)
        {
            R_IOPORT_PinRead(&g_ioport_ctrl,BSP_IO_PORT_00_PIN_04,&S1);
            if(S1 == BSP_IO_LEVEL_LOW)
            {
                i=!i;
                R_IOPORT_PinWrite(&g_ioport_ctrl,BSP_IO_PORT_05_PIN_02,i);
            }
        }
    
#if BSP_TZ_SECURE_BUILD
    /* Enter non-secure code */
    R_BSP_NonSecureEnter();
#endif
}
/*************************************************/
/***************   阻塞式软件消抖  *****************/
/*************************************************/
void hal_entry(void)
{
        bsp_io_level_t S1;
        uint8_t i = 0;
    /* TODO: add your own code here */
        while(1)
        {
            R_IOPORT_PinRead(&g_ioport_ctrl,BSP_IO_PORT_00_PIN_04,&S1);
            if(S1 == BSP_IO_LEVEL_LOW)
            {
                R_BSP_SoftwareDelay(20,BSP_DELAY_UNITS_MILLISECONDS);
                R_IOPORT_PinRead(&g_ioport_ctrl,BSP_IO_PORT_00_PIN_04,&S1);
                if(S1 == BSP_IO_LEVEL_LOW)
                {
                    while(S1 == BSP_IO_LEVEL_LOW)
                    {
                        R_IOPORT_PinRead(&g_ioport_ctrl,BSP_IO_PORT_00_PIN_04,&S1);
                    }
                    i=!i;
                    R_IOPORT_PinWrite(&g_ioport_ctrl,BSP_IO_PORT_05_PIN_02,i);
                }
            }
        }
    
#if BSP_TZ_SECURE_BUILD
    /* Enter non-secure code */
    R_BSP_NonSecureEnter();
#endif
}
/*********************************************/
/************** 非阻塞式软件消抖 ***************/
/*********************************************/
void hal_entry(void)
{
        bsp_io_level_t S1;
        uint8_t i = 0;
    /* TODO: add your own code here */
        while(1)
        {
            R_IOPORT_PinRead(&g_ioport_ctrl,BSP_IO_PORT_00_PIN_04,&S1);
            if(S1 == BSP_IO_LEVEL_LOW)
            {
                R_BSP_SoftwareDelay(20,BSP_DELAY_UNITS_MILLISECONDS);
                R_IOPORT_PinRead(&g_ioport_ctrl,BSP_IO_PORT_00_PIN_04,&S1);
                if(S1 == BSP_IO_LEVEL_LOW)
                {
                    i=!i;
                    R_IOPORT_PinWrite(&g_ioport_ctrl,BSP_IO_PORT_05_PIN_02,i);
                }
            }
        }
    
#if BSP_TZ_SECURE_BUILD
    /* Enter non-secure code */
    R_BSP_NonSecureEnter();
#endif
}
如果需要消抖,推荐的消抖程序
(1)对于学单片机的人来说,都知道野火和正点原子。两者都有其优点,我比较偏向于野火,但是我始终认为两者优点都需要学习。上述说了,野火的按键部分对按键抖动有很好的硬件处理。此时我介绍以下正点原子关于按键消抖的软件处理方案。
(2)当你真正研究透了这段代码,会发现编写这个代码的人真的牛逼。既做到了非阻塞,又成功避免了因为长时间按下不松手造成CPU误认按键按下多次,如果想按下按键保持高速递增也可以。
(3)因为直接讲解这个代码感觉很生硬,所以我举几个可能会出现的情况进行分析。
<1>按下按键不松手:
*我们知道mode我们设置的是0,所以第9行代码可以无视。
*此时我们按下按键,这个时候第11行可以成功执行。
*延时20ms之后,按键的抖动也将会消失,这个时候让S1_up=0,避免按下一次按键,CPU认为按下了多次。之后如果没有松手的话,第11行的代码就永远无法进去。
*执行完操作之后,因为S1_up=0,所以第11行的代码一直无法执行。此时我们松手,第22行的if将会让S1_up=1。这个时候我们又可以重新判断按键是否按下了。
<2>松手时刻的20ms按键抖动:
*假设我们是在CPU执行第19行代码进行松手的,那么第22行的if将会让S1_up=1。这个时候就有一个问题,第11行的if函数可以进来了。因为S1_up=1,同时因为松手时刻的按键抖动,可能会出现一个瞬间S1也为低电平。
*但是不用怕,我们有20ms的软件消抖,这样在第16行的if语句无法进去,因为我们已经松手了。
*这个时候有人就有疑惑了,因为松手时刻的按键抖动,进入了第11行的if,那么 S1_up=0,这样第11行的if将永远进不来了啊。不用担心,第22行的if函数会出手。
void hal_entry(void)
{
        bsp_io_level_t S1;
        uint8_t i = 0,mode = 0;
        static uint8_t S1_up=1;//按键按松开标志
    /* TODO: add your own code here */
        while(1)
        {
            if(mode)S1_up=1;  //mode=1表示支持连按    
            R_IOPORT_PinRead(&g_ioport_ctrl,BSP_IO_PORT_00_PIN_04,&S1);
            if(S1_up && (S1 == BSP_IO_LEVEL_LOW))  //如果按键被按下,同时在没有松手过就无法进来了
            {
                R_BSP_SoftwareDelay(20,BSP_DELAY_UNITS_MILLISECONDS);//延时20ms软件消抖
                S1_up=0;
                R_IOPORT_PinRead(&g_ioport_ctrl,BSP_IO_PORT_00_PIN_04,&S1);
                if(S1 == BSP_IO_LEVEL_LOW)  //软件消抖之后判断是否依旧为低电平,防止松手时候抖动造成第一个if通过了
                {
                    i=!i;
                    R_IOPORT_PinWrite(&g_ioport_ctrl,BSP_IO_PORT_05_PIN_02,i);
                }
            }
            else if(S1 == BSP_IO_LEVEL_HIGH)  //松手了,即使是松手时候造成的抖动,也表示松手了
            {
                S1_up=1;
            }
        }
#if BSP_TZ_SECURE_BUILD
    /* Enter non-secure code */
    R_BSP_NonSecureEnter();
#endif
}

模块化

移植LED程序

(1)了解完了整个流程了,我们依旧要将其进行模块化。因为前面我们已经将led点灯进行了模块化了,所以现在先将它移植进来。
(2)建立key文件夹,并建立.c和.h文件。
(3)利用rasc将文件添加进入工程
(4)将文件路径添加进来

程序

key.c

注意:read_key()这个函数无法在key.c以外的文件中调用。
#include "key.h"

static uint8_t read_key(void);

/*函数说明:按键扫描
 *传入参数:
        * s1_Double_click :表示支持连按
        * s1_NoDouble_click :不支持连按
 *返回参数:
        * s1_down :表示s1被按下
        * s1_up :  表示s1没有被按下
 */
uint8_t KEY_Scan(uint8_t mode)
{
    static uint8_t S1_up=1;//按键按松开标志
    if(mode) S1_up=1;  //mode=1表示支持连按    
    if(S1_up && read_key())
    {
        R_BSP_SoftwareDelay(20,BSP_DELAY_UNITS_MILLISECONDS);//延时20ms软件消抖
        S1_up=0;
        if(read_key()) return s1_down;
    }
    else if(!read_key()) S1_up=1;//松手了,即使是松手时候造成的抖动,也表示松手了
    return s1_up;
}

/*函数说明:读取S1引脚的电平
 *传入参数:无
 *返回参数:
        * s1_down :表示按键按下
        * s1_up :  表示松手
 */
static uint8_t read_key(void)
{
    bsp_io_level_t S1;
    R_IOPORT_PinRead(&g_ioport_ctrl,s1,&S1);
    if(S1 == BSP_IO_LEVEL_LOW) return s1_down;
    else return s1_up;  
}

key.h

#ifndef    __key_H
#define    __key_H

#include "hal_data.h"
 
/*********   参数宏定义     *********/
#define   s1                     BSP_IO_PORT_00_PIN_04
#define   s1_down                1
#define   s1_up                  0
#define   s1_Double_click        1
#define   s1_NoDouble_click      0
/*********   函数宏定义     *********/   

//暂无

/*********   函数声明      *********/
uint8_t KEY_Scan(uint8_t mode);

#endif

led.c

注意,此刻的led.c与上一章的led.c不同,增加了一个led_flip()函数,用于LED翻转电平用。
#include "led.h"

/*函数说明:LED翻转电平
 *传入参数:
        * LED1 :表示LED1进行翻转
        * LED2 :表示LED2进行翻转
 *返回参数:无
 */
void led_flip(uint16_t led)
{
    if(led == LED1)
    {
        static uint8_t led1_flag = 0;
        led1_flag = !led1_flag;
        R_IOPORT_PinWrite(&g_ioport_ctrl,LED1,led1_flag);
    }
    if(led == LED2)
    {
        static uint8_t led2_flag = 0;
        led2_flag = !led2_flag;
        R_IOPORT_PinWrite(&g_ioport_ctrl,LED2,led2_flag);
    }
}

/*函数说明:led1闪烁
 *传入参数:无
 *返回参数:无
 */
void led_1_flicker(void)
{
            LED1_lighting_up;
            R_BSP_SoftwareDelay(1,BSP_DELAY_UNITS_SECONDS);
            LED1_lighting_off;
            R_BSP_SoftwareDelay(1,BSP_DELAY_UNITS_SECONDS);
}

/*函数说明:led2闪烁
 *传入参数:无
 *返回参数:无
 */
void led_2_flicker(void)
{
            LED2_lighting_up;
            R_BSP_SoftwareDelay(1,BSP_DELAY_UNITS_SECONDS);
            LED2_lighting_off;
            R_BSP_SoftwareDelay(1,BSP_DELAY_UNITS_SECONDS);
}

led.h

#ifndef    __led_H
#define    __led_H

#include "hal_data.h"

/*********   参数宏定义     *********/
#define LED1 BSP_IO_PORT_05_PIN_02
#define LED2 BSP_IO_PORT_05_PIN_01

/*********   函数宏定义     *********/
#define LED1_lighting_off    R_IOPORT_PinWrite(&g_ioport_ctrl,LED1,BSP_IO_LEVEL_LOW)
#define LED1_lighting_up     R_IOPORT_PinWrite(&g_ioport_ctrl,LED1,BSP_IO_LEVEL_HIGH)
#define LED2_lighting_off    R_IOPORT_PinWrite(&g_ioport_ctrl,LED2,BSP_IO_LEVEL_LOW)
#define LED2_lighting_up     R_IOPORT_PinWrite(&g_ioport_ctrl,LED2,BSP_IO_LEVEL_HIGH)

/*********   函数声明      *********/
void led_flip(uint16_t led);
void led_1_flicker(void);
void led_2_flicker(void);

#endif
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【致敬未来的攻城狮计划】--RA2E1 开发板测评(3)按键输入 的相关文章

随机推荐

  • mysql auto connect_Mysql autoReconnect 的问题

    MySQL官方不推荐使用autoReconnect true 参见 http bugs mysql com bug php id 5020 需要另外找别的办法来解决超过8小时 链接断开的问题 由于问题产生的根本原因在于服务到数据库的连接长时
  • mysql的workbench在哪_MySQL Workbench的使用方法(图文)

    MySQLWorkbench 是 MySQL AB 最近释放的可视数据库设计工具 这个工具是设计 MySQL数据库的专用工具 MySQLWorkbench 拥有很多的功能和特性 这篇由DjoniDarmawikarta 写的文章通过一个示例
  • JavaScript将一层级对象数组转为children嵌套的三层级树状对象数组(多级树状分类)

    有时候后端返回的数据不适合前端 我们就需要进行转换 比如我想用elementUI的级联选择器 而这个组件对数据格式有要求 本篇文章将介绍如何将一层级对象数组数据格式转为三层级嵌套children数组 JavaScript Vue 小程序等都
  • 信息学奥赛一本通 1240:查找最接近的元素

    题目链接 http ybt ssoier cn 8088 problem show php pid 1240 include
  • 数据库相关知识和进阶知识

    目录 MySQL与Orcale EXPLAIN介绍 EXPLAIN 可以分析慢查询原因 查看MySQL存储空间大小 MySQL事务锁不执行严格的校验
  • Retrofit实现文件上传和下载【二】

    概述 通过前一篇的博客介绍 我们已经对Retrofit的使用有了一个大概的了解 今天来讲讲利用Retrofit进行文件的上传和下载 文件上传 服务器使用的是SSH框架 因此这里是以struts2的方式来获取数据的 我这里定义了三个字段用来接
  • Java发送手机短信验证码

    本次使用的是阿里云的短信服务 1 添加短信签名 签名名称要用的 在阿里云产品中搜 短信服务 gt 免费开通 gt 国内消息 2 添加短信模板 模版CODE需要用的 就在添加签名的旁边 3 创建用户 用户令牌和密码需要用的 然后选择 开始使用
  • bug复刻,解决方案---在改变div层级关系时,导致传参失败

    问题描述 在优化页面时 为了实现网页顶部遮挡效果 内容滚动 顶部导航栏不随着一起滚动 并且覆盖 做法是将内容都放在一个div里面 为这个新的div设置样式 margin top width heigh等 网页效果的确实现了 但是出现的新的问
  • Chisel实验笔记(一)

    最近在学习Risc v 其中伯克利大学开源了一款兼容Risc v指令集的处理器Rocket 而Rocket处理器是采用Chisel编写的 所以要学习Chisel Chisel的简单介绍如下 Chisel Constructing Hardw
  • 基于Arduino的音乐动感节奏灯

    1 音乐动感节奏灯是个什么东西 前段时间听音乐觉得无聊 便想着音乐光听也没意思啊 能不能 看见 音乐呢 于是谷歌了一番 发现还真有人做了将音乐可视化的东西 那就是音乐节奏灯 说的简单点就是LED灯光颜色亮度等随着音乐的节奏而发生变化 看了下
  • 最新机器人工程专业毕设选题推荐

    文章目录 1前言 2 如何选题 3 机器人工程 毕设 选题推荐 4 最后 1前言 近期不少学弟学妹询问学长关于机器人工程专业工程专业相关的毕设选题 学长特意写下这篇文章以作回应 以下是学长亲手整理相关的毕业设计选题 都是经过学长精心审核的题
  • Python-声明变量

    Python如何声明变量 在 Python 中 定义变量非常简单 只需要为变量赋一个值即可自动创建该变量 并推断出变量的数据类型 变量名可以是任意字母 数字或下划线组成 但是不能以数字开头 例如 定义名为 name 的变量 并将字符串 To
  • event类型 php,深入解析PHP的Laravel框架中的event事件操作

    有时候当我们单纯的看 Laravel 手册的时候会有一些疑惑 比如说系统服务下的授权和事件 这些功能服务的应用场景是什么 其实如果没有经历过一定的开发经验有这些疑惑是很正常的事情 但是当我们在工作中多加思考会发现有时候这些服务其实我们一直都
  • 服务器的协议端口在哪里设置,服务器的远程端口号在哪里设置

    服务器的远程端口号在哪里设置 内容精选 换一换 Linux云服务器一般采用SSH连接方式 使用密钥对进行安全地无密码访问 但是SSH连接一般都是字符界面 有时我们需要使用图形界面进行一些复杂操作 本文以Ubuntu 18 04操作系统为例
  • 飞旭体质健康测试云平台学生体质测试管理系统

    飞旭体测数据管理云平台是由体测设备 微信小程序和云平台构成 用户通过设备测试后 数据传输至云端 由云平台对数据进行针对性的统计分析 平台功能包括管理员分级管理 学生体质测试 学生体质测试成绩查询 测试数据管理统计分析 数据上报管理等内容 具
  • 15 周带你学好大一C语言!最详细C语言学习路线

    要学习 C 语言的读者抓紧时间看一下 我按照C语言学习视频的目录整理了一条以 周为单位时间 的学习路线 希望在开学后能按照这个进度去学习一遍 有要学习 C 语言的读者也可以参照 可能有些知识学习起来比较困难 比如说二进制这种涉及到底层方面的
  • iperf linux移植

    参考链接 1 iperf的git地址 windows版下载地址 git clone https github com esnet iperf git 2 下载到ubuntu上 3 找到交叉工具包的位置 opt arm ca9 linux g
  • 绘制复杂的层次的原理图

    一 绘制总体的区域块模块 1 新建一个PCB项目 在new中的project选择PCB 2 在项目中新建一个sheet文件 schemetic 然后找到place中的sheet Symbol Actions 3 修改每一个绿块的名称和文件名
  • 阻止移动端 touchmove 与 scroll 事件冲突

    在移动端开发过程中 如果要实现一个元素或按钮的拖动定位 会出现很多坑 例如 元素上下移动过程中 会触发 body 的 scroll 事件 导致整体的位置偏移 这时就需要 阻止移动端 touchmove 与 scroll 事件冲突 一 解决思
  • 【致敬未来的攻城狮计划】--RA2E1 开发板测评(3)按键输入

    前言 1 首先感谢 李肯前辈的活动 从而申请到了RA2L1开发板的测评 2 本文主要介绍按键输入的内容 3 学习本文需要准备的前提 致敬未来的攻城狮计划 RA2E1 开发板测评 1 keil环境配置 致敬未来的攻城狮计划 RA2L1 开发板