引き続き Analog3 エンベロープジェネレータの話です。1モジュールに 2 ボイス分入れる予定です。前回の記事に則ってエンベロープを実際に生成してみた実験の結果、更新周期は 125μS おき、8kHz ぐらいあるほうが望ましいことがわかりました。そうすると、STM32C092KCT のクロック周波数は最大で 48MHz ですから、6,000 クロックの間に1更新周期を終える必要があります。これは楽なのでしょうかきついでしょうか?エンベロープを生成する演算だけなら楽勝だと思いますが、ハードウェアモジュールとして動かすにはそれだけでは済みません。マイクロコントローラは、ざっと以下の仕事をこなさないといけません
- 各周期ごとに DAC にデータを送る(2チャネル分)
- エンベロープ値を更新
- ADC から操作パラメータを読み取り、エンベロープジェネレータの定数を更新する
- ADC からゲート信号を読み取る
- CAN メッセージを読んで必要なゲート信号を取り出す
この他に Analog3 モジュール管理の仕事もありますがそれは演奏中は走らないのでとりあえず無視します。しかしそれでも、これだけ盛りだくさんな仕事があります。STM32C0 のような安価なコントローラでこなしきれるでしょうか?
この中で時間に厳しくしかもコントローラを占有しそうなものは DAC へのデータ送信です。DAC は MCP4726 を使う予定で、データを渡すには I2C を使います。MCP4726 は 3.4Mbit/s の高速 I2C モードに対応していますが、コントローラの方が非対応で、400 kbit/s のモードを使わざるを得ません。DAC へのデータ送付には 2バイト必要なので、データの送付だけで 40μS 必要です。8kHz の周期は 125μS ですからかなり重たいです。ここで、データは裏に送ってハードウェアによきに計らってもらおう、と目論んで割り込みや DMA モードの I2C 送信を試してみましたが、オーバーヘッドが大きすぎるのか必要な速度が全く出ませんでした。つまり割り込みや DMA モードでの I2C 通信は使えません。I2C レジスタに貼りついて最速で次に進む通常モードなら十分な速度が出るのですが、レジスタに必要なフラグが立つのを待つのにポーリングを行うのでプロセッサを占有してしまい大変に効率が悪いです。試しにどれぐらいの時間プロセッサを占有してしまうか測定してみました。
測定のため更新周期を 4kHz に落として、I2C 送信メソッドのどの部分が時間を食うかわかるようにメソッドの要所にデバッグピンをトグルするコードを加え、デバッグピンの挙動を見てみました。

送信全体で 100μS ほどかかっています。この時間はハードウェアの制約で、短縮はおそらく無理です。I2C は、1バスに複数のデバイスを載せられますが、この感じですと、8kHz 更新周期では 1 バスに一個の DAC しか置くことができなさそうです。幸い使うコントローラは I2C バスを二つ持っているので両方が使えるか後日試してみます。
普通に HAL の送信メソッドを使うとこの 100μS の間メソッドがプロセッサをブロックしてしまうので使える残りは 25μS、こうなるとかなり厳しいです。したがって、もう少しメソッドを細かく分けて計算資源を有効に使う必要がありそうです。恐らく2バイトめを送るまでは処理の遅れは許されないでしょうから多少の無駄には目をつぶってコントローラを占有するのは仕方ないとして、2バイト目の送信が完了するのを待つ間は他の仕事をしていてもかまわないと思います。
そうすると、初めは設計上の選択肢から除外していた PWM による DA 変換も考慮に入れた方が良い気がしてきました。外付けフィルタをつけたくなかったので敬遠していましたが、PWMの更新はコントローラ内部でできるため I2C 通信に大きな時間を割く必要もないし、I2C バスを2本持つとコントローラのピンを4本占有しますが PWM なら2本で済みます。PWM には分解能を上げると更新周期が落ちるという欠点もあるので利点と欠点のバランスを考えて決めるのが良いかもしれません。PWM 方式で実用上十分ならファームウェアのコードはずっと単純に済みます。
スケジューラの実装はかなり大変ですしまだその仕組みを導入していないので、今のところは調べたことを記事に残すだけして、当面は現状のまま、でももう少しだけ更新周期に余裕を持たせてエンベロープ生成アルゴリズムの開発に集中したいと思います。






