CQエレクトロニクス・セミナで使用するファンクション・ジェネレータの プログラム
Dependencies: Array_Matrix mbed SerialTxRxIntr MyTicker7
Revision 0:8c8bc21159d9, committed 2022-02-25
- Comitter:
- MikamiUitOpen
- Date:
- Fri Feb 25 02:36:55 2022 +0000
- Commit message:
- 1
Changed in this revision
diff -r 000000000000 -r 8c8bc21159d9 Array_Matrix.lib --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Array_Matrix.lib Fri Feb 25 02:36:55 2022 +0000 @@ -0,0 +1,1 @@ +https://os.mbed.com/users/MikamiUitOpen/code/Array_Matrix/#d3aa1ddb57e1
diff -r 000000000000 -r 8c8bc21159d9 F446_DAC.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/F446_DAC.cpp Fri Feb 25 02:36:55 2022 +0000 @@ -0,0 +1,28 @@ +//------------------------------------------------------------- +// STM32F446 内蔵の DAC 用のクラス +// DAC_OUT1: A2 (PA_4) +// DAC_OUT2: D13 (PA_5) +// +// 2020/10/17, Copyright (c) 2020 MIKAMI, Naoki +//------------------------------------------------------------- + +#include "F446_DAC.hpp" + +namespace Mikami +{ + DacF446::DacF446(PinName pin) : da_(pin) + { + MBED_ASSERT((pin == A2) || (pin == D13)); + + if (pin == A2) + { + DAC->CR = DAC_CR_EN1; + fpWriteDac = &DacF446::WriteDac1; + } + else + { + DAC->CR = DAC_CR_EN2; + fpWriteDac = &DacF446::WriteDac2; + } + } +}
diff -r 000000000000 -r 8c8bc21159d9 F446_DAC.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/F446_DAC.hpp Fri Feb 25 02:36:55 2022 +0000 @@ -0,0 +1,57 @@ +//------------------------------------------------------------- +// STM32F446 内蔵の DAC 用のクラス(ヘッダ) +// 選択可能な入力端子: +// A2 (PA_4): ---- デフォルト +// D13 (PA_5): このポートはマイコンボードの LED も +// ドライブするので使わない方がよい +// +// 2020/10/17, Copyright (c) 2020 MIKAMI, Naoki +//------------------------------------------------------------- + +#include "mbed.h" + +#ifndef STM32F446xx +#error Not NUCLEO-F446RE. +#endif + +#ifndef F446_DAC_SINGLE_HPP +#define F446_DAC_SINGLE_HPP + +namespace Mikami +{ + class DacF446 + { + public: + // コンストラクタ + explicit DacF446(PinName pin = A2); + + virtual ~DacF446() {} + + // -1.0f <= data <= 1.0f + void Write(float data) { WriteDac(ToUint16(data)); } + + // 0 <= data <= 4095 + void Write(uint16_t data) { WriteDac(__USAT(data, BIT_WIDTH_)); } + + private: + void (DacF446::*fpWriteDac)(uint16_t); + + static const int BIT_WIDTH_ = 12; + AnalogOut da_; + + // DAC の片方のチェンネルへ出力する + void WriteDac1(uint16_t val) { DAC->DHR12R1 = val; } // CH1 へ + void WriteDac2(uint16_t val) { DAC->DHR12R2 = val; } // CH2 へ + + void WriteDac(uint16_t val) { (this->*fpWriteDac)(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 // F446_DAC_SINGLE_HPP
diff -r 000000000000 -r 8c8bc21159d9 FastSin.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/FastSin.hpp Fri Feb 25 02:36:55 2022 +0000 @@ -0,0 +1,26 @@ +//----------------------------------------------------------- +// FastSin() 関数 +// sin(πx/2) の値の計算 +// +// 2020/06/01, Copyright (c) 2020 MIKAMI, Naoki +//----------------------------------------------------------- + +#ifndef FASTSIN_POLYNOMIAL_HPP +#define FASTSIN_POLYNOMIAL_HPP + +namespace Mikami +{ + // 引数の範囲: -2 <= x <= 2 + inline float FastSin(float x) + { + static const float A1 = 1.570320019210f; + static const float A3 = -0.642113166941f; + static const float A5 = 0.071860854119f; + + if (x > 1.0f) x = 2.0f - x; + if (x < -1.0f) x = -2.0f - x; + float x2 = x*x; + return ((A5*x2 + A3)*x2 + A1)*x; + } +} +#endif // FASTSIN_POLYNOMIAL_HPP
diff -r 000000000000 -r 8c8bc21159d9 IIR_Filter/Biquad.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/IIR_Filter/Biquad.hpp Fri Feb 25 02:36:55 2022 +0000 @@ -0,0 +1,62 @@ +//-------------------------------------------------------------- +// 縦続形 IIR フィルタの構成要素として使う 2 次の IIR フィルタ +// b0 は 1 と仮定している +// +// 2020/11/04, Copyright (c) 2020 MIKAMI, Naoki +//-------------------------------------------------------------- + +#include "mbed.h" + +#ifndef IIR_BIQUAD_HPP +#define IIR_BIQUAD_HPP + +class Biquad +{ +public: + // フィルタの係数をまとめて扱うための構造体 + struct Coefs { float a1, a2, b1, b2; }; + + // デフォルト・コンストラクタ + // 係数は構造体 Ceofs で与える + Biquad(const Coefs ck = (Coefs){0, 0, 0, 0}) + : a1_(ck.a1), a2_(ck.a2), b1_(ck.b1), b2_(ck.b2), + un1_(0), un2_(0) {} + + // 係数を個別に与えるコンストラクタ + Biquad(float a1, float a2, float b1, float b2) + : a1_(a1), a2_(a2), b1_(b1), b2_(b2), un1_(0), un2_(0) {} + + virtual ~Biquad() {} + + // 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; + } + + // 係数を設定する + void SetCoefs(const Coefs ck) + { + a1_ = ck.a1; + a2_ = ck.a2; + b1_ = ck.b1; + b2_ = ck.b2; + } + + // 内部変数(遅延器)のクリア + void Clear() { un1_ = un2_ = 0; } + +private: + float a1_, a2_, b1_, b2_; // フィルタの係数 + float un1_, un2_; // 遅延器 + + // コピー・コンストラクタ禁止 + Biquad(const Biquad&); +}; +#endif // IIR_BIQUAD_HPP
diff -r 000000000000 -r 8c8bc21159d9 IIR_Filter/CoefficientsLp4.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/IIR_Filter/CoefficientsLp4.hpp Fri Feb 25 02:36:55 2022 +0000 @@ -0,0 +1,36 @@ +//---------------------------------------------------------------------- +// IIR フィルタの係数,縦続形,float 型 +// +// 2020/10/17, Copyright (c) 2020 MIKAMI, Naoki +// +// セミナ用に遮断周波数を低くしたバージョン +//---------------------------------------------------------------------- + +#include "Biquad.hpp" +using namespace Mikami; +/* +// 低域通過フィルタ +// 連立チェビシェフ特性 +// 次数 : 4 次 +// 標本化周波数:400.00 kHz +// 遮断周波数 : 20.00 kHz +// 通過域のリップル: 0.50 dB +// 阻止域の減衰量 :40.00 dB +const int ORDER_ = 4; +const Biquad::Coefs hk_[] = { + { 1.712306E+00f, -7.512093E-01f, -9.111378E-01f, 1.0f}, + { 1.819915E+00f, -9.194769E-01f, -1.719255E+00f, 1.0f}}; +const float G0_ = 1.196187E-02f; // 利得定数 +*/ +// 低域通過フィルタ +// 連立チェビシェフ特性 +// 次数 : 4 次 +// 標本化周波数:400.0000 kHz +// 遮断周波数 : 4.0000 kHz +// 通過域のリップル: 0.50 dB +// 阻止域の減衰量 :40.00 dB +const int ORDER_ = 4; +const Biquad::Coefs hk_[] = { + { 1.942874E+00f, -9.445931E-01f, -1.941953E+00f, 1.0f}, + { 1.978919E+00f, -9.830717E-01f, -1.988148E+00f, 1.0f}}; +const float G0_ = 9.797098E-03f; // 利得定数
diff -r 000000000000 -r 8c8bc21159d9 IIR_Filter/IirCascade.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/IIR_Filter/IirCascade.hpp Fri Feb 25 02:36:55 2022 +0000 @@ -0,0 +1,57 @@ +//--------------------------------------------------- +// 縦続形 IIR フィルタ +// +// 2020/11/04, Copyright (c) 2020 MIKAMI, Naoki +//--------------------------------------------------- + +#include "Biquad.hpp" +#include "Array.hpp" // Array クラスが定義されている +using namespace Mikami; + +#ifndef IIR_CASCADE_HPP +#define IIR_CASCADE_HPP + +class IirCascade +{ +public: + // コンストラクタ + IirCascade(int order, const Biquad::Coefs ck[], float g0) + : order_(order), hn_((order+1)/2) + { SetCoefs(order, ck, g0); } + + // コンストラクタ + IirCascade(int order, const Biquad hk[], float g0) + : order_(order), hn_((order+1)/2, hk), g0_(g0) {} + + virtual ~IirCascade() {} + + // フィルタ処理を実行する + float Execute(float xn) + { + float yn = g0_*xn; + for (int k=0; k<(order_+1)/2; k++) yn = hn_[k].Execute(yn); + return yn; + } + + // 係数の設定 + void SetCoefs(int order, const Biquad::Coefs ck[], float g0) + { + if (order_ != order) + { + order_ = order; + hn_.SetSize((order+1)/2); + } + g0_ = g0; + for (int k=0; k<(order+1)/2; k++) hn_[k].SetCoefs(ck[k]); + } + + // 内部変数(遅延器)のクリア + void Clear() + { for (int k=0; k<(order_+1)/2; k++) hn_[k].Clear(); } + +private: + int order_; // 次数 + Array<Biquad> hn_; // Biquad クラスのオブジェクトの配列 + float g0_; // 利得定数 +}; +#endif // IIR_CASCADE_HPP
diff -r 000000000000 -r 8c8bc21159d9 MSeq16.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MSeq16.hpp Fri Feb 25 02:36:55 2022 +0000 @@ -0,0 +1,35 @@ +//--------------------------------------------------------- +// M 系列信号発生器(N = 16) +// +// 2021/09/28, Copyright (c) 2021 MIKAMI, Naoki +//--------------------------------------------------------- + +#include "mbed.h" + +#ifndef MSEQ16_HPP +#define MSEQ16_HPP + +namespace Mikami +{ + class MSeq16 + { + public: + MSeq16() : reg_(1) {} + + // 戻り値: 0 => -0.5, 1 => 0.5 + float Execute() + { + msb_ = reg_ >> 15; + reg_ = ((reg_ ^ XOR_[msb_]) << 1) | msb_; + return RET_[msb_]; + } + private: + static const uint16_t XOR_[2]; // XOR の一方の入力 + static const float RET_[2]; // 戻り値として使用 + uint16_t reg_; // 16 段の D フリップ・フロップに対応 + uint16_t msb_; // 16 段目に相当するビット + }; + const uint16_t MSeq16::XOR_[2] = { 0, 0x16 }; + const float MSeq16::RET_[2] = { -0.5f, 0.5f }; +} +#endif // MSEQ16_HPP
diff -r 000000000000 -r 8c8bc21159d9 MyTicker7.lib --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MyTicker7.lib Fri Feb 25 02:36:55 2022 +0000 @@ -0,0 +1,1 @@ +https://os.mbed.com/users/MikamiUitOpen/code/MyTicker7/#f4269105fae0
diff -r 000000000000 -r 8c8bc21159d9 SerialTxRxIntr.lib --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SerialTxRxIntr.lib Fri Feb 25 02:36:55 2022 +0000 @@ -0,0 +1,1 @@ +https://os.mbed.com/users/MikamiUitOpen/code/SerialTxRxIntr/#268977533f95
diff -r 000000000000 -r 8c8bc21159d9 main.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.cpp Fri Feb 25 02:36:55 2022 +0000 @@ -0,0 +1,150 @@ +//---------------------------------------------------------------------- +// ファンクション・ジェネレータ (Nucleo-F446RE 用) +// COM ポートの自動検出に対応(9600 baud) +// +// 設定できる項目 +// 波形の種類: 正弦波,方形波,合成方形波(フーリエ級数の5倍波までの和) +// 振幅: 0.00 ~ 1.00 倍 +// 周波数: 10 Hz ~ 10 kHz +// ノイズ付加の有無 +// 標本化間隔:2.5 μs +// 使用タイマ:TIM7 +// 信号出力のピン: A2 +// 同期信号出力のピン: A5 +// +// PC 側のプログラム +// CQ_FunctionGenerator +// +// 2021/09/29, Copyright (c) 2021 MIKAMI, Naoki +// +// セミナで使うため,白色雑音を生成する際の LPF の遮断周波数を低くしたバージョン +// PC 側のプログラム +// Seminar_FunctionGenerator +// +// 2022/01/20, Copyright (c) 2022 MIKAMI, Naoki +//---------------------------------------------------------------------- + +#include "F446_DAC.hpp" // DA 変換器用 +#include "SerialRxTxIntr.hpp" // シリアル通信用 +#include "MyTicker7.hpp" // タイマ用 +#include "FastSin.hpp" // 高速低精度 sin 関数 +#include "MSeq16.hpp" // ノイズ発生器で使う M 系列信号発生器 +#include "IirCascade.hpp" // ノイズ発生器で使う低域通過フィルタ +#include "CoefficientsLp4.hpp" // 低域通過フィルタの係数 +#include <cctype> // isalpha() で使用 +using namespace Mikami; + +#ifndef __STM32F446xx_H +#error "Use Nucleo-F446RE" +#endif + +const float T0_ = 2.5f; // 出力の標本化間隔: 2.5 μs +const float C0_ = 4.0f; +const float C0_2_ = C0_/2.0f; +const float C0T0_ = C0_*T0_*1.0e-6f; + +MyTicker7 timer_(T0_); // タイマ割り込み用クラスのオブジェクト,TIM7 を利用 +DigitalOut sync_(A5); // 同期信号出力用 + +float phi_ = 0; +float dPhi_ = C0T0_*1000; // 周波数決める変数,開始時は 1 kHz; +float volume_ = 0.9f*0.5f; // 出力の振幅を決める変数,開始時は 0.45 +float volNoise_ = 0.5f; // ノイズの大きさを決める変数 + +// DA 変換器に関する関数等 +DacF446 dac_; // DA 変換器オブジェクト +void DacOut(float x) { dac_.Write(x); } // 引数の値を出力 +void DacZero(float x) { dac_.Write(0.0f); } // 0 を出力 +void (*fpDa)(float) = DacZero; // 起動時は 0 を出力 + +// ノイズ付加に関する関数等 +MSeq16 mSeq_; // M 系列発生器 +IirCascade filter_(ORDER_, hk_, G0_); // 低域通過フィルタ +float Noise() { return volNoise_*filter_.Execute(mSeq_.Execute()); } +float NoiseFree() { return 0; } +float (*fpN)() = NoiseFree; // 起動時はノイズなし + +// 発生する信号を定義する関数 +// 正弦波 +float Sin(float sinx) { return volume_*sinx + fpN(); } +// 方形波 +float Rect(float sinx) +{ + float x = (sinx >= 0) ? volume_ : -volume_; + return x + fpN(); +} +// 合成方形波(5倍波まで) +float Syn(float sinx) +{ + static const float ONE_3 = 1.0f/3.0f; // フーリエ合成で使用 + static const float ONE_5 = 0.2f; // フーリエ合成で使用 + + float sinx2 = sinx*sinx; + float sin3x = (-4.0f*sinx2 + 3.0f)*sinx; + float sin5x = ((16.0f*sinx2 - 20.0f)*sinx2 + 5.0f)*sinx; + + return volume_*(sinx + ONE_3*sin3x + ONE_5*sin5x) + fpN(); +} +float (*fpS)(float) = Sin; // 起動時は正弦波 + +// ラジオボタン,チェックボックスに対応する処理 +void Select(string str) +{ + if (str == "On") fpDa = DacOut; // 選択された信号の出力 + if (str == "Off") fpDa = DacZero; // 0 を出力 + + if (str == "Sin") fpS = Sin; // 正弦波 + if (str == "Rect") fpS = Rect; // 方形波 + if (str == "Syn") fpS = Syn; // 合成方形波 + + if (str == "NsOn") fpN = Noise; // ノイズ付加 + if (str == "NsOff") fpN = NoiseFree; // ノイズなし +} + +// スライダ(TrackBar)に対応する処理 +void NumericCtrl(string str) +{ + char c1 = str[0]; // 先頭の文字を取得 + float x = atof(str.substr(1).c_str()); + + if (c1 == '#') volume_ = x*0.9f; // 出力振幅の変更 + if (c1 == '$') dPhi_ = C0T0_*x; // 周波数の変更 + if (c1 == '%') volNoise_ = x; // ノイズの大きさの変更 +} + +// タイマ割り込みに対する割込みサービス・ルーチン +void TimerIsr() +{ + float sinx = FastSin(phi_); // 基本波発生 + fpDa(fpS(sinx)); // 指定された信号を出力 + GPIOC->BSRR = (sinx >= 0) ? // 同期信号を出力 + 0x1 : 0x10000; + + phi_ += dPhi_; + if (phi_ >= C0_2_) phi_ -= C0_; // オーバーフロー防止 +} + +int main() +{ + SerialRxTxIntr rxTx; // PC との通信用,9600 baud + // 以下の割り込み優先順位の設定を忘れないこと + NVIC_SetPriority(TIM7_IRQn, 0); // 最優先 + NVIC_SetPriority(USART2_IRQn, 1); // USART2 割り込み:次に優先 + + timer_.Attach(&TimerIsr); // タイマ割り込み設定 + + while (true) // PC からの指令に対応する処理 + { + if (rxTx.IsEol()) // 受信バッファのデータが有効になった場合の処理 + { + string str = rxTx.GetBuffer(); + if (str == "FG_Seminar") + rxTx.TxString("ACK\n"); // PC からの "FG" に対して "ACK" を送信する + else + if (isalpha(str[0])) // 先頭が A ~ Z, a ~ z の場合 + Select(str); + else // 先頭が A ~ Z, a ~ z 以外の場合 + NumericCtrl(str); + } + } +}
diff -r 000000000000 -r 8c8bc21159d9 mbed.bld --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed.bld Fri Feb 25 02:36:55 2022 +0000 @@ -0,0 +1,1 @@ +https://os.mbed.com/users/mbed_official/code/mbed/builds/65be27845400 \ No newline at end of file