一、定时器
Linux的时钟节拍率可以在配置的时候设置,默认为100,单位是HZ。系统里面定义了HZ的宏为100,即一个时钟节拍为10ms。同时系统定义了jiffies来记录系统启动以来的系统节拍数。系统初始化的时候会初始化为0。即jiffies/HZ就是运行时间,单位为S。
jiffies和ms、us、ns之间的转换函数:
函数 | 描述 |
int jiffies_to_msecs(const unsigned long j) | 将jiffies类型的参数j分别转换为对应的毫秒、微秒、纳秒。 |
int jiffies_to_usecs(const unsigned long j) | |
u64 jiffies_to_nsecs(const unsigned long j) | |
long msecs_to_jiffies(const unsigned int m) | 将毫秒、微秒、纳秒转换为jiffies类型。 |
long usecs_to_jiffies(const unsigned int u) | |
unsigned long nsecs_to_jiffies(u64 n) |
延时函数:
函数 | 描述 |
oid ndelay(unsigned long nsecs) | 纳秒、微秒和毫秒延时函数。
|
oid udelay(unsigned long usecs) | |
oid mdelay(unsigned long mseces) |
内核定时器:
函数 |
|
void init_timer(struct timer_list *timer) | init_timer函数负责初始化timer_list类型变量 |
void add_timer(struct timer_list *timer) | add_timer函数用于向Linux内核注册定时器 |
int del_timer(struct timer_list * timer) | del_timer函数用于删除一个定时器,不管定时器有没有被激活,都可以使用此函数删除 |
int del_timer_sync(struct timer_list *timer) | del_timer_sync函数是del_timer函数的同步版,会等待其他处理器使用完定时器再删除,del_timer_sync不能使用在中断上下文中 |
int mod_timer(struct timer_list *timer, unsigned long expires) | mod_timer函数用于修改定时值,如果定时器还没有激活的话,mod_timer函数会激活定时器 |
|
|
struct timer_list timer; /* 定义定时器 */
/* 定时器回调函数 */
void function(unsigned long arg)
{
/*
* 定时器处理代码
*/
* 如果需要定时器周期性运行的话就使用mod_timer
* 函数重新设置超时值并且启动定时器。
*/
mod_timer(&dev->timertest, jiffies + msecs_to_jiffies(2000));
}
/* 初始化函数 */
void init(void)
{
init_timer(&timer); /* 初始化定时器 */
timer.function = function; /* 设置定时处理函数 */
timer.expires=jffies + msecs_to_jiffies(2000);/* 超时时间2秒 */
timer.data = (unsigned long)&dev; /* 将设备结构体作为参数 */
add_timer(&timer); /* 启动定时器 */
}
/* 退出函数 */
void exit(void)
{
del_timer(&timer); /* 删除定时器 */
/* 或者使用 */
del_timer_sync(&timer);
}
二、中断
我们知道单片机中中断是用来处理突发事件的,中断处理函数处理得越快越好,同时不能嵌套。在linux中,中断分为上下部,并有一套中断的机制高效的处理。下面是linux下中断的处理机制和应用说明。
机制 | 应用情景 | ||
类似简单单片机,一个中断处理函数完成中断处理 | 处理事情很少,紧急,耗时不太长 | ||
中断函数分为上半部和下半部(上半部关中断,下半部开中断) |
| ||
上半部(紧急) | 下半部(不紧急) |
| |
用来处理紧急的事情 | 中断处理函数里面,应用程序没有在跑 | 软中断 | 处理得事情很多,耗时不太长 |
Tasklet(小任务,基于软中断) | 处理得事情很多,耗时不太长 | ||
创建内核线程在中断外处理剩下的工作,可以跟APP一样竞争执行 | 工作队列 | 处理得事情很多,耗时很长 | |
thread irq (跟工作队列原理一样,多考虑了多核处理器的处理,更优) | 处理得事情很多,耗时很长 |
中断相关函数说明
函数 | 说明 |
int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev) | irq:要申请中断的中断号 handler:中断处理函数 flags:中断标志,可以在文件include/linux/interrupt.h里面查看所有的中断标志,这里我们 name:中断名字,设置以后可以在/proc/interrupts文件中看到对应的中断名字 dev:如果将flags设置为IRQF_SHARED的话,dev用来区分不同的中断,一般情况下将dev设置为设备结构体,dev会传递给中断处理函数irq_handler_t的第二个参数 返回值:0 中断申请成功,其他负值中断申请失败,如果返回-EBUSY的话表示中断已经被申请了 |
void free_irq(unsigned int irq, void *dev) | irq:要释放的中断。 dev:一般情况下将dev设置为设备结构体 |
irqreturn_t (*irq_handler_t) (int, void *) | 第一个参数是要中断处理函数要相应的中断号。第二个参数是一个指向void的指针,也就是个通用指针,需要与request_irq函数的dev参数保持一致。用于区分共享中断的不同设备,dev也可以指向设备数据结构。 |
中断使用示例代码:
/* 中断处理函数 */
irqreturn_t test_handler(int irq, void *dev_id)
{
......
......
}
/* 驱动入口函数 */
static int __init xxxx_init(void)
{
......
/* 注册中断处理函数 */
request_irq(xxx_irq, test_handler, 0, "xxx", &xxx_dev);
......
}
/* 驱动出口函数 */
static void __exit xxxx_exit(void)
{
......
/* 释放中断 */
free_irq(xxx_irq, &xxx_dev);
......
}
tasklet函数说明
函数 | 说明 |
void tasklet_init(struct tasklet_struct *t, void (*func)(unsigned long), unsigned long data); | t:要初始化的tasklet func:tasklet的处理函数。 data:要传递给func函数的参数 |
void tasklet_schedule(struct tasklet_struct *t) | t:要调度的tasklet |
|
|
tasklet示例代码
/* 定义taselet */
struct tasklet_struct testtasklet;
/* tasklet处理函数 */
void testtasklet_func(unsigned long data)
{
/* tasklet具体处理内容 */
}
/* 中断处理函数 */
irqreturn_t test_handler(int irq, void *dev_id)
{
......
/* 调度tasklet */
tasklet_schedule(&testtasklet);
......
}
/* 驱动入口函数 */
static int __init xxxx_init(void)
{
......
/* 初始化tasklet */
tasklet_init(&testtasklet, testtasklet_func, data);
/* 注册中断处理函数 */
request_irq(xxx_irq, test_handler, 0, "xxx", &xxx_dev);
......
}
工作队列相关函数说明
函数 | 说明 |
#define INIT_WORK(_work, _func) | _work表示要初始化的工作,_func是工作对应的处理函数。 |
bool schedule_work(struct work_struct *work) | work:要调度的工作。 |
/* 定义工作(work) */
struct work_struct testwork;
/* work处理函数 */
void testwork_func_t(struct work_struct *work);
{
/* work具体处理内容 */
}
/* 中断处理函数 */
irqreturn_t test_handler(int irq, void *dev_id)
{
......
/* 调度work */
schedule_work(&testwork);
......
}
/* 驱动入口函数 */
static int __init xxxx_init(void)
{
......
/* 初始化work */
INIT_WORK(&testwork, testwork_func_t);
/* 注册中断处理函数 */
request_irq(xxx_irq, test_handler, 0, "xxx", &xxx_dev);
......
}
三、设备树中断节点信息
①、#interrupt-cells,指定中断源的信息cells个数。
②、interrupt-controller,表示当前节点为中断控制器。
③、interrupts,指定中断号,触发方式等。
④、interrupt-parent,指定父中断,也就是中断控制器。
获取中断号
函数 | 说明 |
unsigned int irq_of_parse_and_map(struct device_node *dev, int index) | dev:设备节点。 index:索引号,interrupts属性可能包含多条中断信息,通过index指定要获取的信息。 返回值:中断号。 |
int gpio_to_irq(unsigned int gpio) | gpio:要获取的GPIO编号。 返回值:GPIO对应的中断号。 |