[问题] 如何固定每个循环的执行周期?

楼主: wtchen (没有存在感的人)   2016-11-06 04:07:30
开发平台(Platform): (Ex: Win10, Linux, ...)
Raspberry pi model B+ / Raspberry pi 3 model B
编译器(Ex: GCC, clang, VC++...)+目标环境(跟开发平台不同的话需列出)
gcc 4.9
问题(Question):
之前希望能固定数据读取的周期,像这样:
while(...)
{
ReadData();
_usleep(3600); // 用nanosleep实作,不过单位换成us。
}
ReadData()是读取sensor的函式,它有可能读取1-5个sensor
(共用一个I2C,所以要设lock),
因为每个sensor更新资料的周期又不一样
void ReadData()
{
if (out_of_data0) readSensor0();
if (out_of_data1) readSensor1();
if (out_of_data2) readSensor2();
if (out_of_data3) readSensor3();
if (out_of_data4) readSensor4();
}
其中Sensor0跟Sensor1的周期最短,到没啥问题。
问题是Sensor2/Sensor3/Sensor4周期比较长,读取也需要200-400us。
只读0/1跟5个全读的所需时间有可能会差到500-600us。
之前为了固定住ReadData()的周期,我是这样实作的:
....
pthread_create(thread2, readSensor2); //细节不多写,会意就好
pthread_create(thread3, readSensor3);
pthread_create(thread4, readSensor4);
void ReadData()
{
readSensor0();
readSensor1();
if (out_of_data2) pthread_cond_signal(&cond[thread1]); // 唤醒thread2
if (out_of_data3) pthread_cond_signal(&cond[thread1]); // 唤醒thread3
if (out_of_data4) pthread_cond_signal(&cond[thread1]); // 唤醒thread4
_usleep(3600);
}
(这几个thread被设成读取完自动睡回去,等待下次被唤醒)
意思就是说,做完readSensor0/readSensor1就暂停然后开始计时,
计时的同时被唤醒的readSensor2/readSensor3/readSensor4可以开始工作。
这招让我的周期变化<1ms
(使用Preempt RT的情况,我想用xenomai应该会更好,等我程式完成的差不多就试)。
不过我不太满足就是,想找个可以用lock-free的方法。
试过一个方法是这样:
void ReadData()
{
uint64_t time1 = get_nsec(); //使用clock_gettime得到当前时间
if (out_of_data0) readSensor0();
if (out_of_data1) readSensor1();
if (out_of_data2) readSensor2();
if (out_of_data3) readSensor3();
if (out_of_data4) readSensor4();
uint64_t time2 = get_nsec();
_usleep(4000-(int)(time2-time1));
}
就是按两次码表,把执行的时间算出来,然后周期扣掉执行时间就是该暂停的时间。
结果发现clock_gettime太频繁会得到奇怪的结果(不稳定)。
不然还有一个办法:把整个ReadData()丢到可以被唤醒的thread去
void ReadData()
{
if (out_of_data0) readSensor0();
if (out_of_data1) readSensor1();
if (out_of_data2) readSensor2();
if (out_of_data3) readSensor3();
if (out_of_data4) readSensor4();
}
void period()
{
while(....)
{
pthread_cond_signal(&cond[thread_of_ReadData]);
_usleep(4000);
}
}
不过有个极小的风险是如果这个thread没有在暂停的时间内跑完就....
请问还有别的方法可以让周期的误差更低?
补充说明(Supplement):
四轴的程式龟速改写中,不要催....
作者: EdisonX (卡卡兽)   2016-11-06 04:22:00
我没记错的话, 就算你都不用 delay, 接示波器后会发现单一 gpio 周期是近 100K , 而且周期变动率会很大.rpi 的时间我觉得没很准,在做这东西时建议至少接便宜的LA 看讯息会比计时的准很多。我是买淘宝 300~500 ntd,24MHZ, 对 RPI 就够用了。
楼主: wtchen (没有存在感的人)   2016-11-06 04:29:00
单一 gpio周期?我之前看有人实测是可以到5MHz以上
作者: EdisonX (卡卡兽)   2016-11-06 04:31:00
也可能我记错周期吧, 我是用 bcm2835 去做控制。补一下 , 网络上找到的资料建议验证, 有些我手边的情况和网络找的不一样。
楼主: wtchen (没有存在感的人)   2016-11-06 04:33:00
我看到的就是用bcm2835 goo.gl/bK0Cij不过我要求的频率顶多400Hz,不算多夸张阿便宜的LA大都只吃Windows....
作者: EdisonX (卡卡兽)   2016-11-06 04:36:00
是啊!所以是用 Windows SW 接 LA, Pin 脚接 RPI 没错啊好吧,若您找到稳定的方式,请不吝分享,之前我专案有时效性发现 PWM 不稳时,就挂一颗 MCU 上去,RPI 和 MCU 交握。
楼主: wtchen (没有存在感的人)   2016-11-06 04:40:00
PWM有现成的chip可以加阿,为何非要MCU?
作者: EdisonX (卡卡兽)   2016-11-06 04:40:00
MCU 不只做 PWM, 还有其他东西要加, 考虑成本当然挂 MCU
作者: EdisonX (卡卡兽)   2016-11-06 04:42:00
唉.. RPI .. 地狱.. 若可以的话我想转战 undo x86
楼主: wtchen (没有存在感的人)   2016-11-06 05:55:00
udoo已经可以买了?是为啥非用RPi不可阿,BBB价位稍高但也跟RPi+MCU差不多了想听你说RPi的地狱....
作者: kevingwn (如云如风的人生)   2016-11-06 06:01:00
试看看uint64_t timer = get_nsec();while (...){ReadData();timer += 4000;int delay = (int)(timer - get_nsec());if (delay > 0) _usleep(delay);}
作者: descent (“雄辩是银,沉默是金”)   2016-11-06 11:39:00
https://goo.gl/WgiaeR 这个 la, 其软件有 linux 版本
作者: firejox (Tangent)   2016-11-06 12:31:00
你的按两次码表的code如果read超过4000 nsec 不就不正确了?更正 4000us
作者: EdisonX (卡卡兽)   2016-11-06 14:27:00
会用 RPI 是因为它的相机分辨率高、传输快、cost 低,加上有 raspicam library 可直接呼叫 , 说 rpi 差也不对 , 只是我在linux上有很多的不熟悉,像是权限常让我绑手绑脚.还有从 rpi2 转到 rpi3 时 ur 的设定也花了时间, 最后转到 rpi3 时必须做整个系统散热机制,要不 rpi 容易烧掉.想转战udoo纯粹是我现在必须开发 rpi/windows 二套 ap,转过去后我可以全在vs上做开发,但的确也还有一些问题要解.补一下,我从rpi2转到rpi3时,OS从1.4.2升到1.9.2也吃了苦.当然现在用2.0.0,lib 和设定花了一、二天解掉 @@现在恼人的反而是在rpi3上写qt,这时才意识到我被vs惯坏了code::blocks 上挂 qt 我反而不会 debug
楼主: wtchen (没有存在感的人)   2016-11-06 17:16:00
LA这种东西怎么只要一support linux价钱就10倍起跳?我在linux写久了要我跳Windows我也不习惯 XD

Links booklink

Contact Us: admin [ a t ] ucptt.com