历史上的今天
返回首页

历史上的今天

今天是: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);

}

主要是描述一下按键状态机的思维,使用定时器中断的方法,按键按下将其加入队列中,在主函数的循环中实现出队。亲测可用。


推荐阅读

史海拾趣

CoolerMaster公司的发展小趣事

林仁政跳出已有的框架,不断思考机箱的设计。他敏锐地觉察到人们对于免工具安装的主板散热片的偏好,并深知散热效果不仅仅取决于散热片和风扇,更需要考虑空气的流动情况。结合日本高水准的立体设计灵感,林仁政带领工程师挑战困难,成功设计出了一台铝制机箱。这一机箱不仅具有高度的创新性,更因其纯手工制作的特性而显得弥足珍贵,对Cooler Master的发展具有重要意义。

EVER-WAY公司的发展小趣事

在电子行业,技术创新是企业发展的关键。EVER-WAY公司深知这一点,因此在技术研发方面投入了大量资源。公司成立了专门的研发团队,不断引进先进的生产设备和技术人才,致力于开发具有自主知识产权的电子产品。经过多年的努力,EVER-WAY公司成功推出了一系列高性能、高可靠性的电子元器件产品,获得了国内外客户的广泛认可。这些技术创新的突破不仅提升了公司的核心竞争力,也为公司带来了丰厚的经济回报。

EMS GmbH公司的发展小趣事

EMS GmbH公司自创立之初,就专注于汽车转换器注塑件(微动开关)的研发和生产。公司投入大量资源进行技术创新,逐步积累起了一系列核心专利技术。这些技术不仅涵盖了汽车转换器注塑件生产的所有关键技术,还获得了多个国际品质管理组织机构及主流整车厂的双重、三重认证。这些专利技术的积累,使EMS GmbH公司在汽车转换器注塑件领域确立了行业领先地位。

上海晶丰明源(BPS)公司的发展小趣事

上海晶丰明源半导体股份有限公司的创立,标志着中国在半导体领域的又一重要布局。公司自成立之初,就专注于电源管理芯片的研发与销售,凭借对技术的深入理解和市场需求的敏锐洞察,迅速在行业中崭露头角。初步发展阶段,晶丰明源通过不断优化产品设计、提升生产工艺,逐步赢得了客户的信任,并在市场上占据了一席之地。

EDSYN公司的发展小趣事

Econais公司自创立之初,就致力于研发超低功耗(ULP)的无线模块。随着物联网(IoT)和机器对机器(M2M)通信技术的兴起,市场对低功耗嵌入式模块的需求日益增长。Econais工程师团队凭借几十年的行业经验,成功研发出了一系列超低功耗Wi-Fi模块,这些模块不仅性能卓越,而且易于集成,为各种物联网应用提供了强大的技术支持。

Alpha Industries公司的发展小趣事

随着电子技术的飞速发展,军事装备也在不断升级。Alpha Industries敏锐地捕捉到了这一趋势,开始将电子技术应用于其军事服装设计中。例如,公司研发了一款带有GPS定位功能的飞行夹克,帮助飞行员在复杂环境中快速定位。此外,Alpha Industries还利用电子传感器技术,开发出能够监测士兵体温和心率的战地风衣,为军队提供更为全面的保障。

问答坊 | AI 解惑

求助通过51单片机测量身高及体重的电路

有谁愿意提供开发一套可以测量体重及身高的单片机原理电路或产品,并可以将数据发送到计算机内。…

查看全部问答>

PC与三菱FX2N PLC 通讯怎么发送命令

请问PC与PLC通讯怎么发送命令? 在网上看到有下面这段代码: Public Function GetData(ByVal StartAddress As String, ByVal GetBytes As Integer, ByVal Hex_Bytes As String) As String       MSComm1.InputLen = GetByte ...…

查看全部问答>

各位大侠:请问AT89C52的FID和特征字是啥?

各位大侠:请问AT89C52的FID和特征字是啥?…

查看全部问答>

如何把wininet.lib加到WinCE500SDK里面同时符合Core License?

如何把wininet.lib加到WinCE500SDK里面同时符合Core License?…

查看全部问答>

TI模拟信号类芯片选型手册

本帖最后由 dontium 于 2015-1-23 11:35 编辑 TI模拟信号类芯片选型手册包括如下:Amplifiers, Data Converters, Interface,Clocks, Timing and Wireless Connectivity. …

查看全部问答>

请问这些元件你用过吗?

以上是全景 在修奥林巴斯显微镜底部的调光电路,这些元件以前没玩过,请问他们是什么,有什么用,最好能将具体点,求大神!!! …

查看全部问答>

硬件I2C调试电容触摸屏

各位好,我现在在调试电容触摸屏,但是是用的硬件I2C,ARM这边是主,触摸屏这边是从,可是怎么调都调不通,怎么回事,各位高手帮帮忙。…

查看全部问答>

虚拟串口问题,解决有报酬

想实现这么一个功能: 在计算机上插三张卡,每张卡虚拟16个虚拟串口 例如卡1对应1-16串口,卡2对应17-32串口,卡3对应33-48串口,但当我关机把第二张卡拔掉后,串口的会自动重新分配,卡1对应1-16,卡3对应17-32,现在就想实现卡3仍旧对应的是33-4 ...…

查看全部问答>

求大神指导怎么用verilog语言写出TM1803的功能

1、 所选FPGA:EP1C3T144C8。2、 能通过一根信号线完成数据的接收与解码。3、 单线通讯方式,采用归零码的方式发送信号。4、 具有数据自动整形与转发功能。5、 三路LED驱动输出,三路PWM输出。6、 输出接三色LED灯,gamma调整系数为1.8。7、 PWM控 ...…

查看全部问答>