8. I2Sとサウンドモジュール

 I2Sについては、すでにこのコーナーで「I2S DACカード」を使ったオーディオプレーヤーを体験済みです。その後も、製作した2つのプレーヤーの音質を楽しんでいます。このプロジェクトでも、マイク入力にI2Sテクノロジーを利用したいと考えています。今回はそのための準備編です。
 I2Sを利用するには、まずRaspberry PiのGPIOピンの機能変更が必要になります。また、関連モジュールのセットアップには、Linuxカーネルをダウンロードしてコンパイルしたり、サウンドモジュールのダウンロードとコンパイル、インストールなどを手動で行ったりと、時間と手間を要します。
 手元にはI2Sデジタル出力マイクが出番を待っているので、これがどんな音を集めてくれるのかを楽しみにしながら、ゆっくり、そして注意深く進めることにしましょう。



1.I2Sの概要

 I2SはInter-IC Soundの通称で、IC間でデジタル・オーディオデータをシリアル転送するための規格です。デジタル・オーディオデバイスを接続するための規格と言い換えることができるでしょう。一般に、音声入力用のA/Dコンバーター(ADC)の出力や、音声出力用D/Aコンバーター(DAC)の入力などに用いられます。データ信号とクロックを分けて送るのでズレ(ジッター)の発生が少なく、精度が高いことから音質が良いとされています。

 I2Sは3本の信号線(GND:グランドを入れると4本)で構成されています。メーカーによって信号の名称が異なる場合がありますが、代表的な名称と信号の役割は次の通りです。
 ・LRCLK:LR Clock(WS:Word Select、WDCLK:Word Clockとも)
   2チャンネルステレオにおいて、音声信号のLチャネルとRチャネルを区別するための信号です。
 ・BCLK:Bit Clock(SCLまたはSCLK:Serial Clockとも)
   一般にビットクロックよ呼ばれるもので、データを32ビット読み出すための基準クロックになります。
   SDATAの信号のタイミングをとり、SDATAが若干ぶれてもビット列の正確な伝送が可能です。
 ・SDATA:Serial Data(SDとも)
   PCM(Pulse Code Modulation:パルス符号変調)などでデジタル化された音声データです。
 ICの規格によっては、この他に基準クロック用の信号を必要とする場合があります。
 ・MCLK(Master Clock または SYSCLK: System Clock)
   デジタル信号の動作基準となるクロック信号。ICによっては、外部クロックから供給せずに、上記3信号と
   同期するようにMCLKの供給が必要になることがあります。

 SDATAは、PCMの場合はサンプリング周波数が32ビットまで対応でき、2チャンネルステレオの場合、L・Rが交互に32ビット単位で切り替えられます(読み書きされます)。このPCMとは、音声を数値化して表現する変調方式であり、デジタル音声データのもっとも一般的なフォーマットです。その仕組みは大変重要なのですが、ここではこれにとどめ、改めて『アナログ/デジタル変換』の章で取り上げる予定です。


2.I2Sインターフェイスの準備

 Pi ZeroのI2Sインターフェイスの準備を始めます。まだUSBポートにミニマイクをつないでいれば外してください。
 すでに述べたように、GPIOピンの機能は代替機能 (Alternate Function)によって割り当てることができます。代替機能は[Alt0]~[Alt5]の6種類がありますが、I2Sインターフェイスは[Alt0]を指定することで設定できます。
 代替機能とGOPIピンの関係は、Raspberry Pi Shopさんの『Raspberry PiのGOIPピンと機能』がたいへん便利なので参考にしてください。
①現在のGPIOピンの機能を確認する

 poderosa(SSHターミナルエミュレーター)を立ち上げてRaspberry Pi Zeroに接続し、「gpio readall」と入力します。次のようにGPIOピンに割り当てられた機能が表示されます。
   表は左右2列のピン配列に対応していて、見出しは次のような意味を持っています。
    ・BCM: GPIOのポート番号
    ・wPi: WiringPiで指定された番号です
    ・Mode: 入出力の状態
    ・V: HighかLowの状態
    ・Physical: コネクタのピン番号


