この投稿は マイクロマウス(2) Advent Calendar 2024 の 12/24 の記事です。
私はマイコンのペリフェラルをいっぱい使っている方だと思いますので、事例を紹介してみようと思います。
はじめに
マイコンのペリフェラルを使い倒す事により低CPU負荷で高精度・高処理能力を得る事ができます。
以降では、さくらねずみ玄2の光センサでの実装を例として解説します。 MUX式光センサ回路(をベースに受光もMUXするように改造したもの)を搭載しています。
この測定を行うための下記の処理をすべてハードウェアに自動的に行わせています。
- GPIOによるチャンネル切り替え
- IOで発光の制御(またはDACで発光量の調整)
- AD変換
また、私の今の設定では出力波形とADの時間分解能は、1us となっており、
センサの応答波形の中から、1us刻みで好きな場所を選んでセンサ値を採用する事が可能です。
(今は行っていませんが、1msの間に4回ではなく20回とか、もっと細かく切り替える事も可能です。)
メリット・デメリット
マイコンのペリフェラルを使い倒す事によるメリット・デメリットを紹介します。
マイコンのペリフェラルを使い倒す事によるメリット
- 信じられないほどの処理をマイコンに行わせる事ができる
- 未だに自分でも信じられない事があるくらいいっぱい処理できます
- 厳密なタイミングで処理を行う事ができる
- usオーダーのタイミングをソフトウェアに任せるな
- 設計がパズル感覚で楽しい(楽しめるかどうかは個人差がありそうですね)
マイコンのペリフェラルを使い倒す事によるデメリット
- 設計で考慮すべき項目が増える
- マニュアルを読むのが辛すぎる
- 3000ページのリファレンスマニュアルのうち、500ページくらいは熟読する覚悟が必要です
- デバッグが辛すぎる
- メーカー提供のペリフェラルライブラリが使えないかも?
- メーカー提供のライブラリでは設定し切れなかったり、バグを踏む事がそこそこあるイメージ
- 私は最近はライブラリを使っていないので最近の事情があまり分かっていません
- 機能の変更が難しい
- カッチリと作り込んでしまうので、機能を変更する難易度がソフトウェアによる実装よりも難しくなります
実装の紹介
※以下、ペリフェラルの名称はSTM32H562のものになりますが、自身の採用しているマイコンに置き換えて見てみてください。
以下が光センサを構成するペリフェラルの組み合わせです。
本来はもうちょっとエレガントな構成になるはずでした。
基板の設計ミスとやりたい事が増えた影響で、ペリフェラルの割当に余裕がなくなった結果、DMAで殴る構成となってしまっています。
処理の流れは以下のような物です。
- TIM15を1MHz周期で動作させる
- コンペアマッチ1でCC1信号を出す
- タイマー周期更新タイミングでTRGO信号を出す
- CC1信号を受け、GPDMA2_C1がRAMからGPIOに発光用パターンを転送する
- 転送ごとにRAM側アドレスをインクリメントし、1000回の転送毎にループするように設定する
- (1kHzでRAM上に用意した特定パターンを繰り返すようになる)
- TRGO信号を受け、GPDMA2_C2がRAMからGPIOにMUX選択パターンを転送する
- 上記と同様に1000回転送でループするように設定する
- TRGO信号を受け、ADCが光センサ電圧のA/D変換を開始する
- A/D変換完了をトリガとしてGPDMA2_C0がA/D変換結果ADC1からをRAMに転送する
- A/D変換の転送は2000回で一周するように設定する
- GPDMA2_C0が1000回転送した時点で半転送割り込みが入り、2000回で転送完了割り込みが発生する
- A/D変換結果はダブルバッファされている状態になり、DMAが書き込む領域と、SWが読み出す領域が切り分けられる
- 割り込みは1ms周期で発生する(TIM15の1us x 1000)
- 割り込み発生時点で全てのデータがRAM上に揃っており、後は純粋にRAM上の値を処理するだけ
処理の流れを見ても、CPUが全く介入していない事がわかると思います。
タイマーをトリガとして動作する部分は下図のような感じになっています。
DMAによって転送されるメモリを図示すると下図のようになります。
AD変換の結果はソフトウェアからアクセスする為に2000要素となっており、
半転送完了割り込みが発生した後はBank0から読み出し
転送完了割り込みが発生した後はBank1から読み出し
とする事でDMAとCPUのデータ競合を防ぎます。
バッファにいっぱいRAMを使っている気がしますが、(4kB + 4kB + 2kBx2x2 = 16kB).
今回採用しているマイコンはRAM640kBあるのでかすり傷です。
補足
-
実際に動かしている例で出したかったので、こうしましたがこの例はかなり力技です。
- ピン配置やペリフェラルの設計が立っていれば、下記のようにより良い構成にできる可能性があります。
- チャンネル選択信号はタイマーで2相のPWMとして生成する
- 発光信号はチャンネル選択タイマーのスレーブとしてワンショットパルスを生成するようにタイマーを設定する
- ワンショットタイマーの信号以外のタイミングでA/Dトリガーをかける
- ピン配置やペリフェラルの設計が立っていれば、下記のようにより良い構成にできる可能性があります。
-
DMAを使うとCPUは使いませんが、バス帯域を持っていかれるので、結果的にCPUがバス待ちさせられる時間が増えるリスクがあります。
- 今回の例くらいならそこまで影響ないと想像していますが、検証方法を知らないので本当に大丈夫かはわかりません。
- バスマトリックスの経路がかぶらないようにすると良かったりするらしい?
- 今回の例で言うと、AD変換結果のBank0とBank1を違うバスにつながっているSRAMブロックに割り当てると、CPUとDMAのバス衝突が起きづらくなったりするはず?
-
今回始めて使ったのですが、STM32のGPIOにあるBSRR便利ですね。
- 今回の例のような遅い信号ならタイマーとDMAを組み合わせて、簡易タイマー出力みたいな事ができました。(設計ミスのリカバリー)
-
(参考)前作の時に出力してみた、光センサの応答波形(A/D変換バッファの配列部分をPCに転送してグラフ化したもの, MUX式ではないので複数chの変換結果を重ねている)
- フォトトラの過渡応答が見えているだけですが何となく嬉しい気分になりますね
やっとのまともなDAからのまともなAD。
— sshun (@sshun_robot) September 26, 2019
というかだいぶ前からちゃんと動いていたっぽいがデータの出力方法が間違っていた。 pic.twitter.com/kKug8NdZzM
- (参考)前作の時に試してみていた、光センサの定電流回路のリファレンス電圧をDAから与えて変調してみるテスト。
- 今作も、発光パターンとなっているピンは実はDAのピンになっており、こういう事ができます。
- センサ読み出しの時間差が問題になるような次元になったら、発光をCDMA変調して全センサ同時取得、とかもできるかもしれません。夢がありますね。
体調不良で早退したのにマウスやってしまった。
— sshun (@sshun_robot) September 24, 2019
光センサの定電流回路、DAからのrefとFB抵抗の計測値。
かなり良い感じである。 pic.twitter.com/YODG7bfM0G
もっと色々やっている事はあるので、気が向いたら紹介します。
おわりに
マイクロマウスとしての性能に直接関わる所ではありませんが、
パズル感覚で楽しめる&マイコンのポテンシャルを感じられるので、
時間のある時に面白い事を実現できないか、考えてみてはいかがでしょうか?