stm32按键FIFO的实现
学习目标:
1、理解FIFO的基本概念和设计按键FIFO的意义
2、写出实现按键FIFO的代码
1、设计按键FIFO的优点
要介绍实现按键FIFO的优点,首先要了解FIFO的一些基本概念。FIFO即First In First Out,是一种先进先出的数据缓存方式,例如在超市购物之后我们会提着满满的购物车来到收银台排在结账队伍的最后等待付款,先排队的客户先付款离开,后面排队的只有等待前面付款离开才能进行付款。说白了FIFO就是这样一种先进先出机制,先存入的数据在读取时最先被读取到。
设计按键FIFO注意有三个方面的优点(来自于安富莱电子Eric2013大佬总结):
1、可以有效记录按键事件的发生,特别是系统要实现记录按键按下、松开、长按时,使用FIFO来实现是一种不错的选择方式。
2、系统是阻塞的,这样系统在检测到按键按下的情况,由于机械按键抖动的原因不需要在这里等待一段时间,然后在确定按键是否正常按下。
3、按键FIFO程序在系统定时器中定时检测按键状态,确认按键按下后将状态写入FIFO中,不一定在主程序中一直做检测,这样可以有效降低系统资源的消耗。
2、按键的硬件设计
按键的原理图如上图所示,对于KEY0~KEY2这三个按键,一端接地,另一端连接stm32的GPIO端口。当按键按下时相应的IO口被拉低,如果把GPIO口配置为输入模式,此时读取相应的IO口电平,就可以检测到按键是否被按下。对于KEY_UP按键则是与前面三个按键相反,IO口配置为输入模式时,读取到高电平时表示按键按下。因为机械固有的物理特性,按键按下内部弹簧片在瞬间接触的时候会有力学的回弹,造成2-8毫秒内信号不稳定,所以在设计检测机械按键是否按下的程序时,应考虑到按键消抖问题。
3、按键FIFO代码的设计
3.1 按键FIFO代码主要框图
bsp_KeyScan()检测到按键状态时以3*x+1关系计算出(x代表按键编号)写入FIFO值,例如:
FIFO中读取值1---------------->按键1按下
FIFO中读取值2---------------->按键1弹开
FIFO中读取值3---------------->按键1长按
注意:在配置按键GPIO相应模式时,如果按键硬件设计没有电阻上拉,那么在配置GPIO口时必须将GPIO口内部配置成上拉状态,否则对读取按键结构有影响!
3.2 按键FIFO代码实现
bsp_key.c实现
- #include "bsp_key.h"
- /*
- 开发板 按键口线分配:
- K0 键 : PH3 (低电平表示按下)
- K1 键 : PH2 (低电平表示按下)
- K2 键 : PC14 (低电平表示按下)
- WAKE_UP键 : PA0 (高电平表示按下)
- */
- /* 按键连接GPIO对应RCC时钟 */
- #define RCC_ALL_KEY (RCC_AHB1Periph_GPIOH|RCC_AHB1Periph_GPIOC|RCC_AHB1Periph_GPIOA)
- /* 按键0的GPIO----------->GPIOH3 */
- #define KEY0_GPIO_PORT GPIOH
- #define KEY0_GPIO_PIN GPIO_Pin_3
- /* 按键1的GPIO----------->GPIOH2 */
- #define KEY1_GPIO_PORT GPIOH
- #define KEY1_GPIO_PIN GPIO_Pin_2
- /* 按键2的GPIO----------->GPIOC13 */
- #define KEY2_GPIO_PORT GPIOC
- #define KEY2_GPIO_PIN GPIO_Pin_13
- /* 按键WK_UP的GPIO----------->GPIOA0 */
- #define KEY_WKUP_GPIO_PORT GPIOA
- #define KEY_WKUP_GPIO_PIN GPIO_Pin_0
- static KEY_T s_tBtn[KEY_COUNT];
- static KEY_FIFO_T s_tKey; /* 按键FIFO变量,结构体 */
- static void bsp_InitKeyVar(void);
- static void bsp_InitKeyHard(void);
- static void bsp_DetectKey(uint8_t i);
- /*
- *********************************************************************************************************
- * 函 数 名: IsKeyDownX
- * 功能说明: 判断按键是否按下
- * 形 参: 无
- * 返 回 值: 返回值1 表示按下,0表示未按下
- *********************************************************************************************************
- */
- static uint8_t IsKeyDown0(void) {if((KEY0_GPIO_PORT->IDR & KEY0_GPIO_PIN) == ) return ; else return ;};
- static uint8_t IsKeyDown1(void) {if((KEY1_GPIO_PORT->IDR & KEY1_GPIO_PIN) == ) return ; else return ;};
- static uint8_t IsKeyDown2(void) {if((KEY2_GPIO_PORT->IDR & KEY2_GPIO_PIN) == ) return ; else return ;};
- static uint8_t IsKeyDown3(void) {if((KEY_WKUP_GPIO_PORT->IDR & KEY_WKUP_GPIO_PIN) == KEY_WKUP_GPIO_PIN) return ; else return ;};
- /*
- *********************************************************************************************************
- * 函 数 名: bsp_InitKey
- * 功能说明: 配置按键相关的GPIO,该函数被 bsp_Init()调用。
- * 形 参: 无
- * 返 回 值: 无
- *********************************************************************************************************
- */
- void bsp_InitKey(void)
- {
- bsp_InitKeyVar(); /* 初始化按键变量 */
- bsp_InitKeyHard(); /* 初始化按键硬件 */
- }
- /*
- *********************************************************************************************************
- * 函 数 名: bsp_InitKeyHard
- * 功能说明: 配置按键相关的GPIO,该函数被 bsp_InitKey()调用。
- * 形 参: 无
- * 返 回 值: 无
- *********************************************************************************************************
- */
- static void bsp_InitKeyHard(void)
- {
- GPIO_InitTypeDef GPIO_InitStructure;
- /* 打开按键连接GPIO的RCC时钟 */
- RCC_AHB1PeriphClockCmd(RCC_ALL_KEY, ENABLE);
- /* 设置按键连接GPIO为浮空输入模式 */
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; /* 设为输入口 */
- GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; /* 上拉电阻 */
- GPIO_InitStructure.GPIO_Pin = KEY0_GPIO_PIN;
- GPIO_Init(KEY0_GPIO_PORT, &GPIO_InitStructure);
- GPIO_InitStructure.GPIO_Pin = KEY1_GPIO_PIN;
- GPIO_Init(KEY1_GPIO_PORT, &GPIO_InitStructure);
- GPIO_InitStructure.GPIO_Pin = KEY2_GPIO_PIN;
- GPIO_Init(KEY2_GPIO_PORT, &GPIO_InitStructure);
- GPIO_InitStructure.GPIO_Pin = KEY_WKUP_GPIO_PIN;
- GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN; /* 下拉电阻 */
- GPIO_Init(KEY_WKUP_GPIO_PORT, &GPIO_InitStructure);
- }
- /*
- *********************************************************************************************************
- * 函 数 名: bsp_InitKeyVar
- * 功能说明: 初始化按键变量
- * 形 参: 无
- * 返 回 值: 无
- *********************************************************************************************************
- */
- static void bsp_InitKeyVar(void)
- {
- uint8_t i;
- /* 对按键FIFO读写指针清零 */
- s_tKey.Read = ;
- s_tKey.Write = ;
- s_tKey.Read2 = ;
- /* 给每个按键结构体成员变量赋一组缺省值 */
- for (i = ; i < KEY_COUNT; i++)
- {
- s_tBtn[i].LongTime = KEY_LONG_TIME; /* 长按时间 0 表示不检测长按键事件 */
- s_tBtn[i].Count = KEY_FILTER_TIME / ; /* 计数器设置为滤波时间的一半 */
- s_tBtn[i].State = ; /* 按键缺省状态,0为未按下 */
- //s_tBtn[i].KeyCodeDown = 3 * i + 1; /* 按键按下的键值代码 */
- //s_tBtn[i].KeyCodeUp = 3 * i + 2; /* 按键弹起的键值代码 */
- //s_tBtn[i].KeyCodeLong = 3 * i + 3; /* 按键被持续按下的键值代码 */
- s_tBtn[i].RepeatSpeed = ; /* 按键连发的速度,0表示不支持连发 */
- s_tBtn[i].RepeatCount = ; /* 连发计数器 */
- }
- /* 如果需要单独更改某个按键的参数,可以在此单独重新赋值 */
- /* 比如,我们希望按键1按下超过1秒后,自动重发相同键值 */
- // s_tBtn[KID_JOY_U].LongTime = 100;
- // s_tBtn[KID_JOY_U].RepeatSpeed = 5; /* 每隔50ms自动发送键值 */
- /* 判断按键按下的函数 */
- s_tBtn[].IsKeyDownFunc = IsKeyDown0;
- s_tBtn[].IsKeyDownFunc = IsKeyDown1;
- s_tBtn[].IsKeyDownFunc = IsKeyDown2;
- s_tBtn[].IsKeyDownFunc = IsKeyDown3;
- }
- /*
- *********************************************************************************************************
- * 函 数 名: bsp_PutKey
- * 功能说明: 将1个键值压入按键FIFO缓冲区。可用于模拟一个按键。
- * 形 参: _KeyCode : 按键代码
- * 返 回 值: 无
- *********************************************************************************************************
- */
- void bsp_PutKey(uint8_t _KeyCode)
- {
- s_tKey.Buf[s_tKey.Write] = _KeyCode;
- if(++s_tKey.Write >= KEY_FIFO_SIZE)
- {
- s_tKey.Write = ;
- }
- }
- /*
- *********************************************************************************************************
- * 函 数 名: bsp_GetKey
- * 功能说明: 从按键FIFO缓冲区读取一个键值。
- * 形 参: 无
- * 返 回 值: 按键代码
- *********************************************************************************************************
- */
- uint8_t bsp_GetKey(void)
- {
- uint8_t ret;
- if (s_tKey.Read == s_tKey.Write)
- {
- return KEY_NONE;
- }
- else
- {
- ret = s_tKey.Buf[s_tKey.Read];
- if(++s_tKey.Read >= KEY_FIFO_SIZE)
- {
- s_tKey.Read = ;
- }
- return ret;
- }
- }/*
- *********************************************************************************************************
- * 函 数 名: bsp_GetKeyState
- * 功能说明: 读取按键的状态
- * 形 参: _ucKeyID : 按键ID,从0开始
- * 返 回 值: 1 表示按下, 0 表示未按下
- *********************************************************************************************************
- */
- uint8_t bsp_GetKeyState(KEY_ID_E _ucKeyID)
- {
- return s_tBtn[_ucKeyID].State;
- }
- /*
- *********************************************************************************************************
- * 函 数 名: bsp_SetKeyParam
- * 功能说明: 设置按键参数
- * 形 参:_ucKeyID : 按键ID,从0开始
- * _LongTime : 长按事件时间
- * _RepeatSpeed : 连发速度
- * 返 回 值: 无
- *********************************************************************************************************
- */
- void bsp_SetKeyParam(uint8_t _ucKeyID, uint16_t _LongTime, uint8_t _RepeatSpeed)
- {
- s_tBtn[_ucKeyID].LongTime = _LongTime; /* 长按时间 0 表示不检测长按键事件 */
- s_tBtn[_ucKeyID].RepeatSpeed = _RepeatSpeed; /* 按键连发的速度,0表示不支持连发 */
- s_tBtn[_ucKeyID].RepeatCount = ; /* 连发计数器 */
- }
- /*
- *********************************************************************************************************
- * 函 数 名: bsp_ClearKey
- * 功能说明: 清空按键FIFO缓冲区
- * 形 参:无
- * 返 回 值: 按键代码
- *********************************************************************************************************
- */
- void bsp_ClearKey(void)
- {
- s_tKey.Read = s_tKey.Write;
- }
- /*
- *********************************************************************************************************
- * 函 数 名: bsp_DetectKey
- * 功能说明: 检测一个按键。非阻塞状态,必须被周期性的调用。
- * 形 参: 按键结构变量指针
- * 返 回 值: 无
- *********************************************************************************************************
- */
- static void bsp_DetectKey(uint8_t i)
- {
- KEY_T *pBtn;
- /*
- 如果没有初始化按键函数,则报错
- if (s_tBtn[i].IsKeyDownFunc == 0)
- {
- printf("Fault : DetectButton(), s_tBtn[i].IsKeyDownFunc undefine");
- }
- */
- pBtn = &s_tBtn[i];
- if (pBtn->IsKeyDownFunc())
- {
- if (pBtn->Count < KEY_FILTER_TIME)
- {
- pBtn->Count = KEY_FILTER_TIME;
- }
- else if(pBtn->Count < * KEY_FILTER_TIME)
- {
- pBtn->Count++;
- }
- else
- {
- if (pBtn->State == )
- {
- pBtn->State = ;
- /* 发送按钮按下的消息 */
- bsp_PutKey((uint8_t)( * i + ));
- }
- if (pBtn->LongTime > )
- {
- if (pBtn->LongCount < pBtn->LongTime)
- {
- /* 发送按钮持续按下的消息 */
- if (++pBtn->LongCount == pBtn->LongTime)
- {
- /* 键值放入按键FIFO */
- bsp_PutKey((uint8_t)( * i + ));
- }
- }
- else
- {
- if (pBtn->RepeatSpeed > )
- {
- if (++pBtn->RepeatCount >= pBtn->RepeatSpeed)
- {
- pBtn->RepeatCount = ;
- /* 常按键后,每隔10ms发送1个按键 */
- bsp_PutKey((uint8_t)( * i + ));
- }
- }
- }
- }
- }
- }
- else
- {
- if(pBtn->Count > KEY_FILTER_TIME)
- {
- pBtn->Count = KEY_FILTER_TIME;
- }
- else if(pBtn->Count != )
- {
- pBtn->Count--;
- }
- else
- {
- if (pBtn->State == )
- {
- pBtn->State = ;
- /* 发送按钮弹起的消息 */
- bsp_PutKey((uint8_t)( * i + ));
- }
- }
- pBtn->LongCount = ;
- pBtn->RepeatCount = ;
- }
- }
- /*
- *********************************************************************************************************
- * 函 数 名: bsp_KeyScan
- * 功能说明: 扫描所有按键。非阻塞,被systick中断周期性的调用
- * 形 参: 无
- * 返 回 值: 无
- *********************************************************************************************************
- */
- void bsp_KeyScan(void)
- {
- uint8_t i;
- for (i = ; i < KEY_COUNT; i++)
- {
- bsp_DetectKey(i);
- }
- }
bsp_key.c文件中核心函数是bsp_DetectKey(),这个函数先通过函数指针调用检测按键GPIO口状态的函数,判断是否有按键按下。
检测到按键按下时先进行相应的消抖操作,消抖持续时间通过bsp_key.h中定义的宏 KEY_FILTER_TIME设置。确定按键按下后,如果在此之前按键是松开的就把相应标志位置位,把按键按下的状态值写入到FIFO
中。如果使能了检测按键长按功能(LongCount>0),程序会继续检测按键是否达到长按时间,如果到达长按时间,将长按状态值写入到FIFO中,在继续检测是否支持长按时状态连续发送功能,如果使能了长按时连
续发送功能(RepeatSpeed>0),达到周期发送时间时,会继续写入FIFO按键按下状态值。
检测到按键释放时,首先也会进行相应滤波处理。确认按键松开后,若之前按键处于按下状态,会将检查到的按键松开状况写入到FIFO中,然后清除长按、重复发生的计数值,为下次查询做准备。
bsp_key.h实现
- #ifndef __BSP_KEY_H
- #define __BSP_KEY_H
- #define KEY_COUNT 4 /* 按键个数, 4个独立按键 */
- /* 根据应用程序的功能重命名按键宏 */
- #define KEY_DOWN_K0 KEY_0_DOWN
- #define KEY_UP_K0 KEY_0_UP
- #define KEY_LONG_K0 KEY_0_LONG
- #define KEY_DOWN_K1 KEY_1_DOWN
- #define KEY_UP_K1 KEY_1_UP
- #define KEY_LONG_K1 KEY_1_LONG
- #define KEY_DOWN_K2 KEY_2_DOWN
- #define KEY_UP_K2 KEY_2_UP
- #define KEY_LONG_K2 KEY_2_LONG
- #define KEY_DOWN_WP KEY_4_DOWN /* 上 */
- #define KEY_UP_WP KEY_4_UP
- #define KEY_LONG_WP KEY_4_LONG
- /* 按键ID, 主要用于bsp_KeyState()函数的入口参数 */
- typedef enum
- {
- KID_K1 = ,
- KID_K2,
- KID_K3,
- KID_WAKE_UP,
- }KEY_ID_E;
- /*
- 按键滤波时间50ms, 单位10ms。
- 只有连续检测到50ms状态不变才认为有效,包括弹起和按下两种事件
- 即使按键电路不做硬件滤波,该滤波机制也可以保证可靠地检测到按键事件
- */
- #define KEY_FILTER_TIME 5
- #define KEY_LONG_TIME 100 /* 单位10ms, 持续1秒,认为长按事件 */
- /*
- 每个按键对应1个全局的结构体变量。
- */
- typedef struct
- {
- /* 下面是一个函数指针,指向判断按键手否按下的函数 */
- uint8_t (*IsKeyDownFunc)(void); /* 按键按下的判断函数,1表示按下 */
- uint8_t Count; /* 滤波器计数器 */
- uint16_t LongCount; /* 长按计数器 */
- uint16_t LongTime; /* 按键按下持续时间, 0表示不检测长按 */
- uint8_t State; /* 按键当前状态(按下还是弹起) */
- uint8_t RepeatSpeed; /* 连续按键周期 */
- uint8_t RepeatCount; /* 连续按键计数器 */
- }KEY_T;
- /*
- 定义键值代码, 必须按如下次序定时每个键的按下、弹起和长按事件,检测按键时使用
- */
- typedef enum
- {
- KEY_NONE = , /* 0 表示按键事件 */
- KEY_0_DOWN, /* 1键按下 */
- KEY_0_UP, /* 1键弹起 */
- KEY_0_LONG, /* 1键长按 */
- KEY_1_DOWN, /* 2键按下 */
- KEY_1_UP, /* 2键弹起 */
- KEY_1_LONG, /* 2键长按 */
- KEY_2_DOWN, /* 3键按下 */
- KEY_2_UP, /* 3键弹起 */
- KEY_2_LONG, /* 3键长按 */
- KEY_3_DOWN, /* 4键按下 */
- KEY_3_UP, /* 4键弹起 */
- KEY_3_LONG, /* 4键长按 */
- }KEY_ENUM;
- /* 按键FIFO用到变量 */
- #define KEY_FIFO_SIZE 10
- typedef struct
- {
- uint8_t Buf[KEY_FIFO_SIZE]; /* 键值缓冲区 */
- uint8_t Read; /* 缓冲区读指针1 */
- uint8_t Write; /* 缓冲区写指针 */
- }KEY_FIFO_T;
- /* 供外部调用的函数声明 */
- void bsp_InitKey(void);
- void bsp_KeyScan(void);
- void bsp_PutKey(uint8_t _KeyCode);
- uint8_t bsp_GetKey(void);
- uint8_t bsp_GetKeyState(KEY_ID_E _ucKeyID);
- void bsp_SetKeyParam(uint8_t _ucKeyID, uint16_t _LongTime, uint8_t _RepeatSpeed);
- void bsp_ClearKey(void);
- #endif
stm32按键FIFO的实现的更多相关文章
- 【STM32H7教程】第19章 STM32H7的GPIO应用之按键FIFO
完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第19章 STM32H7的GPIO应用之按键FIF ...
- stm32按键识别
刚写了一个关于stm32单片机的按键识别的程序.目的,同时识别多个按键,并且不浪费cpu的时间. 关于去抖动,以前以为是在按键的时候,手会抖动.通过程序验证,这个确实是误解.这个应该是防止意外干扰.以 ...
- STM32按键控制程序
由于最近时间比较匆忙 已经有很久的时间没有写博客了 这次和大家分享的是STM32的一个按键的小程序 他的优点呢也是和上面一个LED一样就是便于移植 更改管脚方便 虽然都是些小程序 但是我觉得他们就像基 ...
- A7139 无线通信驱动(STM32) 添加FIFO扩展模式,能够发送超大数据包
A7139 拥有电磁波唤醒以及10mW的发射功率,很easy实现长距离通信,眼下測试有障碍物能够轻松达到300m以上. 通过几天的调试,眼下能够发送随意大小的数据包,大小为1-16KB.所有使用中断收 ...
- STM32 按键输入
#include "stm32f10x.h"#include "key.h" //按键初始化函数void KEY_Init(void) { GPIO_InitT ...
- stm32按键配置
前言:我们都知道开发板上除了有经典的流水灯之外,还有一个必备的练习硬件--按键(key),下面继续来完成按键的配置. 1.通过查看原理图,找出按键(key)的管脚名字和对应芯片上的I/O口,四个I/O ...
- stm32 按键操作
抖动时间的长短由按键的机械特性决定,一般为5ms-10ms void key() { static u8 flag = 1; if(flag == 1 && KEY_UP == 1) ...
- stm32 按键
//°´¼ü³õʼ»¯º¯Êý void KEY_Init(void) //IO³õʼ»¯ { GPIO_InitTypeDef GPIO_InitStructure; //³õʼ»¯KEY0- ...
- STM32按键输入
下面3个接上拉电阻 WK_UP接上拉电阻 因为用到了PA,PC,PH所以要使能3个模块 STATIC静态变量只会初始化一次 每次调用flag++,不会再初始化为0:起记忆作用. 最关键的是头 件不要忘 ...
随机推荐
- 查询login什么时候过期
-- Show all logins where the password is over 60 days old --查看60天没改密码的login SELECT name, LOGINPROPER ...
- Office 365实现单点登录系列(1)—域环境搭建
Hello 小伙伴们, 2018新年快乐,作为2018年首篇文章,怎么能不给大家带来点干货呢?这篇文章其实我9月底的时候已经在MSDN上发布过了,为表诚意,我更新了这篇文章,并把它组成了一个系列,2. ...
- 如何在Code First、Database First和Model First之间选择
Code First.Database First和Model First基本图解: 1)Database First: 如果数据库已经存在,可以使用VS自动生成数据模型,已经相关的edmx信息 2) ...
- ASP.NET Core 2.1以上 Bootstrap 4前端模板文件,开发环境与发布环境前端模板 environment的使用
笔者的前端文件如下 笔者增加Bootstrap 4 和 FontAwersome(字体图标),因为Bootsrap 4已经不再包含图标了. ASp.Net Core 中,通常在 _Layout.csh ...
- rsync- sersync -inotify
Rsync简介 Rsync是一款优秀的.快速的.多功能的本地或远程数据镜像同步备份工具.适用于unix/linux/windows等多种平台 从软件的名称Rsync(Remote Rynhroniza ...
- JSON与对象的序列化与反序列化
一.利用JavaScriptSerializer 类 System.Web.Script.Serialization空间,位于System.Web.extensions.dll中. JavaScrip ...
- Java 读取Excel数据——POI-3.11 XSSF
POI - the Java API for Microsoft Documents 1.在Apache官网下载Apache最新poi版本:poi-bin-3.11-20141221.zip,解压: ...
- form表单提交行为
项目中有一个表单如下图,当我填完数据源名称这个input后,点击回车键本意是想跳到下一个input处,然而呢却触发了下面的添加这个按钮的事件,这是怎么回事呢,明明添加这个按钮并没有设置type=&qu ...
- 优酷视频上传api及demo代码
1,优酷正常上传流程: 1). create:连接开放平台上传接口服务器,服务器端会返回upload_token以及upload_server_uri.2). create_file:连接上传服务器( ...
- BZOJ1821:[JSOI2010]部落划分(并查集,二分)
Description 聪聪研究发现,荒岛野人总是过着群居的生活,但是,并不是整个荒岛上的所有野人都属于同一个部落,野人们总是拉帮结派形成属于自己的部落,不同的部落之间则经常发生争斗.只是,这一切都成 ...