Realtime sound spectrogram using FFT or linear prediction. Spectrogram is displayed on the display of PC. リアルタイム・スペクトログラム.解析の手法:FFT,線形予測法.スペクトログラムは PC のディスプレー装置に表示される.PC 側のプログラム:F446_Spectrogram.

Dependencies:   Array_Matrix mbed SerialTxRxIntr F446_AD_DA UIT_FFT_Real

Revision:
0:a539141b9dec
Child:
1:cc596a8d40c9
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Fri Feb 17 04:55:10 2017 +0000
@@ -0,0 +1,192 @@
+//---------------------------------------------------------------------
+//  スペクトログラム (Nucleo-F446RE 用),条件等を PC から受け取るテスト
+//
+//      ● ST-Link Firmware の V2.J28.M16 で動作確認
+//
+//      ● ST-Link Firmware のアップグレードには stsw-link07.zip
+//        に含まれている "ST-LinkUpgrade.exe" を使う 
+//
+//      ● PC 側のプログラム: "F446_Spectrogram"
+//      ● ボーレート: 460800 baud
+//      ● 受信データの文字列の終了マーク: "\r"
+//
+//  2017/02/17, 三上 直樹
+//---------------------------------------------------------------------
+
+#include "mbed.h"
+#include <string>
+#include "myFunctions.hpp"
+#include "Array.hpp"
+#include "F446_ADC_Interrupt.hpp"
+#include "FFT_Analyzer.hpp"
+#include "LPC_Analyzer.hpp"
+using namespace Mikami;
+
+#ifndef __STM32F446xx_H
+#error "Use Nucleo-F446RE"
+#endif
+
+const int N_FFT_ = 512;             // FFT の点数
+const int N_DATA_ = N_FFT_ + 1;     // スペクトル解析に使うデータ数(差分処理を考慮)
+const int N_FRAME_ = N_FFT_/2 + 1;  // 1フレーム当たり標本化するデータ数
+const int N_FFT_2_ = N_FFT_/2;      // FFT の点数の半分
+const float AMP_ = 1.0f/2048.0f;    // uint16_t 型のデータを float 型に変換する際の定数
+
+uint16_t xPing_[N_FRAME_];  // 標本化したデータのバッファ1
+uint16_t xPong_[N_FRAME_];  // 標本化したデータのバッファ2
+uint16_t *inPtr_ = xPing_;  // AD 変換データの格納先を指すポインタ
+uint16_t *outPtr_ = xPing_; // 取り出すデータを指すポインタ
+
+__IO int inCount_ = 0;      // 入力データのカウンタ
+__IO int pingPong_ = 0;     // 入力データの格納先,0: xPing_[], 1: xPong_[]
+__IO bool full_ = false;    // AD 変換データが満杯のとき true
+
+const int FS_ = 16000;      // 標本化周波数: 16 kHz
+AdcDual_Intr myAdc_(FS_);   // "F446_ADC_Interrupt.hpp" で定義
+DacDual myDac_;             // "F446_DAC" で定義
+
+// FFT によるスペクトル解析オブジェクトの生成
+FftAnalyzer *fftAnlz_ = new FftAnalyzer(N_DATA_, N_FFT_);
+// 線形予測法 によるスペクトル解析オブジェクトの生成
+LpcAnalyzer *lpcAnlz_ = new LpcAnalyzer(N_DATA_, N_FFT_, 20);
+AnalyzerBase *analyzer_ = fftAnlz_;
+
+Serial pc_(USBTX, USBRX);   // PC との通信で使うオブジェクト
+DigitalOut myLed_(D10, 1);  // LED1 が使えないので D10 を使う
+
+DigitalOut pulse(D2, 0);    // 時間測定用
+
+const int DATA_SIZE_ = N_FFT_/2 + 1;
+Array<int16_t> txData_(DATA_SIZE_); // 送信用データ
+string rxBuffer_;           // 受信バッファ
+float levelShift_ = 20;     // dB 計算の際のシフト量の初期値
+float empha_ = 0.8f;        // 高域強調器の係数
+
+__IO bool eol_;             // "\r" を受信した場合に true
+
+// 入力チャンネルを選択する関数とそれを割り当てる関数ポインタ
+float InputL(float x1, float x2) { return x1; }
+float InputR(float x1, float x2) { return x2; }
+float InputLR(float x1, float x2) { return (x1 + x2)/2; }
+typedef float (*FP_INPUT)(float, float);
+FP_INPUT InputCurrent = InputLR;    // 最初は左右チャンネルを使う
+FP_INPUT InputNew = InputCurrent;
+
+// ADC 変換終了割り込みに対する割り込みサービス・ルーチン
+void AdcIsr()
+{
+    uint16_t sn1, sn2;
+    myAdc_.Read(sn1, sn2);
+    uint16_t xn = InputCurrent(sn1, sn2);
+    inPtr_[inCount_] = xn;
+    myDac_.Write(xn, xn);
+
+    if (++inCount_ >= N_FRAME_) // データが満杯か調べる
+    {
+        full_ = true;           // データが満杯
+        inCount_ = 0;           // 以降のデータ取得のため
+        pingPong_ = (pingPong_+1) & 0x01;               // バッファの切り替えのため
+        inPtr_ = (pingPong_ == 0) ? xPing_ : xPong_;    // バッファのポインタ指定
+        InputCurrent = InputNew;                // 入力の切り替え
+        analyzer_->SetHighEmphasizer(empha_);   // 高域強調の有無の指令
+    }
+}
+
+int main()
+{
+    float sn[N_DATA_];      // スペクトル解析の対象となるデータ
+    float db[N_FRAME_];     // 解析結果である対数スペクトル [dB]
+    for (int n=0; n<N_DATA_; n++) sn[n] = 0;
+    for (int n=0; n<N_FRAME_; n++) xPong_[n] = 2048;    // uint16_t 型の 0 に対応
+
+    rxBuffer_ = "";     // 受信バッファのクリア
+    eol_ = false;
+
+    pc_.baud(115200*4); // ボーレートの設定
+    pc_.format();       // default: 8 bits, nonparity, 1 stop bit
+
+    NVIC_SetPriority(ADC_IRQn, 1);      // AD変換終了割り込みの優先度が最高
+    NVIC_SetPriority(USART2_IRQn, 2);
+
+    pc_.attach(&Rx);            // 受信割り込みの割り当て
+
+    full_ = false;
+    myAdc_.SetIntrVec(&AdcIsr); // AD変換終了割り込みの割り当て
+
+    __IO bool ready = false;    // スペクトルの計算終了で true
+    __IO bool okGo = false;     // "GO" を受信したら true
+    while (true)
+    {
+        // PC からのコマンド解析
+        if (eol_)
+        {
+            if (rxBuffer_.find("ENQ") != string::npos)
+                pc_.printf("ACK\n");    // "ACK" を PC へ転送
+            else if (rxBuffer_.find("GO") != string::npos)
+            {
+                // rxBuffer_ の内容
+                // [0]  'G'
+                // [1]  'O'
+                // [2]  入力チャンネルの選択:'L', 'R', or '+'
+                // [3]  スペクトルの値のレベルシフト:' ' ~ 'I' が -20 ~ 20 に対応
+                // [4]  高域強調器の有無:'Y', 'N'
+                // [5]  解析方法  F: FFT,L: 線形予測法
+
+                switch (rxBuffer_[2])   // 'L', 'R', or '+'
+                {
+                    case 'L': InputNew = InputL;  break;
+                    case 'R': InputNew = InputR;  break;
+                    case '+': InputNew = InputLR; break;
+                    default : InputNew = InputLR; break;
+                }
+
+                levelShift_ = (float)(rxBuffer_[3] - ' ');  // dB 計算の際のシフト量
+
+                if (rxBuffer_[4] == 'Y') empha_ = 0.8f;     // 高域強調器は有
+                else                     empha_ = 0;        // 高域強調器は無
+
+                if (rxBuffer_[5] == 'F') analyzer_ = fftAnlz_;  // FFT
+                else                     analyzer_ = lpcAnlz_;  // 線形予測法
+ 
+                okGo = true;            // データの転送要求あり
+            }
+
+            eol_ = false;
+            rxBuffer_ = "";     // 受信バッファのクリア
+            wait_ms(1);
+        }
+
+        if (full_)  // 入力データが満杯かどうか調べる
+        {
+            full_ = false;
+
+            outPtr_ = (pingPong_ == 1) ? xPing_ : xPong_;
+            // フレームの後半のデータを前半に移動する
+            for (int n=0; n<N_FFT_2_; n++)
+                sn[n] = sn[n+N_FRAME_];
+            // フレームの後半には新しいデータを格納する
+            for (int n=0; n<N_FRAME_; n++)
+                sn[n+N_FFT_2_] = AMP_*(outPtr_[n] - 2048);
+
+            analyzer_->Execute(sn, db); // スペクトル解析の実行
+
+            const float FACTOR = 4095.0f/60.0f;     // 表示範囲: 0 ~ 60 dB
+            for (int n=0; n<DATA_SIZE_; n++)
+            {
+                int16_t spc = (int16_t)(FACTOR*(db[n] + 30.0f + levelShift_));
+                if (spc > 4095) spc = 4095;
+                if (spc < 0) spc = 0;
+                txData_[n] = spc;
+            }
+            ready = true;       // スペクトル解析終了
+        }
+
+        // 転送要求がありスペクトル解析が終了している場合にデータを PC へ転送する
+        if (okGo && ready)
+        {
+            Xfer(txData_);      // データを PC へ転送
+            ready = false;
+            okGo = false;
+        }
+    }
+}