一、中断的实现
对于中断的请求,在linux内核中,有一套标准的接口函数,可以实现中断的响应和处理。
#include <linux/interrupt.h>
//1 申请中断
static inline int __must_check request_irq(unsigned int irq,
irq_handler_t handler,
unsigned long flags,
const char *name,
void *dev)
1)unsigned int irq ---->中断号,每个中断源有一个唯一中断号
2)irq_handler_t handler --->中断请求发生后,被调用的中断服务函数 typedef irqreturn_t (*irq_handler_t)(int, void *);
/**
* enum irqreturn
* @IRQ_NONE interrupt was not from this device 中断发生异常时
* @IRQ_HANDLED interrupt was handled by this device 正常返回
* @IRQ_WAKE_THREAD handler requests to wake the handler thread
*/
enum irqreturn {
IRQ_NONE = (0 << 0),
IRQ_HANDLED = (1 << 0),
IRQ_WAKE_THREAD = (1 << 1),
};
typedef enum irqreturn irqreturn_t;
3)unsigned long flags ---->中断的标志----->中断触发方式
4) const char *name --->中断的名称,是由用户自定义
5)void *dev ---->向中断服务程序传递的参数
返回值:
成功:0
失败:返回负数的错误码
查看开发板linux系统中的中断:
[root@GEC6818 /proc]#cat interrupts
CPU0 CPU1 CPU2 CPU3 CPU4 CPU5 CPU6 CPU7
33: 0 0 0 0 0 0 0 0 GIC pl08xdmac
34: 0 0 0 0 0 0 0 0 GIC pl08xdmac
37: 0 0 0 0 0 0 0 0 GIC rtc 1hz
39: 18 14 13 10 22 14 14 29 GIC nxp-uart
48: 0 0 0 1 0 0 0 0 GIC s3c2440-i2c.1
49: 3197 153 3935 3803 3735 3151 3387 3896 GIC s3c2440-i2c.2
56: 22946 15098 13068 14422 15060 15512 15454 18817 GIC Event Timer IRQ
63: 0 0 0 0 0 0 0 0 GIC nxp-wdt
65: 1402 876 712 851 786 901 919 982 GIC nxp-disp
66: 0 0 0 0 0 0 0 0 GIC nxp-disp
68: 0 0 1 0 0 0 0 0 GIC hdmi-int
中断号 中断被处理的次数 中断控制器 中断名称
在linux内核中,如果中断号被使用或者被其他申请了,再申请就会出错:
[root@GEC6818 /6818_driver]#insmod led_drv.ko
[ 1022.779000] gec6818_interrupt_init
[ 1022.779000] ------------[ cut here ]------------
[ 1022.782000] WARNING: at kernel/irq/manage.c:1370 request_threaded_irq+0x114/0x13c()
[ 1022.789000] Modules linked in: led_drv(O+)
[ 1022.793000] [<c001517c>] (unwind_backtrace+0x0/0x134) from [<c0044880>] (warn_slowpath_common+0x54/0x64)
[ 1022.803000] [<c0044880>] (warn_slowpath_common+0x54/0x64) from [<c004492c>] (warn_slowpath_null+0x1c/0x24)
[ 1022.813000] [<c004492c>] (warn_slowpath_null+0x1c/0x24) from [<c00b8fd8>] (request_threaded_irq+0x114/0x13c)
[ 1022.822000] [<c00b8fd8>] (request_threaded_irq+0x114/0x13c) from [<bf00203c>] (gec6818_interrupt_init+0x3c/0x50 [led_drv])
[ 1022.833000] [<bf00203c>] (gec6818_interrupt_init+0x3c/0x50 [led_drv]) from [<c0008710>] (do_one_initcall+0x11c/0x174)
[ 1022.844000] [<c0008710>] (do_one_initcall+0x11c/0x174) from [<c0097dc0>] (sys_init_module+0xaa4/0x1e14)
[ 1022.853000] [<c0097dc0>] (sys_init_module+0xaa4/0x1e14) from [<c000ec40>] (ret_fast_syscall+0x0/0x30)
[ 1022.863000] ---[ end trace d4f7d5ddfdae4527 ]---
insmod: can't insert 'led_drv.ko': invalid parameter ---->不合法参数,request_irq的参数不对
成功输出:
[root@GEC6818 /6818_driver]#insmod led_drv.ko
[ 2711.551000] gec6818_interrupt_init
[root@GEC6818 /6818_driver]#lsmod
led_drv 977 0 - Live 0xbf008000 (O)
[root@GEC6818 /6818_driver]#cat /proc/interrupts
134: 0 0 0 0 0 0 0 0 GPIO my_interrupt
134 --->中断号 IRQ_GPIO_A_START + 28 --->106 + 28 --->134
//2 释放中断
void free_irq(unsigned int, void *)
参数说明:
unsigned int ---->要释放的中断的中断号
void * ----> 中断服务程序的参数
二、中断号
unsigned int irq
中断号是跟中断源有关,每一个中断源有一个唯一中断号,中断源由外部与内部,中断源是跟硬件有关,每一个处理器平台,中断源不一样的。
1、硬件平台的中断源的定义
kernel\arch\arm\mach-s5p6818\include\mach\s5p6818_irq.h
/*
* Physical Interrupt Number 64 (0~63)
*/
#define IRQ_PHY_UART1 (6 + 32) // pl01115_Uart_modem
#define IRQ_PHY_UART0 (7 + 32) // UART0_MODULE
#define IRQ_PHY_UART2 (8 + 32) // UART1_MODULE
#define IRQ_PHY_UART3 (9 + 32) // pl01115_Uart_nodma0
#define IRQ_PHY_UART4 (10 + 32) // pl01115_Uart_nodma1
#define IRQ_PHY_UART5 (11 + 32) // pl01115_Uart_nodma2
/*
* GPIO Interrupt Number 160 (106~265)
*/
#define IRQ_GPIO_START IRQ_PHY_MAX_COUNT
#define IRQ_GPIO_END (IRQ_GPIO_START + 32 * 5) // Group: A,B,C,D,E
#define IRQ_GPIO_A_START (IRQ_GPIO_START + PAD_GPIO_A)
#define IRQ_GPIO_B_START (IRQ_GPIO_START + PAD_GPIO_B)
#define IRQ_GPIO_C_START (IRQ_GPIO_START + PAD_GPIO_C)
#define IRQ_GPIO_D_START (IRQ_GPIO_START + PAD_GPIO_D)
#define IRQ_GPIO_E_START (IRQ_GPIO_START + PAD_GPIO_E)
关于中断号与GPIO: KEY2->GPIOA28
按键 中断号 GPIO口号 GPIO口
KEY2 IRQ_GPIO_A_START+28 PAD_GPIO_A+28 GPIOA28
头文件:#include <cfg_type.h>
三、中断标志
unsigned long flags
注意:中断一旦安装成功(request_irq),默认是打开的
中断标志分为:外部中断和内部中断
1、外部中断标志
#define IRQF_TRIGGER_NONE 0x00000000
#define IRQF_TRIGGER_RISING 0x00000001
#define IRQF_TRIGGER_FALLING 0x00000002
#define IRQF_TRIGGER_HIGH 0x00000004
#define IRQF_TRIGGER_LOW 0x00000008
双边沿触发:IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING
2、内核内部中断
#define IRQF_DISABLED 0x00000020 //当响应当前中断的时候,关闭其他的中断
#define IRQF_SHARED 0x00000080 //当一个中断源,注册多次时,可以对应多个中断服务程序
#define IRQF_TIMER (__IRQF_TIMER | IRQF_NO_SUSPEND | IRQF_NO_THREAD) //当注册定时器中断时,使用该标志
四、中断服务程序
irq_handler_t handler
typedef irqreturn_t (*irq_handler_t)(int, void *);
通常情况下,一个中断源对应一个中断服务程序(中断处理函数),也可以多个中断源对应一个中断服务程序。
irqreturn_t key_irq_handler(int irq,void* dev)
{
}
1 根据中断号来区分中断处理
2 通过中断请求传递的参数来区分中断处理
头文件:#include <linux/interrupt.h>
注意:对于嵌入式系统中,中断服务程序是一个原子操作,所谓的原子操作就是该过程被执行时,不能被打断。 在中断服务服务程序中,不能有阻塞的操作,如睡眠,copy_to_user,copy_from_user,锁等。
在linux内核中,使用睡眠函数要包含头文件:
#include <linux/delay.h>
static inline void ssleep(unsigned int seconds) ---->秒级睡眠
void msleep(unsigned int msecs); ----->毫秒
五、中断与等待队列(wait_queue)
1、等待队列
设置一个等待条件,如果条件满足,则进程继续往下执行;如果条件不满足,则进程就进入等待队列中。当条件满足时,中断会唤醒等待队列中的进程,进程再继续往下执行。等待队列也是一种同步方法。
2、等待队列的相关API接口函数
#include <linux/wait.h>
#include <linux/sched.h>
//等待队列结构体:
typedef struct __wait_queue wait_queue_t;
struct __wait_queue
{
unsigned int flags; //条件变量的标志
#define WQ_FLAG_EXCLUSIVE 0x01
void *private;
wait_queue_func_t func;
struct list_head task_list;
};
1)定义一个等待队列 和等待的条件
static wait_queue_t gec6818_wait;
static in key_press_flag = 0;
2)初始化等待队列
#define init_wait(wait) \
do { \
(wait)->private = current; \
(wait)->func = autoremove_wake_function; \
INIT_LIST_HEAD(&(wait)->task_list); \
(wait)->flags = 0; \
} while (0)
static inline void INIT_LIST_HEAD(struct list_head *list)
由此可得:
void init_wait(wait_queue_t* wait);
如 init_wait(&gec6818_wait);
#define init_waitqueue_head(q) \
do { \
static struct lock_class_key __key; \
\
__init_waitqueue_head((q), #q, &__key); \
} while (0)
得到原型:void init_waitqueue_head(wait_queue_head_t*);
struct __wait_queue_head
{
spinlock_t lock;
struct list_head task_list;
};
typedef struct __wait_queue_head wait_queue_head_t;
比如:wait_queue_head_t gec6818_wq_head;
3)进入等待队列
#define wait_event_interruptible(wq, condition) \
({ \
int __ret = 0; \
if (!(condition)) \
__wait_event_interruptible(wq, condition, __ret); \
__ret; \
})
struct __wait_queue_head {
spinlock_t lock;
struct list_head task_list;
};
typedef struct __wait_queue_head wait_queue_head_t;
void wait_event_interruptible(wait_queue_head_t wait,int condition)
4)唤醒队列
#define wake_up(x) __wake_up(x, TASK_NORMAL, 1, NULL)
void wake_up(wait_queue_head_t* head)
觉得有帮助的话,打赏一下呗。。