IMX6ULL开发版linux中断驱动程序学习笔记(附应用app程序读键值)

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-04-20

标签:定时器   数组   初始化   变量   驱动程序   按键   函数   字符   属性   结构   程序   设备

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2008-2024 All Rights Reserved. Powered By bs178.com 闽ICP备11008920号-3
闽公网安备35020302034844号

Top