PSoC5LP Prototyping Kitで作るUSBオーディオI/F(I2S出力対応)

 PSoC5にはCE95390 - USB Audio with PSoC 3/5LPというUSBオーディオの公式サンプルがありますが、32kHz 8ビット モノラルなんですよねー。ということで、これを96kHz 16ビット ステレオに拡張しつつ、内蔵の8bit DACだけでなく外部DACも利用できるように、I2S出力にも対応してます(*´▽`*)

 前置きはいいからとりあえず鳴らしてみたい!という方は、下記のダウンロード先に最小限の説明があるので、それを見て試してみてね(●´ω`●)


PSoC Creator用プロジェクトのダウンロード


必要な部品

必須パーツ

オプションパーツ(無くてもOKです)

  • I2S DACボード
    マスタークロックを自己生成できるのものが使えます。信号電圧が3.3Vならそのまま使えますが、5V駆動の場合はTopDesign.cysch内のI2Sコンポーネントの出力先を変更して、3.3V生成部分を使わずピン直接出力すればOKです。私は、PCM5102A搭載のちっこいボードを買いました(送料込み512円)。
    Interface I2S PCM5102 DAC Decoder GY-PCM5102 I2S Player Module For Raspberry Pi
  • 16x2 LCD(LCD1602)
    I2C接続で信号電圧が5Vのもの。よくあるArduino用のが使えます。繋いでおくと、Audio出力のON/OFF状態と現在の再生周波数が表示されます。
  • アンプボード
    アンプ内蔵スピーカーで鳴らす場合は不要ですが、私は持っていなかったので、お安いD級アンプボードを買いました(送料込み71円)。
    new 1pcs PAM8403 mini 5V digital amplifier board with switch potentiometer can be USB powered
  • スピーカー
    D級アンプって、ヘッドフォン端子のように左右のマイナス端子を共通にできないんですね(゜ロ゜)ということで、左右のマイナス端子が独立してるちいさめのボックススピーカーを買いました。千円ちょいくらいのを3種類買って聴き比べてみましたが、下記の1,168円のが一番気に入りました(≧∇≦)b
    AIYIMA 2Pcs Audio Speakers 10045 LED TV Speaker 8Ohm 5W Double diaphragm Speaker Bass 10045

    ところで、D級アンプの出力ってどうなってるの!?

    D級アンプの出力は原理的にBTL(「Balanced Transformer Less」,「Bridged Transformer Less」,「Bridge-Tied Load」)になるようで、例えば5V駆動なら、スピーカーのマイナス端子とプラス端子を「0Vと5V」「0Vと0V」「5Vと0V」の3つの組み合わせに高速に切り替え、3値(+5V、0V、-5V)のPWMで鳴らすような仕組みのようです。そのため、プラス端子だけでなくマイナス端子も0Vと5Vの間で高速に切り替わっていて、さらにステレオ音源なら左右でパルスの位相があわない期間(左スピーカーのマイナス端子0V、右スピーカーのマイナス端子5Vとか)があるので、「ヘッドフォンに繋ぎたいから左右のマイナス端子同士をつないでGNDにしよう(*´▽`*)」とかすると、おもいっきりショートします(((( ;゚д゚)))アワワワワ(というか、させました(*ノノ))一応、ヘッドフォン端子にもつなげられるように、マイナス端子を共通化(アンバランス化)する方法はあるようです(゜◇゜)

ピンアウトと部品の接続

 ピンアウトと部品の接続は下記のとおりです。
  • C2は容量的に電解コンデンサになりますが、PSoC側を+にしてください。
  • PSoCの基板にはUSB端子が2つありますが、microUSBのほうがUSBオーディオI/Fとして認識されます。標準サイズのほうは、デバッグ用のシリアル出力やEZI2Cを使った内部モニタリングに使用しています。
P0[6] --- 内蔵DACのスピーカー出力(左)
P0[7] --- 
内蔵DACのスピーカー出力(右)

P1[6] --- LCD1602 I2C SDA
P1[7] --- LCD1602 I2C SCL

P12[2] --- I2S SCK (BCLKという表記の場合もあります)
P12[3] --- I2S WS (LRCLKという表記の場合もあります)
P12[4] --- I2S SDO (SDATAという表記の場合もあります)

P15[0] --- C1(100pF) --- GND
P15[2] --+
   R1(100kΩ)
P15[5] --+-- C2(1uF) --- GND

Breadboard image


I2Sコンポーネント用のビットクロックについて


今回のPSoC内の回路は、大まかに
  • USBインターフェース
  • I2Sのビットクロック生成用のVCO(Voltage Controlled Ocsillator)
  • 内蔵DAC部
  • I2S出力部
から成ります。このうち、ビットクロック生成が一番苦労したところでした。というのも、I2Sコンポーネントのデータシートを読んで分かったのですが、ビットクロックってサンプリング周波数*送信データのbit幅(16bitステレオなら32)*2の周波数が必要なんですねー(゜ロ゜)この「bit幅*2」があるせいで、

  • 44.1kHzなら44100Hz*32*2=2.82MHz
  • 96.0kHzなら96000Hz*32*2=6.14MHz

というMHz帯のクロックが必要になっちゃうのでした。でも、PSoC5LPで使えるクロックは最大でも64MHzで、これを分周してビットクロックを生成しようとすると

  • 2.8224MHzを作りたい場合:64MHz/23=2.78MHz or 64MHz/22=2.91MHz
  • 6.1440MHzを作りたい場合:64MHz/11=5.82MHz or 64MHz/10=6.40MHz

Σ (゚Д゚;)えーっと・・・
分周比が1変わるだけでこんなに周波数が変わるんじゃ、2.82MHzや6.14MHzなんて生成できるはずがないじゃないですかーやだぁ(´・ω・`)

