電話でのお問い合わせ0120-439-296

第1回 マイク録音とスピーカ再生

著者:山田浩之(Y-Logic)  企画・編集:ZEPエンジニアリング

■ タッチパネル LCD 搭載キット“STM32H747I-DISCO” 誕生

● 無線や計測の信号処理システム開発に

ST マイクロエレクロニクスは、STM32 マイコンの評価を目的にしたキットを多数販売しています。中でも、マイコンの主要な機能を試せるのが “DISCO”(DiscoveryKit)です。図1 に示す “STM32H747I-DISCO” は、STM32シリーズの最上位品のひとつである “STM32H747XIH6” を搭載したスタータキット(Discovery Kit)です。
STM32H747XIH6 は、Cortex-M7 と Cortex-M4 のデュアル・コアを内蔵し、最大動作クロックは 480MHz です。ディジタル信号処理にも十分な CPU 性能(1000DMIPS以上)を備えます。

● 組み込んだままプログラミング&デバッグ

STM32H747I-DISCO には、MIPI DSI インターフェースのタッチパネル LCD、256M ビット SDRAM、2x512M ビット NOR フラッシュ、オーディオ CODEC などを搭載しています。MIPI は “Mobile Industry Processor Interface”、DSI は “Display Serial Interface” の略です。
Arduino 互換のピン・ソケットや USB、microSD、オーディオ・ジャック、カメラ、Ethernet など、外部接続用のコネクタも多彩です。1 本の microUSB ケーブルを接続して使います。“ST-LinkV3E” も搭載しているので、これ 1 枚でマイコンの書き込みからデバッグまで可能です。

図1 オールイン Arm マイコン・キット “STM32H747I-DISCO”(CortexM4/-M7/DSP、A-D/D-A 内蔵、動作クロック 480MHz のハイスペック CPU、 タッチパネル LCD、オーディオ CODEC、マイク入力、デバッガを搭載)

■ オーディオ・スペアナを作ろう

● 第1歩はマイクとスピーカを使った録再

STM32H747I-DISCO では、ボード上でオーディオ入出力が利用できます。追加回路を設けることなく、ボード上の LCD と組み合わせて、図2 のようなオーディオ・スペクトラム・アナライザを作ることが可能です。録音だけではなく再生も制御できると、スペアナ以外にもいろいろな応用ができるようになります(第 2 回でスピーカの周波数特性を測定する予定)。

図2 自作のスタンドアローン・スペクトラム・アナライザ

今回は、DISCO ボードの緑色のオーディオ・ジャックにスピーカを接続し、マイクからの音声がスピーカから聞こえることを確認します。ボード上のディジタル・マイクの音声を STM32 マイコンに入力してスピーカに出力します。音量が小さい場合は、CODEC の音量設定によって調整可能です。

■ 作り方

● STEP1:ST社純正のボード固有API「BSPドライバ」を利用する

BSP(Board Support Package)ドライバは、STマイクロエレクトロニクスが提供する STM32Cube ソフトウェア・パッケージに含まれるボード固有のAPI です。BSPドライバを使うことで、CODECの制御やデジタルマイクのPDM-PCM変換を実装する手間が大幅に削減されます。

● STEP2:BSPドライバをユーザ・プロジェクトに組み込む

STM32H747I のオーディオ・データ・パスは 図3 のようになっています。

図2 STM32H747I ボード上のオーディオ・データ・パス

入出力として、3.5mm のオーディオ・ジャックとディジタル・マイクが使用可能です。オーディオは STM32 マイコンに内蔵されている A-D コンバータ/D-A コンバータではなく、ボード上の外付け CODEC IC を経由しているため、STM32CubeMXが生成するドライバは利用できません。このため、「STM32CubeH7 パッケージ」に含まれる BSP の Audio Driver をユーザ・プロジェクトに明示的に組み込むことを考えます [1]。

● STEP3:STM32CubeMXを設定する

STM32CubeMX から Audio Driver を自動生成する機能はなさそうなので、組み込みはある程度手動で行う必要があります。STM32H747I のような Dual CoreMCU の場合 IDE プロジェクト内のファイルは Linked File になっていて、フォルダにソースコードを追加しただけではビルド・ツリーに追加されません。
ここでは、TouchGFX Designer で作成したプロジェクトに対して、BSP AudioDriver を追加していきます。TouchGFX Designer で、DISCO 用の新規プロジェクトを生成(Generate Code)したら、表 1 のように、STM32CubeH7 から必要なファイルをコピーします。

コピー元(STM32CubeH7)コピー先(Project)
Drivers\BSP\STM32H747IDISCO\stm32h747i discovery audio.c, .hDrivers\BSP\STM32H747I-Discovery\
Drivers\BSP\Components\Common\audio.hDrivers\BSP\Components\Common\
Drivers\BSP\Components\wm8994\*Drivers\BSP\Components\wm8994\