②GPIOピンに機能を割り当てる

 先の『Raspberry PiのGOIPピンと機能』にアクセスして、[Alt0]を指定した場合のGPIOピンの機能を表示してみましょう。

 そもそもI2SはPCMデータを取り扱うインターフェイスなので、PCMで始まる機能が対象になります。上図からわかるように、[Alt0]にはPCM関係の機能が揃っています。当面は使用しないPCM DOUTを含むPCM関連ピン4本すべて、それにPWM0,1のピンを[Alt0]に指定しましょう。
    GPIO18 : PCM CLK(PCM clock)
    GPIO19 : PCM FS(Frame frame sync)
    GPIO20 : PCM DIN(PCM data in)
    GPIO21 : PCM DOUT(PCM data out)
    GPIO12 : PWM0(Pulse with modulator)
    GPIO13 : PWM1(Pulse with modulator)

 次のようにgpioコマンドで6つのGPIOピンの機能を変更します。
$ gpio -g mode 12 ALT0 $ gpio -g mode 13 ALT0 $ gpio -g mode 18 ALT0 $ gpio -g mode 19 ALT0 $ gpio -g mode 20 ALT0 $ gpio -g mode 21 ALT0


③変更結果の確認

 結果を確認してみましょう。

 これらgpio_altで設定した内容はリブートでリセットされるので、再起動時に自動設定できるようにしておく必要があります。/etc/rc.localファイルの最終行「exit 0」の前に、以下のgpioコマンド6行を追加します。
$ gpio -g mode 12 ALT0 sudo nano /etc/rc.local  gpio -g mode 12 ALT0  gpio -g mode 13 ALT0  gpio -g mode 18 ALT0  gpio -g mode 19 ALT0  gpio -g mode 20 ALT0  gpio -g mode 21 ALT0  exit 0
 リブートして、再度「gpio readall」で正しく設定されていることを確認しておきましょう。

<お願い・注意!!>
 このシリーズの後半で、アナログ/デジタル・コンバーターを製作してアナログマイクを試す予定です。その際にI2Sモジュールの再セットアップが必要になります。これに備えて、現時点のシステム(マイクロSDカード)をバックアップしてください。方法は、本プロジェクトの第5章『5.Raspberry Pi ZeroにOSをインストール』の最終節を参照してください。
 これを忘れると、再度ここまでのインストールをやり直さなければならなくなります。


3.I2Sモジュールのセットアップ

 I2Sオーディオドライバーを組み込むために、最新のLinuxカーネルをダウンロードして手動でコンパイルとインストールを行います。続いて、I2Sサウンドモジュールのダウンロードとコンパイル、インストールをして、起動時に自動でモジュールをロードするように設定します。
 これらのセットアップ作業は、共立プロダクツさんの『MEMS マイクの使用方法』を参考にさせていただきました。インストール途中の応答で気をつけなければならない点などを含め、手順を整理します。
 なお、一連の作業にはかなり時間がかかります。数時間を見込んで余裕をもって取り組んでください。


<< ヒント >>
 以降の作業は、本文中のコマンド等を[コピー]して、poderosa(SSHターミナルエミュレーター)の[ペースト]で貼り付けると簡単確実です!


①I2Sの有効化

 I2Sインターフェースは初期状態で無効となっているため有効化します。/boot/config.txtファイルにある記述「#dtparam=i2s=on」のコメント記号、「#」を削除します。
$ sudo nano /boot/config.txt


②サウンドモジュールの有効化

 /etc/modulesファイルの末尾に「snd-bcm2835」を追加して、リブートします。
$ sudo nano /etc/modules   snd-bcm2835 $ sudo reboot
リブート後に、次のコマンドでモジュールがロードされていることを確認します。
  「snd_pcm_dmaengine,snd_soc_bcm2835_i2s,snd_bcm2835,snd_soc_core」が含まれていればOK。
$ lsmod | grep snd snd_soc_bcm2835_i2s 20480 0 regmap_mmio 16384 1 snd_soc_bcm2835_i2s snd_soc_core 192512 1 snd_soc_bcm2835_i2s snd_compress 20480 1 snd_soc_core snd_pcm_dmaengine 16384 1 snd_soc_core snd_bcm2835 24576 1 snd_pcm 98304 4 snd_pcm_dmaengine,snd_soc_bcm2835_i2s,snd_bcm2835,snd_soc_core snd_timer 32768 1 snd_pcm snd 73728 7 snd_compress,snd_timer,snd_bcm2835,snd_soc_core,snd_pcm


