日期:2014-05-16  浏览次数:20652 次

Linux设备驱动学习(五)------Linux下定时器操作

在裸机编程中,我们知道常用的三大模块,GPIO,定时器,UART(串口),前面几篇文章已经介绍了GPIO的模块与应用的分而治之的模型,还有串口通讯的统一接口,那么接下来就是定时器的操作了:

让我们想想,我们在裸机的时候,定时器一般实现什么功能?延迟(这个必须有),计数(这个很常用),超时中断(用的也挺多),那么在Linux下,我们又怎么实现他们呢?


延迟:

Linux下的延迟比较简单,和windows下编程差不多,sleep()函数,没错,就是它,在us/os中,任务的调度就发生在延迟函数下,而裸机编程我们要迟延时候,就只能在那里空转盲等,非常的浪费资源。

sleep延迟的单位是s,如果想要延迟毫秒或者微妙,就用usleep();usleep的基本单位是us(微妙),所以如果一个操作我想要延迟2ms,就值需要调用usleep(2000);就可以延迟2ms,是不是比裸机编程操作方便了很多,而且效率还高,不会浪费硬件资源在哪里盲等!


计数:

这个也很常用,我们常常要记录下一个操作,或者一个引脚的电平的长度时候,就需要使用计数功能。裸机编程下,一般都会有个寄存器存放从计时开始到现在的计数次数,我们一般通过这个方法读出寄存器里面的数值,而在Linux却完全不是这样,也是通过一个结构体,通过操作统一的函数接口来获取。

Linux下,对于时间操作有个很重要的结构体:

	struct timeval
	{
		tv_sec;		//秒数
		tv_usec;	//微妙数
	}

这个结构体记录着需要计时的秒数,和微妙数,如果看过我前面串口通讯的文章的朋友,对这个结构体不陌生,我在使用select方法的时候,设置一个超时的时间,用的就是这个结构体。这个可以精确的对时间进行操作。

让我们看一段代码,怎么获取某个操作的时间长度:

#include <unistd.h>
#include <stdio.h>
#include <sys/time.h>

int main()

{

	struct timeval tpstart, tpend;			//记录记录时间开始和结束的两个变量
	double timeuse;					//最后换算成double类型的,如1.23s

	int i = 0;					//短暂延迟变量

	gettimeofday(&tpstart, NULL); 			//记录开始计时时间tpstart

	for (i = 0; i < 65536; i++);				//延迟一会

	gettimeofday(&tpend, NULL); 				//记录计时结束时间tpend

	timeuse = 1000000 * (tpend.tv_sec - tpstart.tv_sec) + tpend.tv_usec- tpstart.tv_usec;

	timeuse /= 1000000;							//换算成*.* s

	printf("processor time is %lf s\n", timeuse);

	return 0;

}
这个程序很简单,可能大家不理解的是,gettimeofday这个函数,大家应该都知道Linux所有时间起始位1970年1月1日,00:00开始,这个函数就是计算从这个时间点,到现在的精确时间,精确到微妙,经过差值换算,就可以得到执行for语句所耗费的时间,Linux中也很多不精确计时法,只能计时到秒级,这里就不多说了。一般这样的操作可以满足大多数情况。

超时中断:

这个功能我没有在应用程序层实验过,但是在内核驱动层,却是有这个功能,我用的也不多,只是简单的介绍下:

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/timer.h>

struct timer_list timer;							//定义定时器结构体

//超时中断服务子函数
void timeout(void)
{
	printk("<0>Time out!The data is %d\n",timer.data);
}

static int __init timer_init(void)  
{
	init_timer(&timer);							//初始化定时器
		
	timer.data = 5;								//为了验证进入了中断
	timer.expires = jiffies + (5*HZ);					//设置超时时间
	timer.function = timeout;						//设置超时之后中断服务子程序入口

	add_timer(&timer);							//启动定时器
	return 0;
}


static void __exit timer_exit(void)	
{
	del_timer(&timer);
	printk("Goodbye!Kernel\n");
}

module_init(timer_init);
module_exi