IMX6ULL开发板Linux中断驱动程序学习笔记
第一步:更改添加设备树相关属性(加粗内容为中断)
key{
compatible = "alientek,key";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_key>;
key-gpios = <&gpio1 18 GPIO_ACTIVE_LOW>;
status = "okay";
interrupt-parent = <&gpio1>;
interrupts = <18 IRQ_TYPE_EDGE_BOTH>;
};
第二步:写驱动程序
备注:步骤1~5 是字符设备构建框架
步骤6是按键及其处理函数过程
步骤7是按键消抖处理过程
步骤8 是与应用程序沟通过程
以下是驱动程序
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define IMX6UIRQ_CNT 1 /*6.0.0:设备号个数*/
#define IMX6UIRQ_NAME "imx6uirq" /*6.0.0:名字*/
#define KEY_NUM 1 /*6.0.0:按键个数*/
#define KEY0VALUE 0X01 /*6.2.2.0:键值*/
#define INVAKEY 0XFF /*6.2.2.0:无效*/
/*6.1.1:单独做个结构体的目的:如果有很多按键需要一个数组来表示一个按键是一个数组元素,得用一个结构体来描述按键相关属性*/
/*key结构体-用一个结构体来描述按键相关属性(一个结构体描述一个设备或一个物体属性)*/
struct irq_keydesc{ /*keydesc按键中断描述之意*/
int gpio; /*按键结构体里有IO编号*/
int irqnum; /*中断号*/
unsigned char value; /*键值*/
char name[10]; /*名字*/
irqreturn_t (*handler) (int, void *); /*6.2.1.2:重要的中断函数*/
};
/*3.1:设备结构体*/
struct imx6uirq_dev{
dev_t devid; /*设备号*/
int major; /*主设备号*/
int minor;
struct cdev cdev;/*3.3.1结构体里先定义字符设备*/
struct class *class; /*4.1:类*/
struct device *device; /*4.1:设备*/
struct device_node *nd; /*5.1设备节点*/
struct irq_keydesc irqkey[KEY_NUM]; /*6.1.2:一个按键一个数组,irq_keydesc里的内容放到这个结构体下面*/
struct timer_list timer; /*7.1.0消抖用的定时器timer*/
atomic_t keyvalue; /*8.0:两个原子变量*/
atomic_t releasekey; /*8.0:两个原子变量*/
};
struct imx6uirq_dev imx6uirq; /*3.2:设备*/
/*3.4.2:以下3个函数是对应file_operations具体函数*/
static int imx6uirq_open(struct inode *inode, struct file *filp)
{
filp->private_data = &imx6uirq;
return 0;
}
static ssize_t imx6uirq_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
/*8.0用read函数 读取*/
int ret = 0;
unsigned char keyvalue;
unsigned char releasekey;
struct imx6uirq_dev *dev = filp->private_data; /*私有数据*/
keyvalue = atomic_read(&dev->keyvalue);
releasekey =atomic_read(&dev->releasekey);
if(releasekey){ /*有效按键*/
if(keyvalue & 0x80){
keyvalue &= ~0x80;
ret = copy_to_user(buf,&keyvalue, sizeof(keyvalue));
} else {
goto data_error;
}
atomic_set(&dev->releasekey, 0); /*按下标志清零*/
}else{
goto data_error;
}
return ret;
data_error:
return -EINVAL;
}
static ssize_t imx6uirq_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos)
{
return 0;
}
static int imx6uirq_release(struct inode *inode, struct file *filp)
{
return 0;
}
static const struct file_operations imx6uirq_fops = { /*3.4.1:定义字符设备操作集*/
.owner = THIS_MODULE,
.write = imx6uirq_write,
.open = imx6uirq_open,
.read = imx6uirq_read,
.release = imx6uirq_release,
};
/*6.2.1.1:按键中断处理函数*/
static irqreturn_t key0_handler(int irq, void *dev_id)
{ /*6.3:此函数内容为具体操作*/
struct imx6uirq_dev *dev = dev_id; /*(volatile long)类型转换*/
dev->timer.data = (volatile unsigned long)dev_id; /*7.2.0在此中断处理函数中开启触发定时器,*/
/*传递给这个定时器处理函数是dev_id也就是imx6uirq这个变量(volatile unsigned long)是类型转换*/
mod_timer(&dev->timer, jiffies + msecs_to_jiffies(20) );/*10ms定时,执行后timer_func就会执行*/
return IRQ_HANDLED;
}
/*7.1.1.1定时器功能函数*/
static void timer_func(unsigned long arg)
{
int value = 0;
struct imx6uirq_dev *dev = (struct imx6uirq_dev*)arg; /*7.3:arg强制类型转换(struct imx6uirq_dev*)*/
// printk("timer_funcr ");
/*7.4然后执行读取按键值*/
value = gpio_get_value(dev->irqkey[0].gpio);
if (value == 0){ /*按下*/
printk("key0 pushed!r ");
atomic_set(&dev->keyvalue, dev->irqkey[0].value);/*将dev->irqkey[0].value值写给keyvalue*/
}else if(value == 1){ /*释放*/
atomic_set(&dev->keyvalue, 0X80 | (dev->irqkey[0].value));/*或上一个0X80 把最高位置1,置1就代表释放掉了*/
atomic_set(&dev->releasekey,1);/*表示是完整的按键过程:有按下有释放*/
printk("key0 released!r ");
}
}
/*6.0.按键初始化*/
static int keyio_init(struct imx6uirq_dev *dev)
{
int ret = 0;
int i =0;
/*6.1.3:以下按键初始化*/
dev->nd = of_find_node_by_path("/key"); /*of...找设备节点函数*/
if (dev->nd == NULL){
ret = -EINVAL;
goto fail_nd;
}
for(i=0; i dev->irqkey[i].gpio = of_get_named_gpio(dev->nd, "key-gpios", i); /*.gpio就是按键的io编号,从设备树里得到*/ } /*初始化按键io*/ for(i=0; i memset(dev->irqkey[i].name, 0, sizeof(dev->irqkey[i].name)); /*m每个io名字不一样,需给每个io去命名,用memeset函数给key结构体里的成员变量*/ /*name初始化一个字符串,在初始化之前先要清一下数组*/ sprintf(dev->irqkey[i].name, "KEY%d", i); /*清零后,给name填入一些值,i就是代表key0,key1,key2...*/ gpio_request(dev->irqkey[i].gpio, dev->irqkey[i].name); /*irqkey[i].name有新值就可以request了*/ gpio_direction_input(dev->irqkey[i].gpio); /*用这个函数将GPIO设置为输入*/ /*以上6.1.3-end*/ /*6.2.0:以下中断相关初始化*/ dev->irqkey[i].irqnum = gpio_to_irq(dev->irqkey[i].gpio);/*获取中断号*/ //dev->irqkey[i].irqnum = irq_of_parse_and_map(dev->nd, i); /*获取中断号方法2*/ } /*6.2.1.0:按键中断初始化*/ dev->irqkey[0].handler = key0_handler; /*6.2.1.3*/ dev->irqkey[0].value = KEY0VALUE; /*6.2.2.1*/ /*6.2.1.1:申请*/ for(i=0; i ret = request_irq(dev->irqkey[i].irqnum, dev->irqkey[i].handler, /*第二个参数先去6.2.1.1~3步骤初始化定义*/ IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING, dev->irqkey[i].name, &imx6uirq);/*第二个参数是出发方式选上升沿或下降沿跳变法用“|”*/ /*第三个参数是传递中断处理函数irqreturn_t key0_handler的参数,把结构体&imx6uirq传进去*/ /*当key0_handler函数执行时,void *dev_id就是imx6uirq_dev结构体,通过此大结构体就可以访问相关属性信息*/ if (ret){ printk("irq %d request failed!r
", dev->irqkey[i].irqnum); goto fail_irq; } } /*7.0消抖处理*/ /*7.1需要先在结构体里加一个定时器见7.1.0*/ /*7.1.1初始化定时器*/ init_timer(&imx6uirq.timer); imx6uirq.timer.function = timer_func; /*7.1.1.0:timer_func需要定义个函数见7.1.1.1*/ return 0; fail_irq: for(i=0; i gpio_free(dev->irqkey[i].gpio); /*request失败就释放掉gpio*/ } fail_nd: return ret; } /*2.入口函数*/ static int __init imx6uirq_init(void) { int ret = 0; /*初始化信号量*/ /*3:注册字符设备*/ /*3.0:注册字符设备号*/ imx6uirq.major = 0;/*3.2:表示设备号由内核分配*/ if(imx6uirq.major){ /*如果分配了设备号*/ imx6uirq.devid = MKDEV(imx6uirq.major, 0);/*那么主设备号和次设备号进行拼凑*/ ret = register_chrdev_region(imx6uirq.devid, IMX6UIRQ_CNT, IMX6UIRQ_NAME); }else { /*如果没有分配了设备号,那么要申请一个设备号*/ ret = alloc_chrdev_region(&imx6uirq.devid, 0, IMX6UIRQ_CNT, IMX6UIRQ_NAME); imx6uirq.major = MAJOR(imx6uirq.devid); imx6uirq.minor = MINOR(imx6uirq.devid); } printk("imx6uirq major = %d, minor =%dr
", imx6uirq.major, imx6uirq.minor); if (ret < 0){ goto fail_devid; } /*3.3:添加字符设备*/ imx6uirq.cdev.owner = THIS_MODULE; cdev_init(&imx6uirq.cdev, &imx6uirq_fops); /*3.4初始化cdev()里第二个参数就是字符设备的操作集见3.4.1*/ ret = cdev_add(&imx6uirq.cdev, imx6uirq.devid, IMX6UIRQ_CNT); if (ret < 0){ goto fail_cdevadd; } /*4:自动创建设备节点*/ imx6uirq.class = class_create(THIS_MODULE, IMX6UIRQ_NAME); if(IS_ERR(imx6uirq.class)){ ret = PTR_ERR(imx6uirq.class); goto fail_class; } imx6uirq.device = device_create(imx6uirq.class, NULL, imx6uirq.devid, NULL, IMX6UIRQ_NAME); if(IS_ERR(imx6uirq.device)){ ret = PTR_ERR(imx6uirq.device); goto fail_device; } /*6.1.4:初始化IO--static int keyio_init(struct imx6uirq_dev *dev)*/ ret = keyio_init(&imx6uirq); if(ret < 0){ goto fail_keyinit; } /*8.1:初始化原子变量*/ atomic_set(&imx6uirq.keyvalue, INVAKEY);/*默认是无效的按键值*/ atomic_set(&imx6uirq.releasekey, 0); return 0; fail_keyinit: fail_device: class_destroy(imx6uirq.class); fail_class: cdev_del(&imx6uirq.cdev); fail_cdevadd: unregister_chrdev_region(imx6uirq.devid, IMX6UIRQ_CNT); fail_devid: return ret; } /*2.出口口函数*/ static void __exit imx6uirq_exit(void) { int i = 0; /*7.1.2删除定时器*/ del_timer_sync(&imx6uirq.timer); /*释放中断*/ for(i=0; i free_irq(imx6uirq.irqkey[i].irqnum, &imx6uirq); /**dev就是结构体地址*/ } /*释放IO*/ for(i=0; i gpio_free(imx6uirq.irqkey[i].gpio); } /*3.5删除字符设备*/ cdev_del(&imx6uirq.cdev); /*3.5.1释放设备号*/ unregister_chrdev_region(imx6uirq.devid, IMX6UIRQ_CNT); device_destroy(imx6uirq.class, imx6uirq.devid); /*摧毁类*/ class_destroy(imx6uirq.class); } /*1.注册驱动和卸载驱动*/ module_init(imx6uirq_init); module_exit(imx6uirq_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("ZXL"); 以下是应用程序: #include #include #include #include #include #include #include #include int main(int argc, char *argv[]) { int fd, ret; char *filename; unsigned char data; if(argc !=2) { printf("Error usage!r
"); return -1; } filename = argv[1]; /*int open(const char *pathname, int flags);*/ fd = open(filename, O_RDWR); if (fd < 0) { printf("can't open file %sr
", filename); return -1; } /*模拟应用占用驱动25秒*/ while(1){ ret = read(fd, &data, sizeof(data)); if(ret <0){ }else { if(data){ printf("key value = %#xr
", data); } } } close(fd); return 0; } 以下是开发板操作: /lib/modules/4.1.15 # ls imx6uirq.ko imx6uirqAPP /lib/modules/4.1.15 # depmod /lib/modules/4.1.15 # modprobe imx6uirq.ko imx6uirq major = 249, minor =0 /lib/modules/4.1.15 # ./imx6uirqAPP /dev/imx6uirq (按开发板上key0键后显示如下表示成功) key0 pushed! key0 released! key value = 0x1
页面更新:2024-03-23
本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828
© CopyRight 2020-2024 All Rights Reserved. Powered By 71396.com 闽ICP备11008920号-4
闽公网安备35020302034903号