IMX6ULL开发版linux中断驱动程序学习笔记(附应用app程序读键值)
IMX6ULL开发板Linux中断驱动程序学习笔记
第一步:更改添加设备树相关属性(加粗内容为中断)
key{
compatible="alientek,key";
pinctrl-names="default";
pinctrl-0=pinctrl_key;
key-gpios=gpio118GPIO_ACTIVE_LOW;
status="okay";
interrupt-parent=gpio1;
interrupts=18IRQ_TYPE_EDGE_BOTH;
};
第二步:写驱动程序
备注:步骤1~5是字符设备构建框架
步骤6是按键及其处理函数过程
步骤7是按键消抖处理过程
步骤8是与应用程序沟通过程
以下是驱动程序
includelinux/
includelinux/
includelinux/
includelinux/
includelinux/
includelinux/of_
includeasm/
includelinux/
includelinux/
includelinux/
includelinux/
defineIMX6UIRQ_CNT1/*6.0.0:设备号个数*/
defineKEY_NUM1/*6.0.0:按键个数*/
defineINVAKEY0XFF/*6.2.2.0:无效*/
/*6.1.1:单独做个结构体的目的:如果有很多按键需要一个数组来表示一个按键是一个数组元素,得用一个结构体来描述按键相关属性*/
/*key结构体-用一个结构体来描述按键相关属性(一个结构体描述一个设备或一个物体属性)*/
structirq_keydesc{/*keydesc按键中断描述之意*/
intgpio;/*按键结构体里有IO编号*/
intirqnum;/*中断号*/
unsignedcharvalue;/*键值*/
charname[10];/*名字*/
irqreturn_t(*handler)(int,void*);/*6.2.1.2:重要的中断函数*/
};
/*3.1:设备结构体*/
structimx6uirq_dev{
dev_tdevid;/*设备号*/
intmajor;/*主设备号*/
intminor;
structcdevcdev;/*3.3.1结构体里先定义字符设备*/
structclass*class;/*4.1:类*/
structdevice*device;/*4.1:设备*/
structdevice_node*nd;/*5.1设备节点*/
structirq_keydescirqkey[KEY_NUM];/*6.1.2:一个按键一个数组,irq_keydesc里的内容放到这个结构体下面*/
structtimer_listtimer;/*7.1.0消抖用的定时器timer*/
atomic_tkeyvalue;/*8.0:两个原子变量*/
atomic_treleasekey;/*8.0:两个原子变量*/
};
structimx6uirq_devimx6uirq;/*3.2:设备*/
/*3.4.2:以下3个函数是对应file_operations具体函数*/
staticintimx6uirq_open(structinode*inode,structfile*filp)
{
filp-private_data=imx6uirq;
return0;
}
staticssize_timx6uirq_read(structfile*filp,char__user*buf,size_tcnt,loff_t*offt)
{
/*8.0用read函数读取*/
intret=0;
unsignedcharkeyvalue;
unsignedcharreleasekey;
structimx6uirq_dev*dev=filp-private_data;/*私有数据*/
keyvalue=atomic_read(dev-keyvalue);
releasekey=atomic_read(dev-releasekey);
if(releasekey){/*有效按键*/
if(keyvalue0x80){
keyvalue=~0x80;
ret=copy_to_user(buf,keyvalue,sizeof(keyvalue));
}else{
gotodata_error;
}
atomic_set(dev-releasekey,0);/*按下标志清零*/
}else{
gotodata_error;
}
returnret;
data_error:
return-EINVAL;
}
staticssize_timx6uirq_write(structfile*filp,constchar__user*buf,size_tcount,loff_t*ppos)
{
return0;
}
staticintimx6uirq_release(structinode*inode,structfile*filp)
{
return0;
}
staticconststructfile_operationsimx6uirq_fops={/*3.4.1:定义字符设备操作集*/
.owner=THIS_MODULE,
.write=imx6uirq_write,
.open=imx6uirq_open,
.read=imx6uirq_read,
.release=imx6uirq_release,
};
/*6.2.1.1:按键中断处理函数*/
staticirqreturn_tkey0_handler(intirq,void*dev_id)
{/*6.3:此函数内容为具体操作*/
structimx6uirq_dev*dev=dev_id;/*(volatilelong)类型转换*/
=(volatileunsignedlong)dev_id;/*7.2.0在此中断处理函数中开启触发定时器,*/
/*传递给这个定时器处理函数是dev_id也就是imx6uirq这个变量(volatileunsignedlong)是类型转换*/
mod_timer(dev-timer,jiffies+msecs_to_jiffies(20));/*10ms定时,执行后timer_func就会执行*/
returnIRQ_HANDLED;
}
/*7.1.1.1定时器功能函数*/
staticvoidtimer_func(unsignedlongarg)
{
intvalue=0;
structimx6uirq_dev*dev=(structimx6uirq_dev*)arg;/*7.3:arg强制类型转换(structimx6uirq_dev*)*/
//printk("timer_func\r\n");
/*7.4然后执行读取按键值*/
value=gpio_get_value(dev-irqkey[0].gpio);
if(value==0){/*按下*/
printk("key0pushed!\r\n");
atomic_set(dev-keyvalue,dev-irqkey[0].value);/*将dev-irqkey[0].value值写给keyvalue*/
}elseif(value==1){/*释放*/
atomic_set(dev-keyvalue,0X80|(dev-irqkey[0].value));/*或上一个0X80把最高位置1,置1就代表释放掉了*/
atomic_set(dev-releasekey,1);/*表示是完整的按键过程:有按下有释放*/
printk("key0released!\r\n");
}
}
/*6.0.按键初始化*/
staticintkeyio_init(structimx6uirq_dev*dev)
{
intret=0;
inti=0;
/*6.1.3:以下按键初始化*/
dev-nd=of_find_node_by_path("/key");/*of找设备节点函数*/
if(dev-nd==NULL){
ret=-EINVAL;
gotofail_nd;
}
for(i=0;iKEY_NUM;i++){/*,有很多按键的话就用循环来获取编号*/
dev-irqkey[i].gpio=of_get_named_gpio(dev-nd,"key-gpios",i);/*.gpio就是按键的io编号,从设备树里得到*/
}
/*初始化按键io*/
for(i=0;iKEY_NUM;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-*/
/*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;iKEY_NUM;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_tkey0_handler的参数,把结构体imx6uirq传进去*/
/*当key0_handler函数执行时,void*dev_id就是imx6uirq_dev结构体,通过此大结构体就可以访问相关属性信息*/
if(ret){
printk("irq%drequestfailed!\r\n",dev-irqkey[i].irqnum);
gotofail_irq;
}
}
/*7.0消抖处理*/
/*7.1需要先在结构体里加一个定时器见7.1.0*/
/*7.1.1初始化定时器*/
init_timer();
=timer_func;/*7.1.1.0:timer_func需要定义个函数见7.1.1.1*/
return0;
fail_irq:
for(i=0;iKEY_NUM;i++){
gpio_free(dev-irqkey[i].gpio);/*request失败就释放掉gpio*/
}
fail_nd:
returnret;
}
/*2.入口函数*/
staticint__initimx6uirq_init(void)
{
intret=0;
/*初始化信号量*/
/*3:注册字符设备*/
/*3.0:注册字符设备号*/
=0;/*3.2:表示设备号由内核分配*/
if(){/*如果分配了设备号*/
=MKDEV(,0);/*那么主设备号和次设备号进行拼凑*/
ret=register_chrdev_region(,IMX6UIRQ_CNT,IMX6UIRQ_NAME);
}else{/*如果没有分配了设备号,那么要申请一个设备号*/
ret=alloc_chrdev_region(,0,IMX6UIRQ_CNT,IMX6UIRQ_NAME);
=MAJOR();
=MINOR();
}
printk("imx6uirqmajor=%d,minor=%d\r\n",,);
if(ret0){
gotofail_devid;
}
/*3.3:添加字符设备*/
=THIS_MODULE;
cdev_init(,imx6uirq_fops);/*3.4初始化cdev()里第二个参数就是字符设备的操作集见3.4.1*/
ret=cdev_add(,,IMX6UIRQ_CNT);
if(ret0){
gotofail_cdevadd;
}
/*4:自动创建设备节点*/
=class_create(THIS_MODULE,IMX6UIRQ_NAME);
if(IS_ERR()){
ret=PTR_ERR();
gotofail_class;
}
=device_create(,NULL,,NULL,IMX6UIRQ_NAME);
if(IS_ERR()){
ret=PTR_ERR();
gotofail_device;
}
/*6.1.4:初始化IO--staticintkeyio_init(structimx6uirq_dev*dev)*/
ret=keyio_init(imx6uirq);
if(ret0){
gotofail_keyinit;
}
/*8.1:初始化原子变量*/
atomic_set(,INVAKEY);/*默认是无效的按键值*/
atomic_set(,0);
return0;
fail_keyinit:
fail_device:
class_destroy();
fail_class:
cdev_del();
fail_cdevadd:
unregister_chrdev_region(,IMX6UIRQ_CNT);
fail_devid:
returnret;
}
/*2.出口口函数*/
staticvoid__exitimx6uirq_exit(void)
{
inti=0;
/*7.1.2删除定时器*/
del_timer_sync();
/*释放中断*/
for(i=0;iKEY_NUM;i++){
free_irq([i].irqnum,imx6uirq);/**dev就是结构体地址*/
}
/*释放IO*/
for(i=0;iKEY_NUM;i++){
gpio_free([i].gpio);
}
/*3.5删除字符设备*/
cdev_del();
/*3.5.1释放设备号*/
unregister_chrdev_region(,IMX6UIRQ_CNT);
device_destroy(,);
/*摧毁类*/
class_destroy();
}
/*1.注册驱动和卸载驱动*/
module_init(imx6uirq_init);
module_exit(imx6uirq_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("ZXL");
以下是应用程序:
includesys/
includesys/
intmain(intargc,char*argv[])
{
intfd,ret;
char*filename;
unsignedchardata;
if(argc!=2){
printf("Errorusage!\r\n");
return-1;
}
filename=argv[1];
/*intopen(constchar*pathname,intflags);*/
fd=open(filename,O_RDWR);
if(fd0){
printf("can'topenfile%s\r\n",filename);
return-1;
}
/*模拟应用占用驱动25秒*/
while(1){
ret=read(fd,data,sizeof(data));
if(ret0){
}else{
if(data){
printf("keyvalue=%ls
/lib/modules/4.1.15
imx6uirqmajor=249,minor=0
/lib/modules/4.1.15#./imx6uirqAPP/dev/imx6uirq
(按开发板上key0键后显示如下表示成功)
key0pushed!
key0released!
keyvalue=0x1
推荐阅读
-
南通苏通科技产业园区:打造跨国合作新地标 推动高质量发展新跨越
项目建设攻坚战成效显著一年来,园区切实按照“领导精力向项目招引集中、项目向园区集中、要素向特色产业集中”的思路,主动出击,精准发力,全面开展“大招商,招大商”,持之以恒聚焦重大项目,一着不让猛攻有效投入,保持高速发展态势。项目开工仪式招商引资成绩斐然。全年累计签约亿元以上产业项目35个,其中,10亿...
-
追寻红色记忆,探索科技文明!卓雅小学学子走进工业科技劳动教育基地
深圳商报•读创客户端首席记者吴吉通讯员刘雪玲3月28日,卓雅小学4-6年级开展了以“追寻红色记忆,探索科技文明”为主题的研学实践活动。让同学们走出课堂,走进工业科技劳动教育基地,感受工业科技创新带来的变革,用不一样的学习方式开拓视野和启迪智慧。深圳工业科技劳动教育基地是深圳唯一一家以工业科技为主题的...
-
双牌、新田县来蓝山交流科技治超工作
考察组在科技治超信息平台查阅资料。红网时刻永州6月3(通讯员马朝辉)6月2日上午,双牌、新田县治超办、交警、交通、公路等部门相关人员组成的考察组一行来蓝山县交流科技治超工作。永州市治超办副主任刘国恩,蓝山县治超办、交警、交通、公路等部门负责人参加交流座谈。在科技治超指挥中心交流。考察组一行先后来到蓝...
-
“圈”出群众幸福感 阳信县劳店镇便民服务“等变送”
有事“找上门”与服务“送上门”,二者选其一,老百姓自然会选择第二种。今年来,阳信县劳店镇便民服务中心牢固树立服务至上理念,深入推进政务服务标准化、规范化、便利化工作,完成了镇(村)两级便民服务中心(站)的提档升级。从过去“等上门”的服务方式,转变为“送上门”的崭新理念,让便民服务真正融入千家万户。大...