日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

Linux按键输入实验(体验一下输入驱动,实际开发使用input子系统处理)

發布時間:2023/12/10 linux 21 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux按键输入实验(体验一下输入驱动,实际开发使用input子系统处理) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

目錄

  • Linux下按鍵驅動原理(使用原子操作)
  • 硬件原理圖分析
  • 實驗程序編寫
    • 修改設備樹文件
    • 按鍵驅動程序編寫
    • 編寫測試APP(循環讀取按鍵值)
  • 運行測試
    • 編譯驅動程序和測試APP
    • 運行測試(while循環導致CPU占用率很高,后面使用阻塞非阻塞IO處理)

在前幾章我們都是使用的GPIO 輸出功能,還沒有用過GPIO 輸入功能,本章我們就來學習一下如果在Linux 下編寫GPIO 輸入驅動程序。I.MX6U-ALPHA 開發板上有一個按鍵,我們就使用此按鍵來完成GPIO 輸入驅動程序,同時利用第四十七章講的原子操作來對按鍵值進行保護。

Linux下按鍵驅動原理(使用原子操作)

按鍵驅動和LED 驅動原理上來講基本都是一樣的,都是操作GPIO,只不過一個是讀取GPIO 的高低電平,一個是從GPIO 輸出高低電平。本章我們實現按鍵輸入,在驅動程序中使用一個整形變量來表示按鍵值,應用程序通過read 函數來讀取按鍵值,判斷按鍵有沒有按下。在這里,這個保存按鍵值的變量就是個共享資源,驅動程序要向其寫入按鍵值,應用程序要讀取按鍵值。所以我們要對其進行保護,對于整形變量而言我們首選的就是原子操作,使用原子操作對變量進行賦值以及讀取。Linux 下的按鍵驅動原理很簡單,接下來開始編寫驅動。

注意,本章例程只是為了演示Linux 下GPIO 輸入驅動的編寫,實際中的按鍵驅動并不會采用本章中所講解的方法,Linux 下的input 子系統專門用于輸入設備!

硬件原理圖分析

本章實驗硬件原理圖參考15.2 小節即可。

實驗程序編寫

本實驗對應的例程路徑為:開發板光盤-> 2、Linux 驅動例程-> 11_key。

修改設備樹文件

1、添加pinctrl 節點

I.MX6U-ALPHA 開發板上的KEY 使用了UART1_CTS_B 這個PIN,打開imx6ull-alientek-emmc.dts,在iomuxc 節點的imx6ul-evk 子節點下創建一個名為“pinctrl_key”的子節點,節點內容如下所示:

1 pinctrl_key: keygrp { 2 fsl,pins = < 3 MX6UL_PAD_UART1_CTS_B__GPIO1_IO18 0xF080 /* KEY0 */ 4 >; 5 };

第3 行,將GPIO_IO18 這個PIN 復用為GPIO1_IO18,電氣屬性為0xF080。

2、添加KEY 設備節點

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

