CQエレクトロニクス・セミナで使用するファンクション・ジェネレータの プログラム

Dependencies:   Array_Matrix mbed SerialTxRxIntr MyTicker7

Files at this revision

API Documentation at this revision

Comitter:
MikamiUitOpen
Date:
Fri Feb 25 02:36:55 2022 +0000
Commit message:
1

Changed in this revision

Array_Matrix.lib Show annotated file Show diff for this revision Revisions of this file
F446_DAC.cpp Show annotated file Show diff for this revision Revisions of this file
F446_DAC.hpp Show annotated file Show diff for this revision Revisions of this file
FastSin.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/CoefficientsLp4.hpp Show annotated file Show diff for this revision Revisions of this file
IIR_Filter/IirCascade.hpp Show annotated file Show diff for this revision Revisions of this file
MSeq16.hpp Show annotated file Show diff for this revision Revisions of this file
MyTicker7.lib 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
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/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
--- /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;
+        }
+    }
+}
--- /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
--- /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
--- /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
--- /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;    // 利得定数
--- /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
--- /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
--- /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
--- /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
--- /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);
+		}
+	}
+}
--- /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