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

发布于 2025-02-18
49
目录

    推荐阅读