Linux 按鍵輸入實驗

Bathwind_W發表於2024-06-16

Linux 按鍵輸入實驗

1、新增 pinctrl 節點

首先修改在裝置樹裡面新增關於按鍵的節點。I.MX6U-ALPHA 開發板上的 KEY 使用了 UART1_CTS_B 這個 PIN,開啟 imx6ull-alientekemmc.dts,在 iomuxc 節點的 imx6ul-evk 子節點下建立一個名為“pinctrl_key”的子節點,節點內容如下所示:

pinctrl_key: keygrp {
	fsl,pins = <
	MX6UL_PAD_UART1_CTS_B__GPIO1_IO18		0xF080	/* KEY0 */
	>;
};

2、 新增key裝置節點:

在根節點“/”下建立 KEY 節點,節點名為“key”,節點內容如下:

	key {
		#address-cells = <1>;
		#size-cells = <1>;
		compatible = "atkalpha-key";
		pinctrl-names = "default";
		pinctrl-0 = <&pinctrl_key>;
		key-gpio = <&gpio1 18 GPIO_ACTIVE_LOW>; /* KEY0 */
		interrupt-parent = <&gpio1>;
		interrupts = <18 IRQ_TYPE_EDGE_BOTH>; /* FALLING RISING */
		status = "okay";
	};

pinctrl-0 屬性設定 KEY 所使用的 PIN 對應的 pinctrl 節點。key-gpio 屬性指定了 KEY 所使用的 GPIO。

3、檢查 PIN 是否被其他外設使用

在本章實驗中蜂鳴器使用的 PIN 為 UART1_CTS_B,因此先檢查 PIN 為 UART1_CTS_B 這個 PIN 有沒有被其他的 pinctrl 節點使用,如果有使用的話就要遮蔽掉,然後再檢查 GPIO1_IO18這個 GPIO 有沒有被其他外設使用,如果有的話也要遮蔽掉。
按鍵驅動和 LED 驅動原理上來講基本都是一樣的,都是操作 GPIO,只不過一個是讀取GPIO 的高低電平,一個是從 GPIO 輸出高低電平。本章我們實現按鍵輸入,在驅動程式中使用一個整形變數來表示按鍵值,應用程式透過 read 函式來讀取按鍵值,判斷按鍵有沒有按下。
按鍵驅動程式為:

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>

#define KEY_CNT 1      /* 裝置號個數 */
#define KEY_NAME "key" /* 名字 */
/* 定義按鍵值 */
#define KEY0VALUE 0XF0 /* 按鍵值 */
#define INVAKEY 0X00   /* 無效的按鍵值 */
struct key_dev
{
    dev_t devid;            /* 裝置號 */
    struct cdev cdev;       /* cdev */
    struct class *class;    /* 類 */
    struct device *device;  /* 裝置 */
    int major;              /* 主裝置號 */
    int minor;              /* 次裝置號 */
    struct device_node *nd; /* 裝置節點 */
    int key_gpio;           /* led 所使用的 GPIO 編號 */
    atomic_t keyvalue;      /* 原子變數 */
};
struct key_dev keydev; /* led 裝置 */
static int keyio_init(void)
{
    keydev.nd = of_find_node_by_path("/key");
    if (keydev.nd == NULL)
    {
        return -EINVAL;
    }
    keydev.key_gpio = of_get_named_gpio(keydev.nd, "key-gpio", 0);
    if (keydev.key_gpio < 0)
    {
        printk("can't get key0\r\n");
        return -EINVAL;
    }
    printk("key_gpio=%d\r\n", keydev.key_gpio);

    /* 初始化 key 所使用的 IO */
    gpio_request(keydev.key_gpio, "key0"); /* 請求 IO */
    gpio_direction_input(keydev.key_gpio); /* 設定為輸入 */
    return 0;
}
static int key_open(struct inode *inode, struct file *filp)
{
    int ret = 0;
    filp->private_data = &keydev; /* 設定私有資料 */
    ret = keyio_init();            /* 初始化按鍵 IO */
    if (ret < 0)
    {
        return ret;
    }
    return 0;
}
/*
 * @description : 從裝置讀取資料
 * @param – filp : 要開啟的裝置檔案(檔案描述符)
 * @param - buf : 返回給使用者空間的資料緩衝區
 * @param - cnt : 要讀取的資料長度
 * @param – offt : 相對於檔案首地址的偏移
 * @return : 讀取的位元組數,如果為負值,表示讀取失敗
 */
