ファンクション・ジェネレータ このプログラムの説明は,CQ出版社「トランジスタ技術」の2021年10月号から開始された連載記事「STM32マイコンではじめるPC計測」の中にあります.このプログラムといっしょに使うPC側のプログラムについても同誌を参照してください.

Dependencies:   Array_Matrix mbed SerialTxRxIntr MyTicker7

main.cpp

Committer:
MikamiUitOpen
Date:
2021-09-09
Revision:
0:53c0fa8a9aa2
Child:
1:0430f1ed6c2c

File content as of revision 0:53c0fa8a9aa2:

//----------------------------------------------------------------------
//  ファンクション・ジェネレータ (Nucleo-F446RE 用)
//  COM ポートの自動検出に対応(9600 baud)
//
//  設定できる項目
//      波形の種類:  正弦波,矩形波,合成矩形波(フーリエ級数の5倍波までの和)
//      振幅:         0.00 ~ 1.00 倍
//      周波数:       10 Hz ~ 10 kHz
//      ノイズ付加の有無
//  標本化間隔:2.5 μs
//  使用タイマ:TIM7
//  信号出力のピン:      A2
//
//  PC 側のプログラム
//      CQ_FunctionGenerator
//
//  2021/09/05, 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() で使用
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 を利用

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;          // 矩形波(5倍波まで)

    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));            // 指定された信号を出力

    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")
                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);
        }
    }
}