[1] Interrupts in the
Linux kernel
The principles of interrupts in the Linux kernel are the same as those in the bare metal learned in the ARM course . Both need to follow four major steps and three small steps.
在linux内核中注册中断处理函数
int request_irq(unsigned int irq, irq_handler_t handler,
unsigned long flags,const char *name, void *dev)
功能:在linux内核中注册中断处理函数
参数:
@irq :软中断号:根据gpio号通过映射得到软中断号
A B C D E
组号m: 0 1 2 3 4
A组内偏移号n: 0 - 31
gpio的编号 = 组号*32+组内偏移号
gpioc18: = 2*32+18
将gpio号映射成软中断号:
软中断号 = gpio_to_irq(gpio号);
@handler:中断处理函数的指针
typedef irqreturn_t (*irq_handler_t)(int, void *);
中断函数
@irqno :软中断号
@args :request_irq最后一个参数传递过来的值
irqreturn_t handle_key_irq(int irqno, void * args)
{
return IRQ_NONE :
1.中断处理函数执行失败,返回IRQ_NONE
2.如果是共享中断,同时有两个中断处理函数,
都被同时触发了,不需要执行中断处理函数
的设备就返回IRQ_NONE
return IRQ_HANDLED :中断正常执行成功了
}
@flags :中断的触发方式
#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
#define IRQF_DISABLED 0x00000020 #快速中断,在处理本中断的时候关闭其他中断
#define IRQF_SHARED 0x00000080 #共享中断
@name :中断的名字 cat /proc/interrupts(最后一个字段)
@dev :向中断处理函数传递的参数
返回值:成功返回0,失败返回错误码
void free_irq(unsigned int irq, void *dev_id)
功能:释放中断
参数:
@irq :软中断号
@dev_id:中断处理函数最后一个成员传递的参数
按键中断:
gpiob8 左键
gpiob16 右键
PM:
[1] Error when installing the driver:
1. The error prompted is: Resource is busy (146 interrupt is occupied)
2.通过命令 cat /proc/interrupts
"nxp-keypad"
3.在内核中通过搜索找到了驱动的文件
nxp_io_key.c
通过Makefile知道选项名KEYBOARD_NXP_KEY
│
│ Type : tristate │
│ Prompt: SLsiAP push Keypad support │
│ Defined at drivers/input/keyboard/Kconfig:583 │
│ Depends on: !S390 && !UML && INPUT [=y] && INPUT_KEYBOARD [=y] && ARCH_CPU_SLSI [=y] │
│ Location: │
│ -> Device Drivers │
│ -> Input device support │
│ -> Generic input layer (needed for keyboard, mouse, …) (INPUT [=y]) │
│ -> Keyboards (INPUT_KEYBOARD [=y])
4.将上述路径的 SLsiAP push Keypad support 前的*去掉
5.重新编译内核
make uImage
6.将uImage文件拷贝到tftpboot目录
7.让开发板重新上电
8.重新安装驱动
insmod farsight_irq.ko
[2] Key debounce
Because there can be no delay or time-consuming operation in the interrupt processing function.
The key debounce here cannot be completed with delay. You can use the Linux
kernel timer to complete the debounce.
[3] Linux kernel timer
1. How to obtain the current time of the kernel timer?
jiffies: The number of kernel clock ticks
. This value will increase since the Linux kernel is powered on . jiffies is a 64-bit integer.
2.内核定时器加1代表走多长时间?
在内核的顶层目录.config这个文件CONFIG_HZ=1000,
它就是内核定时器的频率值
A53 = 内核定时器加1代表走了1ms的时间。
ubuntu = 内核定时器加1代表走了4ms的时间。
1.内核定时器的对象
struct timer_list {
unsigned long expires; //定时的时间
void (*function)(unsigned long data);//定时器的处理函数
unsigned long data; //向定时器处理函数传递的参数
}
struct timer_list mytimer;
2.初始化
void timer_function(unsigned long data)
{
}
mytimer.expires = jiffies+10;
mytimer.function = timer_function;
mytimer.data = 0;
init_timer(&mytimer); //初始化定时器中其他的成员
3.定时器的注册
add_timer(struct timer_list *timer)
定时器注册,定时器就会启动了,并且只会执行一次
定时器只能被注册一次,如果下一次在注册定时器,此时
linux内核就崩溃了。
4.重新启动定时器
int mod_timer(struct timer_list *timer, unsigned long expires)
参数:
@timer :定时器结构体指针
@expires:下一次定时的时间
返回值:如果定时器再次启动成功了返回1,否则返回0
5.定时器的删除
int del_timer(struct timer_list *timer)
功能:注销定时器
参数:
@timer :定时器结构体指针
返回值:删除不活动的定时器返回1,删除活动的定时器返回0;
[4] gpio subsystem (gpiolib.c)
int gpio_request(unsigned gpio, const char *label)
function: apply for a gpio to use
Parameters:
@gpio: gpio number
@label: name, NULL
return value: return 0 successfully, Return error code on failure
int gpio_direction_input(unsigned gpio)
功能:设置gpio为输入
参数:
@gpio :gpio编号
返回值:成功返回0,失败返回错误码
int gpio_direction_output(unsigned gpio, int value)
功能:设置gpio为输出
参数:
@gpio :gpio编号
@value: 1输出高 0输出低
返回值:成功返回0,失败返回错误码
void gpio_set_value(unsigned gpio, int value)
功能:设置gpio输出的高低电平
参数:
@gpio :gpio编号
@value: 1输出高 0输出低
int gpio_get_value(unsigned gpio)
功能:获取gpio的高低电平的值
参数:
@gpio :gpio编号
返回值:读到1表示输出高电平 读到0表示输出低
void gpio_free(unsigned gpio)
功能:释放gpio
参数:
@gpio :gpio编号
[5] The mechanism of the bottom half of
the interrupt. In the upper half of the interrupt, delay or relatively time-consuming operations cannot be performed,
but in the actual development process, it is inevitable to deal with delay
or relatively time-consuming operations when the interrupt comes . For this reason, the Linux kernel introduced a
mechanism to interrupt the bottom half. Soft interrupt, tasklet, work queue.
软中断:软中断在linux内核中是有32个个数限制的,一般留给
操作系统使用,在写驱动的时候不使用。
tasklet:taklet是基于软中断实现的,没有个数显
示,tasklet工作在中断上下文。
原理:在中断上半部执行完之后会清除中断状态标志位,
只要中断状态标志位被清除了,就可以响应下一次中断了。
在清除中断状态标志位下面,就可以启动中断底半部工作。
这个中断底半部也属于中断的一个部分,所以它工作在中断
上下文。由于它工作在中断上下文不在在这个底半部执行
能够引起系统调用的操作。
1.分配tasklet对象
struct tasklet_struct
{
//struct tasklet_struct *next; //指向下一个节点
//unsigned long state; //是否需要执行中断底半部的状态标识
//atomic_t count; //计数
void (*func)(unsigned long); //中断底半部处理函数
unsigned long data; //向中断底半部处理函数传递的参数
};
struct tasklet_struct tasklet;
2.初始化tasklet
void tasklet_init(struct tasklet_struct *tasklet,
void (*func)(unsigned long),
unsigned long data)
3.调用底半部处理函数执行
void tasklet_schedule(struct tasklet_struct *tasklet)
在中断顶半部即将执行结束的时候调用底半部执行
工作队列:在linux内核启动的时候会启动一个events的内核线程,
默认这个内核线程是休眠的,它内部维护了一个队列。如
你想让队列执行,只需要向这个队列尾部添加自己的work.
添加完之后调用一个唤醒的函数,当这个events被唤醒之后
会从工作队列中取出一个work在这个events的线程内执行。
所以它工作在进程上下文,可以脱离中断执行。在工作队列
中可以有延时、耗时、甚至休眠的操作。
1.分配工作队列对象
struct work_struct {
work_func_t func;
};
typedef void (*work_func_t)(struct work_struct *work);
2.工作队列的初始化
void work_func(struct work_struct *work)
{
}
INIT_WORK(&work, work_func);
3.调用执行
schedule_work(&work);