static ssize_t key_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
    int ret = 0;
    unsigned char value;
    struct key_dev *dev = filp->private_data;
    if(gpio_get_value(dev->key_gpio) == 0)
    {
        while(!gpio_get_value(dev->key_gpio));
        atomic_set(&dev->keyvalue,KEY0VALUE);
    }
    else{
        atomic_set(&dev->keyvalue,INVAKEY);
    }
    value = atomic_read(&dev->keyvalue);
    ret = copy_to_user(buf,&value,sizeof(value));
    return ret;
}
/* 裝置操作函式 */
static struct file_operations key_fops = {
    .owner = THIS_MODULE,
    .open = key_open,
    .read = key_read,
};
static int __init mykey_init(void)
{
    int ret = 0;
    atomic_set(&keydev.keyvalue, INVAKEY);
    /*1、建立裝置號*/
    if (keydev.major)
    {
        keydev.devid = MKDEV(keydev.major, 0);
        register_chrdev_region(keydev.devid, KEY_CNT, KEY_NAME);
    }
    else
    {
        alloc_chrdev_region(&keydev.devid, 0, KEY_CNT, KEY_NAME);
        keydev.major = MAJOR(keydev.devid);
        keydev.minor = MINOR(keydev.devid);
    }
    printk("newcheled major: %d minor: %d", keydev.major, keydev.minor);
    keydev.cdev.owner = THIS_MODULE;
    cdev_init(&keydev.cdev, &key_fops);
    cdev_add(&keydev.cdev, keydev.devid, KEY_CNT);
    keydev.class = class_create(THIS_MODULE, KEY_NAME);
    if (IS_ERR(keydev.class))
    {
        return PTR_ERR(keydev.class);
    }
    keydev.device = device_create(keydev.class, NULL, keydev.devid, NULL, KEY_NAME);
    if (IS_ERR(keydev.device))
    {
        return PTR_ERR(keydev.device);
    }
    return 0;
}
static void __exit mykey_exit(void)
{
    /* 登出字元裝置驅動 */
    gpio_free(keydev.key_gpio);
    cdev_del(&keydev.cdev); /* 刪除 cdev */
    unregister_chrdev_region(keydev.devid,KEY_CNT);
    device_destroy(keydev.class, keydev.devid);
    class_destroy(keydev.class);
}
module_init(mykey_init);
module_exit(mykey_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("wyw");

其實按鍵與LED相似的就是一個是讀一個是寫。按鍵則是讀取高低電平。也就是檢測這個GPIO編號時候,利用原子變數寫入key->value這個值。
key_open 函式透過呼叫 keyio_init 函式來始化按鍵所使用的 IO,應用程式每次開啟按鍵驅動檔案的時候都會初始化一次按鍵 IO。key_read 函式,應用程式透過 read 函式讀取按鍵值的時候此函式就會執行。讀取按鍵 IO 的電平,如果為 0 的話就表示按鍵按下了,如果按鍵按下的話就等待按鍵釋放。按鍵釋放以後標記按鍵值為 KEY0VALUE。
測試App程式為:

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"

#define KEY0VALUE 0XF0
#define INVAKEY 0X00
int main(int argc, char *argv[])
{
    int fd, retvalue;
    char *filename;
    unsigned char keyvalue;
    if (argc != 2)
    {
        printf("Error Usage!\r\n");
        return -1;
    }
    filename = argv[1];
    fd = open(filename, O_RDWR);
    if (fd < 0)
    {
        printf("file %s open failed!\r\n", argv[1]);
        return -1;
    }
    while (1)
    {
        read(fd, &keyvalue, sizeof(keyvalue));
        if(keyvalue == KEY0VALUE)
        {
            printf("KEY0 Press, value = %#X\r\n", keyvalue);/* 按下 */
        }
    }
    printf("App running finished!");
    retvalue = close(fd); /* 關閉檔案 */
    if (retvalue < 0)
    {
        printf("file %s close failed!\r\n", argv[1]);
        return -1;
    }
    return 0;
}

現在就是按鍵按下終端輸出如下圖所示的現象:
在這裡插入圖片描述

相關文章