• 所谓定时器,就是闹钟,时间到后你就要做某些事。
    • 有 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 的
    • 定时器处理按键抖动
      • image.png
      • 核心在于:在 GPIO 中断中并不立刻记录按键值,而是修改定时器超时时间,10ms 后再处理。
      • 如果 10ms 内又发生了 GPIO 中断,那就认为是抖动,这时再次修改超时时间为 10ms。
      • 只有 10ms 之内再无 GPIO 中断发生,那么定时器的函数才会被调用
    • image.png在初始化一个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;

    1. val = gpiod_get_value(gpio_key->gpiod);
    2. printk("key_timer_expire key %d %d\n", gpio_key->gpio, val);
    3. key = (gpio_key->gpio << 8) | val;
    4. put_key(key);
    5. wake_up_interruptible(&gpio_key_wait);
    6. 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);

    1. /* 修改定时器超时时间 */
    2. mod_timer(&gpio_key->key_timer, jiffies + HZ/5);
    3. return IRQ_HANDLED;

    }

    /* 1. 从platform_device获得GPIO

      1. gpio=>irq
      1. 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) {

        1. printk("%s %s line %d, of_get_gpio_flags fail\n", __FILE__, __FUNCTION__, __LINE__);
        2. 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;

    1. device_destroy(gpio_key_class, MKDEV(major, 0));
    2. class_destroy(gpio_key_class);
    3. unregister_chrdev(major, "100ask_gpio_key");
    4. count = of_gpio_count(node);
    5. for (i = 0; i < count; i++)
    6. {
    7. free_irq(gpio_keys_100ask[i].irq, &gpio_keys_100ask[i]);
    8. /* 删除定时器 */
    9. del_timer(&gpio_keys_100ask[i].key_timer);
    10. }
    11. kfree(gpio_keys_100ask);
    12. return 0;

    } ```