この記事はMbed Advent Calendar 2017の23日目の記事です。
STM32のCubeMXの吐いたコードをmbedにコピペしても、ふつうにコンパイルできます。HALライブラリのAPIもふつうに使えます。そこで、Nucleo-F767ZIをmbedで使用しつつ、CubeMXで生成したコードをコピペしてより高度なペリフェラルの機能を使ってみましょう。
やりたいこと
タイマのイベントとDMAを使ってDACから音声を再生します。音声データは内蔵Flashに持ちます。システムサウンドのようなごく短い音声の再生を想定しています。
- 使用するボード:Nucleo-F767ZI (STM32F767ZI搭載、クロック:216MHz、Flash:2Mバイト)
- 使用するツール:CubeMX、mbedオンラインコンパイラ
- 使用するペリフェラル:DAC, DMA1, TIM6
- 再生する音声データ: 12bit量子化、11.025kHzサンプリング
CubeMXでの設定
下記の記事を参考にCubeMXでペリフェラルの設定をして初期化コードを生成します。
TIM6の設定
- [Pinout]
- Activated
- [Configuration]-[Parameter Settings]
- Prescaler = 0 (108MHz)
- Counter Mode = Up
- Counter Period = 9796-1 (108MHz / 9796 ≒ 11.025kHz)
- Trigger Event Selection = Update Event (DACをキックするためのイベント)
DACの設定
- [Pinout]
- OUT1 Configuration
- [Configuration]-[Parameter Settings]
- Output Buffer = Enable
- Trigger = Timer 6 Trigger Out event (TIM6のイベントをトリガとする)
- Wave generation mode = Disable
- [Configuration]-[DMA Settings]
- DAC1 - DMA1 Stream5 / Memory To Peripheral / Low
- Mode = Normal
- Peripheral (DAC)
- Increment Address = no (アドレス固定)
- Data Width = Half Word (16bit)
- Memory (内蔵フラッシュ)
- Increment Address = yes (アドレスインクリメント)
- Data Width = Hald Word (16bit)
音声データの作成
mbedのオンラインコンパイラでバイナリのリソースファイルを扱うことって、たぶんできないですよね?(リンカスクリプトが隠蔽されてるので)そこで、WAVファイルをテキスト形式に変換してC言語の配列定義に埋め込むツールを作成しました。ついでにDACの精度に合わせてデータを符号なし12ビットの値に変換します。
※ 音声データファイルを上記フォーマットのWAVファイルに変換する方法はこちらを参考にしてください。
下記のような配列定義のソースファイルを生成しました。
#include "stdint.h" extern const uint16_t PICO_data[] = { 0x07FF, 0x0800, 0x07FF, 0x0800, 0x07FF, 0x07FF, 0x0800, 0x07FF, 0x0800, 0x07FF, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x07FF, 0x0800, 0x0800, 0x0800, 0x07FF, 0x0800, 0x07FF, 0x0800, 0x07FF, 0x0800, 0x0800, 0x07FF, 0x0800, 0x07FF, 0x0800, 0x07FF, 0x0800, (中略) 0x0800, 0x07FF, 0x0800, 0x0800, 0x07FF, 0x0800, 0x07FF, 0x0800, 0x07FF, 0x0800, 0x0800, 0x07FF, 0x0800, 0x07FF, 0x0800, 0x0800, 0x0800, }; extern const int PICO_size = 0x06C1;
クラス化
CubeMXで生成したコードから必要な部分(タイマ、DAC、DMAの設定)を抜き出して、mbedの環境にコピペします。使いやすいように、SystemSoundというクラスを作ってCubeMXのコードを隠蔽します。
// System Sound class class SystemSound { public: // initialize void init(); // play sound void play(const uint16_t* data, uint32_t len); };
使い方はこんなかんじです。
#include "mbed.h" #include "SystemSound.h" // sound "Pico!" extern const uint16_t PICO_data[]; extern const int PICO_size; SystemSound sound; int main() { sound.init(); // "Pico!...Pico!...Pico!..." (1sec cycle) while(1) { sound.play(PICO_data, PICO_size); wait(1); } }