不韋 呂 / F446_AD_DA_Multirate

Dependencies:   Array_Matrix

Dependents:   F446_UpSampling_GraphicEqualizer F446_UpSampling_ReverbSystem F446_UpSampling_FrqShifter_Weaver Demo_F446_AD_DA_Multirate ... more

Files at this revision

API Documentation at this revision

Comitter:
MikamiUitOpen
Date:
Wed May 16 01:40:50 2018 +0000
Child:
1:abd7e93549b2
Commit message:
1

Changed in this revision

Array_Matrix.lib Show annotated file Show diff for this revision Revisions of this file
Biquad.hpp Show annotated file Show diff for this revision Revisions of this file
F446_ADC.cpp Show annotated file Show diff for this revision Revisions of this file
F446_ADC.hpp 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
F446_Multirate.cpp Show annotated file Show diff for this revision Revisions of this file
F446_Multirate.hpp Show annotated file Show diff for this revision Revisions of this file
IIR_Cascade.hpp 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	Wed May 16 01:40:50 2018 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/MikamiUitOpen/code/Array_Matrix/#a25dba17218c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Biquad.hpp	Wed May 16 01:40:50 2018 +0000
@@ -0,0 +1,65 @@
+//--------------------------------------------------------------
+// 縦続形 IIR フィルタの構成要素として使う 2 次の IIR フィルタ
+//      b0 は 1 と仮定している
+//
+// 2018/04/19, Copyright (c) 2017 MIKAMI, Naoki
+//--------------------------------------------------------------
+
+#ifndef IIR_BIQUAD_HPP
+#define IIR_BIQUAD_HPP
+
+#include "mbed.h"
+
+// 2 次の IIR フィルタ
+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)
+    { Clear(); }       
+
+    // 係数を個別に与えるコンストラクタ
+    Biquad(float a1, float a2, float b1, float b2)
+        : a1_(a1), a2_(a2), b1_(b1), b2_(b2)
+    { Clear(); }       
+
+    // 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&);
+    // 代入禁止
+    Biquad& operator=(const Biquad&);     
+};
+
+#endif  // IIR_BIQUAD_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/F446_ADC.cpp	Wed May 16 01:40:50 2018 +0000
@@ -0,0 +1,91 @@
+//----------------------------------------------------------------
+//  AD Conversion by interrupt using ADC2 or ADC3 on STM32F446
+//
+//  STM32F446 の ADC2 または ADC3 を使って割込みによりアナログ信号を
+//  入力するクラス ― マルチ・レート処理用
+//
+//  2018/04/18, Copyright (c) 2018 MIKAMI, Naoki
+//----------------------------------------------------------------
+
+#include "F446_ADC.hpp"
+
+namespace Mikami
+{
+    AdcF446::AdcF446(int frequency, PinName pin)
+    {
+        if ( (pin != A0) && (pin != A1) )
+        {
+        	fprintf(stderr, "Invalid pin name\r\n");
+        	while (true) {}
+        }
+       
+        // PA0 または PA1 を ADC 入力として使うための設定
+        __HAL_RCC_GPIOA_CLK_ENABLE();
+        GPIO_InitTypeDef gpioInit;
+        gpioInit.Pin = (pin == A0) ? GPIO_PIN_0 : GPIO_PIN_1;
+        gpioInit.Mode = GPIO_MODE_ANALOG;
+        gpioInit.Pull = GPIO_NOPULL;
+        gpioInit.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
+        HAL_GPIO_Init(GPIOA, &gpioInit);
+
+        if (pin == A0)	// ADC2 の設定 (入力ポート:PA0)
+        {
+	        __HAL_RCC_ADC2_CLK_ENABLE();
+	        adc_ = ADC2;
+	        adc_->SQR3 = 0x0;		// CH0 を使う
+		}
+		else			// ADC3 の設定 (入力ポート:PA1)
+		{
+    	    __HAL_RCC_ADC3_CLK_ENABLE();
+    	    adc_ = ADC3;
+	        adc_->SQR3 = 0x1;       // CH1 を使う
+        }
+       	adc_->CR2 = ADC_EXTERNALTRIGCONVEDGE_RISING // 外部トリガの立ち上がりで開始される
+           	      | ADC_EXTERNALTRIGCONV_T8_TRGO    // 外部トリガ: Timer8 TRGO event
+               	  | ADC_CR2_ADON;                   // ADC を有効にする
+        adc_->CR1 = ADC_CR1_EOCIE;					// ADC の変換終了割り込みを有効にする
+
+        // ADC 共通の設定
+        ADC->CCR = 0x0;     // 念のため
+
+        // AD 変換器の外部トリガに使うタイマ (TIM8) の設定
+        // 標本化周波数としてアップサンプリングに対応する値を設定する
+        SetTim8(frequency);
+    }
+
+	// 割込みベクタを設定し,ADC 割込みを有効にする
+    void AdcF446::SetIntrVec(void (*Func)())
+    {
+        NVIC_SetVector(ADC_IRQn, (uint32_t)Func);   // "cmsis_nvic.h" 参照
+        NVIC_EnableIRQ(ADC_IRQn);                   // "core_cm4.h" 参照
+    }
+
+
+    void AdcF446::SetTim8(int frequency)
+    {
+        __HAL_RCC_TIM8_CLK_ENABLE();    // クロック供給. "stm32f4xx_hal_rcc.h" 参照
+        SystemCoreClockUpdate();        // クロックの更新. See "system_stm32f4xx.h" 参照
+        TIM_TypeDef* const myTim = TIM8;
+
+        myTim->CR2 = TIM_CR2_MMS_1; // Update event を TRGO (trigger output) とする
+
+    	uint32_t psc = 0;
+        uint16_t mul = 1;
+        uint32_t arr;
+        while (true)
+        {
+            arr = SystemCoreClock/(mul*frequency);
+            if (arr <= 65536) break;
+            psc++;
+            mul++;
+            if (psc > 65535)
+            {
+                fprintf(stderr, "Sampling frequency: too low.\r\n");
+                while (true) {}
+            }
+        }
+        myTim->ARR = arr - 1;       // Auto-reload レジスタの設定
+        myTim->PSC = psc;           // Prescaler の設定
+        myTim->CR1 = TIM_CR1_CEN;   // TIM8 を有効にする
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/F446_ADC.hpp	Wed May 16 01:40:50 2018 +0000
@@ -0,0 +1,64 @@
+//----------------------------------------------------------------
+//  AD Conversion by interrupt using ADC2 or ADC3 on STM32F446
+//  ---- Header ----
+//
+//  STM32F446 の ADC2 または ADC3 を使って割込みによりアナログ信号を
+//  入力するクラス ― マルチ・レート処理用(ヘッダ)
+//
+//      選択可能な入力端子:
+//          A0 (PA_0) :  ADC2 CH0 ---- デフォルト
+//          A1 (PA_1) :  ADC3 CH1
+//
+//  2018/04/16, Copyright (c) 2018 MIKAMI, Naoki
+//----------------------------------------------------------------
+
+#include "mbed.h"
+
+#ifndef STM32F446xx
+#error Not NUCLEO-F446RE.
+#endif
+
+#include "F446_DAC.hpp"
+
+#ifndef F446_ADC_SINGLE_HPP
+#define F446_ADC_SINGLE_HPP
+
+namespace Mikami
+{
+    class AdcF446
+    {
+    public:
+        // コンストラクタ
+        //      frequency: 標本化周波数
+        AdcF446(int frequency, PinName pin = A0);
+        
+        virtual ~AdcF446() {}
+
+        // -1.0f <= AD変換された値 < 1.0f
+        //      ad1: left, ad2: right
+        float Read() { return ToFloat(adc_->DR); }
+
+        // 割込みベクタを設定し,ADC 割込みを有効にする
+        void SetIntrVec(void (*Func)());
+        
+        // ADC 割込みを無効にする
+        void DisableAdcIntr()
+        {   NVIC_DisableIRQ(ADC_IRQn); }
+
+    private:
+        static const float AMP_ = 1.0f/2048.0f;
+        ADC_TypeDef *adc_;
+
+        float ToFloat(uint16_t x) { return AMP_*(x - 2048); }
+
+        // AD 変換器の外部トリガに使うタイマ (TIM8) の設定
+        void SetTim8(int frequency);
+
+        // for inhibition of copy constructor
+        AdcF446(const AdcF446&);
+        // for inhibition of substitute operator
+        AdcF446& operator=(const AdcF446&);     
+    };
+}
+#endif  // F446_ADC_SINGLE_HPP
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/F446_DAC.cpp	Wed May 16 01:40:50 2018 +0000
@@ -0,0 +1,47 @@
+//--------------------------------------------------------
+//  Class for buit-in single DAC on STM32F446
+//
+//  STM32F446 内蔵の DAC 用のクラス
+//      DAC_OUT1: A2  (PA_4)
+//      DAC_OUT2: D13 (PA_5)
+//
+//  2018/04/16, Copyright (c) 2018 MIKAMI, Naoki
+//--------------------------------------------------------
+
+#include "F446_DAC.hpp"
+
+namespace Mikami
+{
+    DacF446::DacF446(PinName pin) : da_(pin)
+    {
+        if ( (pin != A2) && (pin != D13) )
+        {
+            fprintf(stderr, "Invalid pin name\r\n");
+            while (true) {}
+        }
+        if (pin == A2)
+        {
+            DAC->CR = DAC_CR_EN1 | DAC_CR_TEN1 | DAC_CR_TSEL1;
+            fpWriteDac = &DacF446::WriteDac1;
+        }
+        else
+        {
+            DAC->CR = DAC_CR_EN2 | DAC_CR_TEN2 | DAC_CR_TSEL2;
+            fpWriteDac = &DacF446::WriteDac2;
+        }
+    }
+
+    // DAC の CH1 へ出力する
+    void DacF446::WriteDac1(uint16_t val)
+    {
+        DAC->DHR12R1 = val;
+        DAC->SWTRIGR = DAC_SWTRIGR_SWTRIG1;
+    }
+    // DAC の CH2 へ出力する
+    void DacF446::WriteDac2(uint16_t val)
+    {
+        DAC->DHR12R2 = val;
+        DAC->SWTRIGR = DAC_SWTRIGR_SWTRIG2;
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/F446_DAC.hpp	Wed May 16 01:40:50 2018 +0000
@@ -0,0 +1,61 @@
+//--------------------------------------------------------
+//  Class for buit-in single DAC on STM32F446 ---- Header
+//
+//  STM32F446 内蔵の DAC 用のクラス(ヘッダ)
+//      選択可能な入力端子:
+//          A2  (PA_4): ---- デフォルト
+//          D13 (PA_5): このポートはマイコンボードの LED もドライブするので
+//                       このポートは使わない方がよい
+//
+//  2018/05/16, Copyright (c) 2018 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:
+        // Constructor
+        explicit DacF446(PinName pin = A2);
+
+        virtual ~DacF446() {}
+
+        // -1.0f <= data <= 1.0f
+        void Write(float data) { WriteDac(ToUint16(data)); }
+
+        // 0 <= data1<= 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);   // CH1 へ
+        void WriteDac2(uint16_t 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_); }
+
+        // for inhibition of copy constructor
+        DacF446(const DacF446&);
+        // for inhibition of substitute operator
+        DacF446& operator=(const DacF446&);     
+    };
+}
+#endif  // F446_DAC_SINGLE_HPP
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/F446_Multirate.cpp	Wed May 16 01:40:50 2018 +0000
@@ -0,0 +1,97 @@
+//-----------------------------------------------------------
+//  出力を 4 倍にアップサンプリングするクラス:高域の補正を行う場合
+//  Nucleo-F446RE 専用
+//  
+//  2018/05/15, Copyright (c) 2018 MIKAMI, Naoki
+//-----------------------------------------------------------
+
+#include "F446_Multirate.hpp"
+
+F446_Multirate::F446_Multirate() : indexW_(0)
+{
+// 補間用フィルタの設定
+// 全体は biquad フィルタ4段とし,最初の3段は 6 次の LPF とし,最後の1段は
+// 外付けの 1 次のアナログフィルタによる,高域の低下分を補正するための,2 次の
+// LPF を使用.
+// 利得定数は,両者の利得定数の積に sqrt(2) を乗算した.これは 2 次のフィルタの
+// 利得の最大値が 1 倍のため,利得の最大値を sqrt(2) 倍にするため
+    //--------------------------------
+    // 1 ~ 3 段目
+    // 低域通過フィルタ
+    // 連立チェビシェフ特性
+    // 次数    : 6 次
+    // 標本化周波数: 40.00 kHz
+    // 遮断周波数 :  4.80 kHz
+    // 通過域のリップル: 0.50 dB
+    // 阻止域の減衰量 :35.00 dB
+    //--------------------------------
+    const Biquad::Coefs HK[] = {
+        {1.370091E+00f, -5.353523E-01f,  2.500437E-01f, 1.0f},  // 1段目
+        {1.402004E+00f, -8.228448E-01f, -1.182903E+00f, 1.0f},  // 2段目
+        {1.426447E+00f, -9.646314E-01f, -1.362836E+00f, 1.0f},  // 3段目
+    //--------------------------------
+    // 4 段目:高域補正用フィルタ
+    // 低域通過フィルタ
+    // 連立チェビシェフ特性
+    // 次数    : 2 次
+    // 標本化周波数: 40.00 kHz
+    // 遮断周波数 :  5.20 kHz
+    // 通過域のリップル: 3.00 dB
+    // 阻止域の減衰量 : 8.00 dB
+    //--------------------------------
+        {1.240986E+00f, -7.647923E-01f, -1.053681E+00f, 1.0f}}; // 4段目
+    const float G0 = FACTOR_*3.016500E-02f*3.918621E-01f*sqrtf(2);  // 利得定数
+    interpolator_ = new IirCascade(8, HK, G0);  // 補間用フィルタの初期化
+
+    NVIC_SetPriority(ADC_IRQn, 0);      // ADC 終了割り込み:最優先
+    NVIC_SetPriority(USART2_IRQn, 1);   // USART2 割り込み:次に優先    
+}
+
+// AD 変換終了割り込みを使えるようにする
+void F446_Multirate::SetIntr(int frequency)
+{
+    adc_ = new AdcF446(frequency*FACTOR_);      // AD変換器の初期化
+    wait_us(1000);    // ある程度の待ち時間が必要
+    adc_->SetIntrVec(&F446_Multirate::AdcIsr);  // ISR の設定
+}
+
+// AD変換の結果を取り出す
+float F446_Multirate::Input()
+{
+    while (!okIn_) {}               // AD変換の結果を取り出せるまで待つ
+    NVIC_DisableIRQ(USART2_IRQn);   // Output() を実行するまで USART2 割込みを禁止
+    okIn_ = false;
+    return xn_;
+}
+
+// 補間用フィルタを実行し,処理結果を出力用バッファへ書き込む
+void F446_Multirate::Output(float yn)
+{
+    for (int n=0; n<FACTOR_; n++)
+    {
+        buf_[ModCounter(indexW_)] = interpolator_->Execute(yn);
+        yn = 0;     // 2回目からは補間用フィルタの入力を 0 値とする
+    }
+    NVIC_EnableIRQ(USART2_IRQn);    // USART2 割込みを有効にする
+}
+
+// ADC 変換終了割り込みに対する割り込みサービス・ルーチン
+void F446_Multirate::AdcIsr()
+{
+    static int count = 0;
+
+    xn_ = adc_->Read();     // AD変換器の値を読み込む
+    dac_.Write(buf_[ModCounter(indexR_)]);  // 出力バッファの内容を DAC へ書き込む
+    
+    if (count == 0) okIn_ = true;   // AD変換器からの入力信号は4回に1回使う
+    count = ++count & MASK_FACTOR_; // 出力時に4倍にアップサンプリングするので,
+                                    // 入力を4回に1回行うための処理
+}
+
+// static メンバの実体の宣言/初期化
+AdcF446 *F446_Multirate::adc_;
+DacF446 F446_Multirate::dac_;
+Array<float> F446_Multirate::buf_(2*FACTOR_, 0.0f);
+int F446_Multirate::indexR_ = FACTOR_ - 1;
+float F446_Multirate::xn_;
+__IO bool F446_Multirate::okIn_ = false;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/F446_Multirate.hpp	Wed May 16 01:40:50 2018 +0000
@@ -0,0 +1,69 @@
+//---------------------------------------------------
+//  出力を 4 倍にアップサンプリングするクラス(ヘッダ)
+//  Nucleo-F446RE 専用
+//
+//      入力端子: A0 (PA_0) 
+//      出力端子: A2 (PA_4) 
+//
+//  2018/05/15, Copyright (c) 2018 MIKAMI, Naoki
+//---------------------------------------------------
+
+#include "mbed.h"
+#include "F446_ADC.hpp"
+#include "F446_DAC.hpp"
+#include "IIR_Cascade.hpp"
+
+#ifndef F446_MULTIRATE_HPP
+#define F446_MULTIRATE_HPP
+
+namespace Mikami
+{
+    class F446_Multirate
+    {
+    public:
+        // コンストラクタ
+        F446_Multirate();
+        
+        virtual ~F446_Multirate()
+        {
+            delete adc_;
+            delete interpolator_;
+        }
+
+        // AD 変換終了割り込みを使えるようにする
+        //      frequency: 入力の標本化周波数
+        void SetIntr(int frequency);
+
+        // AD変換の結果を取り出す
+        float Input();
+        
+        // 補間用フィルタを実行し,処理結果を出力用バッファへ書き込む
+        void Output(float yn);          
+        
+    private:
+        static const int FACTOR_ = 4;   // アップサンプリング倍率:4 倍
+                                        // この倍率は 2 のべき乗にすること
+        static const int MASK_FACTOR_ = FACTOR_ - 1;
+        static const int MASK_BUF_ = 2*FACTOR_ - 1;
+
+        IirCascade *interpolator_;  // 補間用フィルタのポインタ
+        static AdcF446 *adc_;       // AD変換器のオブジェクトのポインタ
+        static DacF446 dac_;        // DA変換器のオブジェクト
+        
+        static Array<float> buf_;   // DA変換器に出力するデータ用バッファ
+        int indexW_;                // buf_ へ書き込む際のインデックス
+        static int indexR_;         // buf_ から読み出す際のインデックス
+        static float xn_;           // AD変換器から入力されたデータ
+        static __IO bool okIn_;     // AD変換されたデータが使える場合に true となる
+
+        // 引数を 0 ~ (アップサンプリング倍率-1) の間でカウントアップ
+        static inline int ModCounter(int &index)
+        {
+            index = ++index & MASK_BUF_;
+            return index;
+        }
+        // ADC 変換終了割り込みに対する割り込みサービス・ルーチン
+        static void AdcIsr();
+    };
+}
+#endif  // F446_MULTIRATE_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/IIR_Cascade.hpp	Wed May 16 01:40:50 2018 +0000
@@ -0,0 +1,54 @@
+//---------------------------------------------------
+//  縦続形 IIR フィルタ
+//
+//  2018/05/14, Copyright (c) 2018 MIKAMI, Naoki
+//---------------------------------------------------
+
+#ifndef IIR_CASCADE_HPP
+#define IIR_CASCADE_HPP
+
+#include "Biquad.hpp"
+#include "Array.hpp"    // Array クラスが定義されている
+using namespace Mikami;
+
+class IirCascade
+{
+public:
+    // コンストラクタ
+    IirCascade(int order = 0, const Biquad::Coefs ck[] = NULL, float g0 = 1)
+    {
+        SetCoefs(order, ck, g0);
+        Clear();
+    }
+
+    // フィルタ処理を実行する
+    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]);
+        Clear();    // 係数切り替え時の雑音発生防止用
+    }
+    
+    // 内部変数(遅延器)のクリア
+    void Clear()
+    {   for (int k=0; k<(order_+1)/2; k++) hn_[k].Clear(); }
+
+private:
+    int order_;         // 次数
+    float g0_;          // 利得定数
+    Array<Biquad> hn_;  // Biquad クラスのオブジェクトの配列
+};
+#endif  // IIR_CASCADE_HPP