这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 嵌入式开发 » 国产MCU » 状态机实现的按键扫描

共2条 1/1 1 跳转至

状态机实现的按键扫描

院士
2026-09-15 21:29:34     打赏

按键的实现方案挺多,本次先尝试一下按键扫描。

硬件上按键采用按下时对GND短接,故需要配置GPIO引脚为内置上拉电阻,以稳定空闲按键电平。硬件原理图如下:

按键硬件原理图.jpg

软件的按键初始化与检测代码如下:

void bsp_gpio_init(void)
{
  GPIO_Config_T gpioConfig;

  gpioConfig.speed = GPIO_SPEED_2MHz;
  gpioConfig.mode = GPIO_MODE_IN;
  gpioConfig.pupd = GPIO_PUPD_PU;
  gpioConfig.pin = GPIO_PIN_0;
  GPIO_Config(GPIOA, &gpioConfig);

  gpioConfig.pin = GPIO_PIN_1;
  GPIO_Config(GPIOA, &gpioConfig);
}

static inline int keybutton_ispressd(key_id_t key_id)
{
  int ret = 0;
  switch (key_id)
  {
  case key_id_0:
  {
    ret = GPIO_ReadInputBit(GPIOA, GPIO_PIN_0);
    break;
  }
  case key_id_1:
  {
    ret = GPIO_ReadInputBit(GPIOA, GPIO_PIN_1);
    break;
  }
  }                                   
  if(ret == 0)
  {
    return (1);
  }
  else
  {
    return (0);
  }
}

由于采用轮询方式回读GPIO状态,所以,我们必须添加软件去抖的算法。我这里和其它实现方案一样,也是采用状态机实现方式。简单说,两次采样一致则认为状态稳定。我定义Key的状态如下:

typedef enum
{
  kb_state_initial = 0,
  kb_state_idle,
  kb_state_down,
  kb_state_down2,
  kb_state_up,
  kb_state_up2
} kb_state_t;

按键的状态轮转图如下图所示:

按键实现-状态转换图.jpg

本次代码还是蛮多的,于是先写了一段测试代码:

int main(void)
{
  int ret = 0;
  bsp_init();
  SysTick_Config(SystemCoreClock / 1000);

  while (1)
  {
    ret = keybutton_ispressd(key_id_0);
    reg_zone[0] = ret;
    ret = keybutton_ispressd(key_id_1);
    reg_zone[1] = ret;
  }
}

当我按下按键时,可以看到reg_zone[0]=1。这也验证了我的GPIO输入引脚工作正常。

第一阶段已经验证完毕,我们赶紧实现状态机的代码。

void keybutton_init(void)
{
  key_inst[0].id = key_id_0;
  key_inst[0].event = kb_event_null;
  key_inst[0].state = kb_state_idle;
  key_inst[1].id = key_id_1;
  key_inst[1].event = kb_event_null;
  key_inst[1].state = kb_state_idle;
}

void keybutton_scan(key_class_t *kb)
{
  int ret = 0;
  switch (kb->state)
  {
  case kb_state_idle:
  {
    ret = keybutton_ispressd(kb->id);
    if (ret != 0)
    {
      kb->state = kb_state_down;
    }
    break;
  }
  case kb_state_down:
  {
    ret = keybutton_ispressd(kb->id);
    if (ret != 0)
    {
      kb->state = kb_state_down2;
    }
    else
    {
      kb->state = kb_state_idle;
    }
    break;
  }
  case kb_state_down2:
  {
    ret = keybutton_ispressd(kb->id);
    if (ret == 0)
    {
      kb->state = kb_state_up;
    }
    break;
  }
  case kb_state_up:
  {
    ret = keybutton_ispressd(kb->id);
    if (ret == 0)
    {
      kb->state = kb_state_up2;
    }
    else
    {
      kb->state = kb_state_down2;
    }
    break;
  }
  case kb_state_up2:
  {
    ret = keybutton_ispressd(kb->id);
    if (ret == 0)
    {
      kb->event = kb_event_click;
      kb->state = kb_state_idle;
    }
    else
    {
      kb->state = kb_state_up;
    }
    break;
  }
  }
}

实现了一下,效果还是不错的。

按键实现结果.jpg





关键词: 状态机     按键     扫描    

高工
2026-09-16 00:31:48     打赏
2楼

感觉就是一个连消抖都不稳定的实现。我的理解,做成两层状态机可能更合适,最底层就是最简单的按键的按下松开判断,然后上面一层就是根据是否按下的计时时长去切换状态,并抛出相应的按键事件在上层的应用处理


共2条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]