最近在写linux 下的定时器编程实验,测试发现 usleep函数在 x86 架构下的定时还是比较准确的,在arm9下 就不太准了.
今天用linux 下的setitimer()函数进行了定时 器的测试,代码如下:
1 #include <stdio.h> 2 #include <time.h> 3 #include <sys/time.h> 4 #include <stdlib.h> 5 #include <signal.h> 6 #include <math.h> 7 #define pi 3.1415926 8 9 /*四元数的元素,代表估计方向 */ 10 float q0 = 1, q1 = 0, q2 = 0, q3 = 0; 11 float q0_inc,q1_inc,q2_inc,q3_inc; 12 /*用于对四元进行更新,角增量,不是真实的欧拉角*/ 13 float Roll_inc,Pitch_inc,Yaw_inc; 14 float Roll,Pitch,Yaw; /*真实欧拉角*/ 15 16 void FromEulerAngle(float Roll_add,float Pitch_add,float Yaw_add) ; 17 void ToEulerAngle(); 18 void Quaternion_nor(); 19 void Multiply(float q0_n,float q1_n,float q2_n,float q3_n); 20 21 char flag=0; 22 int count = 0; 23 void set_timer() 24 { 25 struct itimerval itv, oldtv; 26 itv.it_interval.tv_sec = 0; 27 itv.it_interval.tv_usec =10000; 28 itv.it_value.tv_sec = 0; 29 itv.it_value.tv_usec = 10000; 30 31 setitimer(ITIMER_REAL, &itv, &oldtv); 32 } 33 34 void sigalrm_handler(int sig) 35 { 36 flag=1; 37 38 //printf("timer signal.. %d\n", count); 39 } 40 41 int main() 42 { 43 44 float time_use=0; 45 struct timeval start; 46 struct timeval end; 47 48 signal(SIGALRM, sigalrm_handler); 49 set_timer(); 50 while (count < 1000) 51 { 52 if(flag) 53 { 54 Roll_inc=0.01; 55 Pitch_inc=0.01; 56 Yaw_inc=0.01; 57 58 gettimeofday(&start,NULL); 59 60 FromEulerAngle(Roll_inc,Pitch_inc,Yaw_inc) ; 61 /*更新四元 */ 62 Multiply(q0_inc,q1_inc,q2_inc,q3_inc); 63 ToEulerAngle(); 64 65 gettimeofday(&end,NULL); 66 time_use+=(end.tv_sec-start.tv_sec)*1000000+(end.tv_usec-start.tv_usec);//微秒 67 68 printf("The count is %i\n",count); 69 printf("yaw=%f\n",Yaw*57.3); 70 printf("pitch=%f\n",Pitch*57.3); 71 printf("roll=%f\n",Roll*57.3); 72 flag=0; 73 count++; 74 } 75 76 } 77 printf("time_use is %f\n",time_use); 78 79 exit(0); 80 81 } 82 83 /*欧拉角转四元,其它坐标系 */ 84 /*这里是否采用小角近似???*/ 85 86 void FromEulerAngle(float Roll_add,float Pitch_add,float Yaw_add)/*这里只是机体转角近似成欧拉*/ 87 { 88 89 /*在其他人的解算中,用的是小角近似 q=[1,Ω*t/2,Ω*t/2,Ω*t/2]T*/ 90 91 float fCosHRoll = (float)cos(Roll_add * .5f); 92 float fSinHRoll = (float)sin(Roll_add * .5f); 93 float fCosHPitch = (float)cos(Pitch_add * .5f); 94 float fSinHPitch = (float)sin(Pitch_add * .5f); 95 float fCosHYaw = (float)cos(Yaw_add * .5f); 96 float fSinHYaw = (float)sin(Yaw_add * .5f); /*回来看看这三角函数运算用了多长时间*/ 97 /*下面这个运算要根据坐标第进行修改*/ 98 q0_inc = fCosHRoll * fCosHPitch * fCosHYaw + fSinHRoll * fSinHPitch * fSinHYaw; 99 q1_inc = fSinHRoll * fCosHPitch * fCosHYaw - fCosHRoll * fSinHPitch * fSinHYaw; 100 q2_inc = fCosHRoll * fSinHPitch * fCosHYaw + fSinHRoll * fCosHPitch * fSinHYaw; 101 q3_inc = fCosHRoll * fCosHPitch * fSinHYaw - fSinHRoll * fSinHPitch * fCosHYaw; 102 103 } 104 /*四元数转欧拉角*/ 105 void ToEulerAngle() 106 { 107 Roll=atan2(2 * (q0 * q1 + q2 * q3) , 1 - 2 * (q1 * q1 + q2 * q2)); 108 Yaw = atan2(2 * (q0 * q3 + q1 * q2) , 1 - 2 * (q2 * q2 + q3 * q3)); 109 Pitch = asin(2*(q0*q2-q3*q1)) ; 110 } 111 112 /*更新四元数*/ 113 /* q_n 这里代表新来的四元数,这里指的是增量 */ 114 void Multiply(float q0_n,float q1_n,float q2_n,float q3_n) 115 { 116 float q0_temp=q0*q0_n -q1*q1_n -q2*q2_n -q3*q3_n; 117 float q1_temp=q0*q1_n +q1*q0_n +q2*q3_n -q3*q2_n; 118 float q2_temp=q0*q2_n -q1*q3_n +q2*q0_n +q3*q1_n; 119 float q3_temp=q0*q3_n +q1*q2_n -q2*q1_n +q3*q0_n; 120 q0=q0_temp; 121 q1=q1_temp; 122 q2=q2_temp; 123 q3=q3_temp; 124 float s=sqrt(q0*q0+q1*q1+q2*q2+q3*q3); //这里重新进行规范化,避免的累积误差 125 q0=q0/s; 126 q1=q1/s; 127 q2=q2/s; 128 q3=q3/s; 129 130 }
代码简介,我这里用的之前写的姿态解算的代码.这里进行100HZ的定时 ,在PC 上测试 10s ,运行结果如下图
其中姿态解算部分占用 3042us ,也就是说每次解算用时 3.042us .
我在am9平台下测试,结果如下:
在freescale cortex-a9 双核 测试结果
全志 cortex-a7 双核 测试结果
这里测试结果差别还是比较大的,姿态解算用了 188213us, 平均为 0.188ms ,相对与 10ms 的解算周期,占用还是比较小的.
补充一点,在arm9下linux 到200HZ还是可以的,但是就不太准了.这里我也同时测试了usleep函数和利用select()这个系统调用,延时都不理想.
下面介紹一下是利用RTC进行定时,下面的程序来自这里,http://www.linuxidc.com/Linux/2007-01/1821p2.htm
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #include <unistd.h> 5 #include <errno.h> 6 #include <fcntl.h> 7 8 #include <linux/rtc.h> 9 #include <sys/ioctl.h> 10 int main(int argc, char* argv[]) 11 { 12 unsigned long i = 0; 13 unsigned long data = 0; 14 int retval = 0; 15 int fd = open ("/dev/rtc", O_RDONLY); 16 if(fd < 0) 17 { 18 perror("open"); 19 exit(errno); 20 } 21 /*Set the freq as 4Hz*/ 29 if(ioctl(fd, RTC_IRQP_SET, 4) < 0) 30 { 31 perror("ioctl(RTC_IRQP_SET)"); 32 close(fd); 33 exit(errno); 34 } 35 /* Enable periodic interrupts */ 36 if(ioctl(fd, RTC_PIE_ON, 0) < 0) 37 { 38 perror("ioctl(RTC_PIE_ON)"); 39 close(fd); 40 exit(errno); 41 } 42 for(i = 0; i < 100; i++) 43 { /*Blocking read*/ 44 if(read(fd, &data, sizeof(unsigned long)) < 0) 45 { 46 perror("read"); 47 close(fd); 48 exit(errno); 49 } 50 printf("timer\n"); 51 } 52 /* Disable periodic interrupts */ 53 ioctl(fd, RTC_PIE_OFF, 0); 54 close(fd); 55 return 0; 56 }
程序说明:代码第 15行处的open()函数,非root下会被拒绝访问。RTC定时有一定的局限性,频率只能为2幂。
2013.7.28,进行 posix timer 接口的编程测试,测试代码如下:
1 #include <stdio.h> 2 #include <time.h> 3 #include <signal.h> 4 #include <pthread.h> 5 #include <string.h> 6 #include <unistd.h> 7 char flag; 8 void handle(union sigval v) 9 { 10 flag=1; 11 return; 12 } 13 14 int create (int ms,int id) 15 { 16 timer_t tid; 17 struct sigevent se; 18 struct itimerspec ts,ots; 19 memset (&se,0,sizeof(se)); 20 se.sigev_notify = SIGEV_THREAD; 21 se.sigev_notify_function = handle; 22 se.sigev_value.sival_int = id; //作为handle()的参数 23 if(timer_create(CLOCK_REALTIME,&se,&tid)<0) //create the timer 24 { 25 perror("timer_creat"); 26 return -1; 27 } 28 puts("timer_create successfully."); 29 ts.it_value.tv_sec = 0; 30 ts.it_value.tv_nsec =1000*1000*ms ; 31 ts.it_interval.tv_sec = 0; 32 ts.it_interval.tv_nsec = 1000*1000*ms; 33 if(timer_settime (tid,TIMER_ABSTIME,&ts,&ots) < 0) //start/stop the timer 34 { 35 perror("timer_settime"); 36 return -1; 37 } 38 return 0; 39 } 40 41 int main(void) 42 { 43 //create(3,1); 44 int num=0; 45 create(5,2); 46 while(num<2000) 47 { 48 if(flag) 49 { 50 flag=0; 51 printf("the num is %i\n",num); 52 num++; 53 } 54 55 } 56 printf("2013.7.28.11\n"); 57 return 0; 58 }
代码参考百度空间,在PC 环境 下运行的时间为 10s,在arm linux下定时 200hz ,实际运行的时间为 20S,所以是极为不准确的.这期间我现时进行了 gettimeofday()这个方法的测试,结果都不理想.总结这面这些代码 ,要想进行 ms 级的精确定时 ,只有到驱动层面进行编写相关程序.
博文为本人所写,转载请表明出处:博客园:梦工厂2012.