③カーネルのダウンロードとコンパイル

 I2S オーディオドライバを組み込む際に使用するLinuxカーネルを最新バージョンに更新して、リブートします。
$ sudo apt update $ sudo apt install rpi-update $ sudo rpi-update       :     (省 略)       : ############################################################# WARNING: This update bumps to rpi-5.4.y linux tree See: https://www.raspberrypi.org/forums/viewtopic.php?f=29&t=269769 'rpi-update' should only be used if there is a specific reason to do so - for example, a request by a Raspberry Pi engineer or if you want to help the testing effort and are comfortable with restoring if there are regressions. DO NOT use 'rpi-update' as part of a regular update process. ############################################################## Would you like to proceed? (y/N) <=== 'n'を返答します! $ sudo reboot


④依存関係パッケージのインストール

 カーネルの取得・コンパイルに必要なパッケージをインストールします。「続行しますか?」には 'y'を入力します。
$ sudo apt install git bc libncurses5-dev bison flex libssl-dev

 カーネルソースをダウンロードしてコンパイルします。かなり時間がかかります。
  (注意)
    最終行の「rpi-source --skip-gcc」の実行では、
       Code coverage for fuzzing (KCOV) [N/y/?] (NEW)」
    と表示されたら、デフォールトを指示するために [Enter]をクリックします。
$ sudo wget https://raw.githubusercontent.com/notro/rpi-source/master/rpi-source -O /usr/bin/rpi-source    <---- 前の行に続いています! $ sudo chmod +x /usr/bin/rpi-source $ /usr/bin/rpi-source -q --tag-update $ rpi-source --skip-gcc


⑤I2Sモジュールの準備

 I2Sが使用できるかを確認します。次の入力で「mount: debugs is already mounted」などのように表示されればOKです。
$ sudo mount -t debugfs debugs /sys/kernel/debug

 次のコマンドで、モジュール名「20203000.i2s」が表示されることを確認する。
$ sudo cat /sys/kernel/debug/asoc/components 20203000.i2s 20203000.i2s snd-soc-dummy snd-soc-dummy


⑥サウンドモジュールのソースコードをダウンロードする

 次のようにダウンロードします。
$ git clone https://github.com/PaulCreaser/rpi-i2s-audio $ cd rpi-i2s-audio
 続いて、nanoでソースコードファイルmy_loader.cを開いて次の2カ所を変更します。
   ・.platform = "3f203000.i2s" → .platform = "20203000.i2s"
   ・.name = "3f203000.i2s" → .name = "20203000.i2s"
$ sudo nano my_loader.c


⑦モジュールをコンパイルする

 モジュールをコンパイルしてインストールします。
$ make -C /lib/modules/$(uname -r)/build M=$(pwd) modules $ sudo insmod my_loader.ko

 以下の2通りの方法で、モジュールが正常にインストールできているか確認します。
  1) lsmod を実行し、my_loader で始まる行が出力されること。
$ lsmod | grep my_loader my_loader 16384 0

  2) dmesg を実行し、カーネルメッセージログの最後に下記の内容を含む行が表示されていること。
    asoc-simple-card asoc-simple-card.0: snd-doc-dummy-dai <-> 3d203000.i2s mapping ok
$ dmesg | tail [ 35.948393] Bluetooth: BNEP socket layer initialized [ 36.793450] Bluetooth: RFCOMM TTY layer initialized [ 36.793486] Bluetooth: RFCOMM socket layer initialized [ 36.793537] Bluetooth: RFCOMM ver 1.11 [ 50.643759] fuse init (API version 7.27) [ 2054.447163] my_loader: loading out-of-tree module taints kernel. [ 2054.492918] request module load 'bcm2708-dmaengine': 0 [ 2054.493215] register platform device 'asoc-simple-card': 0 [ 2054.493230] Hello World :) [ 2054.609760] asoc-simple-card asoc-simple-card.0: snd-soc-dummy-dai <-> 20203000.i2s mapping ok


⑧起動時に自動でモジュールをロードするように設定

 Raspberry Pi Zero起動時にモジュールを自動ロードするように設定します。
