Linux按鍵驅(qū)動(dòng)測(cè)試方式
一、設(shè)備節(jié)點(diǎn)添加
首先在設(shè)備樹文件中添加pinctrl以及在根目錄下添加設(shè)備節(jié)點(diǎn)。
如下:
//創(chuàng)建按鍵輸入的pinctrl
pinctrl_key: keygrp {
fsl,pins = <
MX6UL_PAD_UART1_CTS_B__GPIO1_IO18 0xF080 /* KEY0 */
>;
};
//創(chuàng)建按鍵節(jié)點(diǎn)
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ū)動(dòng)文件代碼
2.1 核心數(shù)據(jù)結(jié)構(gòu)
定義結(jié)構(gòu)體,其中包含按鍵驅(qū)動(dòng)所需的信息,使用atomic_t類型保證按鍵值的原子操作。
struct key_dev {
dev_t devid; /* 設(shè)備號(hào) */
struct cdev cdev; /* cdev結(jié)構(gòu)體 */
struct class *class; /* 類 */
struct device *device; /* 設(shè)備 */
int major; /* 主設(shè)備號(hào) */
int minor; /* 次設(shè)備號(hào) */
struct device_node *nd; /* 設(shè)備樹節(jié)點(diǎn) */
int key_gpio; /* 按鍵GPIO編號(hào) */
atomic_t keyvalue; /* 按鍵值 */
};2.2 按鍵值定義
驅(qū)動(dòng)中定義了兩個(gè)按鍵狀態(tài):按下(1)和未按下/無效(0)。
#define KEY0VALUE 1 /* 按鍵值 */ #define INVAKEY 0 /* 無效的按鍵值 */
2.3 關(guān)鍵函數(shù)實(shí)現(xiàn)
首先是GPIO初始化:從設(shè)備樹獲取按鍵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"); /* 請(qǐng)求IO */
gpio_direction_input(keydev.key_gpio); /* 設(shè)置為輸入 */
return 0;
}按鍵讀取:驅(qū)動(dòng)會(huì)阻塞等待按鍵釋放后才返回,進(jìn)而實(shí)現(xiàn)了一次完整按鍵周期的檢測(cè)。
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)建測(cè)試文件
在測(cè)試文件中,通過對(duì)字符設(shè)備文件(/dev/key)進(jìn)行標(biāo)準(zhǔn)文件操作實(shí)現(xiàn)與內(nèi)核驅(qū)動(dòng)層的交互。
程序結(jié)構(gòu)包括四個(gè)關(guān)鍵函數(shù):信號(hào)處理函數(shù)sig_handler()、資源清理函數(shù)cleanup_resources()、幫助顯示函數(shù)show_usage()及主函數(shù)main()。
在主函數(shù)中,程序首先檢查命令行參數(shù)格式,注冊(cè)SIGINT信號(hào)處理確??赏ㄟ^Ctrl+C優(yōu)雅退出,然后打開設(shè)備文件獲取文件描述符fd,隨后進(jìn)入核心監(jiān)測(cè)循環(huán),通過read()系統(tǒng)調(diào)用讀取按鍵狀態(tài)并使用前后狀態(tài)比較算法(prev_keyvalue與keyvalue對(duì)比)檢測(cè)按鍵事件邊緣變化,實(shí)時(shí)輸出中文提示信息反饋按鍵狀態(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; /* 程序運(yùn)行標(biāo)志 */
/*
* @description : 信號(hào)處理函數(shù)
* @param - signum : 信號(hào)編號(hào)
* @return : 無
*/
void sig_handler(int signum)
{
if (signum == SIGINT) {
printf("\n程序接收到中斷信號(hào),正在退出...\n");
running = 0;
}
}
/*
* @description : 釋放資源
* @param - filename: 設(shè)備文件名
* @return : 無
*/
void cleanup_resources(const char *filename)
{
if (fd >= 0) {
if (close(fd) < 0) {
printf("文件 %s 關(guān)閉失??!\n", filename);
} else {
printf("已關(guān)閉設(shè)備文件 %s\n", filename);
}
}
}
/*
* @description : 顯示使用幫助
* @param - name : 程序名
* @return : 無
*/
void show_usage(const char *name)
{
printf("使用方法: %s <設(shè)備文件>\n", name);
printf("示例: %s /dev/key\n", name);
}
/*
* @description : main主程序
* @param - argc : argv數(shù)組元素個(gè)數(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ù)錯(cuò)誤!\n");
show_usage(argv[0]);
return -1;
}
filename = argv[1];
/* 注冊(cè)信號(hào)處理函數(shù),捕獲Ctrl+C */
signal(SIGINT, sig_handler);
/* 打開按鍵設(shè)備 */
fd = open(filename, O_RDWR);
if (fd < 0) {
printf("無法打開設(shè)備文件 %s!\n", filename);
return -1;
}
printf("按鍵測(cè)試程序已啟動(dòng)\n");
printf("按下按鍵進(jìn)行測(cè)試,按 Ctrl+C 退出程序\n");
/* 循環(huán)讀取按鍵值數(shù)據(jù) */
while (running) {
if (read(fd, &keyvalue, sizeof(keyvalue)) < 0) {
printf("讀取按鍵數(shù)據(jù)失敗\n");
break;
}
/* 按鍵狀態(tài)變化檢測(cè) */
if (keyvalue == KEY0VALUE && prev_keyvalue != KEY0VALUE) {
printf("按鍵被按下,鍵值 = %d\n", keyvalue);
} else if (keyvalue == INVAKEY && prev_keyvalue == KEY0VALUE) {
printf("按鍵已釋放\n");
}
prev_keyvalue = keyvalue;
usleep(50000); /* 短暫延時(shí),降低CPU占用 */
}
/* 清理資源 */
cleanup_resources(filename);
printf("程序已退出\n");
return 0;
}四、測(cè)試


總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
linux下終端分屏使用的兩種方法(screen和tmux)
這篇文章主要給大家介紹了關(guān)于在linux下終端分屏使用的兩種方法,分別是利用screen和tmux,文中介紹的非常詳細(xì),對(duì)大家具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起看看吧。2017-06-06
LINUX安全設(shè)置之關(guān)于GRUB加密圖文教程全解
關(guān)于LINUX的啟動(dòng)裝載程序GRUB加密,算是一件很平常的工作。但是今天我在網(wǎng)上查這個(gè)東西,發(fā)現(xiàn)好多人都寫的很簡(jiǎn)單,而且方法都比較過時(shí)。所以,在此我更新下GRUB加密。和大家分享下。2010-03-03
深入淺析Linux輕量級(jí)自動(dòng)運(yùn)維工具-Ansible
這篇文章主要介紹了Linux輕量級(jí)自動(dòng)運(yùn)維工具-Ansible的相關(guān)知識(shí),需要的朋友可以參考下2017-09-09

