STM32F103按键操作的另一种实现——状态机

#ifndef _KEY_H_
#define _KEY_H_#include "HAL_gpio.h" // 换成STM32F103对应的GPIO库
#include "type.h"     // type.h主要是一些类型的重命名#define KEY_UP_GRP         GPIOA
#define KEY_UP_IDX         GPIO_Pin_9
#define KEY_UP_IS_DOWN()   GPIO_ReadInputDataBit(KEY_UP_GRP, KEY_UP_IDX)
#define KEY_UP_CONFIG()    GPIOConfig(KEY_UP_GRP, KEY_UP_IDX, GPIO_Mode_IPU) // 这个函数我在之前帖子里面写过#define KEY_DOWN_GRP       GPIOA
#define KEY_DOWN_IDX       GPIO_Pin_10
#define KEY_DOWN_IS_DOWN() GPIO_ReadInputDataBit(KEY_DOWN_GRP, KEY_DOWN_IDX)
#define KEY_DOWN_CONFIG()  GPIOConfig(KEY_DOWN_GRP, KEY_DOWN_IDX, GPIO_Mode_IPU)#define KEY_FUNC_GRP       GPIOA
#define KEY_FUNC_IDX       GPIO_Pin_11
#define KEY_FUNC_IS_DOWN() GPIO_ReadInputDataBit(KEY_FUNC_GRP, KEY_FUNC_IDX)
#define KEY_FUNC_CONFIG()  GPIOConfig(KEY_FUNC_GRP, KEY_FUNC_IDX, GPIO_Mode_IPU) #define KEY_TURN_GRP       GPIOA
#define KEY_TURN_IDX       GPIO_Pin_12 | GPIO_Pin_13
#define KEY_TURN_IS_DOWN() GPIO_ReadInputDataBit(KEY_TURN_GRP, KEY_TURN_IDX)
#define KEY_TURN_CONFIG()  GPIOConfig(KEY_TURN_GRP, KEY_TURN_IDX, GPIO_Mode_IPU)//====================================================================================
typedef enum
{CONFIRM_KEY = 1,FUNC_KEY,UP_KEY,DOWN_KEY
} key_event_t;#define state_keyUp         0       //初始状态,未按键
#define state_keyDown       1       //键被按下
#define state_keyLong       2       //长按
#define state_keyTime       3       //按键计时态#define return_keyUp        0x00    //初始状态
#define return_keyPressed   0x01    //键被按过,普通按键
#define return_keyLong      0x02    //长按
#define return_keyAuto      0x04    //自动连发#define key_down            0       //按下
#define key_up              0xf0    //未按时的key有效位键值
#define key_longTimes       100     //10ms一次,200次即2秒,定义长按的判定时间
#define key_autoTimes       20      //连发时间定义,20*10=200,200毫秒发一次#define KEYS1_VALUE         0xe0    //keyS1 按下
#define KEYS2_VALUE         0xd0    //keyS2 按下
#define KEYS3_VALUE         0xb0    //keyS3 按下
#define KEYS4_5_VALUE       0x70    //keyS4_5 按下//====================================================================================
void KeyProcess(void);  //T0定时器调用的工作函数
void KeyTimerInit(void);#endif /* _KEY_H_ */
#include <stdio.h>
#include "key.h"
#include "timer.h" // STM32F103定时器的配置static uint8_t
key_get(void)       //获取P3口值
{if(KEY_UP_IS_DOWN() == key_down){return KEYS1_VALUE;}if(KEY_DOWN_IS_DOWN() == key_down){return KEYS2_VALUE;}if(KEY_FUNC_IS_DOWN() == key_down){return KEYS3_VALUE;}if(KEY_TURN_IS_DOWN() == key_down){return KEYS4_5_VALUE;}return key_up ;    //0xf0  没有任何按键
}//函数每20ms被调用一次,而我们弹性按键过程时一般都20ms以上
//所以每次按键至少调用本函数2次
static uint8_t
key_read(uint8_t* pKeyValue)
{static uint8_t  s_u8keyState = 0;        //未按,普通短按,长按,连发等状态static uint16_t s_u16keyTimeCounts = 0;  //在计时状态的计数器static uint8_t  s_u8LastKey = key_up ; //保存按键释放时的P3口数据uint8_t keyTemp = 0;              //键对应io口的电平int8_t key_return = 0;          //函数返回值keyTemp = key_up & key_get();  //提取所有的key对应的io口switch(s_u8keyState)           //这里检测到的是先前的状态{case state_keyUp:   //如果先前是初始态,即无动作{if(key_up != keyTemp) //如果键被按下{s_u8keyState = state_keyDown; //更新键的状态,普通被按下}}break;case state_keyDown: //如果先前是被按着的{if(key_up != keyTemp) //如果现在还被按着{s_u8keyState = state_keyTime; //转换到计时态s_u16keyTimeCounts = 0;s_u8LastKey = keyTemp;     //保存键值}else{s_u8keyState = state_keyUp; //键没被按着,回初始态,说明是干扰}}break;case state_keyTime:  //如果先前已经转换到计时态(值为3){//如果真的是手动按键,必然进入本代码块,并且会多次进入if(key_up == keyTemp) //如果未按键{s_u8keyState = state_keyUp;key_return = return_keyPressed;    //返回1,一次完整的普通按键//程序进入这个语句块,说明已经有2次以上10ms的中断,等于已经消抖//那么此时检测到按键被释放,说明是一次普通短按}else  //在计时态,检测到键还被按着{if(++s_u16keyTimeCounts > key_longTimes) //时间达到2秒{s_u8keyState = state_keyLong;  //进入长按状态s_u16keyTimeCounts = 0;      //计数器清空,便于进入连发重新计数key_return = return_keyLong;   //返回state_keyLong}//代码中,在2秒内如果我们一直按着key的话,返回值只会是0,不会识别为短按或长按的}}break;case state_keyLong:  //在长按状态检测连发  ,每0.2秒发一次{if(key_up == keyTemp){s_u8keyState = state_keyUp;}else //按键时间超过2秒时{if(++s_u16keyTimeCounts > key_autoTimes)//10*20=200ms{s_u16keyTimeCounts = 0;key_return = return_keyAuto;  //每0.2秒返回值的第2位置位(1<<2)}//连发的时候,肯定也伴随着长按}key_return |= return_keyLong;  //0x02是肯定的,0x04|0x02是可能的}break;default:break;}*pKeyValue = s_u8LastKey ; //返回键值return key_return;
}
// 这个函数就是要在中断中调用的。主要是使用事件队列的方式。
void
KeyProcess(void)
{uint8_t key_stateValue;uint8_t keyValue = 0;uint8_t* pKeyValue = &keyValue;key_stateValue = key_read(pKeyValue);if ((return_keyPressed == key_stateValue) && (*pKeyValue == KEYS1_VALUE)){//短按keyS1时改变对时状态,将其加入队列,队列的基本操作在将队列的时候写过。if(QueueEventIsEmpty(g_state_manager.process->key_event) ||(!QueueEventIsEmpty(g_state_manager.process->key_event) &&!QUEUE_EVENT_IS_EQUEL(g_state_manager.process->key_event, UP_KEY))){QueueEventPush(g_state_manager.process->key_event, UP_KEY);}}
}//======================================================================================
void
KeyTimerInit(void)
{KEY_UP_CONFIG();KEY_DOWN_CONFIG();KEY_FUNC_CONFIG();KEY_TURN_CONFIG();// 20msTimerConfig(KEY_TIMER, KEY_TIMER_DIV, KEY_TIMER_PERIOD);TimerDisable(KEY_TIMER);TimerEnable(KEY_TIMER);
}
主要是描述一下按键状态机的思维,使用定时器中断的方法,按键按下将其加入队列中,在主函数的循环中实现出队。亲测可用。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/466309.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

二分法的应用:POJ1064 Cable master

/* POJ1064 Cable master时间限制&#xff1a; 1000MS 内存限制&#xff1a; 10000K 提交总数&#xff1a; 58217 接受&#xff1a; 12146 描述Wonderland的居民已经决定举办地区性编程比赛。评委会自愿并承诺举办有史以来最诚实的比赛。决定使用“星形”拓扑结构…

说一下NFC,手机有NFC功能却不能模拟门禁卡?

img1、NFC是什么&#xff1f;NFC&#xff08;Near Field Communication&#xff09; 技术由Philips、Nokia和Sony主推的一种近距离无线通信技术&#xff08;NFCIP-1&#xff09;&#xff0c;是一种短距离非接触式的通信方式&#xff0c;通常有效通讯距离为4厘米以内。工作频率为…

小程序员的大梦想 与盖茨像哥们儿

小程序员的大梦想 与盖茨像哥们儿以10亿的天价转会新华都&#xff0c;让唐骏有机会开创其职业经理人生涯的新局面&#xff0c;但在此之前&#xff0c;他的成功已得到证明面前的唐骏温和得令人吃惊。仿佛“前微软中国区总裁”、“打工皇帝”、“10亿转会身价”这样的光环带给他的…

动态规划详解

个人见解&#xff1a;1.动态规划实现了把问题拆分成多个子问题&#xff0c;然后求解&#xff0c;子问题有解后&#xff0c;问题自然迎刃而解&#xff1b;2.动态规划实现了子问题的状态的迁移&#xff0c;保存每个状态值&#xff0c;递推出答案&#xff0c;但不记录每种状态的求…

学习阶段总结

学习阶段总结 这几天思考了很多东西&#xff0c;感觉很有必要总结一下学习的东西了。从起初进步大学到步入社会已经一年有余&#xff0c;在大学感觉学也学了&#xff0c;玩了玩了。所以不存在什么很大的遗憾。之前零零散散的也学了不少东西了&#xff0c;感觉有必要对自己做一…

我的奶奶

突然想起我奶奶&#xff0c;是突然发现&#xff0c;距离奶奶去世已经有快十年&#xff0c;这十年时间&#xff0c;我也再也没有见过奶奶。奶奶刚去世的前几年&#xff0c;有时候会梦到奶奶跟我说话&#xff0c;她总是会很耐心的告诉我很多道理。奶奶从小到大都没有指责过我&…

CPtrArray、CObArray类

CPtrArray https://baike.baidu.com/item/CPtrArray CObArray类 https://baike.baidu.com/item/CObArray CPtrArray类支持void指针数组。CPtrArray的成员函数类似于CObArray类的成员函数。 外文名 CPtrArray 支 持 void指针数组 成员函数 CObArray类的成员函数 操作符 …

WPF应用程序内存泄漏的一些原因

原文&#xff1a;Finding Memory Leaks in WPF-based applications There are numbers of blogs that folks wrote about memory leaks in Microsoft .Net Framework managed code and unmanaged code based applications. In this blog I wanted to: Show coding practices th…

[转]Eclipse RCP应用系统开发方法与实战2-- 定制应用程序窗口属性

5.1.4 定制应用程序窗口属性 向导生成的应用程序主界面并不能满足要求&#xff0c;存在很多问题&#xff0c;例如&#xff0c;主界面运行时没有自动居中&#xff0c;主界面大小没有固 定&#xff0c;主窗口标题栏文字应该是“高校经费测算系统”。再看看图5-3、图5-4的标题栏有…

bootloader启动流程分析

bootloader启动流程分析 1、Bootloader的概念和作用 Bootloader是嵌入式系统的引导加载程序&#xff0c;它是系统上电后运行的第一段程序。在完成对系统的初始化任务之后&#xff0c;它会将Flash中的Linux内核拷贝到 RAM 中去&#xff0c;跳转到内核的第一条指令处继续执行&…

赢在中国 - 史玉柱经典语录

1.要重视建立销售手册2.凡是先做试点&#xff0c;风险才低。3.找到自己的细分市场。4.不要总想着同竞争对手对立&#xff0c;而是要想办法让自己弥补竞争对手的不足。5. 如果没有价格上的优势与技术上的绝对优势&#xff0c;千万不要进入红海市场&#xff0c;否则你会必输无疑&…

Utility.cpp的函数

C 对文件夹的安全权限操作 https://blog.csdn.net/zipper9527/article/details/6256459 https://blog.csdn.net/sz76211822/article/details/73199181 ZeroMemory https://baike.baidu.com/item/ZeroMemory/961326?fraladdin GetComputerName https://baike.baidu.com/item/G…

类中调用界面ActiveX控件报错当前线程不在单线程单元中因此无法实例化 ActiveX 控件的解决办法...

解决办法是Form类中定义一个静态的ActiveX对象,在formload中将界面上的ActiveX对象赋值给新定义的对象,类中访问该静态对象即可. public static AxClientDriver_NTLib.AxClientDriverCtrl com new AxClientDriver_NTLib.AxClientDriverCtrl(); private void Form1_Load(object…

我最近在学微信小程序开发,一起吗?

从当初的一夜成名&#xff0c;到今天火爆的市场占有率&#xff0c;微信小程序已走过 4 个年头。据今年 1 月阿拉丁发布的报告显示&#xff0c;微信小程序 2020 年 DAU 已破 4 亿&#xff0c;其总数超 380 万。最近有读者问我说&#xff0c;发哥&#xff0c;之前自己一直是个人…

回数

回数是指从左到右读和从右到左读都是一样的数&#xff0c;请利用filter()滤掉非回数(打印出1000内所有回数) def is_palindrome(n): return str(n)str(n)[::-1] # str(n)[::-1]代表字符串从后往前读&#xff0c;也代表字符串的翻转 output filter(is_palindrome,range(1,10…

初始化COM库

初始化COM库 CoCreateInstance //在本机中只创建一个对象 https://baike.baidu.com/item/CoCreateInstance QueryInterface 客户可以通过此函数来查询某个组件是否支持某个特定的接口 https://baike.baidu.com/item/QueryInterface SetNotifyWindow //注册一个窗口来处理事件通…

WebService大讲堂之Axis2(2):复合类型数据的传递

在实际的应用中&#xff0c;不仅需要使用WebService来传递简单类型的数据&#xff0c;有时也需要传递更复杂的数据&#xff0c;这些数据可以被称为复合类型的数据。数组与类&#xff08;接口&#xff09;是比较常用的复合类型。在Axis2中可以直接使用将WebService方法的参数或返…

Linux虚拟摄像头vivid配置

总述最近在看摄像头驱动&#xff0c;需要配置虚拟摄像头的驱动&#xff0c;但是教程里面是linux2.6内核的&#xff0c;实际电脑的是Ubuntu16&#xff0c;内核是linux4.15版本&#xff0c;从2.6到4.15内核好多文件发生了变化&#xff0c;所以我们该怎么操作呢&#xff1f;下面就…

第一次工作报告

要求 1. 对源文件&#xff08;*.txt,*.cpp,*.h,*.cs,*.html,*.js,*.java,*.py,*.php等&#xff09;统计字符数、单词数、行数、词频&#xff0c;统计结果以指定格式输出到默认文件中&#xff0c;以及其他扩展功能&#xff0c;并能够快速地处理多个文件。 2. 使用性能…

TQ210——S5PV210 uboot顶层mkconfig分析

################################################################################################# # SHELL常用内部参数&#xff1a;   # $# —— 传递给程序的总的参数数目 # $? —— 上一个代码或者shell程序在shell中退出的情况&#xff0c;如果正常退出则返回0&am…