当前位置:首页 > > 充电吧
[导读]#ifndef _KEY_H_ #define _KEY_H_ #include "HAL_gpio.h" // 换成STM32F103对应的GPIO库 #include "type.h"     


#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);
}

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



本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: 驱动电源

在工业自动化蓬勃发展的当下,工业电机作为核心动力设备,其驱动电源的性能直接关系到整个系统的稳定性和可靠性。其中,反电动势抑制与过流保护是驱动电源设计中至关重要的两个环节,集成化方案的设计成为提升电机驱动性能的关键。

关键字: 工业电机 驱动电源

LED 驱动电源作为 LED 照明系统的 “心脏”,其稳定性直接决定了整个照明设备的使用寿命。然而,在实际应用中,LED 驱动电源易损坏的问题却十分常见,不仅增加了维护成本,还影响了用户体验。要解决这一问题,需从设计、生...

关键字: 驱动电源 照明系统 散热

根据LED驱动电源的公式,电感内电流波动大小和电感值成反比,输出纹波和输出电容值成反比。所以加大电感值和输出电容值可以减小纹波。

关键字: LED 设计 驱动电源

电动汽车(EV)作为新能源汽车的重要代表,正逐渐成为全球汽车产业的重要发展方向。电动汽车的核心技术之一是电机驱动控制系统,而绝缘栅双极型晶体管(IGBT)作为电机驱动系统中的关键元件,其性能直接影响到电动汽车的动力性能和...

关键字: 电动汽车 新能源 驱动电源

在现代城市建设中,街道及停车场照明作为基础设施的重要组成部分,其质量和效率直接关系到城市的公共安全、居民生活质量和能源利用效率。随着科技的进步,高亮度白光发光二极管(LED)因其独特的优势逐渐取代传统光源,成为大功率区域...

关键字: 发光二极管 驱动电源 LED

LED通用照明设计工程师会遇到许多挑战,如功率密度、功率因数校正(PFC)、空间受限和可靠性等。

关键字: LED 驱动电源 功率因数校正

在LED照明技术日益普及的今天,LED驱动电源的电磁干扰(EMI)问题成为了一个不可忽视的挑战。电磁干扰不仅会影响LED灯具的正常工作,还可能对周围电子设备造成不利影响,甚至引发系统故障。因此,采取有效的硬件措施来解决L...

关键字: LED照明技术 电磁干扰 驱动电源

开关电源具有效率高的特性,而且开关电源的变压器体积比串联稳压型电源的要小得多,电源电路比较整洁,整机重量也有所下降,所以,现在的LED驱动电源

关键字: LED 驱动电源 开关电源

LED驱动电源是把电源供应转换为特定的电压电流以驱动LED发光的电压转换器,通常情况下:LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: LED 隧道灯 驱动电源
关闭