您的位置:首页 > 运维架构 > Linux

Linux设备驱动之timer定时器与延时

2018-01-26 14:03 344 查看

理论知识

原理介绍

软件意义上的定时器是依赖硬件定时器实现的,内核在时钟中断发生后检测各个定时器是否到期,如果到期,将到期的定时器的处理函数作为软中断的下半部执行。实质上,时钟中断处理程序会唤醒TIMER_SOFTIRQ软中断,运行当前处理器上到期的所有定时器。

timer定时器的重要数据结构

struct timer_list {
/*
* All fields that change during normal runtime grouped to the
* same cacheline
*/
struct hlist_node   entry;
unsigned long       expires;                          //定时器的到期时间(jiffies)
void            (*function)(unsigned long);           //定时器时间溢出时的触发函数
unsigned long       data;                             //function的传入参数
u32         flags;
int         slack;

#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一般用户毫秒级别的定时器

微妙和纳秒级别输入高精度定时器hrtimer范围,linux中有hrtimer结构

expires = jiffies + Hz;            //expires触发时间为1S
expires = jiffies + 10*Hz/1000 ;   //expires触发时间为10ms
expires = jiffies + 100*Hz/1000;   //expires触发时间为100mS


timer定时器的常用方法

定义-初始化-触发

struct timer_list my_timer;                    //定义
void init_timer(struct timer_list *timer);      //初始化
add_timer(struct timer_list * timer);           //添加定时器
del_timer(struct timer_list * timer);           //删除定时器
mod_timer(struct timer_list *timer, unsigned long expires);    //修改定时器


需要说明的是,add_timer或者mod_timer方法往往不会在init或者probe函数中调用,此函数为打开定时器的方法。

timer定时器使用示例

改示例代码中,定义一个gpio口为输入中断,有按键按下,1S后打印log。

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/delay.h>
#include <linux/time.h>
#include <linux/input.h>
#include <linux/init.h>
#include <linux/gpio.h>
#include <linux/timer.h>       //timer结构体和相关函数的头文件
#include <linux/jiffies.h>     //jiffies和HZ的头文件

#include <asm/io.h>
#include <asm/irq.h>

//平台相关,gpio口的定义
#define IMX_GPIO_NR(bank, nr)               (((bank) - 1) * 32 + (nr))
#define CYNO_GPIO_BEEP_NUM                  IMX_GPIO_NR(6,10)

//定义timer定时器
struct timer_list my_timer;

static struct pin_desc{
int irq;
unsigned char *name;
unsigned int pin;
};

static struct pin_desc beep_desc = {
0,
"beep_num",
CYNO_GPIO_BEEP_NUM
};

//定时器处理函数
void timer_function(void)
{
printk("%s\n", __func__);
}

//按键中断触发函数
static irqreturn_t beep_interrupt_handler(int irq, void *dev_id)
{
printk("%s\n", __func__);
mod_timer(&my_timer, jiffies + 1 * HZ);   //按键按下,1S后出发定时器处理函数
return IRQ_HANDLED;
}

//module初始化函数
static int timer_base_init(void)
{
int ret;

printk(KERN_INFO "%s\n", __func__);

if(gpio_request(beep_desc.pin ,beep_desc.name)){
printk(KERN_ERR "%s : request gpio %d error\n", __func__, beep_desc.pin);
goto err_gpio_request;
}
gpio_direction_input(beep_desc.pin);
beep_desc.irq = gpio_to_irq(beep_desc.pin);
printk(KERN_INFO "%s : the irq num is %d\n", __func__, beep_desc.irq);
ret = request_irq(beep_desc.irq, beep_interrupt_handler , IRQF_TRIGGER_RISING | IRQF_ONESHOT, beep_desc.name , &beep_desc);
if(ret){
printk(KERN_ERR "%s : request_irq is error\n", __func__);
goto err_request_irq;
}
printk("%s : init end\n", __func__);

init_timer(&my_timer);      //定时器的初始化

my_timer.function = &timer_function;   //绑定定时器的触发函数

return 0;

err_request_irq:
free_irq(beep_desc.irq, &beep_desc);

err_gpio_request:
gpio_free(beep_desc.pin);
return -1;
}

static void timer_base_exit(void)
{
printk("%s\n", __func__);
free_irq(beep_desc.irq, &beep_desc);
gpio_free(beep_desc.pin);
del_timer(&my_timer);    //写在模块记得删除定时器
}

module_init(timer_base_init);
module_exit(timer_base_exit);

MODULE_AUTHOR("xiaolei");
MODULE_DESCRIPTION("timer base use");
MODULE_LICENSE("GPL");


一些常用的延时方法

sleep延时

void msleep(unsigned int millisecs);                         //不能被打断
void long msleep_interruptible(unsigned int millisecs);     //可被打断
void ssleep(unsigned int seconds);                          //不可被打断


忙等待延时

也就是while(1)式延时

用于短延时!!! 一般用于硬件延时,比如i2c协议等

void ndelay(unsigned long nsecs);
void udelay(unsigned long usecs);
void mdelay(unsigned long msecs);
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Linuxtimer