以前の記事に書いたように、製作中のエンベロープジェネレータのブレッドボード上での動作確認では、D/A 変換に MCP4726 を使っているのですが、使用予定のマイクロコントローラの I2C 速度の制約のため、更新周期は 8 kHz 程度が限界で、しかもデータ送信のためにマイクロコントローラの時間をかなりの割合で使ってしまうことがわかりました。効果的なスケジューリングのコードを書く必要があり、実装が面倒そうです。さらに良くないことに、更新周期を変えてみる実験によって、実は更新周期は 8 kHz でもやや不満とわかってもう少し速くしたいのですが、データ送信が間に合わないためそれは無理そうです。では、マイクロコントローラ内部で高速に更新できる PWM を使う方が楽にファームウェアをコーディングできるのではないの?と思いそちらも検討してみました。
PWM をなぜ最初避けたのかというと、平滑化のためフィルタを外付けする必要があることと、振幅の分解能を上げると時間の分解能が下がるため良い妥協点を見つけるのが面倒だったのが理由です。しかしよく考えてみると、外付けの DAC を用いても得られる出力は最大 3.3 V と小さいのでオペアンプで増幅する必要があってどのみちオペアンプを外付けするし、DAC デバイスを使っても出力は階段状ですから結局フィルタは必要、ということで DAC でも PWM でも外付けアナログ回路の規模は変わりません。さらに PWM では DAC を外付けしなくて済む分全体の回路は小さくて済むでしょう。振幅と時間の分解能の妥協点を探るのは依然面倒ですがそこを詰めてみました。
MCP4726 を使っての実験から、エンベロープジェネレータの振幅の分解能は 12 bit 4096 ステップあれば十分ということがわかっています。ここから、11 bit、10 bit と減らして音の印象が変わるか試してみました。11 bit なら特に問題ないようですが、10 bit ですとどうも怪しく感じました。エンベロープジェネレータの DAC は 10 bit でやる作例が多いので僕の気のせいかもしれませんが、気になるので大事をとって 11 bit 2048 ステップでやることにします。この PWM を STM32C092 で生成する場合、クロック周波数が最大 48 MHz ですから更新周期は
と、約 23 kHz となります。DAC を外付けするより倍以上速くできます。良い感触です。PWM を平滑するフィルタは、23 kHz の半分ぐらいの 10 kHz をカットオフ周波数とした二次の RC フィルタを使うことにします。

出力を実測したのが以下です。赤線が平滑前の PWM 出力、青線が平滑後の出力の AC 部分です。測定レンジは 500 mV が最小でこれ以上縦軸を拡大できませんが、だいたい 10 mV から 20 mV の振幅で PWM 波形が漏れ出てくるようです。しかし、出力の電圧は 6V 程度が最大なためこの程度の PWM 波形の漏れでは音に影響は出ないようです。そもそも 23 kHz は可聴域を超えているので漏れても聞こえませんが VCO 波形と干渉して嫌なビートが出るようなこともありませんでした。

ということで、この振幅と時間の分解能で行けそうな感触が得られました。2ボイスで製作する予定なので、更新に使えるクロック数は最大で 1024、単純な ADSR 計算なら余裕でしょう。凝ったカーブを生成する場合演算量を気にする必要があるかもしれません。
その「凝ったカーブ」を生成してみました。まだ 1 ボイスで動かしているので、高速化など気にしないアホ演算でやりましたが何とか追いついているようです。出力の波形がガサガサに見えますが、この回路に限らず PicoScope ではゆっくりとした動きのある波形を測定するとこんなノイズが観測されがちです。どうもノイズの出方がすごく不規則ですし、オシロスコープ側の問題のようにも思えるんですが。アナログスコープではきれいに波形が取れますがそれを画像として記録にとるのは難しく。うまくないものです。良いデジタルスコープが時々欲しくなりますが、手持ちの機材で曲がりなりにも一応測定できているので入手はずっと後回しにされたままであります。

音も聞いてみましたが何とか大丈夫そうでした。この先は PWM 方式で行こうと思います。



