使用gpio子系统和pinctrl子系统实现按键的操作
步骤一:看硬件原理图,获知按键用到的IO口。由以下两图可知key0用的是UART_CTS
步骤二:查imx6ull的参考手册,获取寄存器相关信息。
第一张图显示的寄存器设置的IO复用的信息,也就是将IO复用为GPIO1_IO18,对应的寄存器的值为0x05.
下边的图设置的是GPIO1_IO18的电器属性,如:上拉、下拉、开漏、频率。。。。对应寄存器的值为0xF080.
步骤三:设备树配置。
上图中MX6UL_PAD_UART1_CTS_B__GPIO1_IO18是一个宏定义,
!!!要注意驱动里边注释的问题,分配内存的地方,用完记得释放
key_drv.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>
#define KEYVALUE 0XF0 //按键值
#define INVAKEY 0X00 //无效按键值
struct newchrkey_dev{
struct device_node *dev_nod;
int key_gpio;
int major;
int minor;
dev_t devid;
struct cdev cdev;
struct device *device;
struct class *cls;
atomic_t my_atomic;
};
struct newchrkey_dev *newchrkey;
static int key_drv_open (struct inode *inod, struct file *filp){
printk("----------------%s---------------\n",__FUNCTION__);
filp->private_data = newchrkey;
return 0;
}
static ssize_t key_drv_read (struct file *filp, char __user *buf, size_t cnt, loff_t *offt){
//printk("----------------%s---------------\n",__FUNCTION__);
int val,ret;
struct newchrkey_dev *dev = filp->private_data;
if(gpio_get_value(dev->key_gpio) == 0){//按键按下
while(!gpio_get_value(dev->key_gpio));
atomic_set(&dev->my_atomic,KEYVALUE);
}else{
atomic_set(&dev->my_atomic,INVAKEY);
}
val = atomic_read(&dev->my_atomic);
ret = copy_to_user(buf,&val,sizeof(val));
return ret;
}
static ssize_t key_drv_write (struct file *filp, const char __user *buf, size_t cnt, loff_t *offt){
printk("----------------%s---------------\n",__FUNCTION__);
return 0;
}
static int key_drv_release (struct inode *inod, struct file *filp){
printk("----------------%s---------------\n",__FUNCTION__);
atomic_inc(&newchrkey->my_atomic);
return 0;
}
static struct file_operations newchrkey_fops = {
.owner = THIS_MODULE,
.open = key_drv_open,
.read = key_drv_read,
.write = key_drv_write,
.release = key_drv_release,
};
static int __init key_drv_init(void){
printk("----------------%s---------------\n",__FUNCTION__);
newchrkey = (struct newchrkey_dev *)kzalloc(sizeof(struct newchrkey_dev), GFP_KERNEL);
if(newchrkey == NULL){
printk("newchrkey kzalloc failed!!!\n");
return -1;
}
atomic_set(&newchrkey->my_atomic,INVAKEY);
int ret;
//01.从设备树获取key的节点信息
newchrkey->dev_nod = of_find_node_by_path("/gpio_key");
if(newchrkey->dev_nod == NULL){
printk("can not find gpio_key int dts!!\n");
return -EINVAL;
}else{
printk("gpio_key have been found!!\n");
}
//02.获取gpio属性信息
newchrkey->key_gpio = of_get_named_gpio(newchrkey->dev_nod, "key-gpio", 0);
if(newchrkey->key_gpio < 0){
printk("can not get key-gpio!!\n");
return EINVAL;
}
printk("key_gpio = %d \n",newchrkey->key_gpio);
/* 初始化key所使用的IO */
//注意!!!!
//gpio_request()使用过后,不用gpio_free()释放,当第二次insmod时会出现段错误。。
//本人就犯了这个错误
gpio_request(newchrkey->key_gpio, "--key"); /* 请求IO */
//03.设置gpio为输入
ret = gpio_direction_input(newchrkey->key_gpio);
if(ret<0){
printk("can not set gpio !!\n");
}
printk("gpio number have gotten!!\n");
//注册字符设备驱动
//1.向系统申请设备号
if(newchrkey->major){
newchrkey->devid = MKDEV(newchrkey->major, 0);
register_chrdev_region(newchrkey->devid, 1, "Pf_key");
}else{
printk("register chrdev!!\n");
/*犯过错误:一下函数的第一个参数没有取地址,在insmod时出现段错误*/
alloc_chrdev_region(&newchrkey->devid, 0, 1, "Pf_key");
newchrkey->major = MAJOR(newchrkey->devid);
newchrkey->minor = MINOR(newchrkey->devid);
printk(">>>>>>>>>>>register chrdev!!\n");
}
printk("major: %d ,minor: %d \n",newchrkey->major,newchrkey->minor);
//2.初始化cdev
newchrkey->cdev.owner = THIS_MODULE;
cdev_init(&newchrkey->cdev, &newchrkey_fops);
//3.添加一个cdev到系统
cdev_add(&newchrkey->cdev, newchrkey->devid, 1);
//4.创建一个设备
newchrkey->cls = class_create(THIS_MODULE, "Pf_key");
if(IS_ERR(newchrkey->cls)){
return PTR_ERR(newchrkey->cls);
}
newchrkey->device = device_create(newchrkey->cls, NULL, newchrkey->devid, NULL, "Pf_key");
if(IS_ERR(newchrkey->device)){
return PTR_ERR(newchrkey->device);
}
return 0;
}
static void __exit key_drv_exit(void){
printk("----------------%s---------------\n",__FUNCTION__);
//注意!!!!
//gpio_request()使用过后,不用gpio_free()释放,当第二次insmod时会出现段错误
gpio_free(newchrkey->key_gpio);
cdev_del(&newchrkey->cdev);
unregister_chrdev_region(newchrkey->devid, 1);
device_destroy(newchrkey->cls, newchrkey->devid);
class_destroy(newchrkey->cls);
kfree(newchrkey);
}
module_init(key_drv_init);
module_exit(key_drv_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("PEIFENG");
key_app.c
#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
/* 定义按键值 */
#define KEYVALUE 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 == KEYVALUE) { /* KEY */
printf("KEY 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;
}
[root@farsight drv_module]# insmod key_drv.ko
----------------key_drv_init---------------
gpio_key have been found!!
key_gpio = 18
Unable to handle kernel paging request at virtual address 7f0002f8
pgd = 88554000
[7f0002f8] *pgd=8877a811, *pte=00000000, *ppte=00000000
Internal error: Oops: 7 [#1] PREEMPT SMP ARM
Modules linked in: key_drv(O+) [last unloaded: key_drv]
CPU: 0 PID: 117 Comm: insmod Tainted: G O 4.1.15 #5
Hardware name: Freescale i.MX6 Ultralite (Device Tree)
task: 8825a600 ti: 88788000 task.ti: 88788000
PC is at strnlen+0xc/0x54
LR is at string+0x30/0xec
pc : [<802b36e4>] lr : [<802b49bc>] psr: a0000093
sp : 88789ce8 ip : ffffffff fp : 88789d18
r10: 88789dd0 r9 : 80820d74 r8 : 00000002
r7 : 0000ffff r6 : 7f0002f8 r5 : 80c34b88 r4 : 80c347b3
r3 : ff0a0004 r2 : 7f0002f8 r1 : ffffffff r0 : 7f0002f8
Flags: NzCv IRQs off FIQs on Mode SVC_32 ISA ARM Segment user
Control: 10c53c7d Table: 8855406a DAC: 00000015
Process insmod (pid: 117, stack limit = 0x88788210)
Stack: (0x88789ce8 to 0x8878a000)
9ce0: ff0a0004 80c347b3 80a0fdbf 80c34b88 80a0fdc1 802b6504
9d00: 80c34b88 ff0a0004 ffffffff ffffffff 80c347a8 000003e0 ff0a0004 ffffffff
9d20: 88789d5c 000003e0 ffffffff 00000000 80c33d58 00000000 80bf3e58 80c33d58
9d40: 00000000 802b6d38 80b8c9f0 8006b360 80bf3e58 80c347a8 88789d64 807f0858
9d60: 00000000 8006b570 00000000 00000000 60000013 00000000 00000000 00000000
9d80: 00000000 8006b7c4 880abd28 880d42d8 00000000 88746440 8008f96c 88747ec0
9da0: 00000326 8006b7e8 80a0fdb4 88789dcc 00000326 807ea9b8 8855d280 88789dcc
9dc0: fffffff0 802dc168 80a0fdb4 00000012 7f0002f8 808307c0 fffffff0 802dc7dc
9de0: 7f004574 8855d280 00000000 7f0060dc 7f006000 80b83698 80b83698 7f006000
9e00: 80b83698 80b83698 00000000 80009704 8bc658e0 88001f00 88747180 807eb708
9e20: 00000000 00000000 8040003e 800aeea8 80b83604 8bc65840 00000000 8040003e
9e40: 00000001 00000001 88789e5c 88001f00 000000d0 80b7f260 00000326 800e0eb8
9e60: 7f00441c 00000001 887463c0 7f004464 7f00441c 88747ec0 00000326 8009092c
9e80: 7f004464 7f00441c 88789f50 00000001 00000001 800924d0 7f004428 00007fff
9ea0: 8008ffcc 0000065f 80006830 00000000 8008f9e0 88789f50 001d2909 7f004564
9ec0: a0aa259c 00000000 00000000 7f004428 6e72656b 00006c65 00000000 00000000
9ee0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
9f00: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
9f20: 00000000 80aa4770 00000000 00002212 017ea4f2 00000000 a0aa3212 001d2909
9f40: 88788000 00000000 00000000 800928bc a0aa1000 00002212 a0aa209c a0aa1f3b
9f60: a0aa2eec 00000578 000007c8 00000000 00000000 00000000 00000020 00000021
9f80: 00000018 00000015 00000013 00000000 00000000 7ec10e34 00000069 00000080
9fa0: 8000f644 8000f4c0 00000000 7ec10e34 017e82e0 00002212 001d2909 00000000
9fc0: 00000000 7ec10e34 00000069 00000080 7ec10e38 001d2909 7ec10e38 00000000
9fe0: 00000000 7ec10ab8 00038a9c 0000bc00 60000010 017e82e0 2001ebfe 0005e280
[<802b36e4>] (strnlen) from [<802b49bc>] (string+0x30/0xec)
[<802b49bc>] (string) from [<802b6504>] (vsnprintf+0x154/0x38c)
[<802b6504>] (vsnprintf) from [<802b6d38>] (vscnprintf+0xc/0x24)
[<802b6d38>] (vscnprintf) from [<8006b360>] (vprintk_emit+0xa0/0x504)
[<8006b360>] (vprintk_emit) from [<8006b7e8>] (vprintk_default+0x24/0x2c)
[<8006b7e8>] (vprintk_default) from [<807ea9b8>] (printk+0x70/0x88)
[<807ea9b8>] (printk) from [<802dc168>] (gpiod_request+0x74/0xdc)
[<802dc168>] (gpiod_request) from [<7f0060dc>] (key_drv_init+0xdc/0x264 [key_drv ])
[<7f0060dc>] (key_drv_init [key_drv]) from [<80009704>] (do_one_initcall+0x80/0x 1d4)
[<80009704>] (do_one_initcall) from [<8009092c>] (do_init_module+0x58/0x1b0)
[<8009092c>] (do_init_module) from [<800924d0>] (load_module+0x1988/0x1c70)
[<800924d0>] (load_module) from [<800928bc>] (SyS_init_module+0x104/0x128)
[<800928bc>] (SyS_init_module) from [<8000f4c0>] (ret_fast_syscall+0x0/0x3c)
Code: e12fff1e e3510000 01a00001 012fff1e (e5d02000)
---[ end trace e331051ea7f3f506 ]---
note: insmod[117] exited with preempt_count 1
Segmentation fault