跳转至

05.Linux内核定时器

定时器的API

setup_timer(timer, fn, data); 设置定时器,主要是初始化 timer_list 结构体,设置其中的函数、参数。

void add_timer(struct timer_list *timer) 向内核添加定时器。timer->expires 表示超时时间。 当 超 时 时 间 到 达 , 内 核 就 会 调 用 这 个 函 数 :timer->function(timer->data)。

int mod_timer(struct timer_list *timer, unsigned long expires) 修改定时器的超时时间。

del_timer(struct timer_list *timer) 删除定时器

驱动编程

在初始化的时候

struct key_dev{ 
    struct class *class;        
    struct device *device;
    struct device_node  *nd;
    struct gpio_desc *gpiod;
    struct timer_list timer;
    int gpio;
    int irq;
    int major;              
    int minor;              
};

static int gpio_key_probe(struct platform_device *pdev)
{
    /*
    初始化按键
    */

    setup_timer(&key.timer, key_timer_expire, &key);
    key.timer.expires = ~0;
    add_timer(&key.timer);

    /*
    注册字符设备
    */

    return 0;

}

每次按键中断的时候修改定时时间

static irqreturn_t gpio_key_isr(int irq, void *dev_id)
{
    struct key_dev *gpio_key = dev_id;
    mod_timer(&gpio_key->timer, jiffies + HZ/5);
    return IRQ_HANDLED;
}

中断时间到了就到中断服务函数执行相关程序

static void key_timer_expire(unsigned long data)
{
    /* data ==> gpio */
    struct key_dev *gpio_key = (struct key_dev *)data;

    int val;
    val = gpiod_get_value(gpio_key->gpiod);

    printk("key %d %d\n", gpio_key->gpio, val); 
    g_key = (gpio_key->gpio << 8) | val;
    wake_up_interruptible(&gpio_key_wait);
    kill_fasync(&button_fasync, SIGIO, POLL_IN);
}

新内核定时器的使用有不同,from_timer的功能是根据key_dev结构体中timer变量的位置推算出结构体的地址。

//setup_timer修改
timer_setup(&key.timer, key_timer_expire, 0);

//中断服务函数修改,获得数据的方式修改了
static void key_timer_expire(struct timer_list *t)
{
    /* data ==> gpio */
    struct key_dev *gpio_key = from_timer(key_dev, t, timer);
    /*其他处理*/
}

应用编程

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <poll.h>
#include <signal.h>

static int fd;

/*
 * ./button_test /dev/100ask_button0
 *
 */
int main(int argc, char **argv)
{
    int val;
    struct pollfd fds[1];
    int timeout_ms = 5000;
    int ret;
    int flags;

    int i;

    /* 1. 判断参数 */
    if (argc != 2) 
    {
        printf("Usage: %s <dev>\n", argv[0]);
        return -1;
    }


    /* 2. 打开文件 */
    fd = open(argv[1], O_RDWR | O_NONBLOCK);
    if (fd == -1)
    {
        printf("can not open file %s\n", argv[1]);
        return -1;
    }

    for (i = 0; i < 10; i++) 
    {
        if (read(fd, &val, 4) == 4)
            printf("get button: 0x%x\n", val);
        else
            printf("get button: -1\n");
    }

    flags = fcntl(fd, F_GETFL);
    fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);

    while (1)
    {
        if (read(fd, &val, 4) == 4)
            printf("get button: 0x%x\n", val);
        else
            printf("while get button: -1\n");
    }

    close(fd);

    return 0;
}

正点原子

定时器简单API

struct timer_list timer; /* 定义定时器 */
/* 定时器回调函数 */
void function(unsigned long arg)
{
/*
 * 定时器处理代码
 */
/* 如果需要定时器周期性运行的话就使用 mod_timer
 * 函数重新设置超时值并且启动定时器。
 */
    mod_timer(&dev->timertest, jiffies + msecs_to_jiffies(2000));
}
/* 初始化函数 */
void init(void)
{
    init_timer(&timer);/* 初始化定时器 */
    timer.function = function;/* 设置定时处理函数 */
    timer.expires=jffies + msecs_to_jiffies(2000);/* 超时时间 2 秒 */
    timer.data = (unsigned long)&dev; /* 将设备结构体作为参数 */
    add_timer(&timer);/* 启动定时器 */
}

/* 退出函数 */
void exit(void)
{
    del_timer(&timer); /* 删除定时器 */
    /* 或者使用 */
    del_timer_sync(&timer);
}

定时器处理,他的例程在做处理的时候是将函数放在的unlocked_ioctl中。

struct timer_list timer;/* 定义一个定时器*/

static long timer_unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    struct timer_dev *dev =  (struct timer_dev *)filp->private_data;
    int timerperiod;
    unsigned long flags;

    switch (cmd) {
        case CLOSE_CMD:     /* 关闭定时器 */
            del_timer_sync(&dev->timer);
            break;
        case OPEN_CMD:      /* 打开定时器 */
            spin_lock_irqsave(&dev->lock, flags);
            timerperiod = dev->timeperiod;
            spin_unlock_irqrestore(&dev->lock, flags);
            mod_timer(&dev->timer, jiffies + msecs_to_jiffies(timerperiod));
            break;
        case SETPERIOD_CMD: /* 设置定时器周期 */
            spin_lock_irqsave(&dev->lock, flags);
            dev->timeperiod = arg;
            spin_unlock_irqrestore(&dev->lock, flags);
            mod_timer(&dev->timer, jiffies + msecs_to_jiffies(arg));
            break;
        default:
            break;
    }
    return 0;
}