前回は、超小型お手軽価格のRISC-V搭載GD32VF103開発ボード、Sipeed社Longan nanoを使って「ありがちな」PWM出力をやってみました。出力やったのだから、次は入力ということで、今回はPWM波形をTIMER3に入力し、その周波数とデューティを測定するサンプルを動かしてみます。
※「鳥なき里のマイコン屋」投稿順Indexはこちら
今回試してみるタイマの機能は一般に「インプットキャプチャ」として知られている機能です。外から何らかの信号の状態の変化(立ち上がりエッジなど)を捕まえてそのとき「走っているタイマ」のカウント値をレジスタに記録する機能です。この機能を使えば何時、外部イベントが起こったのかを知ることができます。多くの場合、外部イベントが起こったら、そこから起算してタイマカウントxxx後に何らかの制御のための出力をする(アウトプットコンペア)という使用方法が定石だと思います。これにより、マイコンのソフトウエアは設定をするだけ、タイムクリティカルな信号の入出力はタイマのハードウエアにお任せできるわけです。
前回のPWM出力は、一端ソフトウエア設定したら垂れ流しであったので、特に割り込みをハンドリングする必要はありませんでした。しかし、何時到来するかわからない入力信号をハンドリングする場合には、割り込みをハンドリングする必要があります。今回使用させていただいている
gd32vf103-sdk
の場合、スタートアップコードの中で各要因の割り込みハンドラの名前がweakラベルで定義されているので、その名前で割り込みハンドラを「オーバライド」してハンドラが定義できるのでした。詳しいことは「第92回、Longan nano、外部割込み」をご参照ください。
割り込みハンドラを調べてみると、ゴージャスな機能を持つTIMER0には4本もの割り込みが割り当てられているのですが、TIMER1以下の「普通の」タイマには1タイマあたり1本づつです。今回使用するTIMER3(Longan nanoの分かり易い場所に全CHの信号が並んでいるのでこれにしたのでした)の場合は、
TIMER3_IRQHandler
です。まともにハンドラを書くのはメンドイですが、SDKのお陰でほとんどすることはありません。ExamplesディレクトリのTIMER配下をみると、
TIMER2_pwminputcapture
という名のExampleがあるので、これをパクらせていただけば、ほとんど何も考える必要はありません。ただ、TIMER3なので、その部分は書き換えないとなりませんが。
以下に、ハンドラの冒頭部分のみ引用させていただきます。
void TIMER3_IRQHandler(void) { if(SET == timer_interrupt_flag_get(TIMER3,TIMER_INT_CH0)){ timer_interrupt_flag_clear(TIMER3,TIMER_INT_CH0); ic1value=timer_channel_capture_value_register_read(TIMER3, TIMER_CH_0 + 1; // 以下略
ハンドラはコピペで楽に終わりました。タイマの設定の方も同様にお楽ですみます。前回、TIMER3のPWM出力のときの設定から
-
- TIMER3 CH0の端子 PB6を出力設定から入力設定にして外部から信号を入力できるようにする
- 初期設定関数をExample内のものに入れ替えて、忘れずにTIMER3用に書き換える
単純なインプットキャプチャだと、外部から到来する信号の立ち上がりエッジと立下りエッジの両方を捕まえないとパルス幅が測定できないから、2チャンネルに信号入力しなければならないんじゃね、とも思ったのですが、そんな事はとっくにオミトオシでした。そしてSDKには外部入力のPWM波形を1端子で捕捉するための面倒な設定を一撃で行うために以下の関数が準備済です。
timer_input_pwm_capture_config()
実際には内部ではCH0とCH1の2チャンネルを使っているのですが(CH2とCH3には対応していないようです)、端子的にはCH0かCH1の1本で済みます。その上SDKのサンプル通りの設定にすると周期の頭でカウント値を0にしてくれるので、
-
- CH0には周期を示すカウント
- CH1には、パルス幅を示すカウント
が得られます。後の計算処理が非常に簡単。便利。
// 端子の設定(前回からの差分) void initializePeriph(void) { // 略 gpio_init(GPIOB, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_6); // TIMER3 CH0 <-- PWM signal input // 略 // TIMER3の設定 void timer_config(void) { timer_ic_parameter_struct timer_icinitpara; timer_parameter_struct timer_initpara; rcu_periph_clock_enable(RCU_TIMER3); // 略 timer_input_pwm_capture_config(TIMER3, TIMER_CH_0, &timer_icinitpara); // 略
外部PWM信号を捕まえるためのコードをLongan nanoに書き込んだ後、その外部PWM信号を生成するために
Analog Discovery2
に登場願いました。これの「Patterns」という機能を使うと16chまでのデジタル信号発生器としても使えるのです。このときの接続の様子はアイキャッチ画像を御覧ください。
まずは、パタンーンジェネレータにデューティ50%、100Hzの信号を発生するように設定しました。こんな感じ。
そのときTIMER3の割り込みハンドラがデバッグ用のUARTポート経由でPlatformIOのシリアルモニタに報告してきた周波数[Hz]とデューティ[%]は以下のようでした。
100Hzの設定が、99Hzになっていますが、デューティの計算はピッタリ。ま、整数型で切り捨て方向に計算しているので99HzはOKかと思います。
もう一点くらいパラメータを変えて測定してみます。今度は、20Hzでデューティ80%です。この変更パネルは、パターンンジェネレータの信号リストの真ん中辺にある編集ボタンを押すと現れます。
設定できたので、信号印加を再開してみます。
今回も20Hzのところ、19Hzですが、計算の都合なので致し方ありませぬ。デューティはちゃんと検出。
毎度ですが、SDKのお陰で「お手軽」。これでSDKの説明文書でも付属していたら言う事はないのですが、ま、コードを読んだ方が手っ取り早くて正確、という感じ。