$ sudo cp my_loader.ko /lib/modules/$(uname -r) $ echo 'my_loader' | sudo tee --append /etc/modules > /dev/null $ sudo depmod -a $ sudo modprobe my_loader $ sudo reboot


4.システム情報の確認

 I2S関連モジュールのセットアップによって、音声信号処理環境が変化しています。ここではこれらの状態を把握しておきましょう。
①接続状態の確認

 サウンドカードの認識状況を確認します。
$ cat /proc/asound/cards 0 [ALSA ]: bcm2835_alsa - bcm2835 ALSA bcm2835 ALSA 1 [sndrpisimplecar]: snd_rpi_simple_ - snd_rpi_simple_card snd_rpi_simple_card

②サウンドカードの優先度

$ cat /proc/asound/modules 0 snd_bcm2835 1 snd_soc_simple_card

③サウンドデバイスの確認

$ cat /proc/asound/devices 0: [ 0] : control 16: [ 0- 0]: digital audio playback 17: [ 0- 1]: digital audio playback 18: [ 0- 2]: digital audio playback 32: [ 1] : control 33: : timer 48: [ 1- 0]: digital audio playback 56: [ 1- 0]: digital audio capture

④PCMデバイスの状態

$ cat /proc/asound/pcm 00-00: bcm2835 ALSA : bcm2835 ALSA : playback 7 00-01: bcm2835 IEC958/HDMI : bcm2835 IEC958/HDMI : playback 1 00-02: bcm2835 IEC958/HDMI1 : bcm2835 IEC958/HDMI1 : playback 1 01-00: simple-card_codec_link snd-soc-dummy-dai-0 : simple-card_codec_link snd-soc-dummy-dai-0 : playback 1 : capture 1

⑤スピーカー(イヤフォン)の設定確認

$ amixer -c 0 contents numid=3,iface=MIXER,name='PCM Playback Route' ; type=INTEGER,access=rw------,values=1,min=0,max=3,step=0 : values=0 numid=2,iface=MIXER,name='PCM Playback Switch' ; type=BOOLEAN,access=rw------,values=1 : values=on numid=1,iface=MIXER,name='PCM Playback Volume' ; type=INTEGER,access=rw---R--,values=1,min=-10239,max=400,step=0 : values=-14 | dBscale-min=-102.39dB,step=0.01dB,mute=1 numid=5,iface=PCM,name='IEC958 Playback Con Mask' ; type=IEC958,access=r-------,values=1 : values=[AES0=0x02 AES1=0x00 AES2=0x00 AES3=0x00] numid=4,iface=PCM,name='IEC958 Playback Default' ; type=IEC958,access=rw------,values=1 : values=[AES0=0x00 AES1=0x00 AES2=0x00 AES3=0x00]

⑥再生デバイスの確認

$ aplay -l **** ハードウェアデバイス PLAYBACK のリスト **** カード 0: ALSA [bcm2835 ALSA], デバイス 0: bcm2835 ALSA [bcm2835 ALSA] サブデバイス: 7/7 サブデバイス #0: subdevice #0 サブデバイス #1: subdevice #1 サブデバイス #2: subdevice #2 サブデバイス #3: subdevice #3 サブデバイス #4: subdevice #4 サブデバイス #5: subdevice #5 サブデバイス #6: subdevice #6 カード 0: ALSA [bcm2835 ALSA], デバイス 1: bcm2835 IEC958/HDMI [bcm2835 IEC958/HDMI] サブデバイス: 1/1 サブデバイス #0: subdevice #0 カード 0: ALSA [bcm2835 ALSA], デバイス 2: bcm2835 IEC958/HDMI1 [bcm2835 IEC958/HDMI1] サブデバイス: 1/1 サブデバイス #0: subdevice #0 カード 1: sndrpisimplecar [snd_rpi_simple_card], デバイス 0: simple-card_codec_link snd-soc-dummy-dai-0 [simple-card_codec_link snd-soc-dummy-dai-0] サブデバイス: 1/1 サブデバイス #0: subdevice #0

⑦録音デバイスの確認

$ arecord -l **** ハードウェアデバイス CAPTURE のリスト **** カード 1: sndrpisimplecar [snd_rpi_simple_card], デバイス 0: simple-card_codec_link snd-soc-dummy-dai-0 [simple-card_codec_link snd-soc-dummy-dai-0] サブデバイス: 1/1 サブデバイス #0: subdevice #0


