Linux按鍵驅(qū)動測試方式
一、設備節(jié)點添加
首先在設備樹文件中添加pinctrl以及在根目錄下添加設備節(jié)點。
如下:
//創(chuàng)建按鍵輸入的pinctrl pinctrl_key: keygrp { fsl,pins = < MX6UL_PAD_UART1_CTS_B__GPIO1_IO18 0xF080 /* KEY0 */ >; }; //創(chuàng)建按鍵節(jié)點 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 */ status = "okay"; }; };
二、創(chuàng)建驅(qū)動文件代碼
2.1 核心數(shù)據(jù)結構
定義結構體,其中包含按鍵驅(qū)動所需的信息,使用atomic_t類型保證按鍵值的原子操作。
struct key_dev { dev_t devid; /* 設備號 */ struct cdev cdev; /* cdev結構體 */ struct class *class; /* 類 */ struct device *device; /* 設備 */ int major; /* 主設備號 */ int minor; /* 次設備號 */ struct device_node *nd; /* 設備樹節(jié)點 */ int key_gpio; /* 按鍵GPIO編號 */ atomic_t keyvalue; /* 按鍵值 */ };
2.2 按鍵值定義
驅(qū)動中定義了兩個按鍵狀態(tài):按下(1)和未按下/無效(0)。
#define KEY0VALUE 1 /* 按鍵值 */ #define INVAKEY 0 /* 無效的按鍵值 */
2.3 關鍵函數(shù)實現(xiàn)
首先是GPIO初始化:從設備樹獲取按鍵GPIO信息,并配置為輸入
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; }
按鍵讀?。候?qū)動會阻塞等待按鍵釋放后才返回,進而實現(xiàn)了一次完整按鍵周期的檢測。
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); } else { atomic_set(&dev->keyvalue, INVAKEY); /* 無效的按鍵值 */ } value = atomic_read(&dev->keyvalue); ret = copy_to_user(buf, &value, sizeof(value)); return ret; }
三、創(chuàng)建測試文件
在測試文件中,通過對字符設備文件(/dev/key)進行標準文件操作實現(xiàn)與內(nèi)核驅(qū)動層的交互。
程序結構包括四個關鍵函數(shù):信號處理函數(shù)sig_handler()、資源清理函數(shù)cleanup_resources()、幫助顯示函數(shù)show_usage()及主函數(shù)main()。
在主函數(shù)中,程序首先檢查命令行參數(shù)格式,注冊SIGINT信號處理確??赏ㄟ^Ctrl+C優(yōu)雅退出,然后打開設備文件獲取文件描述符fd,隨后進入核心監(jiān)測循環(huán),通過read()系統(tǒng)調(diào)用讀取按鍵狀態(tài)并使用前后狀態(tài)比較算法(prev_keyvalue與keyvalue對比)檢測按鍵事件邊緣變化,實時輸出中文提示信息反饋按鍵狀態(tài)。
#include "stdio.h" #include "unistd.h" #include "sys/types.h" #include "sys/stat.h" #include "fcntl.h" #include "stdlib.h" #include "string.h" #include "signal.h" /* 定義按鍵值 */ #define KEY0VALUE 1 #define INVAKEY 0 /* 全局變量 */ static int fd = -1; /* 文件描述符 */ static int running = 1; /* 程序運行標志 */ /* * @description : 信號處理函數(shù) * @param - signum : 信號編號 * @return : 無 */ void sig_handler(int signum) { if (signum == SIGINT) { printf("\n程序接收到中斷信號,正在退出...\n"); running = 0; } } /* * @description : 釋放資源 * @param - filename: 設備文件名 * @return : 無 */ void cleanup_resources(const char *filename) { if (fd >= 0) { if (close(fd) < 0) { printf("文件 %s 關閉失?。n", filename); } else { printf("已關閉設備文件 %s\n", filename); } } } /* * @description : 顯示使用幫助 * @param - name : 程序名 * @return : 無 */ void show_usage(const char *name) { printf("使用方法: %s <設備文件>\n", name); printf("示例: %s /dev/key\n", name); } /* * @description : main主程序 * @param - argc : argv數(shù)組元素個數(shù) * @param - argv : 具體參數(shù) * @return : 0 成功;其他 失敗 */ int main(int argc, char *argv[]) { char *filename; int keyvalue; int prev_keyvalue = INVAKEY; /* 參數(shù)檢查 */ if (argc != 2) { printf("參數(shù)錯誤!\n"); show_usage(argv[0]); return -1; } filename = argv[1]; /* 注冊信號處理函數(shù),捕獲Ctrl+C */ signal(SIGINT, sig_handler); /* 打開按鍵設備 */ fd = open(filename, O_RDWR); if (fd < 0) { printf("無法打開設備文件 %s!\n", filename); return -1; } printf("按鍵測試程序已啟動\n"); printf("按下按鍵進行測試,按 Ctrl+C 退出程序\n"); /* 循環(huán)讀取按鍵值數(shù)據(jù) */ while (running) { if (read(fd, &keyvalue, sizeof(keyvalue)) < 0) { printf("讀取按鍵數(shù)據(jù)失敗\n"); break; } /* 按鍵狀態(tài)變化檢測 */ if (keyvalue == KEY0VALUE && prev_keyvalue != KEY0VALUE) { printf("按鍵被按下,鍵值 = %d\n", keyvalue); } else if (keyvalue == INVAKEY && prev_keyvalue == KEY0VALUE) { printf("按鍵已釋放\n"); } prev_keyvalue = keyvalue; usleep(50000); /* 短暫延時,降低CPU占用 */ } /* 清理資源 */ cleanup_resources(filename); printf("程序已退出\n"); return 0; }
四、測試
總結
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
linux下終端分屏使用的兩種方法(screen和tmux)
這篇文章主要給大家介紹了關于在linux下終端分屏使用的兩種方法,分別是利用screen和tmux,文中介紹的非常詳細,對大家具有一定的參考學習價值,需要的朋友們下面來一起看看吧。2017-06-06