历史上的今天
返回首页

历史上的今天

今天是:2024年09月13日(星期五)

正在发生

2021年09月13日 | STM8S自学笔记-006 GPIO输入:按键输入 与 按键滤波

2021-09-13 来源:eefocus

GPIO输入

在 《STM8S自学笔记-003 GPIO输出:点亮LED灯 and 跑马灯特效》中,我们曾经把LED的GPIO设置为推挽输出模式,而它只是GPIO输出功能中的一种。同样,GPIO的输入功能也不止有一种。


浮空输入,无中断

上拉输入,无中断

浮空输入,有中断

上拉输入,有中断

今天不讨论带有中断的的输入功能。

我的STM8S开发板上有3个按键,位置分别与3个LED对应,这些按键都有外部的上拉电阻。所以在初始化按键GPIO时,我们可以设置为浮空输入,也可以设置为上拉输入,两种设置方式对按键检测来说没有差别;如果没有外部上拉电阻,那就建议设置为内部上拉,以保证GPIO输入端口电压的稳定。


按键检测

目标功能

今天要实现的功能是:如果按住某个按键,那么,这个按键对应的LED就点亮,否则就熄灭。


对应代码

在Drv_GPIO.c中追加按键GPIO初始化、按键检查 共2个函数。同时,在Drv_GPIO.h中追加新增函数的声明与按键定义。

当前,完整的Drv_GPIO.c内容如下:


/**

  ******************************************************************************

  * @file    Drv_GPIO.c

  * @author  Elsa

  * @version V1.0.0

  * @date    7-August-2021

  * @brief

  ******************************************************************************

  */

  /* Includes ------------------------------------------------------------------*/

#include "Drv_GPIO.h"


/* Private typedef -----------------------------------------------------------*/

/* Private define ------------------------------------------------------------*/

/* Private macro -------------------------------------------------------------*/

/* Private variables ---------------------------------------------------------*/

/* Private function prototypes -----------------------------------------------*/

/* Private functions ---------------------------------------------------------*/

/**

  * @brief  Initializes the LEDx according to the specified parameters.

  * @param  led_map : This parameter contains the pin number

  * @retval None

  */

void LED_Init(uint8_t led_map)

{

  if (led_map & LED1) GPIO_Init(LED1_GPIO, LED1_GPIO_PIN, GPIO_MODE_OUT_PP_LOW_SLOW);    //LED1 ON

  if (led_map & LED2) GPIO_Init(LED2_GPIO, LED2_GPIO_PIN, GPIO_MODE_OUT_PP_LOW_SLOW);    //LED2 ON

  if (led_map & LED3) GPIO_Init(LED3_GPIO, LED3_GPIO_PIN, GPIO_MODE_OUT_PP_LOW_SLOW);    //LED3 ON  

}


/**

  * @brief  Light on the specified LEDs.

  * @param  led_map : This parameter contains the pin number

  * @retval None

  */

void LED_On(uint8_t led_map)

{

  if (led_map & LED1) GPIO_WriteLow(LED1_GPIO, LED1_GPIO_PIN);

  if (led_map & LED2) GPIO_WriteLow(LED2_GPIO, LED2_GPIO_PIN);

  if (led_map & LED3) GPIO_WriteLow(LED3_GPIO, LED3_GPIO_PIN);

}


/**

  * @brief  Light off the specified LEDs.

  * @param  led_map : This parameter contains the pin number

  * @retval None

  */

void LED_Off(uint8_t led_map)

{

  if (led_map & LED1) GPIO_WriteHigh(LED1_GPIO, LED1_GPIO_PIN);

  if (led_map & LED2) GPIO_WriteHigh(LED2_GPIO, LED2_GPIO_PIN);

  if (led_map & LED3) GPIO_WriteHigh(LED3_GPIO, LED3_GPIO_PIN);

}


/**

  * @brief  Blink the specified LEDs.

  * @param  led_map : This parameter contains the pin number

  * @retval None

  */

void LED_Reverse(uint8_t led_map)

{

  if (led_map & LED1) GPIO_WriteReverse(LED1_GPIO, LED1_GPIO_PIN);

  if (led_map & LED2) GPIO_WriteReverse(LED2_GPIO, LED2_GPIO_PIN);

  if (led_map & LED3) GPIO_WriteReverse(LED3_GPIO, LED3_GPIO_PIN);

}


