
CQ出版社セミナ,2021/12/07開催「実習・C++言語によるArmマイコンのプログラミング」で使うプログラム.
Dependencies: Array_Matrix mbed SerialTxRxIntr UIT_FFT_Real
Revision 0:a80f730d32a8, committed 2020-01-15
- Comitter:
- MikamiUitOpen
- Date:
- Wed Jan 15 12:43:11 2020 +0000
- Child:
- 1:a90b573b335a
- Commit message:
- 1
Changed in this revision
--- /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