Linux驱动之中断实验(基于设备树编程)

更新时间:2023-05-12 13:55:25 阅读: 评论:0

Linux驱动之中断实验(基于设备树编程)
概述
Linux内核提供了完善的中断框架,我们只需要申请中断,然后注册中断处理函数即可,使⽤⾮常⽅便,不需要像单⽚机那样进⾏复杂的寄存器配置。
基础知识
(⼀) 中断号
每个中断都有⼀个中断号,通过中断号区分不同的中断。中断号可以在芯⽚datasheet中查找到,⽐如GPIO的中断号,⼀般会在GPIO章节或者interrupt章节会有介绍, 搜索” Interrupt Sources”,或许你就能找到。
内部中断(定时器,IIC,串⼝等),有固定独⽴的中断号。
外部中断(GPIO),可能多个中断共⽤⼀个中断号,如PIOA的中断号6.
当然,也可以在SOC对应的设备树中找到中断号,例如sama5d34ek.dts开发板对应的SOC设备树sama5d3.dtsi中,可以看到GPIOA的中断号为6,与datasheet⼀致
(⼆)request_irq、free_irq函数
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char * devname, void * dev_id)
功能:request_irq向内核注册中断处理函数。
irq:中断号,中断号不能直接传硬件中断号,得从设备树获取,获取时核⼼层函数做了映射将中断号数组中对应的索引作为此函数的参数。可以通过irq_of_par_and_map函数从设备树中获取按键IO对
应的中断号,也可以使⽤gpio_to_irq将某个IO⼝设置为中断状态,并且返回其中断号。
handler:向系统注册的中断处理函数,是⼀个回调函数,中断发⽣时,系统调⽤这个函数,dev_id参数将被传递给它
flags:中断标记。
IRQF_TRIGGER_RISING:上升沿触发
IRQF_TRIGGER_FALLING:下降沿触发
IRQF_TRIGGER_HIGH:⾼电平触发
IRQF_TRIGGER_LOW:低电平触发
IRQF_SAMPLE_RANDOM:为系统随机发⽣器提供⽀持
IRQF_SHARED:中断可在设备间共享
IRQF_DISABLED:是否快速中断
devname:设置中断名称,通常是设备驱动程序的名称 在cat /proc/interrupts中可以看到此名称
dev_id:中断共享时会⽤到,⼀般设置为这个设备的设备结构体或者NULL
void free_irq(unsigned int irq, void *dev_id)
功能:卸载中断处理函数。
irq:中断号,与request_irq中的irq⼀致,⽤于定位action链表;
dev_id:⽤于在action链表中找到要卸载的表项;同⼀个中断的不同中断处理函数必须使⽤不同的dev_id来区分,这就要求在注册共享中断时参数dev_id必须唯⼀。
程序实现
(⼀)增加设备树节点key。
key {
#address-cells = <1>;
#size-cells = <1>;
compatible = "atkalpha-key";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_key>;
key-gpio = <&pioC 0 GPIO_ACTIVE_LOW>;      /* PC0*/
interrupt-parent = <&pioC>;/*设置中断控制器,使⽤pioC作为中断控制器*/
interrupts = <0 IRQ_TYPE_EDGE_BOTH>;/*中断信息,0表⽰GPIOC0,IRQ_TYPE_EDGE_BOTH双边沿触发,在linux/irq.h中有定义*/
status = "okay";
};
编写完成过后,在源码根⽬录下执⾏“make dtbs”重新编译设备树,⽣产xxx.dtb⽂件。
(⼆) 编写驱动
#include <linux/fs.h>        /*包含file_operation结构体*/
#include <linux/init.h>      /* 包含module_init module_exit */
#include <linux/module.h>    /* 包含LICENSE的宏 */
#include <linux/miscdevice.h>/*包含miscdevice结构体*/
#include <linux/kernel.h>    /*包含printk等操作函数*/
#include <asm/uaccess.h>    /*包含copy_to_ur操作函数*/
#include <linux/interrupt.h> /*包含request_irq操作函数*/
#include <linux/of.h>        /*设备树操作相关的函数*/
#include <linux/of_gpio.h>  /*of_get_named_gpio等函数*/
#include <linux/of_irq.h>
#include <linux/irq.h>
/* 中断IO描述结构体 */
struct irq_keydesc {
int gpio;        /* gpio */
int irqnum;        /* 中断号    */
unsigned char value;    /* 按键对应的键值 */
char name[10];      /* 名字 */
irqreturn_t (*handler)(int, void *); /* 中断服务函数 */
struct device_node *nd; /* 设备节点 */
};
struct irq_keydesc irqKeyDesc; /* irq设备 */
/*按键中断处理函数*/
static irqreturn_t key0_handler(int irq, void *dev_id)
{
printk("key interrupt\r\n");
return 0;
}
static int keyio_init(void)
{
int res = 0;
irqKeyDesc.nd = of_find_node_by_path("/key");
if (irqKeyDesc.nd== NULL){
printk("key node not find!\r\n");
return -EINVAL;
}
/
* 提取GPIO */
irqKeyDesc.gpio = of_get_named_gpio(irqKeyDesc.nd ,"key-gpio", 0);
if (irqKeyDesc.gpio < 0) {
if (irqKeyDesc.gpio < 0) {
printk("can't get key0\r\n");
return -EFAULT;
}
/* 初始化key所使⽤的IO,并且设置成中断模式 */
sprintf(irqKeyDesc.name, "KEY0");  /* 组合名字 */
gpio_request(irqKeyDesc.gpio, irqKeyDesc.name);
gpio_direction_input(irqKeyDesc.gpio);
irqKeyDesc.irqnum = irq_of_par_and_map(irqKeyDesc.nd, 0);
printk("key0:gpio=%d, irqnum=%d\r\n", irqKeyDesc.gpio,irqKeyDesc.irqnum);
/* 申请中断 */
irqKeyDesc.handler = key0_handler;
res = request_irq(irqKeyDesc.irqnum, irqKeyDesc.handler,
IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, irqKeyDesc.name, NULL); if(res < 0){
printk("irq %d request failed!\r\n", irqKeyDesc.irqnum);
return -EFAULT;
}
return 0;
}
/
* 定义⼀个打开设备的,read函数 */
ssize_t key_read(struct file *file, char __ur *array, size_t size, loff_t *ppos)
{
return -1;
}
/*字符设备驱动程序就是为具体硬件的file_operations结构编写各个函数*/
static const struct file_operations key_ctl={
.owner          = THIS_MODULE,
.read          = key_read,
};
/*杂项设备,主设备号为10的字符设备,相对普通字符设备,使⽤更简单*/
static struct miscdevice key_miscdev = {
.minor          = 255,
.name          = "key_read",
.fops          = &key_ctl,
};
static int __init key_init(void)
{
/* 1、构建设备号 */
char res;
/*注册杂项设备驱动*/
res = misc_register(&key_miscdev);
printk(KERN_ALERT"key_init %d\n",res);
keyio_init();
return 0;
}
static void __exit key_exit(void)
{
/* 释放中断 */
free_irq(irqKeyDesc.irqnum, NULL);
/*释放杂项设备*/
misc_deregister(&key_miscdev);
printk(KERN_ALERT"key_exit\r\n");
}
module_init(key_init);
module_exit(key_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("boyee");
MODULE_DESCRIPTION("key interrupt");
MODULE_DESCRIPTION("key interrupt");(三) 运⾏测试

本文发布于:2023-05-12 13:55:25,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/fan/89/888218.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:中断   设备   函数
相关文章
留言与评论(共有 0 条评论)
   
验证码:
推荐文章
排行榜
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图