/**

  * @brief  Control the specified LEDs.

  * @param  led_map : This parameter contains the pin number

  * @retval None

  */

void LED_Control(uint8_t led_map)

{

  if (led_map & LED1) GPIO_WriteLow(LED1_GPIO, LED1_GPIO_PIN);

  else                GPIO_WriteHigh(LED1_GPIO, LED1_GPIO_PIN);

  if (led_map & LED2) GPIO_WriteLow(LED2_GPIO, LED2_GPIO_PIN);

  else                GPIO_WriteHigh(LED2_GPIO, LED2_GPIO_PIN);

  if (led_map & LED3) GPIO_WriteLow(LED3_GPIO, LED3_GPIO_PIN);

  else                GPIO_WriteHigh(LED3_GPIO, LED3_GPIO_PIN);

}


/**

  * @brief  Initializes the KEYx according to the specified parameters.

  * @param  key_map : This parameter contains the pin number

  * @retval None

  */

void KEY_Init(uint8_t key_map)

{

  if (key_map & KEY1) GPIO_Init(KEY1_GPIO, KEY1_GPIO_PIN, GPIO_MODE_IN_FL_NO_IT);    //KEY1

  if (key_map & KEY2) GPIO_Init(KEY2_GPIO, KEY2_GPIO_PIN, GPIO_MODE_IN_FL_NO_IT);    //KEY2

  if (key_map & KEY3) GPIO_Init(KEY3_GPIO, KEY3_GPIO_PIN, GPIO_MODE_IN_FL_NO_IT);    //KEY3

}


/**

  * @brief  Read input of specified KEYs.

  * @param  key_map : This parameter contains the pin number

  * @retval None

  */

uint8_t KEY_Read(uint8_t key_map)

{

  uint8_t result = 0;

  if (key_map & KEY1 != RESET && GPIO_ReadInputPin(KEY1_GPIO, KEY1_GPIO_PIN) == RESET)

    result |= KEY1;

  if (key_map & KEY2 != RESET && GPIO_ReadInputPin(KEY2_GPIO, KEY2_GPIO_PIN) == RESET)

    result |= KEY2;

  if (key_map & KEY3 != RESET && GPIO_ReadInputPin(KEY3_GPIO, KEY3_GPIO_PIN) == RESET)

    result |= KEY3;


  return result;

}




/***************************************************************END OF FILE****/


当前,完整的Drv_GPIO.h内容如下:


/**

  ******************************************************************************

  * @file    Drv_GPIO.h

  * @author  ANNA

  * @version V1.0.0

  * @date    03-August-2021

  * @brief   This file contains the headers of the Drv_GPIO.c

  ******************************************************************************

  */


/* Define to prevent recursive inclusion -------------------------------------*/

#ifndef __DRV_GPIO_H

#define __DRV_GPIO_H


/* Includes ------------------------------------------------------------------*/

#include "main.h"


/* Exported types ------------------------------------------------------------*/

/* Exported defines ----------------------------------------------------------*/

//Maps of LEDx

#define LED(n)  ((uint8_t)(0x01<<(uint8_t)(n-1)))

#define LED1    LED(1)

#define LED2    LED(2)

#define LED3    LED(3)

//GPIO Definition of LEDx

#define LED1_GPIO         GPIOC

#define LED1_GPIO_PIN     GPIO_PIN_3

#define LED2_GPIO         GPIOC

#define LED2_GPIO_PIN     GPIO_PIN_4

#define LED3_GPIO         GPIOD

#define LED3_GPIO_PIN     GPIO_PIN_2


//Maps of KEYs

#define KEY(n)  ((uint8_t)(0x01<<(uint8_t)(n-1)))

#define KEY1    KEY(1)

#define KEY2    KEY(2)

#define KEY3    KEY(3)

//GPIO Definition of KEYx

#define KEY1_GPIO         GPIOB

#define KEY1_GPIO_PIN     GPIO_PIN_3

#define KEY2_GPIO         GPIOD

#define KEY2_GPIO_PIN     GPIO_PIN_3

#define KEY3_GPIO         GPIOD

#define KEY3_GPIO_PIN     GPIO_PIN_7


/* Exported constants --------------------------------------------------------*/

/* Exported macro ------------------------------------------------------------*/

/* Exported variables --------------------------------------------------------*/

/* Exported function prototypes ----------------------------------------------*/

void LED_Init(uint8_t led_map);

void LED_On(uint8_t led_map);

