編集履歴一覧に戻る
lyricalmagicalのアイコン画像

lyricalmagical が 2024年05月02日01時30分05秒 に編集

初版

タイトルの変更

+

[Arduino UNO]pulseIn()のパルス幅測定精度を調べてみた

タグの変更

+

Arduino

+

Arduino_Uno

メイン画像の変更

メイン画像が設定されました

記事種類の変更

+

セットアップや使用方法

本文の変更

+

ArduinoのpulsIn()関数はどのくらいの精度で測定してくれるのかを調べてみました。 ターゲットはArduino UNO(SWITCH SCIENCE純正)です。 # やりたいこと 上記に書いた通りですが、pulsIn()関数がどのくらい正確なのかを調べてみたいと思いました。 公式のリファレンスによると、10μ秒から3分までを1μ秒単位で測定してくれることになっています。 しかし本当に1μ秒単位できちんと測定してくれるんですかね、というところを調べたかったです。 やるべきことは1μ秒単位で正確にパルスを出力できるものを作って、Arduinoに入れるだけです。 ではどうやってそのパルスを作るかというところですが、対向もArduinoだとさすがに検証にならないです。 シグナルジェネレータとかがあると簡単にできるとは思いますが、残念ながら持っていないので、水晶発振を分周してFPGAで作りました。 FPGAであれば1μ秒単位でのパルス幅の調整は容易に可能です。 # 実験風景 というわけでこんな感じになります。 ![](https://camo.elchika.com/39d62911a80f455d49577966aaaad635cf056716/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f65646136343062652d306132332d346233662d623939622d6635323639643463393636342f36616162653438372d313436372d346435372d623730632d343865333533633338396432/) # 実験ソース Arduinoは単純なパルス幅測定です ```arduino:Arduinoのパルス幅測定例 int pin = 19; unsigned long duration; void setup() { Serial.begin(9600); pinMode(pin, INPUT); } void loop() { duration = pulseIn(pin, HIGH); Serial.println(duration); } ``` FPGAのほうは可変の分周回路です。uart処理などは大きいので割愛しました。 ```verilog:FPGA(抜粋) reg [4:0] counter_24;//基準は24MHzクロック always @(posedge CLKI or negedge resetn) if (resetn==1'b0) counter_24<=0; else if (counter_24==0) counter_24 <= 11; else counter_24<=counter_24-1; reg [19:0] sigtime; always @(posedge CLKI or negedge resetn) if (resetn==1'b0) sigtime<=100000;//UARTから受けて増減する else if (rx_en==1'b1 && rxdata==8'h20) sigtime<=100000; else if (rx_en==1'b1 && rxdata==8'h61) sigtime<=sigtime-1; else if (rx_en==1'b1 && rxdata==8'h62) sigtime<=sigtime+1; reg [19:0] counter_M; always @(posedge CLKI or negedge resetn) if (resetn==1'b0) counter_M<=0; else if (counter_24 == 0 && counter_M==0) counter_M <= sigtime-1; else if (counter_24 == 0) counter_M<=counter_M-1; reg sig; always @(posedge CLKI or negedge resetn) if (resetn==1'b0) sig<=1'b0; else if (counter_24==0 && counter_M==0) sig<=~sig; reg sig2; always @(posedge CLKI or negedge resetn) if (resetn==1'b0) sig2<=1'b0; else if (counter_24==0 && counter_M==0 && sig==1'b0) sig2<=~sig2; assign SIGO=sig2; //出力信号 ``` # 結果(生値) 実際に測定したベタ値になります。測定は1μ秒~100000μ秒(=0.1秒)まで行いました。 真値:理論値。FPGAで出力したパルス幅。単位μ秒。 平均:Arduinoで測定したパルス幅。約50回の測定値の平均。単位μ秒。 最大:Arduinoで測定したパルス幅の最大値。 最小:Arduinoで測定したパルス幅の最小値。 振れ幅:最大と最小の差 誤差率:真値との誤差率 |真値|平均|最大|最小|振れ幅|誤差率| |---|---|---|---|---|---| |100000|99569.20|99574|99568|6|-0.431%| |99999|99568.22|99574|99567|7|-0.431%| |99998|99567.26|99573|99566|7|-0.431%| |99990|99559.31|99564|99558|6|-0.431%| |90000|89612.18|89613|89606|7|-0.431%| |80000|79654.21|79658|79651|7|-0.432%| |70000|69698.20|69702|69695|7|-0.431%| |60000|59741.51|59746|59739|7|-0.431%| |50000|49784.55|49790|49784|6|-0.431%| |40000|39826.71|39829|39822|7|-0.433%| |30000|29871.14|29874|29867|7|-0.430%| |20000|19914.09|19918|19911|7|-0.430%| |10000|9957.32|9962|9955|7|-0.427%| |9000|8961.88|8967|8960|7|-0.424%| |8000|7965.90|7971|7964|7|-0.426%| |7000|6970.39|6976|6969|7|-0.423%| |6000|5974.70|5980|5973|7|-0.422%| |5000|4978.79|4985|4978|7|-0.424%| |4000|3974.76|3981|3974|7|-0.631%| |3000|2965.16|2972|2963|9|-1.161%| |2000|1976.81|1984|1976|8|-1.159%| |1000|988.26|996|988|8|-1.174%| |950|939.08|946|938|8|-1.149%| |900|889.90|896|887|9|-1.123%| |850|840.25|852|837|15|-1.147%| |800|792.00|802|788|14|-1.000%| |750|742.20|752|737|15|-1.040%| |700|693.00|702|687|15|-1.000%| |650|647.98|652|645|7|-0.310%| |600|594.22|602|587|15|-0.963%| |500|494.79|502|487|15|-1.041%| |450|445.35|451|437|14|-1.032%| |400|397.33|401|387|14|-0.668%| |350|349.06|351|345|6|-0.267%| |300|299.42|301|295|6|-0.193%| |250|249.51|251|244|7|-0.196%| |200|200.00|201|195|6|0.000%| |100|99.93|101|94|7|-0.070%| |90|90.05|91|84|7|0.057%| |80|80.25|81|75|6|0.318%| |70|70.24|71|64|7|0.339%| |60|60.16|61|54|7|0.259%| |50|50.38|51|44|7|0.759%| |40|40.31|41|35|6|0.776%| |30|30.28|31|24|7|0.926%| |20|20.47|21|20|1|2.358%| |10|10.37|11|10|1|3.684%| |9|9.56|10|4|6|6.238%| |8|8.37|9|2|7|4.661%| |7|7.36|8|7|1|5.195%| |6|6.33|7|0|7|5.556%| |5|5.57|6|5|1|11.429%| |4|4.35|5|4|1|8.772%| |3|3.46|4|3|1|15.254%| |2|2.52|3|2|1|25.833%| |1|1.00|2|0|2|0.000%| ※10μs未満は公式には非対応です。 1μsを測定したとき、偶然かライブラリの都合かはわかりませんが、測定値「1」が返ってくることはありませんでした。 # 結果(グラフ) ![1μs~100000μs](https://camo.elchika.com/a488fe0bc6f8e8f36f1113fe66b30779e93074e2/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f65646136343062652d306132332d346233662d623939622d6635323639643463393636342f34633561613264342d343132352d346233382d626565342d333231323136333333613031/) 5000μ秒以下あたりから誤差大きくなっているのでその部分の拡大が下記となります。 ![10μs~5000μs](https://camo.elchika.com/6ceb7985854046942d61a750c39fa6958593b6d2/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f65646136343062652d306132332d346233662d623939622d6635323639643463393636342f37623563613365622d623932382d343463622d383831392d336565323161336134353766/) さらに1000μ秒以下以下を拡大したのが下記になります。 ![1μs~1000μs](https://camo.elchika.com/ee5e4b1bc6ed16dde8ca6cc9fc4d6696fc880f56/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f65646136343062652d306132332d346233662d623939622d6635323639643463393636342f37653730353732632d366265622d343934302d616661632d336665666531393536636265/) # 結論 ・0.1秒~5000μ秒くらいまでは誤差-0.4%程度で、若干早く測定される傾向があるようです。これはライブラリの都合な気がします。 ・1000μ秒以下あたりから誤差が大きくなってきます。 ・100μ秒以下あたりは正直使い物にならないレベルと思います。 というわけで、短いパルスを測定しようとする場合は、あまり測定結果を過信してはいけないということが解りました。 また、測定結果も数μ秒レベルでブレがあるようです。 pulseIn()で短いパルスを測定したいときは、このあたりも考慮して設計する必要がありそうですね。