ファンクション・ジェネレータ このプログラムの説明は,CQ出版社「トランジスタ技術」の2021年10月号から開始された連載記事「STM32マイコンではじめるPC計測」の中にあります.このプログラムといっしょに使うPC側のプログラムについても同誌を参照してください.
Dependencies: Array_Matrix mbed SerialTxRxIntr MyTicker7
Revision 1:0430f1ed6c2c, committed 2021-10-06
- Comitter:
- MikamiUitOpen
- Date:
- Wed Oct 06 12:20:04 2021 +0000
- Parent:
- 0:53c0fa8a9aa2
- Commit message:
- 2
Changed in this revision
MSeq16.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 |
diff -r 53c0fa8a9aa2 -r 0430f1ed6c2c MSeq16.hpp --- a/MSeq16.hpp Thu Sep 09 08:50:21 2021 +0000 +++ b/MSeq16.hpp Wed Oct 06 12:20:04 2021 +0000 @@ -1,7 +1,7 @@ //--------------------------------------------------------- -// M 系列信号発生器(N = 16) +// M 系列信号発生器(N = 16) // -// 2021/08/23, Copyright (c) 2020 MIKAMI, Naoki +// 2021/09/28, Copyright (c) 2021 MIKAMI, Naoki //--------------------------------------------------------- #include "mbed.h" @@ -11,32 +11,25 @@ namespace Mikami { - class MSeq16 - { - public: - MSeq16() : reg_(1) {} + class MSeq16 + { + public: + MSeq16() : reg_(1) {} - // 戻り値: 1 => 1, 0 => -1 - float Execute() - { - if ((reg_ & B_M_) == B_M_) - { - reg_ = ((reg_ ^ XOR_) << 1) | 1; // 1 の場合の処理 - return 0.5f; - } - else - { - reg_ = reg_ << 1; // 0 の場合の処理 - return -0.5f; - } - } - private: - static const uint16_t XOR_ = (1 << (2-1)) - | (1 << (3-1)) - | (1 << (5-1)); // XOR の位置に対応する定数 - static const uint16_t B_M_ = 1 << (16-1); // 16 段目に相当するビットを調べる - - uint16_t reg_; - }; + // 戻り値: 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 \ No newline at end of file +#endif // MSEQ16_HPP \ No newline at end of file
diff -r 53c0fa8a9aa2 -r 0430f1ed6c2c main.cpp --- a/main.cpp Thu Sep 09 08:50:21 2021 +0000 +++ b/main.cpp Wed Oct 06 12:20:04 2021 +0000 @@ -1,140 +1,144 @@ //---------------------------------------------------------------------- -// ファンクション・ジェネレータ (Nucleo-F446RE 用) -// COM ポートの自動検出に対応(9600 baud) +// ファンクション・ジェネレータ (Nucleo-F446RE 用) +// COM ポートの自動検出に対応(9600 baud) // -// 設定できる項目 -// 波形の種類: 正弦波,矩形波,合成矩形波(フーリエ級数の5倍波までの和) -// 振幅: 0.00 ~ 1.00 倍 -// 周波数: 10 Hz ~ 10 kHz -// ノイズ付加の有無 -// 標本化間隔:2.5 μs -// 使用タイマ:TIM7 -// 信号出力のピン: A2 +// 設定できる項目 +// 波形の種類: 正弦波,方形波,合成方形波(フーリエ級数の5倍波までの和) +// 振幅: 0.00 ~ 1.00 倍 +// 周波数: 10 Hz ~ 10 kHz +// ノイズ付加の有無 +// 標本化間隔:2.5 μs +// 使用タイマ:TIM7 +// 信号出力のピン: A2 +// 同期信号出力のピン: A5 // -// PC 側のプログラム -// CQ_FunctionGenerator +// PC 側のプログラム +// CQ_FunctionGenerator // -// 2021/09/05, Copyright (c) 2021 MIKAMI, Naoki +// 2021/09/29, Copyright (c) 2021 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() で使用 +#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 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 を利用 +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; // ノイズの大きさを決める変数 +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); } // 引数の値を出力 +DacF446 dac_; // DA 変換器オブジェクト +void DacOut(float x) { dac_.Write(x); } // 引数の値を出力 void DacZero(float x) { dac_.Write(0.0f); } // 0 を出力 -void (*fpDa)(float) = DacZero; // 起動時は 0 を出力 +void (*fpDa)(float) = DacZero; // 起動時は 0 を出力 // ノイズ付加に関する関数等 -MSeq16 mSeq_; // M 系列発生器 -IirCascade filter_(ORDER_, hk_, G0_); // 低域通過フィルタ +MSeq16 mSeq_; // M 系列発生器 +IirCascade filter_(ORDER_, hk_, G0_); // 低域通過フィルタ float Noise() { return volNoise_*filter_.Execute(mSeq_.Execute()); } float NoiseFree() { return 0; } -float (*fpN)() = NoiseFree; // 起動時はノイズなし +float (*fpN)() = NoiseFree; // 起動時はノイズなし // 発生する信号を定義する関数 // 正弦波 float Sin(float sinx) { return volume_*sinx + fpN(); } -// 矩形波 +// 方形波 float Rect(float sinx) { - float x = (sinx >= 0) ? volume_ : -volume_; - return x + fpN(); + float x = (sinx >= 0) ? volume_ : -volume_; + return x + fpN(); } -// 矩形波(合成,5倍波まで) +// 合成方形波(5倍波まで) float Syn(float sinx) { - static const float ONE_3 = 1.0f/3.0f; // フーリエ合成で使用 - static const float ONE_5 = 0.2f; // フーリエ合成で使用 + 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; + 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(); + return volume_*(sinx + ONE_3*sin3x + ONE_5*sin5x) + fpN(); } -float (*fpS)(float) = Sin; // 起動時は正弦波 +float (*fpS)(float) = Sin; // 起動時は正弦波 // ラジオボタン,チェックボックスに対応する処理 void Select(string str) { - if (str == "On") fpDa = DacOut; // 選択された信号の出力 - if (str == "Off") fpDa = DacZero; // 0 を出力 + 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; // 矩形波(5倍波まで) + 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; // ノイズなし + 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()); + 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; // ノイズの大きさの変更 + if (c1 == '#') volume_ = x*0.9f; // 出力振幅の変更 + if (c1 == '$') dPhi_ = C0T0_*x; // 周波数の変更 + if (c1 == '%') volNoise_ = x; // ノイズの大きさの変更 } // タイマ割り込みに対する割込みサービス・ルーチン void TimerIsr() { - float sinx = FastSin(phi_); // 基本波発生 - fpDa(fpS(sinx)); // 指定された信号を出力 + float sinx = FastSin(phi_); // 基本波発生 + fpDa(fpS(sinx)); // 指定された信号を出力 + GPIOC->BSRR = (sinx >= 0) ? // 同期信号を出力 + 0x1 : 0x10000; - phi_ += dPhi_; - if (phi_ >= C0_2_) phi_ -= C0_; // オーバーフロー防止 + 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 割り込み:次に優先 + 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") - 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); - } - } + timer_.Attach(&TimerIsr); // タイマ割り込み設定 + + while (true) // PC からの指令に対応する処理 + { + if (rxTx.IsEol()) // 受信バッファのデータが有効になった場合の処理 + { + string str = rxTx.GetBuffer(); + if (str == "FG") + 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); + } + } } \ No newline at end of file