zoukankan      html  css  js  c++  java
  • 【Linux开发】linux设备驱动归纳总结(七):2.内核定时器

    linux设备驱动归纳总结(七):2.内核定时器


    xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

    这节将介绍内核定时器的使用。

    xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx


    一、定时器


    之前说过两类跟时间相关的内核结构。

    1、延时:通过忙等待或者睡眠机制实现延时。

    2tasklet和工作队列,通过某种机制使工作推后执行,但不知道执行的具体时间。

    接下来要介绍的定时器,能够使工作在指定的时间点上执行,而且不需要使用忙等待这类的延时方法。通过定义一个定时器,告之内核在哪个时间需要执行什么函数就可以了,等时间一到,内核会就执行指定的函数。


    xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx


    二、使用定时器


    定时器的使用很简单,只需要三部:

    1、定义定时器结构体timer_list

    2、设置超时时间,定义定时器处理函数和传参。

    3、激活定时器。


    接下来一步步来说。


    1、定义并初始化定时器结构体timer_list

    /*include/linux/timer.h*/

    11 struct timer_list {

    12 struct list_head entry;

    13 unsigned long expires; //设置在执行定时器处理函数的时间

    14

    15 void (*function)(unsigned long); //定时器处理函数

    16 unsigned long data; //处理函数的传参

    17

    18 struct tvec_base *base;

    19 #ifdef CONFIG_TIMER_STATS

    20 void *start_site;

    21 char start_comm[16];

    22 int start_pid;

    23 #endif

    24 };

    红色部分是待会我们要自己赋值的,其他内核帮忙搞定。

    这个也是有静态和动态区分,步骤如下:

    静态定义并初始化:

    struct timer_list TIMER_INITIALIZER(_function, _expires, _data);

    初始化结构体的同时给指定测成员赋值

    动态初始化:

    /*定义一个名为my_timertimer_list数据结构*/

    struct timer_list my_timer;

    /*初始化my_timer的部分内部成员*/

    init_timer(&my_timer);


    2、设置超时时间,定义定时器处理函数和传参。

    这一步骤是要填充timer_list的三个成员:expiresfunctiondata

    三定时器处理函数的要求定义一个定时器处理函数:

    /*7th_time_2/1st/test.c*/

    9 void timer_func(unsigned long data) //2.定义定时器处理函数

    10 {

    11 printk("time out![%d] [%s] ", (int)data, current->comm);

    12 }

    然后给timer_list的三个成员赋值:

    /*7th_time_2/1st/test.c*/

    18 my_timer.expires = jiffies + 5*HZ; //2.设定定时器处理函数触发时间为5

    19 my_timer.function = timer_func; //2.给结构体指定定时器处理函数

    20 my_timer.data = (unsigned long)99; //2.设定定时器处理函数的传参

    这里要注意一下,expires是指定定时器处理函数在什么时候触发,我这里定义在当前时间(jiffies)的后5秒(5*HZ)。


    3、激活定时器。


    其实就一个函数,一旦调用,内核就会知道,在当前时候的5秒后,执行相应的处理函数。

    /*7th_time_2/1st/test.c*/

    22 add_timer(&my_timer); //3.激活定时器

    这里要注意,定时器激活后,它只会在指定时间执行一次处理函数,执行后会将定时器在内核中移除。


    这样就大功告成了,给个完整代码:

    /*7th_time_2/1st/test.c*/

    1 #include

    2 #include

    3

    4 #include

    5 #include

    6

    7 struct timer_list my_timer; //1.定义定时器结构体timer_list

    8

    9 void timer_func(unsigned long data) //2.定义定时器处理函数

    10 {

    11 printk("time out![%d] [%s] ", (int)data, current->comm); //打印当前进程

    12 }

    13

    14 static int __init test_init(void) //模块初始化函数

    15 {

    16 init_timer(&my_timer); //1.初始化timer_list结构

    17

    18 my_timer.expires = jiffies + 5*HZ; //2.设定定时器处理函数触发时间为5

    19 my_timer.function = timer_func; //2.给结构体指定定时器处理函数

    20 my_timer.data = (unsigned long)99; //2.设定定时器处理函数的传参

    21

    22 add_timer(&my_timer); //3.激活定时器

    23 printk("hello timer,current->comm[%s] ", current->comm);

    24 return 0;

    25 }

    26

    27 static void __exit test_exit(void) //模块卸载函数

    28 {

    29 printk("good bye timer ");

    30 }

    在看看效果:

    [root: 1st]# insmod test.ko

    hello timer,current->comm[insmod]

    [root: 1st]# time out![99] [swapper] //五秒后打印

    [root: 1st]# rmmod test

    good bye timer

    这里要注意的是,当执行处理函数是,当前进程是swapper,而不是加载模块时的insmod,也就说明,当调用add_timer后,就会将定时器交由内核来管理,当时间一到,内核调用进程swapper来执行处理函数。


    xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx


    三、定时器的删除和修改


    上面说了,激活定时器后只能执行一遍,如果要实现隔指定时间又重复执行,那就要修改一下代码。

    在定时器处理函数中加上两条代码:

    /*7th_time_2/2nd/test.c*/

    14 my_timer.expires = jiffies + 2*HZ; //重新设定时间,在两秒后再执行

    15 add_timer(&my_timer); //再次激活定时器

    这样的话,每个2秒就会再次执行定时器处理函数。

    这两条代码也相当与一下的函数:

    /*7th_time_2/2nd/test.c*/

    17 mod_timer(&my_timer, jiffies + 2*HZ);


    /*kernel/timer.c*/

    689 int mod_timer(struct timer_list *timer, unsigned long expires)

    这是改变定时器超时时间的函数,如果在指定的定时器(timer)没超时前调用,超时时间会更新为新的新的超时时间(expires)。如果在定时器超时后调用,那就相当于重新指定超时时间并再次激活定时器。


    如果想在定时器没有超时前取消定时器,可以调用以下函数:

    /*kernel/timer.c*/

    718 int del_timer(struct timer_list *timer)

    该函数用来删除还没超时的定时器。


    不贴个代码:

    /*7th_time_2/2nd/test.c*/

    7 struct timer_list my_timer; //1.定义定时器结构体timer_list

    8

    9 void timer_func(unsigned long data) //2.定义定时器处理函数

    10 {

    11 printk("time out![%d] [%s] ", (int)data, current->comm);

    12

    13 #if 0

    14 my_timer.expires = jiffies + 2*HZ;

    15 add_timer(&my_timer);

    16 #endif

    17 mod_timer(&my_timer, jiffies + 2*HZ); //mod_timer相当于14.15行两步

    18 }

    19

    20 static int __init test_init(void) //模块初始化函数

    21 {

    22 init_timer(&my_timer); //1.初始化timer_list结构

    23

    24 my_timer.expires = jiffies + 5*HZ; //2.设定定时器处理函数触发时间为5

    25 my_timer.function = timer_func; //2.给结构体指定定时器处理函数

    26 my_timer.data = (unsigned long)99; //2.设定定时器处理函

    数的传参

    27

    28 add_timer(&my_timer); //3.激活定时器

    29 printk("hello timer ");

    30 return 0;

    31 }

    32

    33 static void __exit test_exit(void) //模块卸载函数

    34 {

    35 del_timer(&my_timer); //模块卸载时删除定时器

    36 printk("good bye timer ");

    37 }

    看效果:

    [root: 2nd]# insmod test.ko

    hello timer

    [root: 2nd]# time out![99] [swapper] //五秒后打印第一句

    time out![99] [swapper] //之后每两秒打印一次

    time out![99] [swapper]

    time out![99] [swapper]

    time out![99] [swapper]

    [root: 2nd]# rmmod test

    good bye timer


    xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx


    四、利用定时器实现按键去抖


    之前中断的时候曾经使用过写过按键的程序,不过那时候没有使用去抖,因为本来那个按键的硬件防抖就做得比较好。今天要选一个比较差的按键,来看看按键防抖的效果。

    直接上程序:

    /*th_time_2/3rd/test.c */

    7 struct timer_list my_timer;

    8

    9 void timer_func(unsigned long data)

    10 {

    11 printk("key down ");

    12 }

    13

    14 irqreturn_t irq_handler(int irqno, void *dev_id)

    15 {

    16 printk("irq ");

    17 /*0.5秒触发一次定时器处理函数,则只有最后一次抖动的中断会触发timer_func*/

    18 mod_timer(&my_timer, jiffies + 100); //0.5*200 = 100

    19 return IRQ_HANDLED; //表示中断在处理,IRQ_NONE表示中断没处理

    20 }

    21

    22 int test_init(void)

    23 {

    24 int ret;

    25 init_timer(&my_timer); //初始化my_timer结构体

    26 my_timer.function = timer_func; //并告知结构体处理函数指针

    27 /*注册中断,我的按键对应中断IRQ_EINT3*/

    28 ret = request_irq(IRQ_EINT3, irq_handler, IRQF_TRIGGER_FALLING, "timer i rq", NULL);

    29 /*注意一定要判断一下request_irq申请中断函数的返回值,来确认为什么进不了中断*/

    30 if(ret)

    31 {

    32 printk("request irq failed ");

    33 return ret;

    34 }

    35

    36 printk("hello kernel ,[%d]", HZ);

    37 return 0; 38 }

    39 void test_exit(void)

    40 {

    41 free_irq(IRQ_EINT3, NULL); //注销中断

    42 del_timer(&my_timer); //注销内核中的struct timer_struct

    43 printk("bye ");

    44 }

    38 }

    39 void test_exit(void)

    40 {

    41 free_irq(IRQ_EINT3, NULL); //注销中断

    42 del_timer(&my_timer); //注销内核中的struct timer_struct

    43 printk("bye ");

    44 }

    上面的程序可以看到,每次进入中断,都会刷新定时器的值。当按下按键时,由于抖动的关系,会出现多次的中断,但只有最后的一次中断才会触发一次定时器处理函数。

    看效果:

    [root: 3rd]# insmod test.ko

    hello kernel

    irq /按下一次按键,都由于抖动的关系执行了多次的中断处理函数

    irq //每次的中断处理函数都会调用mod_tiemr更新定时器的时间

    irq

    irq

    irq

    irq

    irq

    irq

    irq

    irq

    irq

    irq

    key down //只有最后一次执行完中断处理函数0.5s后,才会触发定时器,执行定时器处理函数


    xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx


    五、总结


    这节介绍了如果使用定时器和如果通过定时器来实现按键去抖。

    定时器的使用很简单,只需要三部:

    1、定义定时器结构体timer_list

    2、设置超时时间,定义定时器处理函数和传参。

    3、激活定时器。

    另外还可以通过mod_timerdel_timer来修改或者删除定时器。


    xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

    源代码: 7th_time_2.rar   

  • 相关阅读:
    在Mac系统下使用自己安装的PHP
    在一个文件里追加内容和换行
    Linux系统下如何去掉文件的@属性
    composer的安装和使用
    Git SSH Key 生成步骤
    自定义mysql函数时报错,[Err] 1418
    百度echarts
    linux 内存释放命令
    第二届PHP全球开发者大会(含大会的PPT)
    在CentOS上安装Java开发环境:使用yum安装jdk
  • 原文地址:https://www.cnblogs.com/huty/p/8518564.html
Copyright © 2011-2022 走看看