• 驱动开发(五):Linux内核定时器


     驱动开发系列文章:
                    驱动开发(一):驱动代码的基本框架    
                    驱动开发(二):创建字符设备驱动
                    驱动开发(三):内核层控制硬件层 

                    驱动开发(四):Linux内核中断

                    驱动开发(五):Linux内核定时器

    目录

    定时器介绍

    定时器的当前时间如何获取?

    定时器加1代表走了多长时间?

    定时器的使用 

    1.声明一个定时器 

    2.初始化定时器

     3.启动定时器

       3.1添加定时器

       3.2使用定时器

    4.删除定时器

    参考代码


    定时器介绍

    在大部分操作系统中,系统时钟是由计算机硬件提供的。它通常以固定的频率发送时钟信号给计算机的中央处理器(CPU)。这个时钟信号会被操作系统内核接收并用于计算时间。操作系统通过不断监视系统时钟的变化来进行时间的跟踪,从而实现定时功能。

    Linux内核中的定时器是一种机制,用于在一定的时间间隔或特定的时间点执行某个任务或触发某个事件。定时器在内核中广泛使用,用于实现各种功能,如任务调度、延迟处理、超时处理等。

    定时的本质是计时,计时的本质是计数。LInux内核定时器是一种基于未来时间点的计时方式,以当前时刻来启动的时间点,以未来的某一时刻为终止点。

    因此,我们要实现定时需要两个值,一个是当前时刻值(起始时间),一个是目标时间值(触发中断时间)

    定时器的当前时间如何获取?

    jiffies:内核当前时钟节拍数

    jiffies是在板子上电这一刻开始计数,只要板子不断电,这个值一直在增加(64位)。在驱动代码中直接使用即可。

    定时器加1代表走了多长时间?

    Linux内核会使用CONFIG_HZ来设置自己的系统时钟。

    在内核顶层目录下有.config

    Kconfig(组成菜单) ---->menuconfig(配置) ------>.config(最终) 可以直接改.config

    CONFIG_HZ=1000

    周期 = 1/CONFIG_HZ

    所以每加一个数是加1ms;

     在内核顶层目录下使用 vi .config 

    定时器的使用 

     内核提供了一个封装好的结构体 struct timer_list 用来表示定时器。

    struct timer_list 结构体里的内容:

    1. struct timer_list {
    2. /*
    3. * All fields that change during normal runtime grouped to the
    4. * same cacheline
    5. */
    6. struct list_head entry;
    7. unsigned long expires; //定时到的时间
    8. struct tvec_base *base;
    9. void (*function)(unsigned long); //定时器的处理函数
    10. unsigned long data; //向定时器处理函数中填写的值
    11. int slack;
    12. #ifdef CONFIG_TIMER_STATS
    13. int start_pid;
    14. void *start_site;
    15. char start_comm[16];
    16. #endif
    17. #ifdef CONFIG_LOCKDEP
    18. struct lockdep_map lockdep_map;
    19. #endif
    20. };

    1.声明一个定时器 

    可以使用内核给你提供的这个接口直接声明自己的定时器。声明定时器的数量没有限制,想开几个开几个。

    struct timer_list mytimer;    //声明一个结构体变量

    2.初始化定时器

    声明完之后还需要对结构体的基本内容进行填充

    1. mytimer.expires = jiffies + 1000; //1s
    2. mytimer.function = timer_function; //名字叫timer_function的中断处理函数
    3. mytimer.data = 0;
    4. void timer_function(unsigned long data) //定时器的处理函数
    5. {
    6. }

     填完基本的参数之后,剩下的可以交给内核自动填充

    init_timer(&mytimer);  //内核帮你填充你未填充的对象	
    

     3.启动定时器

       3.1添加定时器

    用于向Linux内核注册定时器。

    同一个定时器只能被添加一次,在你添加定时器的时候定时器会先启动一次

    void add_timer(struct timer_list *timer);
       3.2使用定时器

    除了第一次开启定时器使用add_timer,其他时间再次使用定时器要使用mod_timer。

    1. int mod_timer(struct timer_list *timer, unsigned long expires)
    2. //再次启动定时器,参数unsigned long expires为定时时间

    4.删除定时器

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

    不管定时器有没有被激活,都可以使用此函数删除。

    参考代码

     用定时器实现按键消抖

    1. #include <linux/init.h>
    2. #include <linux/module.h>
    3. #include <linux/printk.h>
    4. #include <linux/gpio.h>
    5. #include <linux/interrupt.h>
    6. #include <linux/timer.h>
    7. #define GPIONO(m, n) m * 32 + n
    8. #define GPIO_B8 (GPIONO(1, 8))
    9. #define GPIO_B16 (GPIONO(1, 16))
    10. int gpiono[] = {GPIO_B8, GPIO_B16};
    11. char *name[] = {"gpio_it_8", "gpio_it_16"};
    12. int ret;
    13. int i;
    14. struct timer_list mytimer;
    15. irqreturn_t handler(int irqno, void *dev)
    16. {
    17. mod_timer(&mytimer,jiffies + 10);
    18. return IRQ_HANDLED;
    19. }
    20. void timer_function(unsigned long data) //定时器的处理函数
    21. {
    22. int B8 = gpio_get_value(GPIO_B8);
    23. int B16 = gpio_get_value(GPIO_B16);
    24. if (B8 == 0)
    25. {
    26. printk(KERN_ERR "------------\n");
    27. }
    28. if (B16 == 0)
    29. {
    30. printk(KERN_ERR "++++++++++++\n");
    31. }
    32. }
    33. static int __init hello_init(void)
    34. {
    35. mytimer.expires = jiffies + 10;
    36. mytimer.function = timer_function;
    37. mytimer.data = 0;
    38. init_timer(&mytimer); //内核帮你填充你未填充的对象
    39. add_timer(&mytimer); //开启一次定时器
    40. for (i = 0; i < sizeof(gpiono) / sizeof(int); i++)
    41. {
    42. ret = request_irq(gpio_to_irq(gpiono[i]), handler, IRQF_TRIGGER_FALLING, name[i], NULL);
    43. if (ret != 0)
    44. {
    45. printk(KERN_ERR "%s request irq err\n", name[i]);
    46. return ret;
    47. }
    48. }
    49. return 0;
    50. }
    51. static void __exit hello_exit(void)
    52. {
    53. for (i = 0; i < sizeof(gpiono) / sizeof(int); i++)
    54. {
    55. free_irq(gpio_to_irq(gpiono[i]), NULL);
    56. }
    57. del_timer(&mytimer);
    58. }
    59. module_init(hello_init);
    60. module_exit(hello_exit);
    61. MODULE_LICENSE("GPL");

    现象: 

     

  • 相关阅读:
    大数据开发之数据仓库
    jQuery Validation Engine验证模拟的下拉列表非select
    深度学习——模型选择、欠拟合和过拟合
    C 语言与 C++ 面试知识总结
    接口测试详解
    浅谈一下“敏捷开发”
    数据结构学习笔记——查找算法
    Java集合 —— Map集合
    有了它,让您成为系统搭建高手
    【Linux】安装ZooKeeper
  • 原文地址:https://blog.csdn.net/a1547998353/article/details/139689350