mbedのLチカ周期を測定してみた


はじめに

どうも,としひろです.
今回は以前より気になっていた,mbedのクラスにある
「DigitalOutクラスのIO処理について,コードの違いによる制御周期の変化を」
オシロスコープを使って測定・検証していきます.
某先輩がディスプレイの制御で待機時間(1ms)を入れると見え方が変わるとおっしゃっていたのを機に思い出して測定してみました.

・・・・いやーこりゃ,たまげた結果が出ましたわ.
ライブラリという見えないブラックボックスを安易な気持ちで使用するべきではないと改めて感じました.

主な使用機材

NUCLEO-F401RE(ボード上のLEDを使用)
オシロスコープ(RIGOL DS1104Z 4Channel 100MHz 1GSa/s)
プローブ(60MHz)

*波形が見やすいように,時間軸グリッドは50.0nsで統一しております.

検証1~Lチカ反転手法~

検証1ではmbedのライブラリで用意されているDigitalOutを単純に使用して点滅させてみました.
ちなみに,myled = !myledはmyledの前の状態を反転させる,つまり’点’と’滅’を繰り返しなさいという命令です.

#include "mbed.h"   //include mbed library

DigitalOut myled(LED1); //generate instance

int main() //main function
{
    while(1) 
    {
        myled = !myled; //led blinky code
    }
}

次にボード上に搭載されたLEDには直接プローブを当てづらいので,LEDに接続されているピンにプローブを当ててオシロスコープで測定しました.
下の画像がオシロスコープ画面のスクリーンショットです.

分かったこと
・NUCLEO-F401でDigitalOutクラスライブラリにおいて,その制御周期は約476.0ns(0.4760us)で周波数は約2.10MHzであった.
・オンとオフにかかる処理時間が均等でない.(要検討)
・これはmyledの状態を反転させるために,状態の内部情報を保存したりという作業が行われていると予想している.

検証2~Lチカ随時切替手法~

検証2ではmbedのライブラリで用意されているDigitalOutを’1’から’0’を随時切り替えた時の点滅周期などを測定しました.

#include "mbed.h"   //include mbed library

DigitalOut myled(LED1); //generate instance

int main() //main function
{
    while(1) 
    {
        myled = 1;  //led is high
        myled = 0;  //led is low        
    }
}

同様にして,オシロスコープでポート出力電圧を測定しました.

分かったこと
・NUCLEO-F401でDigitalOutクラスライブラリにおいて,その制御周期は約190.0ns(0.1900us)で周波数は約5.26MHzであった.
・ポート出力電圧(印加電圧)がI/O電源3.3Vであるが,3.3Vに到達する前にLowの処理が実行された.
・オンとオフにかかる処理時間は,サンプリングごとに最大約±0.5%の不均等は見られたが,おおむねオンとオフの時間は均等であった.(要検討)

追加検証1(waitで点滅時間は制御できるか)

結論:wait関数を使って最短LEDオンオフを微調整するのは困難

検証1と検証2の結果から,LEDの点滅を正確かつ高応答にするためには
HighとLowを随時切替えたほうが早くなりました.
しかし,検証2のLチカ随時切替手法においてLEDがきちんと電源電圧まで立ち上がらずにLow処理が実行されるという現象が発生していました.
そこで,wait関数を入れて電源電圧まで上昇するまでにかかる時間を確保してみました.

#include "mbed.h"   //include mbed library

DigitalOut myled(LED1); //generate instance

int main() //main function
{
    while(1) 
    {
        myled = 1;      // LED is high
        wait_us(0.010); // wait 10ns
        myled = 0;      // LED is low
        wait_us(0.010); // wait 10ns
    }
}

オシロスコープの測定結果です.

・・・あれ,いつまで経ってもLowにならんやないかい!

・・・まあ,想定の範囲内ですが.

wait関数自体にも「wait時間+waitするための処理」の時間がかかる訳で,
今回の検証結果から,10usといった超短時間のwait処理は出来ない.
と分かりました.
ただ,立ち上がり時間,立ち下がり時間は変わらず,約30nsでした.
補足ですが,
wait_us()関数をwait()関数に置き換えたら0.3usほど遅くなりました.

追加検証2(ネタコードで意地でも余裕時間確保)

ぱっと思いついた方法として,
オン時間とオフ時間がそれぞれ2倍確保すれば,お手軽じゃね
っていうことで,こんなネタコードを書き込んでみました.

#include "mbed.h"   //include mbed library

DigitalOut myled(LED1); //generate instance

int main() //main function
{
    while(1) 
    {
        myled = 1;      // LED is high
        myled = 1;      // LED is high
        myled = 0;      // LED is low
        myled = 0;      // LED is low
    }
}

オシロスコープの波形はこうなりました.

こんなネタコードでも思い通りになるもんですね・・・(笑)
スマートな書き方ではないので,製品実装とかに使ったら間違いなく怒られそう....

結論

・!myled処理コードは単に視覚的なデバッグ等でLEDを点滅させる目的においては有効な処理コードである と言えるが,LEDの点灯時間,DUTY比は不確定になる可能性がある.

・mbedのDigitalOutクラスを使用するときに,高速IOをしたい場合は面倒だが,

myled = 1;
myled = 0;

と記述することで,制御周期が早くなる.
しかし,LEDをきちんと点灯,点滅させたい(または,点滅時間を確保したい)
場合には,

myled = 1;       //95ns
                 //10ns程度の待機処理が正確にできる関数(オン余裕時間)
myled = 0;       //95ns
                 //10ns程度の待機処理が正確にできる関数(オフ余裕時間)

といった感じで,非常に小さな待機時間を作ってくれる関数(wait以外)を挿入することで,点滅のDuty比を50%に保ちつつ,点滅時間を制御できることが分かった.
お手軽にオン,オフ余裕時間を確保したい場合はコードを重複させるとお手軽にできる.

・検証1,検証2,追加検証において,LEDの立ち上がり時間と立ち下り時間は
どれも約30nsで大きな差異はなかった.
→時定数はプログラム依存ではなく,回路依存(当然?)

さいごに

なんか・・・論文みたいになってしまい,すみませんでした.

なぜなぜという点に関してはmbedライブラリの深層コードに関することなので,
追求はできると思いますが,相当な時間消費が懸念されます.
なので,私はここまでの追求で一旦妥協します.

役に立ったらうれしいです.

気が付いたら6時間程作業してた.寝よ