GPIO按键输入—基于I.MX6UL嵌入式SoC

1、前言

在前面的文章《C语言裸机GPIO控制—基于I.MX6UL嵌入式SoC》中,链接如下:

https://www.cnblogs.com/Cqlismy/p/12445576.html

实现了I.MX6UL嵌入式SoC中通用输入/输出接口外设的输出功能,我们都知道I.MX6UL芯片上的IO口除了能作为输出,还能够作为输入,作为GPIO的输入功能后,能够读取到当前引脚的电平状态,最典型、最简单的应用就是按键,按键一般就两个状态,分别为按下和弹起,当我们将按键的另一端接到I.MX6UL上的IO引脚上,就可以通过读取这个IO引脚的电平值来判断当前按键状态是处于按下还是弹起状态。

 

2、GPIO按键输入原理

单个按键的硬件原理如下所示:

+E为目标板电源,为3.3V,按键K的另一端(Y)连接到I.MX6UL处理器的GPIO4_IO22这个IO引脚上面,GPIO4_IO22这个IO引脚复用功能为GPIO,方向为输入,默认上拉,当按键K未按下时,IO口的引脚状态为高电平,当按键K按下时,IO口直接和GND导通,所以此时IO口的引脚状态为低电平,由于按键的机械结构,当按键按下或者松开期间,会产生一定的抖动,所谓的抖动就是IO的电平会出现多次电平跳动,实际的按键波形图如上所示,如果我们不进行按键抖动消除处理的话,可能会产生误判的现象,有可能按键只按了一次,结果使用程序对IO口电平读取发现按键按下了多次。

使用软件进行消抖的最简单处理就是进行延时处理,延时跳过抖动时间后,再去读引脚的IO口电平,如果此时的IO电平为低,则说明按键确实是被按下了,有事件触发了,需要进行事件处理,一般的延时消抖时间大约10ms即可。

 

3、GPIO按键输入程序

对GPIO按键输入的实现原理有一定的了解后,接下来看看如何编程实现,编程思路如下:

  • 使能相应的按键IO时钟;
  • 设置IO口的复用模式为GPIO,设置GPIO方向为输入并设置IO引脚的电气属性;
  • 主函数中读取GPIO的电平状态,判断是否有按键事件触发,如果有的话,进行相应的事件处理。

先修改gpio驱动模块的相关函数,bsp_gpio.h文件内容如下:

#ifndef __BSP_GPIO_H
#define __BSP_GPIO_H

#include "imx6ul.h"

/* GPIO方向定义 */
typedef enum _gpio_pin_direction {
    kGPIO_DigitalInput = 0U,    /* 表示GPIO方向输入 */
    kGPIO_DigitalOutput = 1U,   /* 表示GPIO方向输出 */
} gpio_pin_direction_t;

typedef struct _gpio_pin_config {
    gpio_pin_direction_t direction; /* GPIO的方向 */
    unsigned char value;  /* GPIO输出时默认引脚电平值 */
} gpio_pin_config_t;

/* GPIO操作函数相关声明 */
void gpio_init(GPIO_Type *base, int pin, gpio_pin_config_t *config);
int gpio_pin_read(GPIO_Type *base, int gpio);
void gpio_pin_write(GPIO_Type *base, int gpio, int value);

#endif

gpio操作的相关函数定义在bsp_gpio.c文件中,内容如下所示:

#include "bsp_gpio.h"

/**
 * gpio_init() - GPIO初始化函数
 * 
 * @base: 要初始化的GPIO组,例如:GPIO1、GPIO2
 * @pin: 要初始化的GPIO组的pin编号
 * @config: gpio引脚配置结构体
 * 
 * @return: 无
 */
void gpio_init(GPIO_Type *base, int pin, gpio_pin_config_t *config)
{
    if (config->direction == kGPIO_DigitalInput) /* GPIO方向为输入 */
        base->GDIR &= ~(1 << pin);
    else {
        base->GDIR |= (1 << pin);
        gpio_pin_write(base, pin, config->value);
    }
}

/**
 * gpio_pin_read() - 读取GPIO引脚的电平
 * 
 * @base: 要读取的GPIO组,例如:GPIO1、GPIO2
 * @pin: 要读取的GPIO组的pin编号
 * 
 * @return: 0表示低电平,1表示高电平
 */
int gpio_pin_read(GPIO_Type *base, int pin)
{
    return (((base->DR) >> pin) & 0x1);
}

/**
 * gpio_pin_write() - 设置GPIO引脚的电平
 * 
 * @base: 要设置的GPIO组,例如:GPIO1、GPIO2
 * @pin: 要设置的GPIO组的pin编号
 * @value: 引脚要设置的电平值:0->低电平,1->高电平
 * 
 * @return: 无
 */
void gpio_pin_write(GPIO_Type *base, int pin, int value)
{
    if (0 == value)
        base->DR &= ~(1 << pin); /* 引脚输出低电平 */
    else
        base->DR |= (1 << pin); /* 引脚输出高电平 */
}