じゃぁI2Sを使ったオーディオ回路を作ってる皆さんはどうしてるのー?と思って調べたのですが、一般的には、USBホストから1ms周期(1kHz)で送られてくるSOF(Start of Frame)パケットを基準に、PLL(Phase Lock Loop)というアナログ回路を使って、これに同期するような所望のクロックを生成するようです。

PLLの仕組みも調べたのですが、VCO(Voltage Controlled Oscillator)という電圧で周波数を変更できる回路を使って、「入力周波数」と「生成した周波数を分周したもの」を比較し、その位相差をVCOの電圧にフィードバックして、入力波とぴったり同期した逓倍波を生成するようです。ということで、あまり外部部品は導入したくなかったのですが、Cypress公式にあったVCOのサンプルを参考に、充電用コンデンサ(C1=100pF)とPWMを平滑化するLPF(R1=100kΩ、C2=1μF)を追加して、VCOを作りましたヽ(´▽`)ノこれは、
  1. IDACで生成した定電流を外付けコンデンサに入れ、コンデンサを充電する
  2. コンデンサの電圧とPWMで生成した電圧をコンパレータで比較し、コンデンサの電圧がPWMで生成した電圧に達したらコンデンサを放電する
というものです。IDAC出力は電圧ではなく電流が一定なので、ノコギリ波の立ち上がり速度は一定になります。そのため、ノコギリ波の山の高さと1波長の周期は比例します。そこで、PWMのディーティー比を変化させて比較用の電圧を変えれば、任意の周波数のノコギリ波を生成できる、という仕組みです。

あとは、所望の周波数になるようにVCOの電圧を制御すれば出来上がりです!ね、簡単でしょう?

・・・

( ・ω・)∩センセー
その制御はどうするんですかー?

はい、これこそが最大の問題で(´;ω;`)ウッ…
コンデンサと抵抗の精度や温度変化のため、どの電圧にすればどの周波数になるのかは動かしてみないと分からないので、狙った周波数になるように電圧を常に調整する制御アルゴリズムが必要なのでした(´・ω・`)
今も試行錯誤してるのですが、とにかく、現在の成果を是非ご覧くださいヽ(´▽`)ノ

44.1kHz用の2.82MHzクロック


96kHz用の6.14MHzクロック

どうでしょう、うまく出てますかにゃー((=゚Д゚=)


最後に、既知の問題(*ノノ)

 何かを再生したままWindowsのスピーカーのプロパティでサンプリングレートを変更すると、VCOの周波数が追従して安定するまでの間、ちょっとだけ「ぎゅぅぅん」ってなります(´・ω・`)周波数が安定するまでミュートするとかできればいいんですが、今後のバージョンで対応したいと思います(  `・ω・)ノ 

コメント

このブログの人気の投稿

ESP32: Visual Studio CodeでESP-IDFを使う(Windows編)

ESP32: Visual Studio CodeでESP-IDFを使う(Linux編)