前回の記事に書いたPT8211で音声を鳴らす制御はこのIC専用になってしまうので、もうちょっと汎用的に使えるI2S規格に則った波形を作ってみたいと思います。
I2Sの基本的な波形は下図の通りです。
出展:https://www.nxp.com/docs/en/user-manual/UM11732.pdf
I2SはPhilips(現NXP)社の独自プロトコルが事実上のスタンダードになった形のようで、
経緯としてはI2Cと同じですね。I2CもPhilipsの独自プロトコルでした。
波形を作るためにCPUでバスを制御しようとすると、音が鳴ってる間ずっと制御する必要があり、他の処理ができなくなります。
なので、バス制御はDMAにやらせて、CPUは出来るだけ別のことに使いたいというのが狙いです。
なので今回は、CH32V203のタイマーとDMAを使って信号波形を作っていきたいと思います。
基本はこの64サイクル(クロックを上げ下げして16bitデータを2回送る)を1つのデータとして作ってメモリに書いて、それをDMAが読み出して実行する形になります。
1サイクル目 WS=0, BCK=0, SD=0
2サイクル目 WS=0, BCK=1, SD=0
3サイクル目 WS=0, BCK=0, SD=1
4サイクル目 WS=0, BCK=1, SD=1
...
のように、BCK=0のタイミングでデータ(SD)を変化させ、BCK=1でサンプリングされるようにします。
実際のデータがどうなるかというと、
uint16_t dma_data[64];
uint16_t base = GPIOA & ~(WS_PIN | BCK_PIN | SD_PIN);
dma_data[0] = base;
dma_data[1] = base | BCK_PIN;
dma_data[2] = base | SD_PIN;
dma_data[3] = base | BCK_PIN | SD_PIN;
※このとき、 #define BCK_PIN GPIO_Pin_0 など、実際に駆動するピンです。
これは何かというと、データを入力する時点でのGPIOAの状態をbaseに保存(使用するピンはクリアして0に)して、
各サイクルごとの状態をOR演算で書いておきます。
こうすると、タイマーが呼ばれるごとに
GPIOA = dma_data[0];
GPIOA = dma_data[1];
...
が順次DMAで実行されることになります。
(DMAで実行されるのでこの間CPUはフリーです)
ただ、このやり方で困るのは、DMA実行中にIOポート(この場合はGPIOA)が
他の制御に使えなくなることです。
----ここまで書いて半年ほどが経ちました----
さて、この困った問題に画期的な対策を思いつきました。
それは「CH32V203C8T6を使うこと」です。
…ってまあ別に対策でも何でもないのですが、
このデバイスには「GPIOC」ポートがちょうど3ピンあるのです。
というわけで、ここをDMA-I2S専用ピンにしてしまおうというわけです。
これは実際上手くいきました。やったぜ。
そんでこの半年の間に、I2Sに対応した安価なDAC-ICを見つけました。
それが"NS4168"です。メーカーは…深圳市永福康科技有限公司?略称とかないのかな。
M5-Stack用のスピーカーベースにも使われているので知名度は高そう。
なのに情報は少なめ。特にCTRLピンの使い方に関する情報が全然ないです。
まあこれは今後色々試してみるとして、とりあえずプルアップしておけば良さそう。
出力はモノラル専用ですが、1個100円弱と、ちょっとした音を出すのにちょうどいい。
(秋月とかで売ってれば良いんですが、今はLCSCで買うしかない感じです)
長くなってきたのでいったんここで分けましょう。
とにかく、「安価に良い音を出すこと」を目指して進めていきます。


