CQ出版社セミナ,2021/12/07開催「実習・C++言語によるArmマイコンのプログラミング」で使うプログラム.

Dependencies:   Array_Matrix mbed SerialTxRxIntr UIT_FFT_Real

Files at this revision

API Documentation at this revision

Comitter:
MikamiUitOpen
Date:
Wed Jan 15 12:43:11 2020 +0000
Child:
1:a90b573b335a
Commit message:
1

Changed in this revision

ADDA_BasePolling/F446_ADC_Intr.hpp Show annotated file Show diff for this revision Revisions of this file
ADDA_BasePolling/F446_ADC_Polling.cpp Show annotated file Show diff for this revision Revisions of this file
ADDA_BasePolling/F446_ADC_Polling.hpp Show annotated file Show diff for this revision Revisions of this file
ADDA_BasePolling/F446_DAC.hpp Show annotated file Show diff for this revision Revisions of this file
Array_Matrix.lib Show annotated file Show diff for this revision Revisions of this file
DoubleBuffer.hpp Show annotated file Show diff for this revision Revisions of this file
IIR_Filter/Biquad.hpp Show annotated file Show diff for this revision Revisions of this file
IIR_Filter/Coefs_IIR6_LP_8k_Biquad.hpp Show annotated file Show diff for this revision Revisions of this file
IIR_Filter/IIR_Cascade.hpp Show annotated file Show diff for this revision Revisions of this file
MyFFT_Analyzer/FFT_Analyzer.cpp Show annotated file Show diff for this revision Revisions of this file
MyFFT_Analyzer/FFT_Analyzer.hpp Show annotated file Show diff for this revision Revisions of this file
MyFFT_Analyzer/Hamming.hpp Show annotated file Show diff for this revision Revisions of this file
SerialTxRxIntr.lib Show annotated file Show diff for this revision Revisions of this file
UIT_FFT_Real.lib Show annotated file Show diff for this revision Revisions of this file
Xfer.hpp Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
mbed.bld Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ADDA_BasePolling/F446_ADC_Intr.hpp	Wed Jan 15 12:43:11 2020 +0000
@@ -0,0 +1,49 @@
+//-------------------------------------------------------------
+//  F446 内蔵 ADC2 を割込み方式で使うための派生クラス
+//      基底クラス: AdcF446_Polling
+//
+//  2020/01/12, Copyright (c) 2020 MIKAMI, Naoki
+//-------------------------------------------------------------
+
+#include "F446_ADC_Polling.hpp"
+
+#ifndef ADC_F446_INTERRUPT_HPP
+#define ADC_F446_INTERRUPT_HPP
+
+namespace Mikami
+{
+    class AdcF446_Intr : public AdcF446_Polling
+    {
+    public:
+        // コンストラクタ
+        //      fSampling 標本化周波数 [kHz]
+        //      pin     入力ピンの名前
+        AdcF446_Intr(float fSampling, PinName pin)
+            : AdcF446_Polling(fSampling, pin)
+        {   myAdc_->CR1 |= ADC_CR1_EOCIE; }     // AD 変換終了割り込みを許可
+
+        virtual ~AdcF446_Intr() {}
+
+        // 割込みベクタの設定と AD 変換割込みを有効にする
+        void SetIntrVec(void (*Func)())
+        {
+            fp = Func;          // 引数として渡された処理を割り当てる
+            NVIC_SetVector(ADC_IRQn, (uint32_t)Isr);    // "core_cm4.h" 参照
+            NVIC_EnableIRQ(ADC_IRQn);                   // "core_cm4.h" 参照
+        }    
+
+        // AD 変換された値を読み込む
+        //      -1.0f <= AD変換された値 < 1.0f
+        virtual float Read() const { return ToFloat(myAdc_->DR); }
+    private:
+        static void (*fp)();    // 割込みサービス・ルーチンの中で実行される関数のポインタ
+
+        // 割込みサービス・ルーチン,このクラスで使っている ADC であることを確認
+        static void Isr()
+        {   if ((myAdc_->SR & ADC_SR_EOC_Msk) == ADC_SR_EOC) fp(); }
+    };
+
+    // static メンバの実体
+    void (*AdcF446_Intr::fp)();
+}
+#endif  // ADC_F446_INTERRUPT_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ADDA_BasePolling/F446_ADC_Polling.cpp	Wed Jan 15 12:43:11 2020 +0000
@@ -0,0 +1,89 @@
+//-------------------------------------------------------------
+//  F446 内蔵 ADC2 のためのポーリング方式のクラス
+//
+//  2020/01/12, Copyright (c) 2020 MIKAMI, Naoki
+//-------------------------------------------------------------
+
+#include "F446_ADC_Polling.hpp"
+#include "PeripheralPins.h" // PinMap_ADC を使う場合に必要
+#pragma diag_suppress 870   // マルチバイト文字使用の警告抑制のため
+// PeripheralPins.c は以下よりたどって行けば取得可能
+// https://gitlab.exmachina.fr/fw-libs/mbed-os/tree/5.8.1
+
+namespace Mikami
+{
+    // コンストラクタ
+    AdcF446_Polling::AdcF446_Polling(float fSampling, PinName pin)
+    {
+        SetGPIO(pin);                   // GPIO の設定
+
+        __HAL_RCC_ADC2_CLK_ENABLE();    // ADC2 にクロックを供給する
+        // __HAL_RCC_ADC2_CLK_ENABLE() の定義:stm32f4xx_hal_rcc_ex.h
+
+        // pin に対応するチャンネルを使うための設定
+        myAdc_->SQR3 = STM_PIN_CHANNEL(pinmap_function(pin, PinMap_ADC));
+        // pinmap_function() のヘッダファイル: mbed\hal\pinmap.h
+        // STM_PIN_CHANNEL() の定義:PinNamesTypes.h
+
+        // ADC の CR1 の設定
+        myAdc_->CR1 = 0x0;      // AD 変換終了割り込みを禁止
+        // ADC の CR2 の設定
+        myAdc_->CR2 = ADC_EXTERNALTRIGCONVEDGE_RISING   // 外部トリガの立ち上がりで開始される
+                    | ADC_EXTERNALTRIGCONV_T8_TRGO      // 外部トリガ: Timer8 TRGO event
+                    | ADC_CR2_ADON;                     // ADC を有効にする
+
+        // AD 変換器の外部トリガに使うタイマ (TIM8) の設定
+        SetTim8(fSampling);
+    }
+
+    // AD 変換器の外部トリガに使うタイマ (TIM8) の設定
+    //      fSampling 標本化周波数 [kHz]
+    void AdcF446_Polling::SetTim8(float fSampling)
+    {
+        __HAL_RCC_TIM8_CLK_ENABLE();    // クロック供給. "stm32f4xx_hal_rcc.h" 参照
+        TIM_TypeDef* const TIM = TIM8;
+
+        TIM->CR2 = TIM_TRGO_UPDATE;     // Update event を TRGO (trigger output) とする
+
+        float arrF = (SystemCoreClock/fSampling)/1000.0f;
+        if (arrF >65535)
+        {
+            fprintf(stderr, "%8.2f kHz : 標本化周波数が低すぎます.\r\n", fSampling);
+            while (true) {}
+        }
+        TIM->ARR = floor(arrF + 0.5f) - 1;  // Auto-reload レジスタの設定
+        TIM->PSC = 0;                   // Prescaler の設定
+        TIM->CR1 = TIM_CR1_CEN;         // TIM8 を有効にする
+    }
+
+    // AD 変換器に関係のある GPIO の設定
+    void AdcF446_Polling::SetGPIO(PinName pin)
+    {
+        // 各シンボルは stm32f4xx_hal_gpio.h で定義されている
+        // MODIFY_REG() マクロは stm32f4xx.h で定義されている
+        uint32_t nShift = STM_PIN(pin) << 1;
+        uint32_t moder = GPIO_MODE_ANALOG << nShift;
+        uint32_t mask = 0x03 << nShift;
+        switch (STM_PORT(pin))
+        {
+            case 0:
+                if (__HAL_RCC_GPIOA_IS_CLK_DISABLED())
+                    __HAL_RCC_GPIOA_CLK_ENABLE();       // GPIOA にクロックを供給
+                MODIFY_REG(GPIOA->MODER, mask, moder);  // GPIOA のピンを設定
+                break;
+            case 1:
+                if (__HAL_RCC_GPIOB_IS_CLK_DISABLED())
+                    __HAL_RCC_GPIOB_CLK_ENABLE();       // GPIOB にクロックを供給
+                MODIFY_REG(GPIOB->MODER, mask, moder);  // GPIOB のピンを設定
+                break;
+            case 2:
+                if (__HAL_RCC_GPIOC_IS_CLK_DISABLED())
+                    __HAL_RCC_GPIOC_CLK_ENABLE();       // GPIOC にクロックを供給
+                MODIFY_REG(GPIOC->MODER, mask, moder);  // GPIOC のピンを設定
+                break;
+        }
+    }
+
+    // static メンバの実体
+    ADC_TypeDef* const AdcF446_Polling::myAdc_ = ADC2;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ADDA_BasePolling/F446_ADC_Polling.hpp	Wed Jan 15 12:43:11 2020 +0000
@@ -0,0 +1,53 @@
+//-------------------------------------------------------------
+//  F446 内蔵 ADC2 のためのポーリング方式のクラス(ヘッダ)
+//
+//  2020/01/12, Copyright (c) 2020 MIKAMI, Naoki
+//-------------------------------------------------------------
+
+#include "mbed.h"
+
+#ifndef ADC_F446_POLLING_HPP
+#define ADC_F446_POLLING_HPP
+
+namespace Mikami
+{
+    class AdcF446_Polling
+    {
+    public:
+        // コンストラクタ
+        //      fSampling 標本化周波数 [kHz]
+        //      pin     入力ピンの名前
+        AdcF446_Polling(float fSampling, PinName pin);
+
+        virtual ~AdcF446_Polling() {}
+
+        // AD 変換された値を読み込む
+        //      -1.0f <= AD変換された値 < 1.0f
+        virtual float Read() const
+        {
+            while ((myAdc_->SR & ADC_SR_EOC) != ADC_SR_EOC) {}
+            return ToFloat(myAdc_->DR);
+        }
+
+    protected:
+////        ADC_TypeDef* const myAdc_;  // AD 変換器に対応するポインタ
+            static ADC_TypeDef* const myAdc_;   // AD 変換器に対応するポインタ
+
+        float ToFloat(uint16_t x) const { return AMP_*(x - 2048); }
+
+    private:
+        static const float AMP_ = 1.0f/2048.0f;
+        // AD 変換器の外部トリガに使うタイマ (TIM8) の設定
+        //      fSampling 標本化周波数 [kHz]
+        void SetTim8(float fSampling);
+        
+        // AD 変換器に関係のある GPIO の設定
+        void SetGPIO(PinName pin);
+
+        // コピー・コンストラクタ禁止のため
+        AdcF446_Polling(const AdcF446_Polling&);
+        // 代入演算子禁止のため
+        AdcF446_Polling& operator=(const AdcF446_Polling&);
+    };
+}
+#endif  // ADC_F446_POLLING2_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ADDA_BasePolling/F446_DAC.hpp	Wed Jan 15 12:43:11 2020 +0000
@@ -0,0 +1,45 @@
+//--------------------------------------------------------
+//  STM32F446 内蔵の DAC 用のクラス
+//      出力端子:   A2  (PA_4)
+//
+//  2020/01/12, Copyright (c) 2020 MIKAMI, Naoki
+//--------------------------------------------------------
+#include "mbed.h"
+
+#ifndef DAC_F446_SINGLE_HPP
+#define DAC_F446_SINGLE_HPP
+
+namespace Mikami
+{
+    class DacF446
+    {
+    public:
+        // コンストラクタ, A2 に接続される CH1 のみを有効にする
+        DacF446() : da_(A2) { DAC->CR = DAC_CR_EN1; }
+
+        virtual ~DacF446() {}
+
+        // -1.0f <= data <= 1.0f
+        void Write(float data) { WriteDacCh1(ToUint16(data)); }
+
+        // 0 <= data <= 4095
+        void Write(uint16_t data) { WriteDacCh1(__USAT(data, BIT_WIDTH_)); }
+
+    private:
+        static const int BIT_WIDTH_ = 12;
+        AnalogOut da_;
+
+        // DAC の CH1 へ右詰めで出力する
+        void WriteDacCh1(uint16_t val) { DAC->DHR12R1 = val; }
+
+        // 飽和処理を行い uint16_t 型のデータを戻り値とする
+        uint16_t ToUint16(float val)
+        {   return __USAT((val + 1.0f)*2048.0f, BIT_WIDTH_); }
+
+        // コピー・コンストラクタ禁止のため
+        DacF446(const DacF446&);
+        // 代入演算子禁止のため
+        DacF446& operator=(const DacF446&);
+    };
+}
+#endif  // DAC_F446_SINGLE_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Array_Matrix.lib	Wed Jan 15 12:43:11 2020 +0000
@@ -0,0 +1,1 @@
+https://os.mbed.com/users/MikamiUitOpen/code/Array_Matrix/#d9dea7748b27
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/DoubleBuffer.hpp	Wed Jan 15 12:43:11 2020 +0000
@@ -0,0 +1,58 @@
+//--------------------------------------------------------
+//  ダブル・バッファの template クラス
+//      内部のバッファは通常の配列を使用
+//
+//  2019/11/22, Copyright (c) 2019 MIKAMI, Naoki
+//--------------------------------------------------------
+
+#ifndef DOUBLE_BUFFER_2DARRAY_HPP
+#define DOUBLE_BUFFER_2DARRAY_HPP
+
+template<class T, int N> class DoubleBuffer
+{
+public:
+    // コンストラクタ
+    explicit DoubleBuffer(T initialValue)
+        : ping_(0), pong_(1), index_(0), full_(false)
+    {
+        for (int k=0; k<2; k++)
+            for (int n=0; n<N; n++) buf_[k][n] = initialValue;
+    }
+    
+    // データを格納
+    void Store(T data)  { buf_[ping_][index_++] = data; }
+    
+    // 出力バッファからデータの取り出し
+    T Get(int n) const { return buf_[pong_][n]; }
+
+    // バッファが満杯でバッファを切り替える
+    void IfFullSwitch()
+    {
+        if (index_ < N) return;
+
+        ping_ ^= 0x1;   // バッファ切換えのため
+        pong_ ^= 0x1;   // バッファ切換えのため
+        index_ = 0;
+        full_ = true;
+    }
+
+    // バッファが満杯で,true を返す
+    bool IsFull()
+    {
+        bool temp = full_;
+        if (full_) full_ = false;
+        return temp;
+    }
+
+private:
+    T buf_[2][N];       // 標本化したデータのバッファ
+    int ping_, pong_;   // バッファ切替用
+    int index_;         // 入力データのカウンタ
+    bool full_;         // 満杯の場合 true
+
+    // コピー・コンストラクタおよび代入演算子の禁止のため
+    DoubleBuffer(const DoubleBuffer&);
+    DoubleBuffer& operator=(const DoubleBuffer&);
+};
+#endif  // DOUBLE_BUFFER_2DARRAY_HPP
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/IIR_Filter/Biquad.hpp	Wed Jan 15 12:43:11 2020 +0000
@@ -0,0 +1,49 @@
+//--------------------------------------------------------------
+// 縦続形 IIR フィルタの構成要素として使う 2 次の IIR フィルタ
+//      b0 は 1 と仮定している
+//
+//      u[n] = x[n] + a1*u[n-1] + a2*u[n-2]
+//      y[n] = u[n] + b1*u[n-1] + b2*u[n-2]
+//
+// 2019/11/22, Copyright (c) 2019 MIKAMI, Naoki
+//--------------------------------------------------------------
+
+#ifndef IIR_BIQUAD_HPP
+#define IIR_BIQUAD_HPP
+
+#include "mbed.h"
+
+// 2 次の IIR フィルタ
+class Biquad
+{
+public:
+    // デフォルト・コンストラクタ
+    Biquad() {}
+
+    // 係数を個別に与えるコンストラクタ
+    Biquad(float a1, float a2, float b1, float b2)
+        : a1_(a1), a2_(a2), b1_(b1), b2_(b2), un1_(0), un2_(0) {}       
+
+    // 2 次のフィルタを実行する
+    float Execute(float xn)
+    {
+        float un = xn + a1_*un1_ + a2_*un2_;
+        float yn = un + b1_*un1_ + b2_*un2_;
+        
+        un2_ = un1_;
+        un1_ = un;
+        
+        return yn;
+    }
+
+private:
+    float a1_, a2_, b1_, b2_;
+    float un1_, un2_;
+
+    // コピー・コンストラクタ禁止
+    Biquad(const Biquad&);
+};
+
+#endif  // IIR_BIQUAD_HPP
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/IIR_Filter/Coefs_IIR6_LP_8k_Biquad.hpp	Wed Jan 15 12:43:11 2020 +0000
@@ -0,0 +1,23 @@
+//-----------------------------------------------------
+//  縦続形 IIR フィルタの次数と係数の定義
+//
+//  2019/11/14, Copyright (c) 2019 MIKAMI, Naoki
+//-----------------------------------------------------
+
+#include "Biquad.hpp"
+
+// 低域通過フィルタ
+// 連立チェビシェフ特性
+// 次数    : 8 次
+// 標本化周波数:160.00 kHz
+// 遮断周波数 :  8.00 kHz
+// 通過域のリップル: 0.50 dB
+// 阻止域の減衰量 :60.00 dB
+const int ORDER_ = 8;
+const Biquad CK_[] = {
+    Biquad(1.809727E+00f, -8.261113E-01f, -8.082254E-01f, 1.0E+00f),    // 1段目
+    Biquad(1.842528E+00f, -8.959587E-01f, -1.755464E+00f, 1.0E+00f),    // 2段目
+    Biquad(1.871837E+00f, -9.562537E-01f, -1.853590E+00f, 1.0E+00f),    // 3段目
+    Biquad(1.890836E+00f, -9.887843E-01f, -1.875058E+00f, 1.0E+00f)};   // 4段目
+const float G0_ = 1.281836E-03f;    // 利得定数
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/IIR_Filter/IIR_Cascade.hpp	Wed Jan 15 12:43:11 2020 +0000
@@ -0,0 +1,39 @@
+//---------------------------------------------------
+//  縦続形 IIR フィルタ
+//
+// 2019/11/12, Copyright (c) 2019 MIKAMI, Naoki
+//---------------------------------------------------
+
+#ifndef IIR_CASCADE_HPP
+#define IIR_CASCADE_HPP
+
+#include "Biquad.hpp"
+#include "Array.hpp"    // Array クラスが定義されている
+using namespace Mikami;
+
+class IirCascade
+{
+public:
+    // コンストラクタ
+    IirCascade(int order, const Biquad hk[], float g0)
+        : ORDER2_((order+1)/2), G0_(g0), hn_((order+1)/2, hk) {}
+
+    // フィルタ処理を実行する
+    float Execute(float xn)
+    {
+        float yn = G0_*xn;
+        for (int k=0; k<ORDER2_; k++) yn = hn_[k].Execute(yn);
+        return yn;
+    }
+
+private:
+    const int ORDER2_;  // 次数/2,ただし次数が奇数の場合は(次数+1)/2
+    const float G0_;    // 利得定数
+    Array<Biquad> hn_;  // Biquad クラスのオブジェクトの配列
+
+    // コピー・コンストラクタおよび代入演算子の禁止のため
+    IirCascade(const IirCascade& );
+    IirCascade& operator=(const IirCascade& );
+};
+#endif  // IIR_CASCADE_HPP
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MyFFT_Analyzer/FFT_Analyzer.cpp	Wed Jan 15 12:43:11 2020 +0000
@@ -0,0 +1,36 @@
+//-------------------------------------------------------
+//  FFT を使ってスペクトル解析を行うクラス
+//
+//  2019/11/22, Copyright (c) 2019 MIKAMI, Naoki
+//-------------------------------------------------------
+
+#include "FFT_Analyzer.hpp"
+
+namespace Mikami
+{
+    FftAnalyzer::FftAnalyzer(int nFft)
+        : N_FFT_(nFft), fft_(nFft), wHm_(nFft, nFft),
+          xData_(nFft), wData_(nFft), yFft_(nFft/2+1) {}
+
+    void FftAnalyzer::Execute(const Array<float> &xn, Array<float> &db)
+    {
+        xData_ = xn;    // データを作業領域にコピー
+
+        // 直流分を除去
+        float sum = 0;
+        for (int n=0; n<N_FFT_; n++) sum = sum + xData_[n];
+        float ave = sum/N_FFT_;
+        for (int n=0; n<N_FFT_; n++) xData_[n] = xData_[n] - ave;
+
+        wHm_.Execute(xData_, wData_);   // 窓掛け
+            
+        fft_.Execute(wData_, yFft_);    // FFT の実行
+        
+        for (int n=0; n<=N_FFT_/2; n++) // dB 値に変換
+        {
+            float norm = Norm(yFft_[n]);
+            db[n] = (norm > 0) ? 10.0f*log10f(norm) : -40.0f;
+        }
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MyFFT_Analyzer/FFT_Analyzer.hpp	Wed Jan 15 12:43:11 2020 +0000
@@ -0,0 +1,44 @@
+//-------------------------------------------------------
+//  FFT を使ってスペクトル解析を行うクラス(ヘッダ)
+//
+//  2019/11/22, Copyright (c) 2019 MIKAMI, Naoki
+//-------------------------------------------------------
+
+#ifndef FFT_ANALYZER_HPP
+#define FFT_ANALYZER_HPP
+
+#include "Array.hpp"
+#include "fftReal.hpp"
+#include "Hamming.hpp"
+
+namespace Mikami
+{
+    class FftAnalyzer
+    {
+    public:
+        // nFft:  FFT のデータ点の数
+        FftAnalyzer(int nFft);
+        virtual ~FftAnalyzer() {}
+        void Execute(const Array<float> &xn, Array<float> &db);
+
+    private:
+        const int N_FFT_;
+
+        FftReal fft_;
+        HammingWindow wHm_;
+
+        Array<float> xData_;    // 解析対象の時系列データ
+        Array<float> wData_;    // 窓掛けされたデータ
+        Array<Complex> yFft_;   // FFT の結果
+
+        // 絶対値の2乗
+        float Norm(Complex x)
+        { return x.real()*x.real() + x.imag()*x.imag(); }
+
+        // コピー・コンストラクタおよび代入演算子の禁止のため
+        FftAnalyzer(const FftAnalyzer& );
+        FftAnalyzer& operator=(const FftAnalyzer& );
+    };
+}
+#endif  // FFT_ANALYZER_HPP
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MyFFT_Analyzer/Hamming.hpp	Wed Jan 15 12:43:11 2020 +0000
@@ -0,0 +1,50 @@
+//-------------------------------------------------------------------
+//  Hamming 窓による窓掛け
+//      ゼロ詰め(zero-padding)の機能を持つ
+//
+//  2019/11/14, Copyright (c) 2019 MIKAMI, Naoki
+//-------------------------------------------------------------------
+
+#ifndef HAMMING_WINDOW_HPP
+#define HAMMING_WINDOW_HPP
+
+#include "mbed.h"
+#include "Array.hpp"
+
+namespace Mikami
+{
+    class HammingWindow
+    {
+    public:
+        // コンストラクタ
+        HammingWindow(uint16_t nData, uint16_t nFft)
+                : N_(nData), NFFT_(nFft), w_(nData)
+        {
+            const float PI2L = 6.283185f/(float)nData;
+            for (int k=0; k<nData; k++)
+                w_[k] = 0.54f - 0.46f*cosf(k*PI2L);
+        }
+        
+        // デストラクタ
+        virtual ~HammingWindow() {}
+        
+        // 窓掛けを実行
+        void Execute(const Array<float> &x, Array<float> &y)
+        {
+            for (int n=0; n<N_; n++) y[n] = x[n]*w_[n];
+            for (int n=N_; n<NFFT_; n++) y[n] = 0;
+        }
+
+    private:
+        const int N_;
+        const int NFFT_;
+                
+        Array<float> w_;
+
+        // コピー・コンストラクタおよび代入演算子の禁止のため
+        HammingWindow(const HammingWindow& );
+        HammingWindow& operator=(const HammingWindow& );
+    };
+}
+#endif  // HAMMING_WINDOW_HPP
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SerialTxRxIntr.lib	Wed Jan 15 12:43:11 2020 +0000
@@ -0,0 +1,1 @@
+https://os.mbed.com/users/MikamiUitOpen/code/SerialTxRxIntr/#6525f1c28ef6
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/UIT_FFT_Real.lib	Wed Jan 15 12:43:11 2020 +0000
@@ -0,0 +1,1 @@
+https://os.mbed.com/users/MikamiUitOpen/code/UIT_FFT_Real/#dc123081d491
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Xfer.hpp	Wed Jan 15 12:43:11 2020 +0000
@@ -0,0 +1,59 @@
+//---------------------------------------------------------------------
+//  スペクトル解析の結果を PC へ転送するためのクラス
+//
+//  2019/11/16, Copyright (c) 2019 MIKAMI, Naoki
+//---------------------------------------------------------------------
+
+#include <string>
+#include "Array.hpp"
+#include "SerialRxTxIntr.hpp"
+using namespace Mikami;
+
+#ifndef XFER_CONVERT_TOPC_HPP
+#define XFER_CONVERT_TOPC_HPP
+
+class Xfer
+{
+public:
+    // コンストラクタ
+    Xfer(SerialRxTxIntr& rxTx, int size)
+        : SIZE_(size), xn_(size), rxTx_(rxTx) {}
+        
+    // スペクトル解析の結果を転送する形式に変換
+    void Convert(const float db[])
+    {
+        static const float FACTOR = 10000.0f/80.0f; // 表示範囲: 0 ~ 80 dB
+        for (int n=0; n<SIZE_; n++)
+        {
+            int32_t xDb = (int32_t)(FACTOR*(db[n] + 40.0f));
+            if (xDb > 10000) xDb = 10000;
+            if (xDb < 0) xDb = 0;
+            xn_[n] = (uint16_t)xDb;
+        }
+    }
+    
+    // データを PC へ転送(0 ~ 10,000 の範囲の値を 2 文字で表すコード化を利用)
+    void ToPC()
+    {
+        string str = "";
+        for (int n=0; n<SIZE_; n++)
+        {
+            div_t a = div(xn_[n], 100);
+            str += a.quot + 0x10;
+            str += a.rem + 0x10;
+        }
+        rxTx_.Tx(str+"\n");
+        rxTx_.Tx("EOT\n");
+    }
+    
+private:
+    const int SIZE_;        // PC に送るデータ数
+    Array<uint16_t> xn_;    // PC に送るデータ
+    SerialRxTxIntr& rxTx_;
+
+    // コピー・コンストラクタおよび代入演算子の禁止のため
+    Xfer(const Xfer&);
+    Xfer& operator=(const Xfer&);
+};
+#endif  // XFER_CONVERT_TOPC_HPP
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Wed Jan 15 12:43:11 2020 +0000
@@ -0,0 +1,108 @@
+//---------------------------------------------------------------------
+//  FFT によるスペクルアナライザ (Nucleo-F446RE 用)
+//
+//      ● ST-Link Firmware の V2.J28.M16 で動作確認
+//
+//      ● ST-Link Firmware のアップグレードには stsw-link07.zip
+//        に含まれている "ST-LinkUpgrade.exe" を使う 
+//
+//      ● PC 側のプログラム: "F446_FFT_Analyzer"
+//      ● ボーレート: 460800 baud
+//      ● 受信データの文字列の終了マーク: "\r"
+//
+//      ● 入力:  A1
+//      ● モニタ用出力:A2
+//
+//  PC 側のプログラムのフォルダ:プログラム\PC\F446_FFT_Analyzer
+//
+//  2020/01/13, Copyright (c) 2020 MIKAMI, Naoki
+//---------------------------------------------------------------------
+
+#include "mbed.h"
+#include <string>
+#include "Array.hpp"
+#include "F446_ADC_Intr.hpp"
+#include "F446_DAC.hpp"
+#include "FFT_Analyzer.hpp"
+#include "DoubleBuffer.hpp"
+#include "Coefs_IIR6_LP_8k_Biquad.hpp"  // 縦続形 IIR フィルタの係数
+#include "IIR_Cascade.hpp"              // 縦続形 IIR フィルタ
+#include "Xfer.hpp"
+using namespace Mikami;
+
+const int N_FFT_ = 512;             // FFT の点数
+const int N_FRAME_ = N_FFT_/2;      // 1フレーム当たり標本化するデータ数
+const int N_FFT_2_ = N_FFT_/2;      // FFT の点数の半分
+const int RATIO_ = 10;              // オーバーサンプリングの倍率
+
+DoubleBuffer<float, N_FRAME_> buf_(0);    // AD の結果を保存するバッファ
+AdcF446_Intr myAdc_(16*RATIO_, A1); // 標本化周波数: 160 kHz
+DacF446 myDac;
+IirCascade df_(ORDER_, CK_, G0_);   // ダウンサンプリング用 Anti-alias フィルタ
+
+// ADC 変換終了割り込みに対する割り込みサービス・ルーチン
+void AdcIsr()
+{
+    static int count = 0;
+
+    float xn = myAdc_.Read();
+    myDac.Write(xn);            // モニタ用
+    float yn = df_.Execute(xn); // ダウンサンプリング用 Anti-alias フィルタの実行
+
+    if (++count >= RATIO_)
+    {
+        buf_.Store(yn);         // ダウンサンプリングされたデータをバッファへ格納
+        count = 0;
+        buf_.IfFullSwitch();    // バッファが満杯であればバッファを切り替える
+    }
+}
+
+int main()
+{
+    SerialRxTxIntr rxTx(32, 460800);    // PC との通信用
+    Xfer tx(rxTx, N_FFT_/2+1);          // PC に転送するためのオブジェクトの生成
+    FftAnalyzer analyzer(N_FFT_);       // FFT によるスペクトル解析オブジェクトの生成
+
+    Array<float> sn(N_FFT_, 0.0f);      // スペクトル解析の対象となるデータ
+    Array<float> db(N_FRAME_);          // 解析結果:対数スペクトル [dB]
+
+    NVIC_SetPriority(ADC_IRQn, 0);      // AD変換終了割り込みの優先度が最高
+    NVIC_SetPriority(USART2_IRQn, 1);
+
+    bool ready = false; // スペクトルの計算終了で true
+    bool okGo = false;  // "GO" を受信したら true
+
+    myAdc_.SetIntrVec(&AdcIsr); // AD変換終了割り込みの割り当て
+    while (true)
+    {
+        // PC からのコマンドの解析
+        if (rxTx.IsEol())      // 受信バッファのデータが有効になった場合の処理
+        {
+            string str = rxTx.GetBuffer();
+            if (str == "FFTAnalyzer")
+                rxTx.Tx("ACK\n");  // PC からの "FFTAnalyzer" に対して "ACK" を送信
+            else if (str == "GO") okGo = true;  // データの転送要求あり
+        }
+
+        if (buf_.IsFull())  // 入力データが満杯の場合,以下の処理を行う
+        {
+            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_] = buf_.Get(n);
+
+            analyzer.Execute(sn, db);   // スペクトル解析の実行
+            tx.Convert(db);             // スペクトル解析の結果を転送する形式に変換
+            ready = true;               // スペクトル解析終了
+        }
+
+        // 転送要求がありスペクトル解析が終了している場合にデータを PC へ転送する
+        if (okGo && ready)
+        {
+            tx.ToPC();      // データを PC へ転送
+            ready = false;
+            okGo = false;
+        }
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed.bld	Wed Jan 15 12:43:11 2020 +0000
@@ -0,0 +1,1 @@
+https://os.mbed.com/users/mbed_official/code/mbed/builds/65be27845400
\ No newline at end of file