学习目标:

  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的实现的更多相关文章

  1. 【STM32H7教程】第19章 STM32H7的GPIO应用之按键FIFO

    完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第19章       STM32H7的GPIO应用之按键FIF ...

  2. stm32按键识别

    刚写了一个关于stm32单片机的按键识别的程序.目的,同时识别多个按键,并且不浪费cpu的时间. 关于去抖动,以前以为是在按键的时候,手会抖动.通过程序验证,这个确实是误解.这个应该是防止意外干扰.以 ...

  3. STM32按键控制程序

    由于最近时间比较匆忙 已经有很久的时间没有写博客了 这次和大家分享的是STM32的一个按键的小程序 他的优点呢也是和上面一个LED一样就是便于移植 更改管脚方便 虽然都是些小程序 但是我觉得他们就像基 ...

  4. A7139 无线通信驱动(STM32) 添加FIFO扩展模式,能够发送超大数据包

    A7139 拥有电磁波唤醒以及10mW的发射功率,很easy实现长距离通信,眼下測试有障碍物能够轻松达到300m以上. 通过几天的调试,眼下能够发送随意大小的数据包,大小为1-16KB.所有使用中断收 ...

  5. STM32 按键输入

    #include "stm32f10x.h"#include "key.h" //按键初始化函数void KEY_Init(void) { GPIO_InitT ...

  6. stm32按键配置

    前言:我们都知道开发板上除了有经典的流水灯之外,还有一个必备的练习硬件--按键(key),下面继续来完成按键的配置. 1.通过查看原理图,找出按键(key)的管脚名字和对应芯片上的I/O口,四个I/O ...

  7. stm32 按键操作

    抖动时间的长短由按键的机械特性决定,一般为5ms-10ms void key() { static u8 flag = 1; if(flag == 1 && KEY_UP == 1) ...

  8. stm32 按键

    //°´¼ü³õʼ»¯º¯Êý void KEY_Init(void) //IO³õʼ»¯ { GPIO_InitTypeDef GPIO_InitStructure; //³õʼ»¯KEY0- ...

  9. STM32按键输入

    下面3个接上拉电阻 WK_UP接上拉电阻 因为用到了PA,PC,PH所以要使能3个模块 STATIC静态变量只会初始化一次 每次调用flag++,不会再初始化为0:起记忆作用. 最关键的是头 件不要忘 ...

随机推荐

  1. mongodb节点配置指南

    修改复制集节点的优先级 复制集节点的 priority 参数的值决定了选举中该节点的优先级.值越高,优先级越高. 我们可以通过修改复制集配置参数中 members 数组位置的优先级来修改对应机器的优先 ...

  2. 安装SCOM Reporting Server

    在SQL群集计算机上可以安装SCOM Reporting Server. 1.运行SQL Server安装程序,选择 全新安装SQL Server,不能向已有的群集实例中添加Reporting Ser ...

  3. 021.10 IO流 打印流

    内容:PrintStream:字节流    PrintWriter:字符流 PrintStream public static void main(String[] args) throws IOEx ...

  4. Angular4 @HostBinding @HostListener

    host属性 @Component({ selector: 'jhi-project', templateUrl: './project.html', styleUrls: [], host: { ' ...

  5. angularJs的过滤器扩展及自定义过滤器

    一.过滤器扩展 1.过滤器的组合使用 <!DOCTYPE HTML> <html ng-app="myApp"> <head> <meta ...

  6. VMware Harbor 学习

    Harbor简介 Harbor是一个用于存储和分发Docker镜像的企业级Registry服务器,通过添加一些企业必需的功能特性,例如安全.标识和管理等,扩展了开源Docker Distributio ...

  7. gluoncv 用已经训练好的模型参数,检测物体

    当然这个模型参数,最好用自己的,否则不够精确,我自己的还没训练完. from matplotlib import pyplot as plt import gluoncv from gluoncv i ...

  8. 「NOIP2018 保卫王国」

    题目 强制选点我们可以把那个点权搞成\(-inf\),强制不选我们搞成\(inf\),之后就真的成为动态\(dp\)的板子题了 由于不想像板子那样再写一个最大独立集的方程,之后利用最小点覆盖=总点权- ...

  9. 「bzoj 4180: 字符串计数」

    题目 真是一道好题 首先根据一个非常显然的贪心,如果给出了一个串\(S\),我们如何算最小操作次数呢 非常简单,我们直接把\(S\)拉到\(T\)的\(SAM\)上去跑,如果跑不动了就停下来,重新回到 ...

  10. 【vue】父向子组件传参、子组件向父传参

    1.父向子组件传参 App.vue为父,引入componetA组件之后,则可以在App.vue中使用标签(注意驼峰写法要改成componet-a写法,因为html对大小写不敏感,componenta与 ...