[驱动开发]Linux内核定时器与中断的简单应用


前言

首先介绍一下定时器原理。
在linux系统中定时器有分为软定时和硬件定时器。
以海思某款芯片为例,定时器模块又称为Timer模块,主要实现定时、计数功能。
Timer 具有以下特点:

  • 带可编程 8 位预分频器的 32bit/16bit 减法定时器/计数器。
  • Timer 的计数时钟为 3MHz 时钟。
  • 支持 3 种计数模式:自由运行模式、周期模式和单次计数模式。
  • 有 2 种载入计数初值的方法,分别通过 TIMERx_LOAD 和 TIMERx_BGLOAD 寄存
    器实现。
  • 当前的计数值可随时读取。
  • 当计数值减到 0 时会产生一个中断。

Timer 基于一个 32bit/16bit(可配置)减法计数器。计数器的值在每个计数时钟的上
升沿减 1。当计数值递减到零,Timer 将产生一个中断。


而基于硬件提供的信号源,系统就可以按照信号中断来计数,计数在固定频率下对应固定的时间,根据预设的时间参数即可产生定时中断信号,这就是软定时。

本文主要介绍软定时器的使用

一、内核定时器

1.介绍

内核定时器是内核用来控制在未来某个时间点(基于jiffies(节拍总数))调度执行某个函数的一种机制,相关函数位于 <linux/timer.h> 和 kernel/timer.c 文件中。

当内核定时器定时时间到达时,会进入用户指定的函数,相当于软中断。内核定时器注册开启后,运行一次就不会再运行(相当于自动注销),我们可以重新设置定时器的超时时间,让定时器重复运行。

每当时钟中断发生时,全局变量jiffies(一个32位的unsigned long 变量)就加1,因此jiffies记录了linux系统启动后时钟中断发生的次数,驱动程序常利用jiffies来计算不同事件间的时间间隔。内核每秒钟将jiffies变量增加HZ次。因此,对于HZ值为100的系统,jiffy+1等于隔了10ms,而对于HZ为1000的系统,jiffy+1仅为1ms。

内核定时器结构体:下面列出了需要关心的成员

struct timer_list {
    unsigned long expires;           //设置超时时间,用jiffies作为基准值
    void (*function)(unsigned long); //类似中断服务函数,设置定时器到时后处理的函数 
    unsigned long data;              //中断服务函数的参数
}

expires设置:以当前时间为基准加上延时时间,时间基准用jiffies变量表示

2、定时器使用

2.1.初始化

定义定时器结构体

struct timer_list my_timer;
static unsigned int out_time = 5;	// 5秒

my_timer.expires = jiffies + out_time * HZ;	// 设置超时时间,jiffies是基准值

定义定时器到时后的处理函数

static void TimeOutFunc(unsigned long data)
{
    printk("TimeOut\n");
}

初始化定时器

timer_setup(&my_timer, TimeOutFunc, 0);

2.2.启动

add_timer(&my_timer);

初始化和启动一般可以写在内核模块驱动的module_init中

2.3.关闭

(void)del_timer(&my_timer);

关闭一般可以写在内核模块驱动的module_exit中


二、中断

1.介绍

不介绍了,反正就是硬件发出个中断信号,linux系统就响应然后执行一段程序(中断处理函数),执行完之后再回到断电处继续执行之前的工作。

介绍一下基本概念:
中断号(IRQ—interrupt request):中断号也叫中断线,每个中断都有一个中断号,通过中断号即可区分不同的中断。Linux 内核中使用一个 int 变量表示中断号。 Linux内核为每个中断线分配一个唯一的中断编号(IRQ)(可以使用gpio_to_irq函数,获取到对应的中断编号),也称为中断请求号。不同的计算机系统分配方式不同的。

2.使用

假设我们的中断由某个GPIO管脚上升沿触发
首先需要使用gpio_request函数向内核申请需要的GPIO引脚

gpio_request(GPIO_XX, NULL);

2.1 初始化

    unsigned int irq_num;
    unsigned int irqflags;
    int iRet;
	static unsigned int gpio_irq_type = RISING_EDGE;
	module_param(gpio_irq_type, uint, S_IRUGO);		// 只读
	MODULE_PARM_DESC(gpio_irq_type, "RISING_EDGE");	//上升沿
	
    /* 设置方向为输入 */
    gpio_dev_input(gpio_num);

    /* 上升沿 共享中断 */
    irqflags = IRQF_TRIGGER_RISING;
    irqflags |= IRQF_SHARED;

    /* 根据GPIO编号映射中断号 */
    irq_num = gpio_to_irq(gpio_num);

    /* 注册中断 */
    iRet = request_irq(irq_num, my_interrupt_func, irqflags, "MY_interrupt", &gpio_irq_type);

其中,中断处理函数

static irqreturn_t wiper_interrupt(int irq, void *dev_id)
{
	printk("interrupt func\n");
	mod_timer(&my_timer, jiffies + out_time * HZ);	// 刷新定时器
	return IRQ_HANDLED;
}

2.2 注销

	/* 释放注册的中断 */
	free_irq(gpio_to_irq(gpio_num), &gpio_irq_type);
	
    /* 释放注册的GPIO编号 */
    gpio_free(GPIO_XX);

	/* 清除定时器 */
	(void)del_timer(&wiper_timer);
    

假设我们的外设在正常运行时总是能每隔一段时间以中断的形式反馈运行状态。
我们通过在GPIO触发的中断里对定时器进行刷新,从而可以实现超时处理操作,也就是说,如果外设没有正常运行,外设没有能够在一定时间内反馈中断从而刷新定时器,则会导致定时器倒计时结束,触发定时器到时后的处理函数。

  • 19
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux内核驱动中的定时器是一种常见的机制,可以在指定的时间间隔内触发中断。它的主要作用是进行定时操作,如周期性地刷新屏幕、进行数据采集等。下面是一个简单Linux内核驱动定时器中断的代码示例: ``` #include <linux/timer.h> #include <linux/init.h> #include <linux/module.h> struct timer_list my_timer; void my_timer_callback(unsigned long data) { printk(KERN_INFO "my_timer_callback called (%ld).\n", jiffies); } static int __init timer_init(void) { int ret; printk(KERN_INFO "timer_init() called\n"); // 设置定时器 setup_timer(&my_timer, my_timer_callback, 0); ret = mod_timer(&my_timer, jiffies + msecs_to_jiffies(1000)); // 定时1秒 if (ret) printk(KERN_ERR "Error in mod_timer\n"); return 0; } static void __exit timer_exit(void) { int ret; ret = del_timer(&my_timer); if (ret) printk(KERN_ERR "The timer is still in use...\n"); printk(KERN_INFO "timer_exit() called\n"); } module_init(timer_init); module_exit(timer_exit); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Linux Timer Driver"); ``` 在上面的示例中,我们定义了一个名为`my_timer`的定时器,并将其设置为1秒钟后触发中断。当定时器到期时,它会调用`my_timer_callback()`函数来处理中断。这个函数会输出一条信息,表明它已被调用。 上述示例只是一个简单的示例,实际上在Linux内核驱动中使用定时器有许多不同的方法和实现方式。如果你需要更深入地了解Linux内核驱动定时器中断代码,你可以查阅相关的文档或书籍,例如《Linux设备驱动开发详解》等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值