Linux内核定时器

讲解

1、学习过 UCOS 或 FreeRTOS 的同学应该知道,UCOS 或 FreeRTOS 是需要一个硬件定时器提供系统时钟,一般使用 Systick 作为系统时钟源。同理,Linux 要运行,也是需要一个系统时钟的,至于这个系统时钟是由哪个定时器提供的,不需要知道,只需要掌握相应的API函数就可以了。
2、由硬件定时器提供的时钟源是可以设置的,设置好以后就周期性的产生定时中断,系统使用定时中断来计时。中断周期性产生的频率就是系统频率,比如1000Hz,100Hz等(因此系统频率也不是越大越好,大了进入中断的次数就变多了,从而消耗的资源增大,不过现在处理器性能强大,影响也不大)。在编译Linux内核的时候可以通过图形化界面设置节拍率(默认是100Hz,精度就是10ms;1000Hz就是1ms)。

-> Kernel Features 
 -> Timer frequency (<choice> [=y])

3、jiffies 系统节拍数

  • Linux 内核使用全局变量 jiffies 来记录系统从启动以来的系统节拍数,系统启动的时候会将 jiffies 初始化为 0,jiffies 定义在文件 include/linux/jiffies.h 中。
  • 系统运行的时间= jiffies/HZ,单位为秒,其中HZ就是上面设置的节拍数(也就是100Hz)。32位操作系统,jiffies就是32位的,64的话就是64的。而32位发生一次绕回(溢出后重新开始)按照1000Hz,也需要49.7天(64则需要上亿年,可忽略)。

API

处理绕回

暂时没用到,用到再写
在这里插入图片描述

jiffies 和 时间的转换函数

函数 描述
int jiffies_to_msecs(const unsigned long j) 将 jiffies 类型的参数 j 分别转换为对应的毫秒、微秒、纳秒。
int jiffies_to_usecs(const unsigned long j)
u64 jiffies_to_nsecs(const unsigned long j)
long msecs_to_jiffies(const unsigned int m) 将毫秒、微秒、纳秒转换为 jiffies 类型。
long usecs_to_jiffies(const unsigned int u)
unsigned long nsecs_to_jiffies(u64 n)

内核定时器使用

1、简介

  • Linux 内核定时器采用系统时钟来实现,并不是我们在裸机篇中讲解的 PIT 等硬件定时器。
  • Linux 内核定时器使用很简单,只需要提供超时时间(相当于定时值)和定时处理函数即可,当超时时间到了以后设置的定时处理函数就会执行,和我们使用硬件定时器的套路一样,只是使用内核定时器不需要做一大堆的寄存器初始化工作。
  • 在使用内核定时器的时候要注意一点,内核定时器并不是周期性运行的,超时以后就会自动关闭,因此如果想要实现周期性定时,那么就需要在定时处理函数中重新开启定时器。
  • Linux 内核使用 timer_list 结构体表示内核定时器,timer_list 定义在文件include/linux/timer.h 中,定义如下(省略掉条件编译):
struct timer_list {
   
	 struct list_head entry;
	 unsigned long expires; /* 定时器超时时间,单位是节拍数 */
	 struct tvec_base *base;
	 void (*function)(unsigned long); /* 定时处理函数 */
	 unsigned long data; /* 要传递给 function 函数的参数 */
 	 int slack;
};

2、使用

  • ①要使用内核定时器首先要先定义一个 timer_list 变量,表示定时器,tiemr_list 结构体的 expires 表示超时时间,单位为节拍数。 比如我们现在需要定义一个周期为 2 秒的定时器,那么这个定时器的超时时间就是 jiffies+(2HZ),因此 expires=jiffies+(2HZ)。function 就是定时器超时以后的定时处理函数,我们要做的工作就放到这个函数里面,需要我们编写这个定时处理函数。
//就是下面的 struct timer_list timer;
/* timer设备结构体 */
struct timer_dev{
   
