ファンクション・ジェネレータ このプログラムの説明は,CQ出版社「トランジスタ技術」の2021年10月号から開始された連載記事「STM32マイコンではじめるPC計測」の中にあります.このプログラムといっしょに使うPC側のプログラムについても同誌を参照してください.
Dependencies: Array_Matrix mbed SerialTxRxIntr MyTicker7
main.cpp@0:53c0fa8a9aa2, 2021-09-09 (annotated)
- Committer:
- MikamiUitOpen
- Date:
- Thu Sep 09 08:50:21 2021 +0000
- Revision:
- 0:53c0fa8a9aa2
- Child:
- 1:0430f1ed6c2c
1
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
MikamiUitOpen | 0:53c0fa8a9aa2 | 1 | //---------------------------------------------------------------------- |
MikamiUitOpen | 0:53c0fa8a9aa2 | 2 | // ファンクション・ジェネレータ (Nucleo-F446RE 用) |
MikamiUitOpen | 0:53c0fa8a9aa2 | 3 | // COM ポートの自動検出に対応(9600 baud) |
MikamiUitOpen | 0:53c0fa8a9aa2 | 4 | // |
MikamiUitOpen | 0:53c0fa8a9aa2 | 5 | // 設定できる項目 |
MikamiUitOpen | 0:53c0fa8a9aa2 | 6 | // 波形の種類: 正弦波,矩形波,合成矩形波(フーリエ級数の5倍波までの和) |
MikamiUitOpen | 0:53c0fa8a9aa2 | 7 | // 振幅: 0.00 ~ 1.00 倍 |
MikamiUitOpen | 0:53c0fa8a9aa2 | 8 | // 周波数: 10 Hz ~ 10 kHz |
MikamiUitOpen | 0:53c0fa8a9aa2 | 9 | // ノイズ付加の有無 |
MikamiUitOpen | 0:53c0fa8a9aa2 | 10 | // 標本化間隔:2.5 μs |
MikamiUitOpen | 0:53c0fa8a9aa2 | 11 | // 使用タイマ:TIM7 |
MikamiUitOpen | 0:53c0fa8a9aa2 | 12 | // 信号出力のピン: A2 |
MikamiUitOpen | 0:53c0fa8a9aa2 | 13 | // |
MikamiUitOpen | 0:53c0fa8a9aa2 | 14 | // PC 側のプログラム |
MikamiUitOpen | 0:53c0fa8a9aa2 | 15 | // CQ_FunctionGenerator |
MikamiUitOpen | 0:53c0fa8a9aa2 | 16 | // |
MikamiUitOpen | 0:53c0fa8a9aa2 | 17 | // 2021/09/05, Copyright (c) 2021 MIKAMI, Naoki |
MikamiUitOpen | 0:53c0fa8a9aa2 | 18 | //---------------------------------------------------------------------- |
MikamiUitOpen | 0:53c0fa8a9aa2 | 19 | |
MikamiUitOpen | 0:53c0fa8a9aa2 | 20 | #include "F446_DAC.hpp" // DA 変換器用 |
MikamiUitOpen | 0:53c0fa8a9aa2 | 21 | #include "SerialRxTxIntr.hpp" // シリアル通信用 |
MikamiUitOpen | 0:53c0fa8a9aa2 | 22 | #include "MyTicker7.hpp" // タイマ用 |
MikamiUitOpen | 0:53c0fa8a9aa2 | 23 | #include "FastSin.hpp" // 高速低精度 sin 関数 |
MikamiUitOpen | 0:53c0fa8a9aa2 | 24 | #include "MSeq16.hpp" // ノイズ発生器で使う M 系列信号発生器 |
MikamiUitOpen | 0:53c0fa8a9aa2 | 25 | #include "IirCascade.hpp" // ノイズ発生器で使う低域通過フィルタ |
MikamiUitOpen | 0:53c0fa8a9aa2 | 26 | #include "CoefficientsLp4.hpp" // 低域通過フィルタの係数 |
MikamiUitOpen | 0:53c0fa8a9aa2 | 27 | #include <cctype> // isalpha() で使用 |
MikamiUitOpen | 0:53c0fa8a9aa2 | 28 | using namespace Mikami; |
MikamiUitOpen | 0:53c0fa8a9aa2 | 29 | |
MikamiUitOpen | 0:53c0fa8a9aa2 | 30 | #ifndef __STM32F446xx_H |
MikamiUitOpen | 0:53c0fa8a9aa2 | 31 | #error "Use Nucleo-F446RE" |
MikamiUitOpen | 0:53c0fa8a9aa2 | 32 | #endif |
MikamiUitOpen | 0:53c0fa8a9aa2 | 33 | |
MikamiUitOpen | 0:53c0fa8a9aa2 | 34 | const float T0_ = 2.5f; // 出力の標本化間隔: 2.5 μs |
MikamiUitOpen | 0:53c0fa8a9aa2 | 35 | const float C0_ = 4.0f; |
MikamiUitOpen | 0:53c0fa8a9aa2 | 36 | const float C0_2_ = C0_/2.0f; |
MikamiUitOpen | 0:53c0fa8a9aa2 | 37 | const float C0T0_ = C0_*T0_*1.0e-6f; |
MikamiUitOpen | 0:53c0fa8a9aa2 | 38 | |
MikamiUitOpen | 0:53c0fa8a9aa2 | 39 | MyTicker7 timer_(T0_); // タイマ割り込み用クラスのオブジェクト,TIM7 を利用 |
MikamiUitOpen | 0:53c0fa8a9aa2 | 40 | |
MikamiUitOpen | 0:53c0fa8a9aa2 | 41 | float phi_ = 0; |
MikamiUitOpen | 0:53c0fa8a9aa2 | 42 | float dPhi_ = C0T0_*1000; // 周波数決める変数,開始時は 1 kHz; |
MikamiUitOpen | 0:53c0fa8a9aa2 | 43 | float volume_ = 0.9f*0.5f; // 出力の振幅を決める変数,開始時は 0.45 |
MikamiUitOpen | 0:53c0fa8a9aa2 | 44 | float volNoise_ = 0.5f; // ノイズの大きさを決める変数 |
MikamiUitOpen | 0:53c0fa8a9aa2 | 45 | |
MikamiUitOpen | 0:53c0fa8a9aa2 | 46 | // DA 変換器に関する関数等 |
MikamiUitOpen | 0:53c0fa8a9aa2 | 47 | DacF446 dac_; // DA 変換器オブジェクト |
MikamiUitOpen | 0:53c0fa8a9aa2 | 48 | void DacOut(float x) { dac_.Write(x); } // 引数の値を出力 |
MikamiUitOpen | 0:53c0fa8a9aa2 | 49 | void DacZero(float x) { dac_.Write(0.0f); } // 0 を出力 |
MikamiUitOpen | 0:53c0fa8a9aa2 | 50 | void (*fpDa)(float) = DacZero; // 起動時は 0 を出力 |
MikamiUitOpen | 0:53c0fa8a9aa2 | 51 | |
MikamiUitOpen | 0:53c0fa8a9aa2 | 52 | // ノイズ付加に関する関数等 |
MikamiUitOpen | 0:53c0fa8a9aa2 | 53 | MSeq16 mSeq_; // M 系列発生器 |
MikamiUitOpen | 0:53c0fa8a9aa2 | 54 | IirCascade filter_(ORDER_, hk_, G0_); // 低域通過フィルタ |
MikamiUitOpen | 0:53c0fa8a9aa2 | 55 | float Noise() { return volNoise_*filter_.Execute(mSeq_.Execute()); } |
MikamiUitOpen | 0:53c0fa8a9aa2 | 56 | float NoiseFree() { return 0; } |
MikamiUitOpen | 0:53c0fa8a9aa2 | 57 | float (*fpN)() = NoiseFree; // 起動時はノイズなし |
MikamiUitOpen | 0:53c0fa8a9aa2 | 58 | |
MikamiUitOpen | 0:53c0fa8a9aa2 | 59 | // 発生する信号を定義する関数 |
MikamiUitOpen | 0:53c0fa8a9aa2 | 60 | // 正弦波 |
MikamiUitOpen | 0:53c0fa8a9aa2 | 61 | float Sin(float sinx) { return volume_*sinx + fpN(); } |
MikamiUitOpen | 0:53c0fa8a9aa2 | 62 | // 矩形波 |
MikamiUitOpen | 0:53c0fa8a9aa2 | 63 | float Rect(float sinx) |
MikamiUitOpen | 0:53c0fa8a9aa2 | 64 | { |
MikamiUitOpen | 0:53c0fa8a9aa2 | 65 | float x = (sinx >= 0) ? volume_ : -volume_; |
MikamiUitOpen | 0:53c0fa8a9aa2 | 66 | return x + fpN(); |
MikamiUitOpen | 0:53c0fa8a9aa2 | 67 | } |
MikamiUitOpen | 0:53c0fa8a9aa2 | 68 | // 矩形波(合成,5倍波まで) |
MikamiUitOpen | 0:53c0fa8a9aa2 | 69 | float Syn(float sinx) |
MikamiUitOpen | 0:53c0fa8a9aa2 | 70 | { |
MikamiUitOpen | 0:53c0fa8a9aa2 | 71 | static const float ONE_3 = 1.0f/3.0f; // フーリエ合成で使用 |
MikamiUitOpen | 0:53c0fa8a9aa2 | 72 | static const float ONE_5 = 0.2f; // フーリエ合成で使用 |
MikamiUitOpen | 0:53c0fa8a9aa2 | 73 | |
MikamiUitOpen | 0:53c0fa8a9aa2 | 74 | float sinx2 = sinx*sinx; |
MikamiUitOpen | 0:53c0fa8a9aa2 | 75 | float sin3x = (-4.0f*sinx2 + 3.0f)*sinx; |
MikamiUitOpen | 0:53c0fa8a9aa2 | 76 | float sin5x = ((16.0f*sinx2 - 20.0f)*sinx2 + 5.0f)*sinx; |
MikamiUitOpen | 0:53c0fa8a9aa2 | 77 | |
MikamiUitOpen | 0:53c0fa8a9aa2 | 78 | return volume_*(sinx + ONE_3*sin3x + ONE_5*sin5x) + fpN(); |
MikamiUitOpen | 0:53c0fa8a9aa2 | 79 | } |
MikamiUitOpen | 0:53c0fa8a9aa2 | 80 | float (*fpS)(float) = Sin; // 起動時は正弦波 |
MikamiUitOpen | 0:53c0fa8a9aa2 | 81 | |
MikamiUitOpen | 0:53c0fa8a9aa2 | 82 | // ラジオボタン,チェックボックスに対応する処理 |
MikamiUitOpen | 0:53c0fa8a9aa2 | 83 | void Select(string str) |
MikamiUitOpen | 0:53c0fa8a9aa2 | 84 | { |
MikamiUitOpen | 0:53c0fa8a9aa2 | 85 | if (str == "On") fpDa = DacOut; // 選択された信号の出力 |
MikamiUitOpen | 0:53c0fa8a9aa2 | 86 | if (str == "Off") fpDa = DacZero; // 0 を出力 |
MikamiUitOpen | 0:53c0fa8a9aa2 | 87 | |
MikamiUitOpen | 0:53c0fa8a9aa2 | 88 | if (str == "Sin") fpS = Sin; // 正弦波 |
MikamiUitOpen | 0:53c0fa8a9aa2 | 89 | if (str == "Rect") fpS = Rect; // 矩形波 |
MikamiUitOpen | 0:53c0fa8a9aa2 | 90 | if (str == "Syn") fpS = Syn; // 矩形波(5倍波まで) |
MikamiUitOpen | 0:53c0fa8a9aa2 | 91 | |
MikamiUitOpen | 0:53c0fa8a9aa2 | 92 | if (str == "NsOn") fpN = Noise; // ノイズ付加 |
MikamiUitOpen | 0:53c0fa8a9aa2 | 93 | if (str == "NsOff") fpN = NoiseFree; // ノイズなし |
MikamiUitOpen | 0:53c0fa8a9aa2 | 94 | } |
MikamiUitOpen | 0:53c0fa8a9aa2 | 95 | |
MikamiUitOpen | 0:53c0fa8a9aa2 | 96 | // スライダ(TrackBar)に対応する処理 |
MikamiUitOpen | 0:53c0fa8a9aa2 | 97 | void NumericCtrl(string str) |
MikamiUitOpen | 0:53c0fa8a9aa2 | 98 | { |
MikamiUitOpen | 0:53c0fa8a9aa2 | 99 | char c1 = str[0]; // 先頭の文字を取得 |
MikamiUitOpen | 0:53c0fa8a9aa2 | 100 | float x = atof(str.substr(1).c_str()); |
MikamiUitOpen | 0:53c0fa8a9aa2 | 101 | |
MikamiUitOpen | 0:53c0fa8a9aa2 | 102 | if (c1 == '#') volume_ = x*0.9f; // 出力振幅の変更 |
MikamiUitOpen | 0:53c0fa8a9aa2 | 103 | if (c1 == '$') dPhi_ = C0T0_*x; // 周波数の変更 |
MikamiUitOpen | 0:53c0fa8a9aa2 | 104 | if (c1 == '%') volNoise_ = x; // ノイズの大きさの変更 |
MikamiUitOpen | 0:53c0fa8a9aa2 | 105 | } |
MikamiUitOpen | 0:53c0fa8a9aa2 | 106 | |
MikamiUitOpen | 0:53c0fa8a9aa2 | 107 | // タイマ割り込みに対する割込みサービス・ルーチン |
MikamiUitOpen | 0:53c0fa8a9aa2 | 108 | void TimerIsr() |
MikamiUitOpen | 0:53c0fa8a9aa2 | 109 | { |
MikamiUitOpen | 0:53c0fa8a9aa2 | 110 | float sinx = FastSin(phi_); // 基本波発生 |
MikamiUitOpen | 0:53c0fa8a9aa2 | 111 | fpDa(fpS(sinx)); // 指定された信号を出力 |
MikamiUitOpen | 0:53c0fa8a9aa2 | 112 | |
MikamiUitOpen | 0:53c0fa8a9aa2 | 113 | phi_ += dPhi_; |
MikamiUitOpen | 0:53c0fa8a9aa2 | 114 | if (phi_ >= C0_2_) phi_ -= C0_; // オーバーフロー防止 |
MikamiUitOpen | 0:53c0fa8a9aa2 | 115 | } |
MikamiUitOpen | 0:53c0fa8a9aa2 | 116 | |
MikamiUitOpen | 0:53c0fa8a9aa2 | 117 | int main() |
MikamiUitOpen | 0:53c0fa8a9aa2 | 118 | { |
MikamiUitOpen | 0:53c0fa8a9aa2 | 119 | SerialRxTxIntr rxTx; // PC との通信用,9600 baud |
MikamiUitOpen | 0:53c0fa8a9aa2 | 120 | // 以下の割り込み優先順位の設定を忘れないこと |
MikamiUitOpen | 0:53c0fa8a9aa2 | 121 | NVIC_SetPriority(TIM7_IRQn, 0); // 最優先 |
MikamiUitOpen | 0:53c0fa8a9aa2 | 122 | NVIC_SetPriority(USART2_IRQn, 1); // USART2 割り込み:次に優先 |
MikamiUitOpen | 0:53c0fa8a9aa2 | 123 | |
MikamiUitOpen | 0:53c0fa8a9aa2 | 124 | timer_.Attach(&TimerIsr); // タイマ割り込み設定 |
MikamiUitOpen | 0:53c0fa8a9aa2 | 125 | |
MikamiUitOpen | 0:53c0fa8a9aa2 | 126 | while (true) // PC からの指令に対応する処理 |
MikamiUitOpen | 0:53c0fa8a9aa2 | 127 | { |
MikamiUitOpen | 0:53c0fa8a9aa2 | 128 | if (rxTx.IsEol()) // 受信バッファのデータが有効になった場合の処理 |
MikamiUitOpen | 0:53c0fa8a9aa2 | 129 | { |
MikamiUitOpen | 0:53c0fa8a9aa2 | 130 | string str = rxTx.GetBuffer(); |
MikamiUitOpen | 0:53c0fa8a9aa2 | 131 | if (str == "FG") |
MikamiUitOpen | 0:53c0fa8a9aa2 | 132 | rxTx.TxString("ACK\n"); // PC からの "FG" に対して "ACK" を送信する |
MikamiUitOpen | 0:53c0fa8a9aa2 | 133 | else |
MikamiUitOpen | 0:53c0fa8a9aa2 | 134 | if (isalpha(str[0])) // 先頭が A ~ Z, a ~ z の場合 |
MikamiUitOpen | 0:53c0fa8a9aa2 | 135 | Select(str); |
MikamiUitOpen | 0:53c0fa8a9aa2 | 136 | else // 先頭が A ~ Z, a ~ z 以外の場合 |
MikamiUitOpen | 0:53c0fa8a9aa2 | 137 | NumericCtrl(str); |
MikamiUitOpen | 0:53c0fa8a9aa2 | 138 | } |
MikamiUitOpen | 0:53c0fa8a9aa2 | 139 | } |
MikamiUitOpen | 0:53c0fa8a9aa2 | 140 | } |