• 正文
    • 1 硬件介紹
    • 2 軟件編寫
    • 3 實(shí)驗(yàn)測(cè)試
    • 3.2 測(cè)試
    • 4 總結(jié)
  • 相關(guān)推薦
申請(qǐng)入駐 產(chǎn)業(yè)圖譜

【i.MX6ULL】驅(qū)動(dòng)開發(fā)7——按鍵輸入捕獲

03/03 13:42
124
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點(diǎn)資訊討論

前面幾篇文章,從最基礎(chǔ)的寄存器點(diǎn)燈,到設(shè)備樹點(diǎn)燈,再到GPIO子系統(tǒng)點(diǎn)燈,一步步了解嵌入式Linux開發(fā)的各種點(diǎn)燈原理。

點(diǎn)燈用到的都是GPIO的輸出功能,這篇,通過按鍵的使用,來學(xué)習(xí)GPIO輸入功能的使用。

1 硬件介紹

1.1 板子上按鍵原理圖

先來看原理圖,我板子上有4個(gè)按鍵sw1~sw4:

1.1.1 SW1

SW1是板子的系統(tǒng)復(fù)位按鍵,不可編程使用

1.1.2 SW2、SW3

SW2:SNVS_TAMPER1,GPIO5_1

平時(shí)是低電平,按下去是高電平。

SW3:ONOFF

它也是系統(tǒng)級(jí)的按鍵,用于長(zhǎng)按進(jìn)行開關(guān)機(jī)。

1.1.3 SW4

SW4是BOOT_MODE1腳,用來進(jìn)行串行燒錄模式切換,需要再與復(fù)位鍵配合使用。

本篇僅測(cè)試按鍵功能,因此可以該按鍵。

1.1.4 使用其中2個(gè)按鍵

板子上這4個(gè)按鍵的功能特性如下表:

本實(shí)驗(yàn)使用SW2和SW4這兩個(gè)按鍵來進(jìn)行實(shí)驗(yàn)。

2 軟件編寫

2.1 修改設(shè)備樹文件

2.1.1 修改iomuxc節(jié)點(diǎn)

修改imx6ull-myboard.dts,在iomuxc節(jié)點(diǎn)的imx6ull-evk字節(jié)點(diǎn)下創(chuàng)建一個(gè)名為pinctrl_key的子節(jié)點(diǎn),節(jié)點(diǎn)內(nèi)容如下:

pinctrl_key: keygrp { 
    fsl,pins = < 
        MX6ULL_PAD_SNVS_TAMPER1__GPIO5_IO01    0x3080 /* SW2 */
        MX6ULL_PAD_BOOT_MODE1__GPIO5_IO11      0xF080 /* SW4 */
    >; 
};

這部分是對(duì)引腳進(jìn)行配置,這兩個(gè)引腳的定義是在imx6ull-pinfunc-snvs.h文件中:

引腳宏定義后面的值,是對(duì)引腳功能的配置:

SW2:0x3080,即0011 0000 1000 0000

SW4:0xF080,即1000 0000 1000 0000

對(duì)照之前講解GPIO的PAD寄存器的配置,根據(jù)兩個(gè)按鍵的實(shí)際電路配置上拉或下拉。

/*
*bit 16:0 HYS關(guān)閉
*bit [15:14]: [00]下拉 [01]47k上拉 [10]100k上拉 [11]22k上拉 <---
*bit [13]: [0]kepper功能 [1]pull功能
*bit [12]: [0]pull/keeper-disable [1]pull/keeper-enable
*bit [11]: 0 關(guān)閉開路輸出
*bit [10:8]: 00 保留值
*bit [7:6]: 10 速度100Mhz
*bit [5:3]: 000 輸出disable <---
*bit [2:1]: 00 保留值
*bit [0]: 0 低轉(zhuǎn)換率
*/

注:SW4 (MX6ULL_PAD_BOOT_MODE1__GPIO5_IO11)這個(gè)GPIO,在設(shè)備中實(shí)際已經(jīng)被其它設(shè)備(spi4)使用了。

在imx6ull-myboard.dts的300多行處,有:

