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
推荐阅读
-
困惑了很久的串联/并联谐振电路详解
在含有电阻、电感和电容的交流电路中,电路两端电压与其电流一般是不同相的,若调节电路参数或电源频率使电流与电源电压同相,电路呈电阻性,称这时电路的工作状态为谐振。谐振现象是正弦交流电路的一种特定现象,它在电子和通讯工程中得到广泛应用,但在电力系统中,发生谐振有可能破坏系统的正常工作。谐振一般分串联谐振...
-
“车看灯”变成“灯看车” ——人工智能红绿灯系统助力破解城市交通拥堵
红绿灯能否由“车看灯、人看灯”变成“灯看车、灯看人”?日前,这一设想,已在国家级车联网先导区——湖北省襄阳市实现。而背后的助力者,是一群来自麻省理工学院、哈佛大学、浙江大学等国内外知名高校的人工智能技术专家。据公安部统计,2023年,我国机动车保有量达4.35亿辆,较5年前增加超过1亿辆。汽车保有量...
-
又有国产新芯驾到:瓴盛科技发布4G手机芯片JR510
6月9日,瓴盛科技公布了旗下4G手机芯片新品JR510,为国产手机带来了新的选择。这款芯片采用了三星先进的11纳米FinFET制程工艺,拥有8核CPU设计,均为ARMCortexA55核心,并且搭配了ARMMaliG52GPU。不过更多的参数信息暂时未公布,CPU的主频我们暂时也无法得知,只能从A5...
-
抽油烟机选择触摸按键好还是凸起按键好
最近,有网友问到“抽油烟机选择触摸按键好还是凸起按键好?”确实,选购抽油烟机按键也是一个细节问题,也有很多朋友忽视的。抽油烟机的按键主要有两种:一个触摸按键,而另外的是凸起按键开关,其中凸起按键开关可以分为琴键和机械按键。下面小编就为大家介绍下这两种按键的优缺点来看看抽油烟机选择触摸按键好还是凸起按...