我們用一種更為高級的方法去驅動所有的按鍵————理論上按鍵最多可以驅動768個,但是我的板子只有三個按鍵
所以現在只能驅動三個,但是驅動三個和驅動768個都是一樣的,
首先我們要利用起設備樹檔案了,如果不用設備樹的話,你就得一個一個的去驅動,雖然你可以在一個模塊上完成所有的按鍵驅動,但是要是真的驅動768個按鍵,那么你估計能寫上萬行代碼,你要跟以前的方法一樣,每一個按鍵都要去實體化設備,然后獲取中斷,地址,…這些資訊,然后再處理這些資訊,
但是如果你用了設備樹,我直接在設備樹上寫好這些資訊,到時候你拿的去用就可以了,
并且你直接整一個for回圈,它就自動把768個設備資訊給你全部拿過來了,豈不爽哉!!
那就讓我們來領略一下設備樹的魅力:
————————————————————————————————————————————————
驅動所有按鍵第一步:
撰寫設備樹:
我們之前也撰寫過,這里我們詳細的講一下:(設備樹就是用來存盤設備資訊的)
首先是在根節點下創建了一個屬于我們的設備叫:key_int_node.
我們要驅動三個按鍵,所以在這個我們的設備下面創建了三個子設備,代表我這設備由這三個設備共同組成,
要是你有一千個設備那么你也可以吧一千個設備寫里面組成你的設備,
好讓我們看一下每一個子設備下存盤了上面資訊
```c
在設備樹檔案中設定這幾個元素:
key_int_node{
compatible = "test_key";
#address-cells = <1>;
#size-cells = <1>;
key_int@0 {
key_name = "key2_power_eint"; //表示我這個子設備名字叫....自定義
key_code = <116>; //設定這個子設備的鍵值,就是代表這個鍵是用來干嘛的,
//你可以隨便設你想讓它有上面功能都可以
gpio = <&gpx1 1 0>; //代表這個引腳在電路圖上是屬于gpx1_1引腳,后面可以讀這個引腳的值來獲
//取按鍵有沒有按下,就很方便
reg = <0x11000C20 0x18>; //物理地址
interrupt-parent = <&gpx1>; //表示中斷繼承了gpx1
interrupts = <1 0>; //代表是gpx1_1的中斷,后面根據節點獲取中斷號就是
};
key_int@1 {
key_name = "key3_vup_eint";
key_code = <115>;
gpio = <&gpx1 2 0>;
reg = <0x11000C20 0x18>;
interrupt-parent = <&gpx1>;
interrupts = <2 0>;
};
key_int@2 {
key_name = "key4_vdown_eint";
key_code = <114>;
gpio = <&gpx3 2 0>;
reg = <0x11000C60 0x18>;
interrupt-parent = <&gpx3>;
interrupts = <2 0>;
};
};
好了第一步完成,你可以根據芯片手冊,把所有的按鍵資訊寫進去
——————————————————————————————————————
驅動所有按鍵第二步:
接下來我們就要撰寫設備模塊代碼了
1)首先就是日常搭框架
2)然后就是想辦法從設備樹上把這些我們寫的檔案資訊拿過來,
我們采用了我通用的設備資訊結構體,
然后我用一個陣列去實體化它,那么我只要一個陣列的名字我就可以存盤并且找到所有的設備資訊了,
struct key_desc{
char *name;
int key_code;
int irqno;
int gpio_num;
void *regbase;
struct device_node *cnp;
};
struct key_desc *all_dev[KEY_NOM];
那我們有這樣一個結構體陣列了,那我還是沒有從設備樹上獲取到資訊啊,
別急——我們寫一個函式,直接對著設備樹就是 —拿來吧你
我們需要一個中介去連接我們的設備樹,和用來存盤資訊的結構體:這個中介就是: struct device_node *cnp; 這是一個設備節點在結構體中已經定義了
首先我們像獲取中斷號一樣,從設備樹上獲取到節點,但是這個節點,不是我們要的兒子節點,所有我們得獲取到子節點點,并且,
要一個設備結構體陣列對應一個子節點資訊:
void get_allirqno(void){
//從設備樹上獲取設備節點
struct device_node *np = of_find_node_by_path("/key_int_node");
if(np == NULL){
printk("find_node error\n");
return 0;
}
struct device_node*lcnp; //用來記錄節點的,到時候再賦給每一個的all_dev的cnp
//要獲取的節點的上一個節點,這樣就可以通過上一個節點的位置獲取到我要獲取的節點的位置
struct device_node *prev = NULL;
int i=0;
do{
if(lcnp != NULL){
all_dev[i++].cnp = lcnp;//將當前的節點記錄下來
}
prev = lcnp; //把當前的設定位prev
}while(of_get_next_child(np, prev) != NULL);
}
這樣我們就可以通過設備結構體陣列中的子節點與設備樹建立了連接,
——————————————————————————————————————————————————
接下來是驅動第三步:
完成模塊裝載入口
我們之前在模塊裝載路口做了三件事:
1,分配一個input device物件
2, 初始化input device物件
3,注冊input device物件
我們現在也是做這三件事;
1)實體化一個輸入設備物件,并發分配空間,和部分初始化
struct input_dev *inputdev; //實體化一個輸入設備物件
inputdev = input_allocate_device(void);
2)可以添加設備資訊
3)呼叫我們的獲取資訊的函式————因為我們接下來要用到這些在設備樹上的資訊
4)初始化input device物件
這里需要初始化倆個:
一個是按鍵型別
//初始化inputdevice物件—設為按鍵型別資訊
_set_bit(EV_KEY, inputdev->evbit);
還有就是按鍵的具體資訊:
但是不要忘了,我們有很多很多的按鍵,所以我們要利用我們之前的設備結構體陣列中的子節點
然后利用for回圈,把設備樹上的其它對應資訊讀到對應的設備結構體陣列中去
我們還要
1)設定鍵值具體資訊:所以要拿到對應的鍵值
of_property_read_u32(cnp,“key_code”, &code
2)申請對應中斷:所以要拿到中斷號
irqno = irq_of_parse_and_map(cnp, 0)
3)我們還可以列印設備名字用來區分設備
of_property_read_string(cnp, “key_name”, &key_name);
這樣我們就完成了,設備的初始化以及中斷申請
4)注冊設備:
我們申請了設備(由很多子設備組成的大設備)就要注冊進去,讓內核幫我們匹配
input_register_device(inputdev);
——————————————————————————————
接下來是驅動第三步:
那我們拿到資料了,怎么把按鍵的值傳出去呢?
,我們寫在中斷處理函式里面,但是我有幾百個設備,我只用了一個中斷處理函式我怎么區分你按的是什么中斷?
首先我們在申請中斷的時候就會把,設備資訊結構體指標傳個中斷處理函式,
我們直接可以區分不同的鍵值,
并且我們還可以直接根據設備樹上的gpionum直接去讀引腳的資料判斷,是否被按下————nice
int gpionum = of_get_named_gpio(pdesc->cnp, “gpio”, 0);
//直接通過gpio獲取按鍵狀態
int value = gpio_get_value(gpionum);
——————————————————總的代碼————————————————————
#include <linux/init.h>
#include <linux/module.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_gpio.h>
#include <asm/io.h>
#define KEY_NUMS 3
#define KEY_NOM 3
#define IRQFLAGS IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING
struct input_dev *inputdev; //實體化一個輸入設備物件
struct key_desc{
char *name;
int key_code;
int irqno;
int gpio_num;
void *regbase;
struct device_node *cnp;
};
struct key_desc all_dev[KEY_NOM];
irqreturn_t input_key_irq_handler(int irqno, void *devid){
//區分不同的按鍵
struct key_desc *pdesc = (struct key_desc *)devid;
int gpionum = of_get_named_gpio(pdesc->cnp, "gpio", 0);
//直接通過gpio獲取按鍵狀態
int value = gpio_get_value(gpionum);
if(value){//抬起
input_report_key(inputdev, pdesc->key_code, 0);
input_sync(inputdev);//上報資料結束
}else{
input_report_key(inputdev, pdesc->key_code, 1);
input_sync(inputdev);//上報資料結束
}
return IRQ_HANDLED;
}
void get_allirqno(void){
//從設備樹上獲取設備節點
struct device_node *np = of_find_node_by_path("/key_int_node");
if(np == NULL){
printk("find_node error\n");
return 0;
}
struct device_node*lcnp; //用來記錄節點的,到時候再賦給每一個的all_dev的cnp
//要獲取的節點的上一個節點,這樣就可以通過上一個節點的位置獲取到我要獲取的節點的位置
struct device_node *prev = NULL;
int i=0;
do{
if(lcnp != NULL){
all_dev[i++].cnp = lcnp;//將當前的節點記錄下來
}
prev = lcnp; //把當前的設定位prev
}while(of_get_next_child(np, prev) != NULL);
}
static int __init akey_dev_init(void){
//撰寫輸入子系統代碼
/*
1,分配一個input device物件
2, 初始化input device物件
3,注冊input device物件
*/
//1、給實體化的輸入設備物件分配空間并部分初始化
inputdev = input_allocate_device(void);
if(inputdev == NULL)
{
printk(KERN_ERR "input_allocate_device error\n");
return -ENOMEM;
}
//添加設備資訊/sys/class/input/eventx/device/
//自定義
inputdev->name = "input key";
inputdev->phys = "key/input/input0";
inputdev->uniq = "simple key0 for 4412";
inputdev->id.bustype = BUS_HOST;
inputdev->id.vendor =0x1234 ;
inputdev->id.product = 0x8888;
inputdev->id.version = 0x0001;
get_allirqno();
//初始化inputdevice物件---設為按鍵型別資訊
_set_bit(EV_KEY, inputdev->evbit);
//利用設備樹多個獲取資訊
int i;
for(i=0;i<KEY_NOM;++i){
struct device_node *cnp = all_dev[i].cnp; //這個cnp記錄了每個設備的節點
//獲取鍵值
int code;
of_property_read_u32(cnp,"key_code", &code);
_set_bit(code, inputdev->keybit); //將從設備樹上獲取的鍵值設定位鍵值資料code
all_dev[i].key_code = code; //把code資訊記錄到每個子設備的結構體中
//獲取中斷號
int irqno;
irqno = irq_of_parse_and_map(cnp, 0);
all_dev->irqno = irqno;
//獲取按鍵name
char *key_name ;
of_property_read_string(cnp, "key_name", &key_name);
all_dev[i].name = key_name;
//申請中斷
ret = request_irq(irqno, input_key_irq_handler, irqflags,
key_name, &all_dev[i]);
if(ret != 0)
{
printk("request_irq error\n");
goto err_1;
}
}
//注冊設備
ret = input_register_device(inputdev);
if(ret != 0)
{
printk(KERN_ERR "input_register_device error\n");
goto err_0;
}
return 0;
err_1:
input_unregister_device(inputdev);
err_0:
input_free_device(inputdev);
}
static int __exit akey_dev_exit(void){
int i;
for(i=0; i<KEY_NOM; i++)
free_irq(all_dev[i].irqno, &all_dev[i]);
input_unregister_device(inputdev);
input_free_device(inputdev);
}
module_init(akey_dev_init);
module_exit(akey_dev_exit);
MODULE_LICENSE("GPL");
—————————————————————應用程式代碼———————————————————————
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/input.h>
int main(void)
{
int fd;
int ret;
struct input_event event;
fd = open("/dev/input/event1", O_RDWR);
if(fd < 0)
{
perror("open");
exit(1);
}
while(1)
{
ret = read(fd, &event, sizeof(struct input_event));
if(ret < 0)
{
perror("read");
exit(1);
}
if(event.type == EV_KEY){
switch(event.code){
case KEY_POWER:
if(event.value){ //按下
printf("__APP_USER__ : power pressed\n");
}else{
printf("__APP_USER__ : power up\n");
}
break;
case KEY_VOLUMEDOWN:
if(event.value){ //按下
printf("__APP_USER__ : vollum dowm pressed\n");
}else{
printf("__APP_USER__ : vollum dowm up\n");
}
break;
case KEY_VOLUMEUP:
if(event.value){ //按下
printf("__APP_USER__ : vollum up pressed\n");
}else{
printf("__APP_USER__ : vollum up up\n");
}
break;
default:
printf("error");
}
}
}
close(fd);
return 0;
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/302864.html
標籤:其他
上一篇:python-mqtt自動化測驗