1 key { 2 #address-cells = <1>; 3 #size-cells = <1>; 4 compatible = "atkalpha-key"; 5 pinctrl-names = "default"; 6 pinctrl-0 = <&pinctrl_key>; 7 key-gpio = <&gpio1 18 GPIO_ACTIVE_LOW>; /* KEY0 */ 8 status = "okay"; 9 };

第6 行,pinctrl-0 屬性設置KEY 所使用的PIN 對應的pinctrl 節點。
第7 行,key-gpio 屬性指定了KEY 所使用的GPIO。

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

在本章實驗中蜂鳴器使用的PIN 為UART1_CTS_B,因此先檢查PIN 為UART1_CTS_B 這個PIN 有沒有被其他的pinctrl 節點使用,如果有使用的話就要屏蔽掉,然后再檢查GPIO1_IO18這個GPIO 有沒有被其他外設使用,如果有的話也要屏蔽掉。

設備樹編寫完成以后使用“make dtbs”命令重新編譯設備樹,然后使用新編譯出來的imx6ull-alientek-emmc.dtb 文件啟動Linux 系統。啟動成功以后進入“/proc/device-tree”目錄中查看“key”節點是否存在,如果存在的話就說明設備樹基本修改成功(具體還要驅動驗證),結果如圖49.3.1.1 所示:

按鍵驅動程序編寫

設備樹準備好以后就可以編寫驅動程序了,新建名為“11_key”的文件夾,然后在11_key文件夾里面創建vscode 工程,工作區命名為“key”。工程創建好以后新建key.c 文件,在key.c里面輸入如下內容:

#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> /*************************************************************** Copyright ? ALIENTEK Co., Ltd. 1998-2029. All rights reserved. 文件名 : key.c 作者 : 左忠凱 版本 : V1.0 描述 : Linux按鍵輸入驅動實驗 其他 : 無 論壇 : www.openedv.com 日志 : 初版V1.0 2019/7/18 左忠凱創建 ***************************************************************/ #define KEY_CNT 1 /* 設備號個數 */ #define KEY_NAME "key" /* 名字 *//* 定義按鍵值 */ #define KEY0VALUE 0XF0 /* 按鍵值 */ #define INVAKEY 0X00 /* 無效的按鍵值 *//* key設備結構體 */ 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; /* key所使用的GPIO編號 */atomic_t keyvalue; /* 按鍵值 */ };struct key_dev keydev; /* key設備 *//** @description : 初始化按鍵IO,open函數打開驅動的時候* 初始化按鍵所使用的GPIO引腳。* @param : 無* @return : 無*/ 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);//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; }/** @description : 打開設備* @param - inode : 傳遞給驅動的inode* @param - filp : 設備文件,file結構體有個叫做private_data的成員變量* 一般在open的時候將private_data指向設備結構體。* @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;int value;struct key_dev *dev = filp->private_data;if (gpio_get_value(dev->key_gpio) == 0) { /* key0按下 */while(!gpio_get_value(dev->key_gpio)); /* 等待按鍵釋放 */atomic_set(&dev->keyvalue, KEY0VALUE); //設置按鍵值為0} else { atomic_set(&dev->keyvalue, INVAKEY); /* 無效的按鍵值 */}value = atomic_read(&dev->keyvalue);ret = copy_to_user(buf, &value, sizeof(value));//發送給上層應用return ret; }/** @description : 向設備寫數據 * @param - filp : 設備文件,表示打開的文件描述符* @param - buf : 要寫給設備寫入的數據* @param - cnt : 要寫入的數據長度* @param - offt : 相對于文件首地址的偏移* @return : 寫入的字節數,如果為負值,表示寫入失敗*/ static ssize_t key_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt) {return 0; }/** @description : 關閉/釋放設備* @param - filp : 要關閉的設備文件(文件描述符)* @return : 0 成功;其他 失敗*/ static int key_release(struct inode *inode, struct file *filp) {return 0; }/* 設備操作函數 */ static struct file_operations key_fops = {.owner = THIS_MODULE,.open = key_open,.read = key_read,.write = key_write,.release = key_release, };/** @description : 驅動入口函數* @param : 無* @return : 無*/ static int __init mykey_init(void) {/* 初始化原子變量 */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); /* 獲取分配號的次設備號 */}/* 2、初始化cdev */keydev.cdev.owner = THIS_MODULE;cdev_init(&keydev.cdev, &key_fops);/* 3、添加一個cdev */cdev_add(&keydev.cdev, keydev.devid, KEY_CNT);/* 4、創建類 */keydev.class = class_create(THIS_MODULE, KEY_NAME);if (IS_ERR(keydev.class)) {return PTR_ERR(keydev.class);}/* 5、創建設備 */keydev.device = device_create(keydev.class, NULL, keydev.devid, NULL, KEY_NAME);if (IS_ERR(keydev.device)) {return PTR_ERR(keydev.device);}return 0; }/** @description : 驅動出口函數* @param : 無* @return : 無*/ static void __exit mykey_exit(void) {/* 注銷字符設備驅動 */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("zuozhongkai");

第36~46 行,結構體key_dev 為按鍵的設備結構體,第45 行的原子變量keyvalue 用于記錄按鍵值。
第56~74 行,函數keyio_init 用于初始化按鍵,從設備樹中獲取按鍵的gpio 信息,然后設置為輸入。將按鍵的初始化代碼提取出來,將其作為獨立的一個函數有利于提高程序的模塊化設計。
第83~94 行,key_open 函數通過調用keyio_init 函數來始化按鍵所使用的IO,應用程序每次打開按鍵驅動文件的時候都會初始化一次按鍵IO。
第104~120 行,key_read 函數,應用程序通過read 函數讀取按鍵值的時候此函數就會執行。第110 行讀取按鍵IO 的電平,如果為0 的話就表示按鍵按下了,如果按鍵按下的話第111 行就等待按鍵釋放。按鍵釋放以后標記按鍵值為KEY0VALUE。
第135~171 行,驅動入口函數,第138 行調用atomic_set 函數初始化原子變量默認為無效值。
第178~186 行,驅動出口函數。

key.c 文件代碼很簡單,重點就是key_read 函數讀取按鍵值,要對keyvalue 進行保護。

編寫測試APP(循環讀取按鍵值)

新建名為keyApp.c 的文件,然后輸入如下所示內容:

#include "stdio.h" #include "unistd.h" #include "sys/types.h" #include "sys/stat.h" #include "fcntl.h" #include "stdlib.h" #include "string.h" /*************************************************************** Copyright ? ALIENTEK Co., Ltd. 1998-2029. All rights reserved. 文件名 : keyApp.c 作者 : 左忠凱 版本 : V1.0 描述 : 按鍵輸入測試應用程序 其他 : 無 使用方法 :./keyApp /dev/key 論壇 : www.openedv.com 日志 : 初版V1.0 2019/1/30 左忠凱創建 ***************************************************************//* 定義按鍵值 */ #define KEY0VALUE 0XF0 #define INVAKEY 0X00/** @description : main主程序* @param - argc : argv數組元素個數* @param - argv : 具體參數* @return : 0 成功;其他 失敗*/ int main(int argc, char *argv[]) {int fd, ret;char *filename;int keyvalue;if(argc != 2){printf("Error Usage!\r\n");return -1;}filename = argv[1];/* 打開key驅動 */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) { /* KEY0 */printf("KEY0 Press, value = %#X\r\n", keyvalue); /* 按下 */}}ret= close(fd); /* 關閉文件 */if(ret < 0){printf("file %s close failed!\r\n", argv[1]);return -1;}return 0; }

第51~56 行,循環讀取/dev/key 文件,也就是循環讀取按鍵值,并且將按鍵值打印出來。

運行測試

編譯驅動程序和測試APP

1、編譯驅動程序
編寫Makefile 文件,本章實驗的Makefile 文件和第四十章實驗基本一樣,只是將obj-m 變量的值改為key.o,Makefile 內容如下所示:

KERNELDIR := /home/zuozhongkai/linux/IMX6ULL/linux/temp/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek CURRENT_PATH := $(shell pwd)obj-m := key.obuild: kernel_moduleskernel_modules:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modulesclean:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

第4 行,設置obj-m 變量的值為key.o。
輸入如下命令編譯出驅動模塊文件:

make -j32

編譯成功以后就會生成一個名為“key.ko”的驅動模塊文件。
2、編譯測試APP
輸入如下命令編譯測試keyApp.c 這個測試程序:

arm-linux-gnueabihf-gcc keyApp.c -o keyApp

編譯成功以后就會生成keyApp 這個應用程序。

運行測試(while循環導致CPU占用率很高,后面使用阻塞非阻塞IO處理)

將上一小節編譯出來的key.ko 和keyApp 這兩個文件拷貝到rootfs/lib/modules/4.1.15 目錄中,重啟開發板,進入到目錄lib/modules/4.1.15 中,輸入如下命令加載key.ko 驅動模塊:

depmod //第一次加載驅動的時候需要運行此命令 modprobe key.ko //加載驅動

驅動加載成功以后如下命令來測試:

./keyApp /dev/key

輸入上述命令以后終端顯示如圖49.4.2.1 所示:

按下開發板上的KEY0 按鍵,keyApp 就會獲取并且輸出按鍵信息,如圖49.4.2.2 所示:

從圖49.4.2.2 可以看出,當我們按下KEY0 以后就會打印出“KEY0 Press, value = 0XF0”,表示按鍵按下。但是大家可能會發現,有時候按下一次KEY0 但是會輸出好幾行“KEY0 Press, value = 0XF0”,這是因為我們的代碼沒有做按鍵消抖處理。如果要卸載驅動的話輸入如下命令即可:

rmmod key.ko

總結

以上是生活随笔為你收集整理的Linux按键输入实验(体验一下输入驱动,实际开发使用input子系统处理)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 香蕉视频在线看 | 成人性生交大片免费看vrv66 | 欧美成人精品一区二区综合免费 | 99re这里只有精品在线 | 成年人视频免费 | 国产丰满麻豆 | 国产精品无码一区二区三区在线看 | 96在线观看 | 我想看毛片 | 久久久久久久久久成人 | 亚洲色图15p| 外国一级片 | 亚洲 欧美 激情 另类 | 日韩熟女一区二区 | 日韩高清三区 | 欧美日韩在线视频播放 | 经典一区二区 | 久久久久一区二区三区四区 | 国产a级黄色 | 亚洲成人动漫在线观看 | 日本综合久久 | 三级毛毛片 | 天天色天天操天天 | 日本亚洲色大成网站www久久 | 黄色av网站免费观看 | 亚洲综合免费视频 | 精品在线观看视频 | 久久久伦理片 | 久久视频在线观看 | 欧美色图久久 | 中文字幕成人网 | 欧美另类tv| 青青草国产在线播放 | 久久久久国产精品国产 | 国产喷水福利在线视频 | 亚洲高清网站 | 你懂的在线观看视频 | 五月激情在线观看 | 日批视频免费在线观看 | 婷婷的五月天 | 无码人妻av一区二区三区波多野 | 超碰在线免费播放 | 中国一级片在线观看 | 欧美一区2区 | 天天综合欧美 | 在线爽| 蜜臀av一区二区三区激情综合 | 国产最新在线视频 | 性歌舞团一区二区三区视频 | 91精品婷婷国产综合久久 | 99亚洲欲妇 | 丰满岳跪趴高撅肥臀尤物在线观看 | 婷婷国产成人精品视频 | 亚洲激情成人 | 欧美精品毛片 | 国产嫩草视频 | 欧美日韩激情视频在线观看 | 成人免费在线网站 | 国产色自拍 | 蜜臀精品一区二区三区 | 欧美精品一区二区蜜桃 | 97色伦影院 | 国产精品午夜视频 | 五月婷视频 | 中文在线а√天堂官网 | 欧美久久久久久又粗又大 | 日本视频不卡 | 日韩一级片av | 操小妞| 99久热在线精品996热是什么 | 免费人成 | 波多野结衣精品 | 中文字幕在线观看第一页 | 私密视频在线观看 | 五月天色视频 | 精品在线视频观看 | h片在线免费看 | 影音先锋在线观看视频 | 日韩在线视频观看 | 操在线视频| 欧美人成在线视频 | 久久久av一区二区三区 | 成年在线观看 | 日本免费无人高清 | 爱情岛论坛自拍亚洲品质极速最新章 | 18色av| 一本色道久久综合亚洲精品 | 91丨国产丨白丝 | 最近中文字幕在线中文视频 | 精品国模一区二区三区 | 欧美视频在线观看一区二区 | 日本黄视频在线观看 | 亚洲一区二区三区四区在线播放 | 欧美视频日韩视频 | 99久国产| 黄色大片免费观看视频 | 免费视频一二三区 | 在线岛国 | 久啪视频 |