表1 コピー元とコピー先の対応表

次に、プロジェクト・フォルダを開き、“.extSettings” というファイルをテキスト・エディタで開きます。このファイルは、STM32CubeMX で生成するプロジェクトの追加設定ファイル [2] です。次のように編集します。

  • 「Drivers/BSP/Components=」の行の末尾に以下(1 行)を追加:
    ;../Drivers/BSP/Components/wm8994/wm8994.c;
    ../Drivers/BSP/Components/wm8994/wm8994 reg.c
  • 「Drivers/BSP/STM32H747I-Discovery=」の行の末尾に以下を追加:
    ../Drivers/BSP/STM32H747I-Discovery/stm32h747i discovery audio.c;
  • 「[CortexM7:Others]」の次の行に以下を追加:
    HALModule=SAI

“STM32H747I-DISCO.ioc” を 開 き 、Middleware and Software Packs の[PDM2PCM M7]の[Enabled]にチェックを入れてコードを生成します。IDE で生成されたコードを開き、“main.h” にリスト 1 を追加します。これで、BSP AudioDriver が使える状態になり、[Clean]-[Build]でビルドが通るようになります。

リスト1: main.h に BSP Audio Driver を使用するためのヘッダ・ファイルの記述を追加する

# include " stm32h747i_discovery_audio . h "

● STEP4:ボード上のディジタル・マイクからオーディオを入力する

STM32CubeH7 の BSP サンプルを参考に、録音・再生処理を実装していきます。Audio ドライバの “BSP AUDIO IN Init()” 関数を呼び出してマイク入力を初期化します。関数の第 1 引数 “Instance” に 1 を指定すると、SAI(Serial AudioInterface)の PDM(Pulse Density Modulation)入力が使用されます。SAI や DMA(discovery audio.h で定義される DMA インスタンスが使われる)、必要なクロックなどの設定は BSP ドライバが行います。
初期化が成功したら、“BSP AUDIO IN RecordPDM()” 関数を呼び出すことで録音を開始します。停止は “BSP AUDIO IN Stop()” 関数を呼び出します。

● STEP5:DMA と割り込みのデータ処理

SAI によって受信された PDM データは、BDMA により録音バッファに転送されます。
BDMA は指定されたバッファ内を繰り返し書き込むよう設定されているので、そのままでは、データがループ再生されます。長時間の録音を行う場合、通常は割り込み処理で別の大きいバッファにコピーしていきます。
図4 に録音処理のデータフローを示します。

図4 録音割り込み処理とデータフロー

ここでは録音バッファの半分あるいは全部が書き込まれたとき、DMA 割り込みが発生するように設定されています。割り込みでは録音バッファを転送中と転送済みの2 つに分け、プログラムから操作して問題ない転送済みバッファのデータ処理を行います(いわゆる Ping-pong Buffer)。このように、バッファを交互に使いながら書き換えたり読み出すことで、長時間のデータを処理落ちなく扱うことができます。
BSP の IRQ ハンドラ関数(BSP AUDIO IN IRQHandler)は、割り込み要因の判定を行い、対応するコールバック関数を実行します。DMA の IRQ ハンドラから、BSP ドライバの IRQ ハンドラの呼び出しはユーザが記述する必要があります。録音が処理落ちしないように、ヘッダ・ファイル “stm32h747i discovery conf.h”で定義される録音割り込み優先度もリスト 2 のように高くしておきます。

リスト2: 録音割り込み優先度を上げて、録音が処理落ちを回避する

# define BSP_AUDIO_IN_IT_PRIORITY 5U

BDMA のアドレス制約に注意

ユーザは、“BSP AUDIO IN TransferComplete CallBack” と “BSP AUDIO IN HalfTraの2つを実装します。録音バッファに格納されたデータはPDMなので 、BSP AUDIO IN PDMToPCM( )関数を呼び出して、PCM(Pulse CodeModulation)データに変換します。
なお、PDM-PCM 変換において、初期状態では変換後のデータ長は変換前の1/4になります。
PDM から録音する場合の注意点として、PDM データを転送する BDMA は、D3Domain に配置されているため、Cortex-M7 のメイン・メモリ(RAM D1)にアクセスすることができません。アドレス制約を満たさない場合、ビルドはとおりますが動作しません。このようにアドレスに制約のあるペリフェラルはいくつかあるので、このマイコンを使用する際は、メモリ管理に留意する必要があります。上記の理由により、PDM 用の録音バッファは、RAM D3 領域に確保します。具体的には、リスト 3 のように “ attribute ” 属性をつけて変数を確保するとよいでしょう。

リスト3: 変数に attribute 属性をつけて定義

