作者wtchen (沒有存在感的人)
看板C_and_CPP
標題[問題] 如何固定每個迴圈的執行週期?
時間Sun Nov 6 04:07:30 2016
開發平台(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):
四軸的程式龜速改寫中,不要催....
--
※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 90.41.211.206
※ 文章網址: https://webptt.com/m.aspx?n=bbs/C_and_CPP/M.1478376452.A.BE6.html
※ 編輯: wtchen (90.41.211.206), 11/06/2016 04:25:19
1F:推 EdisonX: 我沒記錯的話, 就算你都不用 delay, 接示波器後會發現 11/06 04:22
2F:→ EdisonX: 單一 gpio 週期是近 100K , 而且週期變動率會很大. 11/06 04:22
3F:→ EdisonX: rpi 的時間我覺得沒很準,在做這東西時建議至少接便宜的 11/06 04:23
4F:→ EdisonX: LA 看訊息會比計時的準很多。 11/06 04:25
5F:→ EdisonX: 我是買淘寶 300~500 ntd,24MHZ, 對 RPI 就夠用了。 11/06 04:27
6F:→ wtchen: 單一 gpio週期?我之前看有人實測是可以到5MHz以上 11/06 04:29
7F:→ EdisonX: 也可能我記錯週期吧, 我是用 bcm2835 去做控制。 11/06 04:31
8F:→ EdisonX: 補一下 , 網路上找到的資料建議驗證, 有些我手邊的情況 11/06 04:32
9F:→ EdisonX: 和網路找的不一樣。 11/06 04:32
10F:→ wtchen: 我看到的就是用bcm2835 goo.gl/bK0Cij 11/06 04:33
11F:→ wtchen: 不過我要求的頻率頂多400Hz,不算多誇張阿 11/06 04:34
12F:→ wtchen: 便宜的LA大都只吃Windows.... 11/06 04:35
13F:推 EdisonX: 是啊!所以是用 Windows SW 接 LA, Pin 腳接 RPI 沒錯啊 11/06 04:36
14F:→ EdisonX: 好吧,若您找到穩定的方式,請不吝分享,之前我專案有時效性 11/06 04:37
15F:→ EdisonX: 發現 PWM 不穩時,就掛一顆 MCU 上去,RPI 和 MCU 交握。 11/06 04:38
16F:→ wtchen: PWM有現成的chip可以加阿,為何非要MCU? 11/06 04:40
17F:推 EdisonX: MCU 不只做 PWM, 還有其他東西要加, 考慮成本當然掛 MCU 11/06 04:40
18F:→ wtchen: RPi只有一組PWM根本不夠我用,所以我加了顆PCA9685PW 11/06 04:40
19F:→ wtchen: RPi其實本來能做的事就很少(除非你非用linux不可) 11/06 04:41
20F:→ wtchen: 不然直接用MCU就好了.... 11/06 04:41
21F:→ EdisonX: 唉.. RPI .. 地獄.. 若可以的話我想轉戰 undo x86 11/06 04:42
22F:→ wtchen: udoo已經可以買了? 11/06 05:55
23F:→ wtchen: 是為啥非用RPi不可阿,BBB價位稍高但也跟RPi+MCU差不多了 11/06 05:56
24F:→ wtchen: 想聽你說RPi的地獄.... 11/06 05:56
25F:推 kevingwn: 試看看 11/06 06:01
26F:→ kevingwn: uint64_t timer = get_nsec(); 11/06 06:01
27F:→ kevingwn: while (...) 11/06 06:01
28F:→ kevingwn: { 11/06 06:01
29F:→ kevingwn: ReadData(); 11/06 06:01
30F:→ kevingwn: timer += 4000; 11/06 06:01
31F:→ kevingwn: int delay = (int)(timer - get_nsec()); 11/06 06:02
32F:→ kevingwn: if (delay > 0) _usleep(delay); 11/06 06:02
33F:→ kevingwn: } 11/06 06:02
不是不行,是因為我一定得用測量clock_gettime測量每個loop的時間
只要我clock_gettime的間隔太短會出問題
大概try了一下,至少間隔要在1ms以上才會穩定
35F:推 firejox: 你的按兩次碼錶的code如果read超過4000 nsec 不就不正 11/06 12:31
36F:→ firejox: 確了? 11/06 12:31
37F:→ firejox: 更正 4000us 11/06 12:33
38F:→ EdisonX: 會用 RPI 是因為它的相機解析度高、傳輸快、cost 低,加上 11/06 14:27
39F:→ EdisonX: 有 raspicam library 可直接呼叫 , 說 rpi 差也不對 , 只 11/06 14:28
40F:→ EdisonX: 是我在linux上有很多的不熟悉,像是權限常讓我綁手綁腳. 11/06 14:28
41F:→ EdisonX: 還有從 rpi2 轉到 rpi3 時 ur 的設定也花了時間, 最後轉 11/06 14:29
42F:→ EdisonX: 到 rpi3 時必須做整個系統散熱機制,要不 rpi 容易燒掉. 11/06 14:30
43F:→ EdisonX: 想轉戰udoo純粹是我現在必須開發 rpi/windows 二套 ap,轉 11/06 14:32
44F:→ EdisonX: 過去後我可以全在vs上做開發,但的確也還有一些問題要解. 11/06 14:32
45F:→ EdisonX: 補一下,我從rpi2轉到rpi3時,OS從1.4.2升到1.9.2也吃了苦. 11/06 14:45
46F:→ EdisonX: 當然現在用2.0.0,lib 和設定花了一、二天解掉 @@ 11/06 14:45
47F:→ EdisonX: 現在惱人的反而是在rpi3上寫qt,這時才意識到我被vs慣壞了 11/06 14:46
48F:→ EdisonX: code::blocks 上掛 qt 我反而不會 debug 11/06 14:46
49F:→ wtchen: LA這種東西怎麼只要一support linux價錢就10倍起跳? 11/06 17:16
※ 編輯: wtchen (90.41.211.206), 11/07/2016 01:29:00
50F:→ wtchen: 我在linux寫久了要我跳Windows我也不習慣 XD 11/07 01:32
※ 編輯: wtchen (90.41.211.206), 11/07/2016 01:34:15