总结:使用更高级的方法与所有按钮进行交互。 理论上一个按钮最多可以驱动3个按钮,但是我的板子只有3个按钮,所以目前只能驱动3个按钮,但是驱动3个按钮是3个按钮和驱动不一样。 都是一样的。
使用更高级的方法来驱动所有按钮。 理论上,该按钮最多可以驱动768个按钮,但我的板子只有3个按钮,所以现在只能驱动3个。 然而,驾驶 3 与驾驶 768 是一样的。 首先,您需要使用设备树文件。 如果不使用设备树,则必须一一驱动它们。 虽然可以在一个模块中完成所有按钮驱动,但我们估计驱动 768 个按钮将需要数万行代码。 使用与以前相同的方法,每次按下按钮时都需要实例化设备,获取中断、地址等信息,并处理这些信息。 但是,如果您使用设备树,我们会直接将此信息写入设备树,以便您可以检索并使用它。 只需创建一个 for 循环,所有 768 个设备信息就会自动检索。 是不是很美妙? ! 那么让我们了解一下设备树的全部内容。 ———————————————————————————————— —————— 操作所有按钮 第一步:创建设备树:我们之前创建过,这里详细解释一下:(设备树是用来存储设备信息的)首先,在根节点中,创建一个名为 key_int_node 的设备,创建了一个属于的设备。我们。 由于我要操作三个按钮,所以我在设备下创建了三个子设备。 所以该设备由这三个设备组成。 如果你有1000个设备,也可以创建1000个设备组成一个设备。 我们来看看上面每个子设备下存储的信息。
```c 在设备树文件中设置以下元素:key_int_节点 { 兼容性 = "testkey"; #address-cell = ; #size-cell = ; // 子设备名称是....自定义 key_code = ; //设置该子设备的键值来表示该键的预期用途。 //可以任意设置具有以上功能 gpio = ;//原理图中表示该引脚。 这属于 gpx1_1 引脚。 // 非常有用,因为您可以稍后读取该引脚的值来了解按钮是否被按下。 reg = ; // 物理地址interrupt-parent = ; // 表示中断继承gpx1. Interrupts = ;// 代表gpx1_1的中断,稍后根据节点获取中断号。 key_int@1 { key_name = "key3_vup_eint"; GPIO = ; 中断 - ;中断= ; key_int @ 2 { key_name = "key4_vdown_eint"; gpio = ; 中断- ; 中断= OK ; 根据您的芯片手册,在
中写入所有重要信息。 —————————————————————————— ————————— 所有按钮的驱动 步骤2:接下来,添加设备模块代码你需要写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. 分配输入设备对象 2. 初始化输入设备对象 3. 注册输入设备对象 这三件事目前也在做。 1)实例化输入设备对象,同时分配空间,并部分初始化struct input_dev *inputdev。 // 实例化输入设备对象 inputdev = input_allocate_device(2)可以添加设备信息。 3)调用该函数获取信息。 — 然后使用设备树上的信息。
4) 在这里初始化输入设备对象。 您需要初始化两件事。 必须对其进行初始化。 是按键的类型 // 初始化inputdevice对象——设置为按键的类型 information_set_bit(EV_KEY, inputdev->evbit); 另一个是按键的具体信息。 但不要忘记。 由于按钮太多,我们需要利用前面的设备结构体数组的子节点,然后用for循环读取其他对应的信息。 将设备树存储在对应的设备结构体数组中。 另外,1)您需要专门设置键值。 Info:因此,我们需要获取对应的key值 of_property_read_u32(cnp, "key_code", &code. 2) 申请对应的中断。 所以需要获取中断号irqno = irq_of_parse_and_map。 (cnp, 0) 3) 还可以打印设备名称以区分。 Device of_property_read_string(cnp, “key_name”, &key_name);
这样就完成了设备和中断申请的初始化。 4) 设备注册:您申请了一个设备(一个由很多子设备组成的大设备)。 您需要注册它并让内核支持匹配 input_register_device(inputdev)。 —————————————————————————— 接下来是司机。 第 3 步:现在我们有了数据,我们如何将值发送到按钮? ,我会写一下中断处理函数,但是虽然有几百个设备,但只用到了一个中断处理函数。 如何检查按下了哪个中断?首先,在请求中断时,将指向设备信息结构的指针传递给中断处理函数。 可以直接区分不同的键值。 也可以直接根据设备树上的gpionum读取pin数据,判断是否按下。 伟大的整数 gpionum = of_get_named_gpio(pdesc->cnp, “gpio”, 0); // 通过gpio直接获取按键状态 int value = gpio_get_value(gpionum); ————————————————— 总代码—————————————————————
#include #include #include # include #include #include #include #include #define KEY_NUMS 3#define KEY_NOM 3#define IRQFLAGS IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISINGstruct 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 ) //区分不同的key struct key_desc *pdesc = (struct key_desc *)devid; (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){//从Device中获取设备节点tree 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、分配输入设备对象 2、初始化输入设备对象 3、注册输入设备对象*///1、实例化输入设备对象分配空间,部分初始化。 inputdev = input_allocate_device(void); if(inputdev == NULL){printk(KERN_ERR "input_allocate_device 错误/n");return -ENOMEM;}//添加设备信息 /sys/class/input/eventx/device///自定义 inputdev->name = "input key";inputdev->phys = "key/input/input0";inputdev [k4 ] >uniq = "4412 简单密钥 0";inputdev->id.bustype = BUS_HOST;inputdev->id.vendor =0x1234 ;inputdev->id.product = 0x8888;inputdev->id. version = 0x0001;get_allirqno();//初始化输入设备对象---,并将其设置为密钥类型 information_set_bit(EV_KEY, inputdev->evbit);//多个获取信息使用int i的设备树;for(i=0;ikeybit); //将从设备树中检索到的键值设置为键值数据code all_dev[i].key_code = code; //在各个子设备的结构体中记录代码信息 //获取中断号 int irqno;irqno = irq_of_parse_and_map(cnp, 0);all_dev->irqno = irqno;//获取键名 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
评论前必须登录!
注册