pinctrl_spi4: spi4grp {
 fsl,pins = <
     MX6ULL_PAD_BOOT_MODE0__GPIO5_IO10        0x70a1
     MX6ULL_PAD_BOOT_MODE1__GPIO5_IO11        0x70a1
     MX6ULL_PAD_SNVS_TAMPER7__GPIO5_IO07      0x70a1
     MX6ULL_PAD_SNVS_TAMPER8__GPIO5_IO08      0x80000000
     >;
};

理論上我們應(yīng)該把這里的配置給注釋掉,因?yàn)?個(gè)IO是不能同時(shí)進(jìn)行2種功能的。由于本次實(shí)驗(yàn)不使用spi4,暫且也先不管它,看看會(huì)有什么影響,如果影響了本實(shí)驗(yàn),再給把這里的配置給注掉。

2.1.2 添加key節(jié)點(diǎn)

在根節(jié)點(diǎn)下創(chuàng)建名為key的按鍵節(jié)點(diǎn),內(nèi)容如下:

key { 
    #address-cells = <1>; 
    #size-cells = <1>; 
    compatible = "myboard-key"; 
    pinctrl-names = "default"; 
    pinctrl-0 = <&pinctrl_key>; 
    key1-gpio = <&gpio5 1 GPIO_ACTIVE_HIGH>;   /* SW2 */
    key2-gpio = <&gpio5 11 GPIO_ACTIVE_LOW>;   /* SW4 */
    status = "okay"; 
};

2.2 編寫按鍵驅(qū)動(dòng)程序

按鍵驅(qū)動(dòng),也屬于字符設(shè)備驅(qū)動(dòng),和之前的字符設(shè)備驅(qū)動(dòng)的框架一樣,主要的修改點(diǎn)在按鍵的硬件初始化配置已經(jīng)按鍵的讀取。

新建一個(gè)key-Bsp.c

2.2.1 按鍵的硬件初始化

初始化的流程,就是使用OF函數(shù)來從設(shè)備樹中獲取key節(jié)點(diǎn),然后使用GPIO子系統(tǒng)的API函數(shù),將GPIO配置為輸入。

static int keyio_init(void)
{
    keydev.nd = of_find_node_by_path("/key");
    if (keydev.nd== NULL) 
    {
        return -EINVAL;
    }

    keydev.key1_gpio = of_get_named_gpio(keydev.nd ,"key1-gpio", 0);
    keydev.key2_gpio = of_get_named_gpio(keydev.nd ,"key2-gpio", 0);
    if ((keydev.key1_gpio < 0)||(keydev.key2_gpio < 0))
    {
        printk("can't get keyrn");
        return -EINVAL;
    }
    printk("key1_gpio=%d, key2_gpio=%drn", keydev.key1_gpio, keydev.key2_gpio);

    /* 初始化key所使用的IO */
    gpio_request(keydev.key1_gpio, "key1");    /* 請(qǐng)求IO */
    gpio_request(keydev.key2_gpio, "key2");    /* 請(qǐng)求IO */
    gpio_direction_input(keydev.key1_gpio);    /* 設(shè)置為輸入 */
    gpio_direction_input(keydev.key2_gpio);    /* 設(shè)置為輸入 */
    return 0;
}

2.2.2 讀取按鍵的值

讀取按鍵的值,也是GPIO子系統(tǒng)的API函數(shù)來讀取。讀取到按鍵的值后,將該值傳遞出來給應(yīng)用層使用,注意這里使用了原子操作的方式atomic_set和atomic_read實(shí)現(xiàn)數(shù)據(jù)的寫入和讀取。

/* 定義按鍵值 */
#define KEY1VALUE      0X01    /* 按鍵值       */
#define KEY2VALUE      0X02    /* 按鍵值       */
#define INVAKEY        0X00    /* 無效的按鍵值 */

