历史上的今天
今天是:2024年12月06日(星期五)
2020年12月06日 | STM32F103按键操作的另一种实现——状态机
2020-12-06 来源:eefocus
#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 #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(); // 20ms TimerConfig(KEY_TIMER, KEY_TIMER_DIV, KEY_TIMER_PERIOD); TimerDisable(KEY_TIMER); TimerEnable(KEY_TIMER); } 主要是描述一下按键状态机的思维,使用定时器中断的方法,按键按下将其加入队列中,在主函数的循环中实现出队。亲测可用。
史海拾趣
|
请问PC与PLC通讯怎么发送命令? 在网上看到有下面这段代码: Public Function GetData(ByVal StartAddress As String, ByVal GetBytes As Integer, ByVal Hex_Bytes As String) As String MSComm1.InputLen = GetByte ...… 查看全部问答> |
|
如何把wininet.lib加到WinCE500SDK里面同时符合Core License? 如何把wininet.lib加到WinCE500SDK里面同时符合Core License?… 查看全部问答> |
|
本帖最后由 dontium 于 2015-1-23 11:35 编辑 TI模拟信号类芯片选型手册包括如下:Amplifiers, Data Converters, Interface,Clocks, Timing and Wireless Connectivity. … 查看全部问答> |
|
想实现这么一个功能: 在计算机上插三张卡,每张卡虚拟16个虚拟串口 例如卡1对应1-16串口,卡2对应17-32串口,卡3对应33-48串口,但当我关机把第二张卡拔掉后,串口的会自动重新分配,卡1对应1-16,卡3对应17-32,现在就想实现卡3仍旧对应的是33-4 ...… 查看全部问答> |
|
1、 所选FPGA:EP1C3T144C8。2、 能通过一根信号线完成数据的接收与解码。3、 单线通讯方式,采用归零码的方式发送信号。4、 具有数据自动整形与转发功能。5、 三路LED驱动输出,三路PWM输出。6、 输出接三色LED灯,gamma调整系数为1.8。7、 PWM控 ...… 查看全部问答> |




