2019年4月2日火曜日

ラズパイにADC PCM1803A(master)をI2S接続する地雷回避情報

音声信号をPCのwav fileに落とす装置を作りたい.

もっともそれは以前のこの投稿にてPC+Linux+ALSAで実現済みなのだが、PCにアタッチしてゴニョゴニョするのではなく、なるべくADC装置単体で動かせるような物を作りたくなった.

Arduino UNO+usb host shield で作れないかなぁと思ったが、UNOのCPUにはI2S interfaceがHW実装されていない.96kHz24bitのI2S信号をUNOのCPUで毎bit処理して速度が足りるはずが無い.よってArduino UNOを利用するのは断念.

ArduinoにはARMが乗ってる派生機種があるらしく、そういった機種ではI2S HWが実装されているので円満に処理できそう.でも手持ちが無いので断念.

USB2.0のEZ-USBを使ったらどうか? EZ-USBはI2S IFを内蔵していないので、FPGAを搭載して、ADC→I2S→シリパラ変換(FPGA)→EZ-USB→USB2.0 という信号の流れにせざるを得ない.FPGAを追加する気はないので却下.

それじゃぁと持ち出したのが、使ってない Raspberry pi 3B+ だ.これならI2SがHW実装されているのでなんとかなるだろう.

ADCはなるべく単純なものがいい.外付け部品が少なくて、CPUからの設定が不要で、しかも値段が安いICを探した.TIのPCM1803Aがお手軽で良さそうだ.96kHz24bit、I2Sなのでspecは充分.PCM1803Aがmaster(ラズパイがslave)に成れるところもいい感じ.中華通販で5ヶで¥360だった.

結果として、Raspberry pi 3B+、Raspbian、ALSA、PCM1803A(master) の組み合わせでstereo音声をwav fileに落とすことに成功した.

しかし、I2S interfaceを甘く見ていたため、いろいろな地雷があったので回避情報を以下に記す.

間違い情報で貴方がどんな損害を蒙ってもわたしは知らないのではまったら素直に死んでくれ.

------
まずは地雷の説明からしよう.

I2S interfaceというのを今回初めて扱った.I2Cになら馴染みがあったがI2Sは初めてであった.I2SはII2とも呼ばれることがあるようだ.

I2Sは音声データのIN/OUTに良く使われる.
ADCの場合の信号は3つだけ.
・LRCKは48kHzだったり96kHzだったりするいわゆるサンプリング周波数である.
・DOUTは音声データで、16bit,24bit,32bitなどである.Signed, MSB-first
・BCKは音声データのbit毎のclockである.
単純な仕様なので「こんなの実装は簡単だ」と思っていたわたしであった.






地雷1: 音声データの16/24/32bitに対応して、BCKは32fs/48fs/64fsになるのだろうと思っていたのが甘かった.24bitの場合が地雷だ.PCM1803Aの仕様では、24bitであってもBCKは64fsなのである.PCM1803Aが24bit目(LSB)を送出した後には、埋め合わせの8bitのゼロと8発のBCKが送出されるのだ.これはすなわち、ラズパイ側からPCM1803Aを見ると32bit転送に見えるのである.

次に、LRCKとBCKは誰が発生するのかという問題がある.
PCM1803Aは、発生源(master)にもなれるし、受信側(slave)にもなれる.
ラズパイも同様に、master/slaveのどちらにもなれる.さらにLRCKとBCKを独立にmaster/slave設定できる.
いまわたしがやりたいのは、PCM1803A=master、ラズパイ=slaveである.
地雷2: ネットから拾ったドライバを使ったら、両者がmasterになっていて信号が衝突してしまっていた.

clockにまつわる地雷がもうひとつある.
PCM1803Aがデルタシグマ演算するための源発振clock(SCLK)の調達である.
まずPCM1803Aがmasterの場合の周波数関係はこうなる.
SCLKにXtal発振器をつなぐ.PCM1803Aの仕様でSCLKには256fs/384fs/512fsのいずれかが許される.
256fsなどという高い周波数を分周して、BCK(64fs)やLRCK(fs)が作られる.PCM1803Aがmasterの場合はこのようにシンプルでよい.
ところが、PCM1803Aがslaveの場合は厄介だ.
ラズパイは256fsなどという高い周波数を供給してくれはしない.
なので256fsはXtalから独自供給することを余儀なくされる.
するとデルタシグマ回路の出力で異種clockの周波数不一致という厄介事が生じるのだ.
PCM1803Aの仕様書には、この異種clock問題について書かれている.「僅かな周波数ズレならOKだよ」という主旨なのだがそんな設計したくないわ!
合理的設計としてはこのようにPLLでBCKを4逓倍することで解決するが、ADCのclockにPLLをブチ込むのは気が進まない.
地雷3: PCM1803Aをslave使いする場合は、SCLKとBCK/LRCKの非同期問題が悩ましい

ソフトウエア面の地雷について.
地雷4: ラズパイには、I2S DACの応用例が多く、対応ドライバも豊富である.しかし、ラズパイは何故かI2S ADCのドライバがほとんど無いのである.

というわけで、「I2S ADCの実装なんか簡単じゃん」と言ってられなくなってしまったのだった.

