在裸机编程中,我们知道常用的三大模块,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_exit(timer_exit);
MODULE_LICENSE("GPL");
这个模块加载之后,5秒之后就会进入中断程序,打印出一段话,表示成功进入了超时中断。
jiffies为一个全局变量,unsigned long 类型,它是从机子开始运行,到现在为止,产生的节拍总数,在每次时钟中断时都会加1,HZ是个宏定义,表示一秒产生的中断次数,所有上面的表达语句为5秒产生超时中断。
相信到了这里,大家对于Linux编程应该有了一点入门的感觉了,我至少有这个感觉,它的逻辑结构和操作都和裸机编程不一样,不过慢慢的我们要转变过来,下一篇文章我会讲述,我们为什么要在嵌入式系统中采用Linux!