天天看點

Linux裝置驅動之timer定時器與延時

理論知識

原理介紹

軟體意義上的定時器是依賴硬體定時器實作的,核心在時鐘中斷發生後檢測各個定時器是否到期,如果到期,将到期的定時器的處理函數作為軟中斷的下半部執行。實質上,時鐘中斷處理程式會喚醒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[];
            #endif
            #ifdef CONFIG_LOCKDEP
                struct lockdep_map  lockdep_map;
            #endif
            };
           

expires一般使用者毫秒級别的定時器

微妙和納秒級别輸入高精度定時器hrtimer範圍,linux中有hrtimer結構

expires = jiffies + Hz;            //expires觸發時間為1S
expires = jiffies + *Hz/ ;   //expires觸發時間為10ms
expires = jiffies + *Hz/;   //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 = {
    ,
    "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 +  * 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 ;

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

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

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);
           

繼續閱讀