5.動作確認

 Pi Zeroにヘッドフォンアンプをつなぎましょう。アンプの黒線はPi Zeroの[GND]ピンへ、左右のワイヤーはそれぞれ[GPIO12]と[GPIO13]のピンへ接続します。

 ここではPortAudioサンプルプログラムを使って動作確認を行います。USBミニマイクで行ったのと同様に、「コンパイル」の記述にしたがってコンパイルし、「実行」の要領で実行してください。
 まず、PortAudioのexampleフォルダーに移動します。

$ cd $ cd portaudio/examples


①デバイス情報の表示

 ・機能: デバイス情報など、使用可能なデバイスを一覧表示します。
 ・コンパイル: gcc pa_devs.c -lportaudio
 ・実行: ./a.out
実行結果
 以下の内容が表示されます。最初の冗長な部分は省略しています。「device #2」に[ Default Input ]としてsnd_rpi_simple_cardが追加されているのがわかります。
Number of devices = 4 --------------------------------------- device #0 [ Default Output ] Name = bcm2835 ALSA: IEC958/HDMI (hw:0,1) Host API = ALSA Max inputs = 0, Max outputs = 8 Default low input latency = -1.0000 Default low output latency = 0.0016 Default high input latency = -1.0000 Default high output latency = 0.0348 Default sample rate = 44100.00 Supported standard sample rates for half-duplex 16 bit 8 channel output = 44100.00, 48000.00, 88200.00, 96000.00, 192000.00 --------------------------------------- device #1 Name = bcm2835 ALSA: IEC958/HDMI1 (hw:0,2) Host API = ALSA Max inputs = 0, Max outputs = 8 Default low input latency = -1.0000 Default low output latency = 0.0016 Default high input latency = -1.0000 Default high output latency = 0.0348 Default sample rate = 44100.00 Supported standard sample rates for half-duplex 16 bit 8 channel output = 44100.00, 48000.00, 88200.00, 96000.00, 192000.00 --------------------------------------- device #2 [ Default Input ] Name = snd_rpi_simple_card: simple-card_codec_link snd-soc-dummy-dai-0 (hw:1,0) Host API = ALSA Max inputs = 2, Max outputs = 2 Default low input latency = 0.0058 Default low output latency = 0.0087 Default high input latency = 0.0348 Default high output latency = 0.0348 Default sample rate = 44100.00 Supported standard sample rates for half-duplex 16 bit 2 channel input = 8000.00, 11025.00, 16000.00, 22050.00, 32000.00, 44100.00, 48000.00, 88200.00, 96000.00, 192000.00 Supported standard sample rates for half-duplex 16 bit 2 channel output = 8000.00, 11025.00, 16000.00, 22050.00, 32000.00, 44100.00, 48000.00, 88200.00, 96000.00, 192000.00 Supported standard sample rates for full-duplex 16 bit 2 channel input, 2 channel output = 8000.00, 11025.00, 16000.00, 22050.00, 32000.00, 44100.00, 48000.00, 88200.00, 96000.00, 192000.00 --------------------------------------- device #3 Name = dmix Host API = ALSA Max inputs = 0, Max outputs = 2 Default low input latency = -1.0000 Default low output latency = 0.0213 Default high input latency = -1.0000 Default high output latency = 0.0213 Default sample rate = 48000.00 Supported standard sample rates for half-duplex 16 bit 2 channel output = 48000.00 ----------------------------------------------


 == 以下の再生テストの前に、音量を小さく絞っているか確認してください! ==


②ノコギリ波の再生

 ・機能: 単純なノコギリ波を再生します。
 ・コンパイル: gcc paex_saw.c -lportaudio
 ・実行: ./a.out
実行結果
 左右のイヤフォンから音程が異なるノコギリ波が再生されます。


③正弦波の再生

 ・機能: 正弦波を再生します。
 ・コンパイル: gcc paex_sine.c -lportaudio -lm
 ・実行: ./a.out
実行結果
 左右のイヤフォンから、音程の異なるサイン波が5秒間再生されます。


 これでI2Sを使用できる環境が整いました。次回ではI2Sデジタル出力マイクを組み立てて、音声の入力・再生を行ってみます。お楽しみに!