ALIGN_32BYTES(uint16_t g_recordPDMBuf [ AUDIO_IN_PDM_BUFFER_SIZE ]) __attribute__ (( section ( " . RAM_D3" )));

リスト4: リンカ・スクリプトに RAM D3 領域の追加

. RAM_D3(NOLOAD) : { * (. RAM_D3 )} > RAM_D3

“ attribute ” を使うには、リンカ・スクリプト(STM32H747XIHX FLASH.ld)に D3 SRAM のセクション定義を追加する必要があります(リスト 4)。

● STEP6:CODEC IC からオーディオ出力

Audio ドライバの “BSP AUDIO OUT Init()” 関数を呼び出して、CODEC 出力を初期化します。関数の第 1 引数 “Instance” に 0 を指定して、SAI と CODECIC を初期化します。また、“BSP AUDIO OUT SetDevice” 関数の第 2 引数に “AUDIO OUT DEVICE HEADPHONE” を指定して呼び出すことで、ヘッドホン出力(ボードの緑色のオーディオ・ジャック)を選択します。
初期化が終わったら、“BSP AUDIO OUT Play()” 関数を呼び出すことで再生を開始します。停止は “BSP AUDIO OUT Stop()” 関数を呼び出します。オーディオ出力のデータフローもおおむね入力と同様ですが、PDM 変換が不要な点と、BDMA が DMA に変わる点が異なります。DMA では SRAM D1 を宛先に指定できます。再生時のデータフローを図 5 に示しました。IRQ ハンドラとコールバック関数を実装することで、再生処理の実装が完了します。

図5 再生割り込み処理とデータフロー

● STEP7:マイク入力をスピーカ出力にループバックするテスト・プログラムの作成

ここまでの動作テストとして、マイク入力をスピーカ出力にループバックするテスト・プログラムを作成します。録音・再生のサンプリング・レートが一致する場合、リスト 5 のように録音コールバック内で録音バッファを再生バッファにコピーするだけで OK です。
ビルド、実行してスピーカを DISCO ボードの緑色オーディオ・ジャックに接続し、マイクからの音声がスピーカから聞こえることを確認します。音量が小さい場合は、出力側の音量によって調整可能です。

リスト5: 録音-再生のループバック・プログラムの録音コールバック処理

// PDM バッファの処理関数
static void RecBuffer_PDM_Push_for_Loopback ( const uint16_t *
    pdmBuf ) {
    // PDM -> PCM に変換して録音バッファに保存
    BSP_AUDIO_IN_PDMToPCM (1 , ( uint16_t *) pdmBuf , ( uint16_t *) &
        g_recordBuf [ playbackPtr ]) ;
    int i ;
    for ( i =0; i < AUDIO_IN_PDM_BUFFER_SIZE /4/2; i ++)
        g_playBuf [ playbackPtr + i ] = g_recordBuf [ playbackPtr + i ];
    playbackPtr += AUDIO_IN_PDM_BUFFER_SIZE /4/2;
    if ( playbackPtr >= RECORD_BUFFER_SIZE ) playbackPtr = 0;
}
// 録音コールバック
void BSP_AUDIO_IN_TransferComplete_CallBack ( uint32_t Instance
    ) {
    // キャッシュの無効化
    SCB_InvalidateDCache_by_Addr (( uint32_t *) & g_recordPDMBuf [
        AUDIO_IN_PDM_BUFFER_SIZE /2] , AUDIO_IN_PDM_BUFFER_SIZE
        *2) ;
    // PDM バッファの処理
    RecBuffer_PDM_Push_for_Loopback (& g_recordPDMBuf [
        AUDIO_IN_PDM_BUFFER_SIZE /2]) ;
}
void BSP_AUDIO_IN_HalfTransfer_CallBack ( uint32_t Instance ) {
    SCB_InvalidateDCache_by_Addr (( uint32_t *) & g_recordPDMBuf
        [0] , AUDIO_IN_PDM_BUFFER_SIZE *2) ;
    RecBuffer_PDM_Push_for_Loopback (& g_recordPDMBuf [0]) ;
}

まとめ

マイク入力とオーディオ出力が利用できるようになりました。次回は、STM32 マイコンからスピーカの周波数特性を測定するプログラムを作成します。

参考文献
[1] ] Build and ship software on a single, collaborative platform、STMicroelectronics/STM32CubeH7、github。
[2] STM32CubeMX for STM32 configuration and initialization C code generationの 6.4 項、ST マイクロエレクトロニクス。
[3] タッチパネル LCD 搭載! Cortex-M4/M7 × 500MHz!全部入りマイコン・キット STM32H747I-DISCO 誕生、ZEP エンジニアリング株式会社。
[4]
[VOD/KIT/data]Arm M4/M7/DSP × 500MHz! STM32H7 ハイスペック計測通信 Module 開発、ZEP エンジニアリング株式会社。