static ssize_t key_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
    int ret = 0;
    int value;
    struct key_dev *dev = filp->private_data;

    if (gpio_get_value(dev->key1_gpio) == 1)         /* key1按下 */
    {
        printk("get key1: highrn");
        while(gpio_get_value(dev->key1_gpio));       /* 等待按鍵釋放 */
        atomic_set(&dev->keyvalue, KEY1VALUE);
    }
    else if (gpio_get_value(dev->key2_gpio) == 0)    /* key2按下 */
    {
        printk("get key2: lowrn");
        while(!gpio_get_value(dev->key2_gpio));      /* 等待按鍵釋放 */
        atomic_set(&dev->keyvalue, KEY2VALUE);
    }
    else
    {
        atomic_set(&dev->keyvalue, INVAKEY);        /* 無效的按鍵值 */
    }

    value = atomic_read(&dev->keyvalue);
    ret = copy_to_user(buf, &value, sizeof(value));
    return ret;
}

2.3 編寫按鍵應(yīng)用程序

新建一個(gè)key-App.c

按鍵的應(yīng)用層程序,主要就通過驅(qū)動(dòng)程序提供的按鍵讀取接口,來循環(huán)讀取按鍵的值,并在按鍵按下時(shí),將按鍵的值打印出來。

/* 定義按鍵值 */
#define KEY1VALUE   0X01
#define KEY2VALUE   0X02
#define INVAKEY     0X00

int main(int argc, char *argv[])
{
    int fd, ret;
    char *filename;
    int keyvalue;

    if(argc != 2)
    {
        printf("Error Usage!rn");
        return -1;
    }

    filename = argv[1];

    /* 打開key驅(qū)動(dòng) */
    fd = open(filename, O_RDWR);
    if(fd < 0)
    {
        printf("file %s open failed!rn", argv[1]);
        return -1;
    }

    /* 循環(huán)讀取按鍵值數(shù)據(jù)!*/
    while(1)
    {
        read(fd, &keyvalue, sizeof(keyvalue));
        if (keyvalue == KEY1VALUE)
        {
            printf("KEY1 Press, value = %#Xrn", keyvalue);
        }
        else if (keyvalue == KEY2VALUE)
        {
            printf("KEY2 Press, value = %#Xrn", keyvalue);
        }
    }

    ret= close(fd); /* 關(guān)閉文件 */
    if(ret < 0)
    {
        printf("file %s close failed!rn", argv[1]);
        return -1;
    }
    return 0;
}

3 實(shí)驗(yàn)測(cè)試

3.1 編譯程序

3.1.1 編譯設(shè)備樹

編譯設(shè)備樹文件,并將編譯出的dtb文件復(fù)制到啟動(dòng)文件夾:

網(wǎng)絡(luò)方式啟動(dòng)開發(fā)板,查看key節(jié)點(diǎn):

3.1.2 編譯按鍵驅(qū)動(dòng)程序

3.1.3 編譯按鍵應(yīng)用程序

3.2 測(cè)試

3.3 查看CPU占用率

先Ctrl+C結(jié)束掉此按鍵進(jìn)程,然后使用如下指令來后臺(tái)運(yùn)行按鍵程序:

./key-App /dev/key &

然后再使用指令:

top

來查看CPU是使用情況。從下圖可以看出,此時(shí)CPU的使用率是99.8%,全被按鍵檢查程序占用了,因?yàn)榘存I程序中有個(gè)while循環(huán)在一直讀取按鍵的值。

使用指令:

ps

查看按鍵的進(jìn)程號(hào),如下圖為149,再使用:

kill -9 149

來殺掉按鍵進(jìn)程,然后再使用top指令查看,可以看到CPU的使用率又變回了0。

實(shí)際的按鍵使用中,一般不會(huì)使用本篇的這種持續(xù)檢測(cè)導(dǎo)致CPU占滿的方式,本篇只是先來介紹GPIO的輸入功能的使用,后續(xù)會(huì)使用更加高效的按鍵檢測(cè)機(jī)制來實(shí)現(xiàn)按鍵檢測(cè)功能。

4 總結(jié)

本篇主要介紹了i.MX6ULL的按鍵檢測(cè)的使用,主要的知識(shí)點(diǎn)是設(shè)備樹的修改,以及GPIO的輸入配置與高低電平的讀取。

相關(guān)推薦

登錄即可解鎖
  • 海量技術(shù)文章
  • 設(shè)計(jì)資源下載
  • 產(chǎn)業(yè)鏈客戶資源
  • 寫文章/發(fā)需求
立即登錄