void LED_Off(uint8_t led_map);

void LED_Reverse(uint8_t led_map);

void LED_Control(uint8_t led_map);


void KEY_Init(uint8_t key_map);

uint8_t KEY_Read(uint8_t key_map);



#endif

/***************************************************************END OF FILE****/


【提示】

在Drv_GPIO.c中,定义了LED和KEY初始化和操作函数,这些函数的入口是我们自己定义的LED1/2/3和KEY1/2/3,与STM8的底层寄存器无关。也就是说,在调用这些函数时,不会直接调用这些MCU的底层寄存器,而是通过我们自定义的(入口)接口来实现对应的功能。所以,可以将Drv_GPIO.h中形如


#define LED1_GPIO         GPIOC

#define LED1_GPIO_PIN     GPIO_PIN_3


的代码块全部移入Drv_GPIO.c。


功能实现

在主函数中,添加下述代码,实现按键检测与LED显示:


/**

  * @brief  Main program.

  * @param  None

  * @retval None

  */

void main(void)

{

  static uint8_t key_value;

  CLOCK_HSE(CLK_PRESCALER_CPUDIV1);

  KEY_Init(KEY1 | KEY2 | KEY3);

  LED_Init(LED1 | LED2 | LED3);

  while (1)

  {

    /* Key_Scan */

    key_value = KEY_Read(KEY1 | KEY2 | KEY3);


    /* Led Show */

    if (key_value & KEY1)

      LED_On(LED1);

    else

      LED_Off(LED1);

    

    if (key_value & KEY2)

      LED_On(LED2);

    else

      LED_Off(LED2);

    

    if (key_value & KEY3)

      LED_On(LED3);

    else

      LED_Off(LED3);


  }

}


按键滤波

虽然开发板在按键的硬件层面上,从相应的GPIO口各引出了一个接地的滤波电容,但是按下后仍可能有抖动。对此,我们可以从软件上进行滤波。


延时滤波

延时滤波是一种简便的办法。在学校里做单片机实验时,我们就用过这种方法;但在工作中,我从没在按键检测函数里面加过“无用的”延时函数。


 if (key_map & KEY1 != RESET && GPIO_ReadInputPin(KEY1_GPIO, KEY1_GPIO_PIN) == RESET)

 {

  Delay_1ms(7);

  if (key_map & KEY1 != RESET && GPIO_ReadInputPin(KEY1_GPIO, KEY1_GPIO_PIN) == RESET)

  {

  result |= KEY1;

  }

 }


循环滤波

循环滤波可能是最为常见的按键软件检测方法。它要求在按键按下的情况呈现一定规律时,就认为按下有效。

在本例中,我设定的规律是:按键连续按下100次。那么,对应的修改是:


/**

  * @brief  Read input of specified KEYs.

  * @param  key_map : This parameter contains the pin number

  * @retval None

  */

uint8_t KEY_Read(uint8_t key_map)

