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口電平狀態的讀取,並以一個簡單的按鍵輸入實例進行介紹。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM