驱动开发(五):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_STATS
	int start_pid;
	void *start_site;
	char start_comm[16];
#endif
#ifdef CONFIG_LOCKDEP
	struct 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");

现象: 

  • 32
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值