- 所谓定时器,就是闹钟,时间到后你就要做某些事。
- 有 2 个要素:时间、做事,换成程序员的话就是:超时时间、函数。
在内核中使用定时器很简单,涉及这些函数(参考内核源码 include\linux\timer.h):
① 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(timer); timer->expires = expires; add_timer(timer);
但是更加高效。
④ int del_timer(struct timer_list *timer):
删除定时器
- CONFIG_HZ=100:内核每秒中会发生 100 次系统滴答中断(tick),这就像人类的心跳一样,这是 Linux 系统的心跳
- 每发生一次 tick 中断,全局变量 jiffies 就会累加 1;定时器的时间就是基于 jiffies 的
- 定时器处理按键抖动
- 核心在于:在 GPIO 中断中并不立刻记录按键值,而是修改定时器超时时间,10ms 后再处理。
- 如果 10ms 内又发生了 GPIO 中断,那就认为是抖动,这时再次修改超时时间为 10ms。
- 只有 10ms 之内再无 GPIO 中断发生,那么定时器的函数才会被调用
在初始化一个timer时要指定function和data
- 驱动编程 ```c struct gpio_key{ int gpio; struct gpio_desc gpiod; int flag; int irq; struct timer_list key_timer; / 对应每个gpio都有一个定时器 */ } ;
/ 定时器中断处理函数 / static void key_timer_expire(unsigned long data) { / data ==> gpio / struct gpio_key *gpio_key = data; int val; int key;
val = gpiod_get_value(gpio_key->gpiod);
printk("key_timer_expire key %d %d\n", gpio_key->gpio, val);
key = (gpio_key->gpio << 8) | val;
put_key(key);
wake_up_interruptible(&gpio_key_wait);
kill_fasync(&button_fasync, SIGIO, POLL_IN);
}
static irqreturn_t gpio_key_isr(int irq, void dev_id) { struct gpio_key gpio_key = dev_id; printk(“gpio_key_isr key %d irq happened\n”, gpio_key->gpio);
/* 修改定时器超时时间 */
mod_timer(&gpio_key->key_timer, jiffies + HZ/5);
return IRQ_HANDLED;
}
/* 1. 从platform_device获得GPIO
- gpio=>irq
request_irq / static int gpio_key_probe(struct platform_device pdev) { int err; struct device_node *node = pdev->dev.of_node; int count; int i; enum of_gpio_flags flag;
printk(“%s %s line %d\n”, FILE, FUNCTION, LINE);
count = ofgpiocount(node); if (!count) { printk(“%s %s line %d, there isn’t any gpio available\n”, FILE, FUNCTION, __LINE); return -1; }
gpio_keys_100ask = kzalloc(sizeof(struct gpio_key) * count, GFP_KERNEL); for (i = 0; i < count; i++) {
gpio_keys_100ask[i].gpio = of_get_gpio_flags(node, i, &flag); if (gpio_keys_100ask[i].gpio < 0) {printk("%s %s line %d, of_get_gpio_flags fail\n", __FILE__, __FUNCTION__, __LINE__);
return -1;
} gpio_keys_100ask[i].gpiod = gpio_to_desc(gpio_keys_100ask[i].gpio); gpio_keys_100ask[i].flag = flag & OF_GPIO_ACTIVE_LOW; gpio_keys_100ask[i].irq = gpio_to_irq(gpio_keys_100ask[i].gpio);
/ 在 probe 函数中设置定时器、将定时器添加进内核中 / setup_timer(&gpio_keys_100ask[i].key_timer, key_timer_expire, &gpio_keys_100ask[i]); gpio_keys_100ask[i].key_timer.expires = ~0; / 超时时间设为最大值 / add_timer(&gpio_keys_100ask[i].key_timer); / 加入内核后,就定时器就开始计时 / }
for (i = 0; i < count; i++) { err = request_irq(gpio_keys_100ask[i].irq, gpio_key_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, “100ask_gpio_key”, &gpio_keys_100ask[i]); }
/ 注册file_operations / major = register_chrdev(0, “100ask_gpio_key”, &gpio_key_drv); / /dev/gpio_key /
gpiokeyclass = classcreate(THISMODULE, “100askgpiokey_class”); if (IS_ERR(gpio_key_class)) { printk(“%s %s line %d\n”, __FILE, __FUNCTION, __LINE); unregister_chrdev(major, “100ask_gpio_key”); return PTR_ERR(gpio_key_class); }
device_create(gpio_key_class, NULL, MKDEV(major, 0), NULL, “100ask_gpio_key”); / /dev/100ask_gpio_key /
return 0;
}
static int gpio_key_remove(struct platform_device pdev) { //int err; struct device_node node = pdev->dev.of_node; int count; int i;
device_destroy(gpio_key_class, MKDEV(major, 0));
class_destroy(gpio_key_class);
unregister_chrdev(major, "100ask_gpio_key");
count = of_gpio_count(node);
for (i = 0; i < count; i++)
{
free_irq(gpio_keys_100ask[i].irq, &gpio_keys_100ask[i]);
/* 删除定时器 */
del_timer(&gpio_keys_100ask[i].key_timer);
}
kfree(gpio_keys_100ask);
return 0;
} ```