[Windows IoT Preview x RaspberryPi2]_讓HCSR04 Ultrasonic Sensor 超音波感測器運作在Windows IoT上
Windows IoT推出時大家對這個全新的OS在RPI2上面有滿滿的憧憬(還是其實只有我有???)
但實際上在實做的時候碰到了不少問題,我第一個碰到最嚴重的問題就是Sensor沒有Drvier,全部要自己的Driver自己寫…
又因能取得的底層開發的資料有限,所以第一步也沒有直接寫到Native C的程度,
而是用C#來做這件事情,幸好Ultrasonic的測距方式非常容易
是利用一個發送超音波脈衝,另一個接收,所以換成公式就是
(接收時間-發送時間) * 340.29(音速每秒340.29m) / 2(來回距離) = 相隔距離(meter)
Sample Download repo: https://bitbucket.org/thkaw/windows-iot-10-for-rpi2-hcsr04-ultrasonic
在我附上的sample project中,GPIO 23接echo Pin,24接TriggerPin
然後讓TriggerPin通一個高電位打出一個超音波脈衝,然後等echoPin讀取到這個脈衝後,
在計算當初TriggerPim打出去的時間跟讀取到的時間差,再套用上面的公式
但在做的過程中比較tricky的地方在於不能用Eventhandler的方式去做echoPin的ValueChangeEvent
原因在於目前Windows IoT還是有透過一層CLR執行,所以不會這麼即時
如果透過非同步的作法,例如用Task.Delay().Wait()或者Async等動作,
都會讓RaspberryPi2每一行C# Code約會慢到100~200ms,這個時間其實沒辦法解析出正確的距離,
所以在Timing Sensitive的Code,最好遵守下列建議(外國微軟同事內部討論出來的解法):
- 避免有UI Thread(應該是指用Windows IoT Template建立專案,這樣就不會有UI的部分)
- 避免使用使用Async方法
- 用System.Diagnostic.StopWatch與While迴圈來檢查經過的時間(類似取代delay)
甚麼叫做StopWatch間隔呢?
例如我要送出一個500ms的高電位:
_triggerPin.Write(GpioPinValue.Low); //Task.Delay(TimeSpan.FromMilliseconds(500)).Wait(); sw0.Start(); while (sw0.Elapsed.TotalMilliseconds < 500) ; sw0.Reset(); _triggerPin.Write(GpioPinValue.High);
是透過while迴圈持續檢查StopWatch所經過的時間
非常迂迴對吧?…
但這也是目前可以reach到一個200us週期方波反應時間的最佳方式,這個方式拿示波器去看跟真正print出來的值是有落差的,debug print出來的值可能跟你說已經到10~40us,但實際上並不會reach到這種程度…
下面這個連結,也有人做過類似的測量,結果相仿:
http://mattisenhower.com/2015/05/08/raspberry-pi-2-windows-iot-core/
好,這就是沒辦法很直覺的用ValueChangeEvent等方式快速的去判斷送出/接收的時間差
但透過stopwatch,時間精度夠高,就可以抓到正確的數值
最後是透過這一段放在我所寫的UltraSonicSensor物件底下的Timing()做到:
_triggerPin.Write(GpioPinValue.High); now = DateTime.Now; sw0.Start(); while (sw0.Elapsed.TotalMilliseconds < 0.01) ; Debug.WriteLine("Trigger Pulse length: " + sw0.Elapsed.TotalMilliseconds); //參考用 sw0.Reset(); _triggerPin.Write(GpioPinValue.Low); while (_echoPin.Read() == GpioPinValue.Low) ; pulseLength.Start(); while (this._echoPin.Read() == GpioPinValue.High) ; pulseLength.Stop(); //Calculating distance TimeSpan timeBetween = pulseLength.Elapsed; //Debug.WriteLine("time diff : " + timeBetween.ToString()); double distance = timeBetween.TotalMilliseconds * 34.029 / 2.0; Debug.WriteLine("Distance(cm): " + distance.ToString());
眼尖的人可以發現倒數第二行為何是乘上34.029呢?
因為要取得公分,而且時間差是ms不是sec,差了1000倍,一來一往就等於乘上0.1
所以就直接換掉啦~,其實除2也可以直接化簡,節省資源,但好懂好解說還是在code裡除2吧~
另外需要注意的是Debug.WriteLine寫出stopwatch的elaspsed不一定是真實的週期,
所以…做電子電路還是弄台示波器來吧!
還有經過測試,這個版本如果<20cm,精度會開始飄移3~5公分,超過30cm之後比較穩定且準確。
本sample已經上傳bitbucket,可以自由取用修改,如執行的時候有send signal跟Trigger Pulse出現在VS的Output
但卻遲遲沒有Distance,可以檢查一下接線跟換一下反射的物體,拉長一點距離會比較ok~
還有這個專案是開在Windows Universal Template底下,如果換成Windows IoT Core Template也可以運作,沒有UI影響在時間差上會更準確,
但我懶得弄了 :P,請各位自行把MainPage.xaml.cs Main()內的code還有下面的Ultrasonic Sensor物件移動到IoT Core的StartupTask.cs內的Run()吧…
以上,還在跟DHT22 1-wire十分要求Timing的通訊協定奮鬥,希望有機會try出來。
Leave a comment
很抱歉,必須登入網站才能發佈留言。