[問題] 如何固定每個迴圈的執行週期?

看板C_and_CPP (C/C++)作者 (沒有存在感的人)時間9年前 (2016/11/06 04:07), 9年前編輯推噓5(5045)
留言50則, 5人參與, 最新討論串1/2 (看更多)
開發平台(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://www.ptt.cc/bbs/C_and_CPP/M.1478376452.A.BE6.html ※ 編輯: wtchen (90.41.211.206), 11/06/2016 04:25:19

11/06 04:22, , 1F
我沒記錯的話, 就算你都不用 delay, 接示波器後會發現
11/06 04:22, 1F

11/06 04:22, , 2F
單一 gpio 週期是近 100K , 而且週期變動率會很大.
11/06 04:22, 2F

11/06 04:23, , 3F
rpi 的時間我覺得沒很準,在做這東西時建議至少接便宜的
11/06 04:23, 3F

11/06 04:25, , 4F
LA 看訊息會比計時的準很多。
11/06 04:25, 4F

11/06 04:27, , 5F
我是買淘寶 300~500 ntd,24MHZ, 對 RPI 就夠用了。
11/06 04:27, 5F

11/06 04:29, , 6F
單一 gpio週期?我之前看有人實測是可以到5MHz以上
11/06 04:29, 6F

11/06 04:31, , 7F
也可能我記錯週期吧, 我是用 bcm2835 去做控制。
11/06 04:31, 7F

11/06 04:32, , 8F
補一下 , 網路上找到的資料建議驗證, 有些我手邊的情況
11/06 04:32, 8F

11/06 04:32, , 9F
和網路找的不一樣。
11/06 04:32, 9F

11/06 04:33, , 10F
我看到的就是用bcm2835 goo.gl/bK0Cij
11/06 04:33, 10F

11/06 04:34, , 11F
不過我要求的頻率頂多400Hz,不算多誇張阿
11/06 04:34, 11F

11/06 04:35, , 12F
便宜的LA大都只吃Windows....
11/06 04:35, 12F

11/06 04:36, , 13F
是啊!所以是用 Windows SW 接 LA, Pin 腳接 RPI 沒錯啊
11/06 04:36, 13F

11/06 04:37, , 14F
好吧,若您找到穩定的方式,請不吝分享,之前我專案有時效性
11/06 04:37, 14F

11/06 04:38, , 15F
發現 PWM 不穩時,就掛一顆 MCU 上去,RPI 和 MCU 交握。
11/06 04:38, 15F

11/06 04:40, , 16F
PWM有現成的chip可以加阿,為何非要MCU?
11/06 04:40, 16F

11/06 04:40, , 17F
MCU 不只做 PWM, 還有其他東西要加, 考慮成本當然掛 MCU
11/06 04:40, 17F

11/06 04:40, , 18F
RPi只有一組PWM根本不夠我用,所以我加了顆PCA9685PW
11/06 04:40, 18F

11/06 04:41, , 19F
RPi其實本來能做的事就很少(除非你非用linux不可)
11/06 04:41, 19F

11/06 04:41, , 20F
不然直接用MCU就好了....
11/06 04:41, 20F

11/06 04:42, , 21F
唉.. RPI .. 地獄.. 若可以的話我想轉戰 undo x86
11/06 04:42, 21F

11/06 05:55, , 22F
udoo已經可以買了?
11/06 05:55, 22F

11/06 05:56, , 23F
是為啥非用RPi不可阿,BBB價位稍高但也跟RPi+MCU差不多了
11/06 05:56, 23F

11/06 05:56, , 24F
想聽你說RPi的地獄....
11/06 05:56, 24F

11/06 06:01, , 25F
試看看
11/06 06:01, 25F

11/06 06:01, , 26F
uint64_t timer = get_nsec();
11/06 06:01, 26F

11/06 06:01, , 27F
while (...)
11/06 06:01, 27F

11/06 06:01, , 28F
{
11/06 06:01, 28F

11/06 06:01, , 29F
ReadData();
11/06 06:01, 29F

11/06 06:01, , 30F
timer += 4000;
11/06 06:01, 30F

11/06 06:02, , 31F
int delay = (int)(timer - get_nsec());
11/06 06:02, 31F

11/06 06:02, , 32F
if (delay > 0) _usleep(delay);
11/06 06:02, 32F

11/06 06:02, , 33F
}
11/06 06:02, 33F
不是不行,是因為我一定得用測量clock_gettime測量每個loop的時間 只要我clock_gettime的間隔太短會出問題 大概try了一下,至少間隔要在1ms以上才會穩定

11/06 11:39, , 34F
https://goo.gl/WgiaeR 這個 la, 其軟體有 linux 版本
11/06 11:39, 34F

11/06 12:31, , 35F
你的按兩次碼錶的code如果read超過4000 nsec 不就不正
11/06 12:31, 35F

11/06 12:31, , 36F
確了?
11/06 12:31, 36F

11/06 12:33, , 37F
更正 4000us
11/06 12:33, 37F

11/06 14:27, , 38F
會用 RPI 是因為它的相機解析度高、傳輸快、cost 低,加上
11/06 14:27, 38F

11/06 14:28, , 39F
有 raspicam library 可直接呼叫 , 說 rpi 差也不對 , 只
11/06 14:28, 39F

11/06 14:28, , 40F
是我在linux上有很多的不熟悉,像是權限常讓我綁手綁腳.
11/06 14:28, 40F

11/06 14:29, , 41F
還有從 rpi2 轉到 rpi3 時 ur 的設定也花了時間, 最後轉
11/06 14:29, 41F

11/06 14:30, , 42F
到 rpi3 時必須做整個系統散熱機制,要不 rpi 容易燒掉.
11/06 14:30, 42F

11/06 14:32, , 43F
想轉戰udoo純粹是我現在必須開發 rpi/windows 二套 ap,轉
11/06 14:32, 43F

11/06 14:32, , 44F
過去後我可以全在vs上做開發,但的確也還有一些問題要解.
11/06 14:32, 44F

11/06 14:45, , 45F
補一下,我從rpi2轉到rpi3時,OS從1.4.2升到1.9.2也吃了苦.
11/06 14:45, 45F

11/06 14:45, , 46F
當然現在用2.0.0,lib 和設定花了一、二天解掉 @@
11/06 14:45, 46F

11/06 14:46, , 47F
現在惱人的反而是在rpi3上寫qt,這時才意識到我被vs慣壞了
11/06 14:46, 47F

11/06 14:46, , 48F
code::blocks 上掛 qt 我反而不會 debug
11/06 14:46, 48F

11/06 17:16, , 49F
LA這種東西怎麼只要一support linux價錢就10倍起跳?
11/06 17:16, 49F
※ 編輯: wtchen (90.41.211.206), 11/07/2016 01:29:00

11/07 01:32, , 50F
我在linux寫久了要我跳Windows我也不習慣 XD
11/07 01:32, 50F
※ 編輯: wtchen (90.41.211.206), 11/07/2016 01:34:15
文章代碼(AID): #1O7Zm4lc (C_and_CPP)
文章代碼(AID): #1O7Zm4lc (C_and_CPP)