Linux驱动学习—内核定时器

2024-01-07 19:28:22

1、内核定时器

1.1 Linux内核定时器概念

不同于单片机定时器,LInux内核定时器是一种基于未来时间点的计时方式,以当前时刻来启动的时间点,以未来的某一时刻为终止点。比如,现在是10点5分,我要定时5分钟,那么定时就是10点5分+5分钟=10点10分。这个和手机闹钟很相似。比如你要定一个第二天早晨8点的闹钟,就是当前时间定时到第二天早晨8点。

需要注意的是,内核定时器定时精度不高,不能作为高精度定时器使用。并且内核定时器并不是周期性运行的,超时以后就会自动关闭,因此如果想要实现周期性定时,那么就需要在定时处理函数中重新开启定时器。

1.2 Linux内核定时器基础知识

Linux内核使用timer_list结构体表示内核定时器,timer_list定义在文件include/linux/timer.h中,定义如下:

struct timer_list {
    /*
     * All fields that change during normal runtime grouped to the
     * same cacheline
     */
    struct hlist_node   entry;
    unsigned long       expires;/*定时器超时时间,不是时长,单位是节拍数*/
    void            (*function)(unsigned long);/*定时处理函数*/
    unsigned long       data;/*要传递function函数的参数*/
    u32         flags;
?
#ifdef CONFIG_TIMER_STATS
    int         start_pid;
    void            *start_site;
    char            start_comm[16];
#endif
#ifdef CONFIG_LOCKDEP
    struct lockdep_map  lockdep_map;
#endif
};

在上面这个结构体中,有几个参数需要重点关注一下。一个是expires到期时间,单位是节拍数。等于定时的当前的始终节拍计数(存储在系统的全局变量和jiffies)+定时时长对应的时钟节拍数量。

那么如何把时间 转换成节拍数量呢?示例:假如从现在开始定时1秒,转换成节拍数量是多少呢? 内核中有一个宏HZ,表示一秒对应的时钟节拍数,那么我们就可以通过这个宏来把时间转换成节拍数。所以,定时1秒就是expires = jiffies + 1*HZ。

HZ的值我们是可以设置的,也就是一秒对应的时钟拍数我们是可以设置的,Linux内核会使用CONFIG_HZ来设置自己的系统时钟。打开include/asm-generic/param.h,有如下内容:

# undef HZ
# define HZ     CONFIG_HZ   /* Internal kernel timer frequency */
# define USER_HZ    100     /* some user interfaces are */

宏HZ就是CONFIG_HZ,因此HZ=100,表示一秒的节拍数是100,在编译Linux内核的时候可以通过图形化界面设置系统节拍率,按照如下路径打开配置界面。

通过上图我们可以发现可选的系统节拍率为100HZ,200HZ,250HZ,300HZ、500HZ和1000HZ.默认是100HZ。

第二个需要关心的参数是function超时能处理处理函数,这个并不是硬件中断 服务程序。原型:

void function(unsigned long data);

第三个参数是data传递给超时处理函数的参数,可以把一个变量的地址转换成unsigned long。

1.3 Linux内核定时器相关操作函数

1.3.1 时间转换函数
<1>ms转换成时钟节拍函数

在include/linux/jiffies.h

unsigned long msecs_to_jiffies(const unsigned int m);

举例:定时10ms 计算:jiffes+msecs_to_jiffies(10)

<2>us转换成时钟节拍函数
unsigned long usecs_to_jiffies(const unsigned int u);

举例:定时10us 计算:jiffes+usecs_to_jiffies(10)

1.3.2 宏DEFINE_TIMER

在include\linux\timer.h

#define DEFINE_TIMER(_name, _function, _expires, _data)     \
    struct timer_list _name =               \
        TIMER_INITIALIZER(_function, _expires, _data)
参数:_name变量名;__function超时处理函数;_expires到点时间,一般在启动定时前需要重新初始化。

作用:静态定义结构体变量并且初始化function,expires,data成员。

1.3.3 add_timer函数

在include\linux\timer.h

void add_timer(struct timer_list *timer);
参数:timer要注册的定时器。

作用:add_timer函数用于向Linux内核注册定时器,使用add_timer函数向内核注册定时器以后,定时器就会开始运行。

1.3.4 del_timer函数
int del_timer(struct timer_list * timer);
timer:要删除的定时器。
返回值:0,定时器还没被激活;1,定时器已经激活

作用:del_timer函数用于删除一个定时器,不管定时器有没有被激活,都可以使用此函数删除。在多处理器系统上,定时器可能会在其他的处理器运行,因此在调用del_timer函数删除定时器之前要先等待其他处理器的定时处理其函数退出。

1.3.5 mod_timer 函数
int mod_timer(struct timer_list *timer, unsigned long expires);
参数:
timer:要修改超时时间(定时值)的定时器。
expires:修改后的超时时间。
返回值:0,调用mod_timer 函数前定时器违背激活;
     ? 1,调用mod_timer 函数前定时器已被激活

作用:mod_timer 函数用于修改定时值,如果定时器还没有激活的话,mod_timer 函数会激活定时器。

1.4 实验代码

#include <linux/init.h>
#include <linux/module.h>//最基本的文件,支持动态添加和卸载模块
#include <linux/miscdevice.h>//注册杂项设备头文件
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/timer.h>
?
 static void timer_function(unsigned long data);
 
 DEFINE_TIMER(test_timer, timer_function, 0, 0);
 
 static void timer_function(unsigned long data)
 {
    printk("this is timer_function\n");
    mod_timer(&test_timer, jiffies + 1*HZ);
 }
 
?
static int hello_init(void)
{
    test_timer.expires = jiffies + 1 * HZ;
    add_timer(&test_timer);
    return 0;
}
 
static void hello_exit(void)
{
    del_timer(&test_timer);
    printk("hello_exit \n");
}
 
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");

2、实验:使用内核定时器实现按键消抖

Linux驱动学习—中断-CSDN博客

在上面链接的按键中断实验代码上修改:

按下按键:

文章来源:https://blog.csdn.net/suifen_/article/details/135443095
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。