-----
以下では、PCM1803A=master、ラズパイ=slave を実装する.

PCM1803Aの回路図はたったこれだけ.
未結線の端子は内部でpull-downされているので配線をサボッた.
10/11/12pinがラズパイへ配線されるI2Sの3本線である.
16~20pinの設定により、Master、96kHz、24bitが決めうちされる.
BCK=64LRCKの関係はPCM1803Aを24bit使いすると自動的にそうなる.
















ラズパイのI2S配線箇所はこの3箇所.あとGNDも.

-----
ここからはOSやドライバについての情報.

参考にしたのは、I2Sマイクロフォンをラズパイに接続するという、ネットに多くあるネタである.マイクロフォンでもADCでも似たようなものだから少し変更すればいいんじゃね?という目論見だ.
ただし、こういったページで紹介されているマイクロフォンは、マイクロフォンICがslaveで動く(ラズパイがmaster)構成になっている.(=地雷3)

【Raspbian焼き】
最新のraspbianをSDに焼いてラズパイを起動した.これを書いている時点ではこれが最新だった.
   Rapsbian 2018-11-13-raspbian-stretch.zip

【I2S関係のmoduleを動かす】
以後はrootでloginしての作業とする.

/boot/config.txt を編集する.
dtparam=i2s=on      ←コメントはずし
dtoverlay=hifiberry-dac    ←追記
dtoverlay=i2s-mmap        ←追記

/etc/modules を編集する.
i2c-dev
snd-bcm2835    ←追記
my_loader         ←追記

reboot     ←以後のブルー文字はコマンドライン

lsmod | grep snd
サウンド関連moduleにこの2つが見えてればここまではOK.
snd_soc_bcm2835_i2s
snd_bcm2835

【kernelビルドのもろもろの準備】
apt-get update
apt-get install build-essential git bc libncurses5-dev bison flex libssl-dev rpi-update
rpi-update
reboot

【kernel をビルドする】
wget https://raw.githubusercontent.com/notro/rpi-source/master/rpi-source -O /usr/bin/rpi-source
chmod +x /usr/bin/rpi-source
/usr/bin/rpi-source -q --tag-update
rpi-source --skip-gcc         ←/rootにsourceが展開される.しばらくかかる.
いろいろ質問されたら全てEnterkeyを押せばいいだろう.

ビルドが終わったら、ビルドdirectoryを/usr/srcにリンクする.
cd /usr/src
ln -s /root/linux-0ae7718e0c............08d67713 linux
ここの長ったらしい名前のdirectoryは各位の/rootに在るそれっぽいのを当てはめること.

【PCM1803Aを動かすmodule作成】
cd /root
git clone https://github.com/PaulCreaser/rpi-i2s-audio   ←source download
cd /root/rpi-i2s-audio

my_loader.cを編集する.このように直すと、地雷2で述べた衝突を回避できる.
SND_SOC_DAIFMT_CBS_CFS   →   SND_SOC_DAIFMT_CBM_CFM
Sはslaveの意味、Mはmasterの意味.
CBMは、Clock Bit Masterの省略形だろう.つまりBCKはPCM1803Aがmasterになってくれという意味だと思う.またCFMは、Clock Frame Masterの省略形だろう.Frame ClockとはLRCKの別称だと思う.

moduleコンパイル
make -C /lib/modules/$(uname -r )/build M=$(pwd) modules
my_loader.ko が出来るのでloadしてみる.
insmod my_loader.ko
lsmodでmy_loaderが見つかればmoduleがloadされているのでひとまずOK.

※余談だが動作中のmy_loaderを削除するには、
modprobe -r -v my_loader
とするとうまくいくようだ.rmmodではうまくいかない.

【起動時にmy_loaderがloadされるように】
cp my_loader.ko /lib/modules/$(uname -r)
depmod -a
modprobe my_loader
reboot

【動かしてみる】
ALSAライブラリ付属の録音アプリ「arecord」がOS標準で入っている.
まずは、my_loaderで組み込んだmoduleが認識されているかを確認する.
arecord -l
**** ハードウェアデバイス CAPTURE のリスト ****
カード 1: sndrpisimplecar [snd_rpi_simple_card], デバイス 0: simple-card_codec_link snd-soc-dummy-dai-0 []
  サブデバイス: 1/1
  サブデバイス #0: subdevice #0

音声をcaptureしてwav fileを生成してみる.
arecord -D plughw:1 -c 2 -r 96000 -f S32_LE -t wav -v -d 3 adc.wav
ここでfs=96000以外だとデータ化けすると思う.
またPCM1803Aは24bitで動くが、地雷1で述べた理由によりS32_LEのように32bitのふりをしてあげる必要がある.S24_LEだとデータ化けする.
その他のoptionについては arecord --helpを参照のこと.

wav fileを可視化するアプリを使ってadc.wav表示させてみる.
これはPCM1803Aで1kHz/333Hz 3.5Vppを音声キャプチャし、wav fileを表示した画面である.PCM1803Aの仕様どおり、入力レベル3Vppでサチっている.

かしこ

0 件のコメント:

コメントを投稿