Linux驱动学习—内核定时器

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博客

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

#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h> 
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/interrupt.h>
#include <linux/of_irq.h>
#include <linux/timer.h>
#include <linux/input.h>

struct device_node *test_device_node;
struct property *test_node_property;
int gpio_num;
int irq = 0;

DEFINE_TIMER(test_timer, timer_funtion, 0, 0);

static void timer_funtion(ubsigned long data)
{
	printk("this is timer_function\n");
}

static const of_device_id of_match_table_test[] = {//匹配表
    {.compatible = "keys"},
};

static const platform_device_id beep_id_table ={
    .name = "beep_test",
};

irqreturn_t test_key(int irq, void *args)
{
    printk("test_key\n");
    test_timer.expires = jiffies + msec_to_jiffies(20);
    add_timer(&test_timer);
    
    return IRQ_HANDLED;
}

/*设备树节点compatible属性与of_match_table_test的compatible相匹配就会进入该函数,pdev是匹配成功后传入的设备树节点*/
int beep_probe(struct platform_device *pdev)
{
    int ret = 0;
    printk("beep_probe\n");
    
    //查找要查找的节点
    test_device_node = of_find_node_by_path("/test_key");
    if (test_device_node == NULL) {
        printk("test_device_node find error\n");
        return -1;
    }
    printk("test_device_node name is %s\n",test_device_node->name);//test_key
    
    gpio_num = of_get_named_gpio(test_device_node, "gpios", 0);
    if (gpio_num < 0) {
        printk("of_get_named_gpio error\n");
        return -1;
    }
    printk("gpio_num name is %d\n",gpio_num);
    
    gpio_direction_input(gpio_num);
    //获取中断号
    //irq = gpio_to_irq(gpio_num);
    irq = irq_of_parse_and_map(test_device_node, 0);
    printk("irq is %d\n",irq);
    
    ret = request_irq(irq, test_key, IRQF_TRIGGER_RISING, "test_key", NULL);//
    if (ret < 0) {
        printk("request_irq error\n");
        return -1;
    }
    
    return 0;
}
int beep_remove(struct platform_device *pdev)
{
    pritnk("beep_remove \n");
    return 0;
}

strcut platform_driver beep_device = {
    .probe = beep_probe,
    .remove = beep_remove,
    .driver = {
        .owner = THIS_MODULE,
        .name  = "123",
        .of_match_table = of_match_table_test,//匹配表 
    },
    .id_table = &beep_id_table,
};

static int beep_driver_init(void)
{
    int ret = -1;
    ret = platform_driver_register(&beep_device);
    if(ret < 0) {
        printk("platform_driver_register error \n");
    }
    printk("platform_driver_register ok\n");
    return 0;
}

static void  beep_driver_exit(void)
{
    free_irq(irq, NULL);
    platform_driver_unregister(&beep_device);
    printk("beep_driver_exit \n");
}

module_init(beep_driver_init);
module_exit(beep_driver_exit);
MODULE_LICENSE("GPL"); 

按下按键:

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值