やったこと
Raspberry Pi にMEMSマイク(PDM出力)を接続して録音した
ラズパイでPDM入力を使った例が検索しても見つからなかった(2021/09/06)ので書いてみた
書いてる人
エンジニアではありません、ただのオーディオマニアです
Lチカを初めたばかりで簡単なことしかできません
変なところがあれば突っ込みよろしくお願いします
MEMSマイクとは
シリコンマイクとも呼ばれる超小型マイクで急速にエレクトレットコンデンサマイクと置き換わった
iphoneは4からMEMSマイク
小型で熱や衝撃に強く特性も安定している
音質もそこそこ優れたものがある(良くないものもある)
周波数特性はフラットではない(高域に共振によるピークが出る)
アナログ出力のものとデジタル出力(PDM、I2S、SPI)のものがある
今回はMEMSマイクのうちPDM出力のものを使った
PDM(Pulse Density Modulation、パルス密度変調)
アナログ信号をデジタル化する方式で、パルスの密度で振幅を表す
普通のwavやflac、CDはPCM(Pulse Code Modulation)で記録されている
SACDはPDMで記録されている
試した環境
Raspberry Pi 3B ( おそらく2や4でも動くと思うが確認してない )
pi4はクロックが19.2MHzではなくて54MHzなのでこのままでは動きません。試してないのに適当なことを言うのはダメですね。(2021/11/12追記)
OS: Raspberry Pi OS Lite(Release date: May 7th 2021)
C言語(gcc 8.3.0)
Raspberry Pi でPDM入力の使い方
Raspberry Pi のPCMモジュールにPDMモードがあるのでそのまま使う
参考サイト・資料
参考にしたサイト
楽しくやろう。
このサイトをみながらLチカとか試してみた
設定法
メモリマッピング
- mmap でアドレスをマッピングする
これによりGPIO、クロック、PCMの各レジスタにアクセスできるようになる
ベースアドレスはPi0 Pi2-3 pi4で違うので注意が必要 - レジスタへのポインタ変数は全て"volatile"をつけて宣言する
- レジスタは32bitなので uint32_t とする
GPIOの設定
- GPIO18、GPIO20をALT0に設定する
GP_FSEL1 レジスタ、GP_FSEL2 レジスタ
これでGPIO18がPCM_CLK、GPIO20がPCM_DINに設定される
クロック設定
PCMクロック関連レジスタのアドレスは公式のデータシートには載ってない
- CM_PCMCTL レジスタ
ENAB = 0にしてクロック無効に
BUSY = 0を待つ
SRC = 1(OSC 19.2MHz)、 MASH = 0
MASHは分周を非整数分の1にするときに使う
ジッタと引き換えに正確な周波数が得られる
今回はジッタを嫌ってMASHは使わない
- CM_PCMDIV レジスタ
DIVI = 8、DIVF = 0
非整数で分周したい場合には DIVF に 少数部分を4096倍したものを設定する
- CM_PCMCTL レジスタ
ENAB = 1(ほかはそのまま)クロック有効に
これで19.2MHzが1/8に分周されてクロックが2.4MHzに設定される
使うMEMSマイクによってクロックの範囲が違うのでMEMSマイクのデータシートを確認すること
PDM設定
-
PCM_CS_A レジスタ
EN = 0 -
PCM_MODA_A レジスタ
CLKM = 0, FRXP = 1, PDME = 1, PDMN = 0
CLKM = 0 でクロックがマスターモード
PDME = 1 でPDMモードを有効にする
PDMNはデシメーションフィルタ(4次CIC)の設定
PDMN = 0 で 1/16、 データは 16 bits unsigned に変換される
PDMN = 1 で 1/32、 データは 20 bits unsigned に変換される
クロックが2.4MHz、PDMN=0なら 2.4MHz/16 で サンプリング周波数150kHz、16bitのデータになる
FRXP = 1 にすると16bitデータ2つを32bit fifoに詰め込む
PDWM = 1 で使うと16bitからはみ出たデータは捨てられてしまう
- PCM_RXC_A レジスタ
CH1EN = 1, CH2EN = 1
MEMSマイクは設定でクロックの立ち上がりか立ち下がりのどちらかでデータを転送するか選択できる
立ち上がりに設定したマイクと立ち下がりに設定したマイクを接続してCH1ENとCH2ENを両方有効にすれば2chのデータが得られる
FRXP = 1 なら 16bit 2ch分のデータが1つのFIFO(32bit)に入る
- PCM_CS_A レジスタ
EN = 1, RXON = 1
これでPCMモジュールがPDMモードで作動し始めて、PCM_FIFO_A レジスタにデーターが送られてくる
データの受信
割り込みやDMAもできるらしいが面倒なのでポーリング
マイクが作動してから1秒くらいは安定しない
MEMSマイクの消費電力は少ないので常に作動させておけば必要なときにすぐ録音が開始できる
-
PCM_FIFO_A レジスタ
FIFOは64個あるのでデータを64個読み捨てる -
PCM_CS_A レジスタ
RXERR フラグを解除
あとはデーターの読み取りを繰り返すだけ
- RXD = 1 ならPCM_FIFO_Aから読み取る
- RXERR = 1 ならエラーが起きてる
- RXD = 0 ならちょっと待つ
RXDを確認してから読み取っているのでアンダーフローはありえない
エラーが起きた場合にはオーバーフロー(読み取りが間に合ってない)
今回作ったソフトはchrtで優先度を上げないとエラーが出る
取り込んだデータの処理
16bit unsigned なので 32768を引いて 16bit signed に変換する
音声ファイルに変換するならSOXを使うのが便利
今回つかったMEMSマイク
- 秋月電子で買った超音波対応のもの(Knowles社製 SPH0641LU4H-1)
- 可聴周波数でも使える
- SN比は64.3dBなのでまあまあ
MEMSマイクでも70dBを超えるものもあるのでちょっと物足りない - 10%THD が 120dB SPL なので大音量には強くない
したがって大音量楽器の近接での録音なんかには使えない
https://akizukidenshi.com/catalog/g/gK-15577/
https://akizukidenshi.com/download/ds/knowles/SPH0641LU4H-1.PDF
データシートを見るとultrasonic modeにするにはLow-Power Mode か Standard Performance Mode を経由しろとあるので注意
接続
マイクのVDD, GND, CLK, DAT に接続する
VDDは+3.3Vに接続 +5Vと間違わないように
CLK はGPIO18、DATはGPIO20に接続する
作ったソフト
Github に上げてみた
https://github.com/assi-dangomushi/pdm_rec
あとがき
Lチカから始めて意外と簡単にマイクを動作させることができた
基礎知識のないとデータシートを読むのが大変