	dev_t devid;			/* 设备号 	 */
	struct cdev cdev;		/* cdev 	*/
	struct class *class;	/* 类 		*/
	struct device *device;	/* 设备 	 */
	int major;				/* 主设备号	  */
	int minor;				/* 次设备号   */
	struct device_node	*nd; /* 设备节点 */
	int led_gpio;			/* key所使用的GPIO编号		*/
	int timeperiod; 		/* 定时周期,单位为ms */
	struct timer_list timer;/* 定义一个定时器*/
	spinlock_t lock;		/* 定义自旋锁 */
};
struct timer_dev timerdev;	/* timer设备 */
  • init_timer 函数负责初始化 timer_list 类型变量,当我们定义了一个 timer_list 变量以后一定要先用 init_timer 初始化一下。
	//void init_timer(struct timer_list *timer)
	//timer:要初始化定时器。
	init_timer(&timerdev.timer);
	timerdev.timer.function = timer_function;//中断处理函数
	timerdev.timer.data = (unsigned long)&timerdev;//要传递给function函数的参数
  • mod_timer 函数用于修改定时值,如果定时器还没有激活的话,mod_timer 函数会激活定时器 此时就可以工作了
/* mod_timer 函数用于修改定时值,如果定时器还没有激活的话,mod_timer 函数会激活定时器!
			int mod_timer(struct timer_list *timer, unsigned long expires)
			timer:要修改超时时间(定时值)的定时器。
			expires:修改后的超时时间。
			msecs_to_jiffies:将毫秒转换为 jiffies 类型。timerperiod=1000  */
			mod_timer(&dev->timer, jiffies + msecs_to_jiffies(timerperiod));
  • ④删除定时器。del_timer_sync 函数是 del_timer 函数的同步版,会等待其他处理器使用完定时器再删除,del_timer_sync 不能使用在中断上下文中。
/*del_timer_sync 函数是 del_timer 函数的同步版,会等待其他处理器使用完定时器再删除
  del_timer_sync 不能使用在中断上下文中
			int del_timer_sync(struct timer_list *timer)
			timer:要删除的定时器。
			返回值:0,定时器还没被激活;1,定时器已经激活。*/
			del_timer_sync(&dev->timer);
  • ⑤定时器回调函数
/* 定时器回调函数 */
void timer_function(unsigned long arg)
{
   
	struct timer_dev *dev = (struct timer_dev *)arg;
	static int sta = 1;
	int timerperiod;
	unsigned long flags;

	sta = !sta;		/* 每次都取反,实现LED灯反转 */
	gpio_set_value(dev->led_gpio, sta);
	
	/* 重启定时器 */
	spin_lock_irqsave(&dev->lock, flags);
	timerperiod = dev->timeperiod;
	spin_unlock_irqrestore(&dev->lock, flags);
	//mod_timer 函数用于修改定时值,如果定时器还没有激活的话,mod_timer 函数会激活定时器!
	mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->timeperiod)); 
 }

其他的一些函数:
①add_timer 函数

void add_timer(struct timer_list *timer)
  • timer:要注册的定时器。
  • 返回值:没有返回值。

②del_timer 函数
del_timer 函数用于删除一个定时器,不管定时器有没有被激活,都可以使用此函数删除。在多处理器系统上,定时器可能会在其他的处理器上运行,因此在调用 del_timer 函数删除定时器之前要先等待其他处理器的定时处理器函数退出。

int del_timer(struct timer_list * timer)
  • timer:要删除的定时器。
  • 返回值:0,定时器还没被激活;1,定时器已经激活。

Linux 内核短延时函数

函数 描述
void ndelay(unsigned long nsecs) 纳秒延时函数
void udelay(unsigned long usecs) 微妙
void mdelay(unsigned long mseces) 毫秒

代码

实现功能

  • 通过设置一个定时器来实现周期性的闪烁 LED 灯。
  • 输入2打开定时器;输入1关闭定时器;输入3,调节定时器周期

头文件

ctrl+shift+p
在这里插入图片描述

{
   
    "configurations": [
        {
   
            "name": "Linux",
            "includePath": [
                
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

成草

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值