{

  volatile uint8_t result = 0;

  static uint8_t count[3] = {0};

  /* Cyclic filtering */

  count[0] = (key_map & KEY1 != RESET && GPIO_ReadInputPin(KEY1_GPIO, KEY1_GPIO_PIN) == RESET)? (++count[0]):0;

  count[1] = (key_map & KEY2 != RESET && GPIO_ReadInputPin(KEY2_GPIO, KEY2_GPIO_PIN) == RESET)? (++count[1]):0;

  count[2] = (key_map & KEY3 != RESET && GPIO_ReadInputPin(KEY3_GPIO, KEY3_GPIO_PIN) == RESET)? (++count[2]):0;

  /* Overflow check */

  count[0] = (count[0] >= 250)? 250:count[0];

  count[1] = (count[1] >= 250)? 250:count[1];

推荐阅读

史海拾趣

大毅科技公司的发展小趣事

作为一家有社会责任感的企业,大毅科技始终关注环境保护和可持续发展。公司积极推行绿色生产理念,采用环保材料和节能技术,减少生产过程中的废弃物排放和能源消耗。同时,大毅科技还积极参与公益事业和社区活动,回馈社会。这些举措不仅体现了公司的社会责任感,也赢得了社会的广泛赞誉。

以上是关于大毅科技在电子行业中的5个发展故事概述。这些故事展示了大毅科技在创立、生产基地拓展、品质认证与国际化、技术创新与研发以及社会责任与可持续发展等方面的努力和成就。

EDSYN公司的发展小趣事

为了帮助客户更便捷地将设备连接到物联网,Econais与Xively展开了合作。通过这一合作,Econais的WiSmart Wi-Fi用户可以无缝连接到Xively云,为工业、商业和住宅市场带来了创新的医疗保健、智能能源、无线音频控制和监控产品。这种一站式的物联网解决方案大大加快了物联网产品的上市时间,同时也降低了产品的开发成本。

GE Power Electronics Inc公司的发展小趣事
选用质量可靠、性能稳定的元件,如高精度电位器、低噪声放大器等。
Chenmoun Enterprise Ltd公司的发展小趣事

Chenmoun Enterprise Ltd成立之初,专注于研发和生产电子产品的基础元件。随着技术的不断发展,公司敏锐地把握了市场趋势,开始研发具有创新性的芯片技术。通过不懈的努力,Chenmoun成功推出了性能卓越、成本更低的芯片产品,迅速占据了市场份额,并逐渐在电子行业内崭露头角。

ECI公司的发展小趣事

ECI公司自成立以来,一直致力于技术创新和研发。在电子行业的早期,ECI就投入巨资研发先进的半导体技术,成功推出了多款高性能、低功耗的芯片产品。这些产品迅速在市场上获得认可,为公司赢得了良好的口碑。随着技术的不断进步,ECI公司不断推出更加先进的电子产品,引领了电子行业的发展趋势。

Device Engineering Incorporated公司的发展小趣事

在电子行业快速发展的背景下,DEI公司意识到只有不断创新才能保持竞争力。因此,公司加大了对研发的投入,积极引进新技术和人才。通过不断的努力,DEI公司成功推出了一系列具有自主知识产权的创新产品,涵盖了多个领域。这些产品不仅提升了公司的竞争力,也为公司赢得了更多的市场份额。

问答坊 | AI 解惑

电子元器件之继电器

主要介绍了继电器的分类、选用及使用中的注意事项。 继电器…

查看全部问答>

如果写磁盘时突然停电。。。。

硬盘会如何处理?比如正在写一个扇区的时候停电了,那这个扇区是不是只有一部分数据被写入了,还是硬盘能保证要么扇区全部被写入,要么就不写??…

查看全部问答>

DirectDraw问题

1>.\\YX_GPS\\port\\c\\yu_port_gui.c(79) : error C2039: \'Release\' : is not a member of \'IDirectDrawSurface\' 1>        D:\\Program Files\\Windows CE Tools\\wce500\\STANDARDSDK_500\\include\\ARMV4I\ ...…

查看全部问答>

月薪1500的军工工程师的自白 (转载)

当我和厂里的师傅们在为了使数控机床提高一级精度而不分白夜进行调试、翻译德文资料时,当我费尽千辛万苦又没有假期和加班费、满身污垢,满手是伤,操作失败了无数次但最终第一次加工制造出精度达到预计的0.0001毫米要求的样品时,我兴奋了一阵子, ...…

查看全部问答>

关于三轴陀螺仪传感器L3G4200D的程序问题

本人初次接触陀螺仪,读了此陀螺仪datasheet后感到很晕,要做一堆配置…..请大家指点一下该怎么配置,有个范例程序就再好不过了……datasheet 和application note请见附件。…

查看全部问答>

ARM与嵌入式入门的建议

由于很多人总问这个问题,所以这里做一个总结文档供大家参考。这里必须先说明,以下的步骤都是针对Linux系统的,并不面向WinCE。也许你会注意到,现在做嵌入式的人中,做linux研究的人远比做WinCE的人多,很多产家提供的资料也是以linux为主。我一 ...…

查看全部问答>

求助一个vga显示波形的问题

我现在有套系统,FPGA 驱动AD采样,采样结果想通过VGA来显示出波形,我的做法是将ad结果存入fpga内部DPRAM中(注:没有外扩sdram)。然后让vga从DPRAM中读出数据显示。ad是12位的,我每次采样420个点,想在800*600中间的420*400区域中来显示。我的想 ...…

查看全部问答>

谁用ISD1730语音芯片做过语音报时呀?求帮助

我的想法是,先将0~9十个数和时,分,秒分别录到芯片内,然后取的这些数所在的地址,然后将他们组合,播报出来。但问题是分开都很好,组合在一起有的就播放不出来,老是漏掉一些,是不是忙不过来了,加延时也没用。求解!    还有如 ...…

查看全部问答>