gpio_init()函数用完成GPIO的初始化,主要是设置GPIO的方向,输入或者输出,如果GPIO的方向是输出的话,还要设置GPIO的默认输出电平,gpio_pin_read()函数用来读取相应的IO引脚的当前电平状态,是通过读取GPIOx_DR这个寄存器来实现的,gpio_pin_write()函数用来设置GPIO引脚的电平值,也是通过设置GPIOx_DR这个寄存器来实现的。

接下来,进入到bsp目录下,新创建key子目录,用来存放和按键功能相关的驱动文件:

$ cd bsp/bsp
$ mkdir key
$ cd key
$ touch bsp_key.h
$ touch bsp_key.c

bsp_key.h文件用来定义按键的键值以及一些函数声明,内容如下:

#ifndef __BSP_KEY_H
#define __BSP_KEY_H

#include "imx6ul.h"
#include "bsp_gpio.h"
#include "bsp_delay.h"

/* 定义按键值 */
enum _key_value {
    KEY_NONE = 0,
    KEY0_VALUE,
} key_value;

/* 和按键操作相关函数声明 */
void key_init(void);
int key_get_value(void);

#endif

按键驱动函数的实现在bsp_key.c中,内容如下:

#include "bsp_key.h"

/**
 * key_init() - 按键初始化函数
 * 
 * @return: 无
 */
void key_init(void)
{
    gpio_pin_config_t key_config;

    /* 设置CSI_DATA01引脚IO复用为GPIO4_IO22 */
    IOMUXC_SetPinMux(IOMUXC_CSI_DATA01_GPIO4_IO22, 0);

    /* 配置GPIO4_IO22引脚电气属性 
     * bit[16]: 0 关闭HYS
     * bit[15:14]: 11 pull up 22k
     * bit[13]: 1 pull
     * bit[12]: 1 pull/keeper使能
     * bit[11]: 0 禁止开路输出
     * bit[10:8] 000 reserved
     * bit[7:6]: 10 速度为100MHz
     * bit[5:3]: 000 关闭输出
     * bit[2:1]: 00 reserved
* bit[0]: 0 低摆率
*/ IOMUXC_SetPinConfig(IOMUXC_CSI_DATA01_GPIO4_IO22, 0xF080); /* 将按键相关的GPIO方向设置为输入 */ key_config.direction = kGPIO_DigitalInput;
key_config.value = 1; gpio_init(GPIO4,
22, &key_config); } /** * key_get_value() - 获取按键的键值 * * @return: 0表示没有按键按下,1表示按键按下 */ int key_get_value(void) { int ret = KEY_NONE; static unsigned char release = 1; /* 表示按键处于释放状态 */ if ((release == 1) && (gpio_pin_read(GPIO4, 22) == 0)) { /* 按键按下 */ delay(10); /* 延时消抖 */ if (gpio_pin_read(GPIO4, 22) == 0) { /* 再次判断按键是否按下 */ release = 0; ret = KEY0_VALUE; } } else if (gpio_pin_read(GPIO4, 22) == 1) { /* 按键未按下 */ release = 1; /* 标记按键处于释放状态 */ ret = KEY_NONE; } return ret; }

key_init()函数用来完成按键IO口引脚的初始化,主要是完成IO口引脚的复用功能配置以及IO引脚的电气属性配置,最后,需要设置GPIO的方向为输入,key_get_value()函数则是用来获取按键的键值,当按键按下后,该函数返回1,当按键处于松开状态时,该函数返回0,主要是通过读取IO口引脚的电平状态来进行判断的,这就是按键的驱动函数。

app.c文件内容如下:

#include "bsp_clk.h"
#include "bsp_delay.h"
#include "bsp_gpio.h"
#include "bsp_led.h"
#include "bsp_key.h"

/**
 * main() - 主函数
 */
int main(void)
{
    int key_value = KEY_NONE;
    unsigned char led_state = OFF;

    system_clk_enable();   /* 外设时钟使能 */
    led_init();            /* LED灯初始化 */
    key_init();            /* 按键初始化 */

    while (1) {
        key_value = key_get_value();  /* 获取按键状态 */
        if (key_value == KEY0_VALUE) {
            led_state = !led_state;
            led_switch(led_state);
            key_value = KEY_NONE;
        }
        delay(10);
    }

    return 0;
}

在循环里面不断获取按键的状态,也就是获取GPIO引脚的电平状态,如果按键按下,对应的LED灯状态会进行相应的翻转。

 

4、小结

本文主要简单介绍了I.MX6UL嵌入式SoC中的GPIO外设作为输入时,如何进行IO口电平状态的读取,并以一个简单的按键输入实例进行介绍。

posted @ 2020-03-15 22:37  liangliangge  阅读(833)  评论(0编辑  收藏  举报