• linux内核驱动之定时器


    一、定时器的一些基本概念

    1.1 定时器一般分为两大类:

            软件定时器:由操作系统模拟的定时,没有真实存在的时钟源和计数器, 一般对时钟源和计数器硬件有一定的依赖,跨平台性较好。

            硬件定时器:由真正存在的时钟源和计数器组成,跟硬件相关,跨平台的性能不好。

    1.2 定时器一般由什么组成

    一般的定时器都有两部分组成:

    计数器和时钟源;

    计数器:单纯的计数  如:RTC向上的计数器

    时钟源:给计数提供一个节拍,说白了就是决定我们的计数器多久计数一次。比如说时钟源  100HZ

    就表示我们的计数器一秒钟计数100次。

    二、linux内核定时器

            内核的定时器,指的就是Linux下的软件的定时,所有的软件定时器对硬件的定时器是有一定的依赖的,软件定时器也有硬件定时器的特性,软件定时器也有时钟源和计数器。

            如果说我们使用linux3.5的内核里,时钟源默认的是 100HZ 每计数一次是10ms,在.config配置内核的编译配置的时候,改成了 200HZ --那么就是每5ms计数一次。

            在linux内核里,计数器开始计数是从系统上电就开始的,并且只要我的系统不关闭,计数器就会一直计数,计数器从开机到现在所计的数在linux内核里有一个变量记录,这个变量的值是一直变化的,就类似与日历时间,这个变量就叫做jiffies,它的值是每隔5ms自加一次,假如我现在要定时5s去执行某一个动作,让计数器去计时5s,定时器计时五秒钟后,计数器应该计数的值:jiffies+5*HZ。

    三、linux内核定时器相关的API

    函数的功能:初始化一个内核的定时器

    函数的头文件:

    函数的原型:void init_timer(struct timer_list *timer)

    函数的参数:struct timer_list *timer:内核定时器的核心的结构体

    函数的返回值: 无

    ++++++++++++++++++++++++++++++++++++++++++++++++++++++++

    struct timer_list {

            unsigned long expires:计数器要计的数值

            void (*function)(unsigned long):计数器计数完成之后,要调用的回调函数

            unsigned long data:传给回调函数的参数  一般不填

    };

    ++++++++++++++++++++++++++++++++++++++++++++++++++++++++

    函数的功能:向内核注册一个定时器  并运行

    函数的头文件:

    函数的原型: void add_timer(struct timer_list *timer)

    函数的参数:struct timer_list *timer:内核定时器的核心的结构体

    函数的返回值:无

    ++++++++++++++++++++++++++++++++++++++++++++++++++++++++

    函数的功能:重新激活内核定时器

    函数的头文件:

    函数的原型:int mod_timer(struct timer_list *timer, unsigned long expires)

    函数的参数:

            struct timer_list *timer:内核定时器的核心结构体

            unsigned long expires:新的计数器要计的数

    函数的返回值:当前边的定时器时间还未到  返回1

                             否则 成功返回  0 失败返回 -1

    ++++++++++++++++++++++++++++++++++++++++++++++++++++++++

    函数的功能:删除一个内核定时器

    函数的头文件:

    函数的原型:int del_timer(struct timer_list *timer)

    函数的参数: struct timer_list *timer:内核定时器的核心的结构体

    函数的返回值:

            成功返回  0

            失败返回 -1

    计数的时间假如为秒的话

    计数的次数  直接就是jiffies+秒数*HZ

    当计数的时间为微秒的时候

    unsigned long usecs_to_jiffies(const unsigned int u)//指定时间单位us

    到计数的时间为毫秒的时候

    unsigned long msecs_to_jiffies(const unsigned int m)//指定时间单位ms

    四、以下代码是练习代码和运行结果,仅供参考

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. #include
    10. #include
    11. #include
    12. #include
    13. #include
    14. int my_irq;
    15. int value;
    16. int count=0;
    17. struct timer_list mytime;
    18. dev_t mykey1;
    19. struct cdev mycdev;
    20. struct file_operations myfops;
    21. struct class * mykey1_class=NULL;
    22. struct device * mydevicekey1=NULL;
    23. int light=1;
    24. static DECLARE_WAIT_QUEUE_HEAD(myque);
    25. //定时器回调函数
    26. void my_fun(unsigned long data)
    27. {
    28. int ret1;
    29. ret1=gpio_get_value(EXYNOS4_GPX3(2));
    30. if(ret1 == 0)
    31. {
    32. count=1;
    33. wake_up_interruptible(&myque);
    34. //翻转LED1
    35. light =!light;
    36. gpio_set_value(EXYNOS4X12_GPM4(0),light);
    37. printk("翻转LED1\n");
    38. }
    39. //printk("我被调用了\n");
    40. }
    41. //中断函数
    42. irqreturn_t fun1(int num, void *arg)
    43. {
    44. //printk("num:%d\n",num);
    45. mod_timer(&mytime, jiffies+msecs_to_jiffies(20));
    46. return 0;
    47. }
    48. int myopen (struct inode *inode, struct file *file)
    49. {
    50. int fan;
    51. //1.申请中断号(key1)
    52. my_irq = gpio_to_irq(EXYNOS4_GPX3(2));
    53. if(my_irq < 0)
    54. {
    55. printk("申请中断失败\n");
    56. return -1;
    57. }
    58. printk("申请的中断号为:%d\n",my_irq);
    59. //2.使能中断
    60. enable_irq(my_irq);
    61. //3.向内核注册中断
    62. fan=request_irq(my_irq,fun1,IRQF_TRIGGER_FALLING,"mykey",NULL);
    63. printk("按键打开\n");
    64. return 0;
    65. }
    66. int myclose(struct inode *inode, struct file *file)
    67. {
    68. //1.注销中断号
    69. free_irq(my_irq, NULL);
    70. //2.使能中断
    71. disable_irq(my_irq);
    72. gpio_set_value(EXYNOS4X12_GPM4(0),1);
    73. printk("led1已关闭\n");
    74. //printk("按键关闭\n");
    75. return 0;
    76. }
    77. ssize_t myread(struct file *file, char __user *buf, size_t size, loff_t *oft)
    78. {
    79. int ret;
    80. //printk("开始读取\n");
    81. //到这陷入阻塞,放弃cpu
    82. count=0;
    83. wait_event_interruptible(myque,count);
    84. value=gpio_get_value(EXYNOS4_GPX3(2));
    85. ret=copy_to_user(buf,&value,1);
    86. return 0;
    87. }
    88. static int __init mykeys_init(void)
    89. {
    90. //定时器
    91. mytime.expires=jiffies+msecs_to_jiffies(20); //20ms
    92. mytime.function=my_fun;
    93. init_timer(&mytime); //初始化定时器
    94. add_timer(&mytime); //向内核注册一个定时器 并运行
    95. //linux2.6 设备注册
    96. int ret;
    97. //1:申请1个设备号,key1用
    98. ret=alloc_chrdev_region(&mykey1, 0, 1, "key1");
    99. if(ret < 0)
    100. {
    101. printk("申请设备号失败\n");
    102. return -1;
    103. }
    104. printk("mykey1:%d\n",mykey1);
    105. printk("key1主设备号:%d\n",MAJOR(mykey1));
    106. printk("key1次设备号:%d\n",MINOR(mykey1));
    107. //2:初始化linux2.6的核心结构体
    108. myfops.owner=THIS_MODULE;
    109. myfops.open=myopen;
    110. myfops.release=myclose;
    111. myfops.read=myread;
    112. cdev_init(&mycdev,&myfops);
    113. //3:向内核注册linux2.6的核心结构体
    114. cdev_add(&mycdev, mykey1, 1);
    115. //4:创建自动生成设备文件所需要的类结构体
    116. mykey1_class=class_create(THIS_MODULE, "key1");
    117. if(mykey1_class == NULL)
    118. {
    119. printk("创建类结构体失败\n");
    120. return -1;
    121. }
    122. //5:生成设备文件
    123. mydevicekey1=device_create(mykey1_class, NULL, mykey1, NULL, "key1");
    124. if(mydevicekey1 ==NULL)
    125. {
    126. printk("生成设备文件失败\n");
    127. }
    128. return 0;
    129. }
    130. static void __exit mykeys_exit(void)
    131. {
    132. //1:销毁设备文件
    133. device_destroy(mykey1_class, mykey1);
    134. //2:销毁类结构体
    135. class_destroy(mykey1_class);
    136. //3:从内核取消注册linux2.6的核心结构体
    137. cdev_del(&mycdev);
    138. //释放设备号
    139. unregister_chrdev_region(mykey1,1);
    140. }
    141. module_init(mykeys_init);
    142. module_exit(mykeys_exit);
    143. MODULE_LICENSE("GPL");

    运行结果:

  • 相关阅读:
    AGX上移植程序,TensorRT版本升级遇到的问题(7.X---8.X)
    21天打卡挑战学习MySQL——《容器部署MySQL》第三周 第八篇
    c# iot .net6 树莓派读取土壤湿度感应器 代码实例
    讯飞星火大模型 与New Bing实测对比
    Vue-计算属性和属性监听器
    【身份证识别】基于matlab GUI BP神经网络身份证识别【含Matlab源码 2239期】
    Go实战学习笔记-1.2基础语法:变量、常量、包、指针等
    搭建Android自动化python+appium环境
    Spring启动源码分析以及生命周期
    keepalived:常见问题
  • 原文地址:https://blog.csdn.net/wanghongshuai1/article/details/136777114