• 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口电平状态的读取,并以一个简单的按键输入实例进行介绍。

  • 相关阅读:
    优化tableView加载cell与model的过程
    java.net.UnknownHostException: master
    Give root password for maintenance(or type control -D to continue)
    软件自动化部署脚本
    关于yum网络版仓库(本地yum仓库的安装配置,如果没网了,做一个局域网内的yum仓库)
    一脸懵逼学习keepalived(对Nginx进行热备)
    一脸懵逼学习Nginx及其安装,Tomcat的安装
    一脸懵逼学习Linux的Shell编程
    一脸懵逼学习KafKa集群的安装搭建--(一种高吞吐量的分布式发布订阅消息系统)
    一脸懵逼学习Storm的搭建--(一个开源的分布式实时计算系统)
  • 原文地址:https://www.cnblogs.com/Cqlismy/p/12500760.html
Copyright © 2020-2023  润新知