您的位置:首页 > 科技 > IT业 > 驱动开发(五):Linux内核定时器

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

2024/12/26 14:23:49 来源:https://blog.csdn.net/a1547998353/article/details/139689350  浏览:    关键词:驱动开发(五):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 结构体里的内容:

struct timer_list {/** All fields that change during normal runtime grouped to the* same cacheline*/struct list_head entry;  unsigned long expires;    //定时到的时间struct tvec_base *base;void (*function)(unsigned long);  //定时器的处理函数unsigned long data;       //向定时器处理函数中填写的值int slack;#ifdef CONFIG_TIMER_STATSint start_pid;void *start_site;char start_comm[16];
#endif
#ifdef CONFIG_LOCKDEPstruct lockdep_map lockdep_map;
#endif
};

1.声明一个定时器 

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

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

2.初始化定时器

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

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

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

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

 3.启动定时器

   3.1添加定时器

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

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

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

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

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

4.删除定时器

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

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

参考代码

 用定时器实现按键消抖

#include <linux/init.h>
#include <linux/module.h>
#include <linux/printk.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/timer.h>#define GPIONO(m, n) m * 32 + n
#define GPIO_B8 (GPIONO(1, 8))
#define GPIO_B16 (GPIONO(1, 16))
int gpiono[] = {GPIO_B8, GPIO_B16};
char *name[] = {"gpio_it_8", "gpio_it_16"};
int ret;
int i;
struct timer_list mytimer;
irqreturn_t handler(int irqno, void *dev)
{mod_timer(&mytimer,jiffies + 10);return IRQ_HANDLED;
}
void timer_function(unsigned long data) //定时器的处理函数
{int B8 = gpio_get_value(GPIO_B8);int B16 = gpio_get_value(GPIO_B16);if (B8 == 0){printk(KERN_ERR "------------\n");}if (B16 == 0){printk(KERN_ERR "++++++++++++\n");}
}static int __init hello_init(void)
{mytimer.expires = jiffies + 10;mytimer.function = timer_function;mytimer.data = 0;init_timer(&mytimer);  //内核帮你填充你未填充的对象	add_timer(&mytimer);    //开启一次定时器for (i = 0; i < sizeof(gpiono) / sizeof(int); i++){ret = request_irq(gpio_to_irq(gpiono[i]), handler, IRQF_TRIGGER_FALLING, name[i], NULL);if (ret != 0){printk(KERN_ERR "%s request irq err\n", name[i]);return ret;}}return 0;
}
static void __exit hello_exit(void)
{for (i = 0; i < sizeof(gpiono) / sizeof(int); i++){free_irq(gpio_to_irq(gpiono[i]), NULL);}del_timer(&mytimer);
}module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");

现象: 

 

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com