Cutoff frequency variable LPF and HPF by IIR 6th-order Butterworth filter for ST Nucleo F401RE.

Dependencies:   UITDSP_ADDA UIT_ACM1602NI UIT_AQM1602 UIT_IIR_Filter mbed

Files at this revision

API Documentation at this revision

Comitter:
MikamiUitOpen
Date:
Fri Sep 11 09:54:45 2015 +0000
Commit message:
1

Changed in this revision

BilinearDesignLpfHpf/BilinearDesignLH.cpp Show annotated file Show diff for this revision Revisions of this file
BilinearDesignLpfHpf/BilinearDesignLH.hpp Show annotated file Show diff for this revision Revisions of this file
UITDSP_ADDA.lib Show annotated file Show diff for this revision Revisions of this file
UIT_ACM1602NI.lib Show annotated file Show diff for this revision Revisions of this file
UIT_AQM1602.lib Show annotated file Show diff for this revision Revisions of this file
UIT_IIR_Filter.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/BilinearDesignLpfHpf/BilinearDesignLH.cpp	Fri Sep 11 09:54:45 2015 +0000
@@ -0,0 +1,81 @@
+//------------------------------------------------------------------------------
+//  Design of Butterworth LPF and HPF using bilinear transform
+//
+//   2014/06/29, Copyright (c) 2014 MIKAMI, Naoki
+//------------------------------------------------------------------------------
+
+#include "BilinearDesignLH.hpp"
+
+namespace Mikami
+{
+    // Execute design
+    //      input
+    //          fc: Cutoff frequency
+    //      output
+    //          c : Coefficients for cascade structure
+    //          g : Gain factor for cascade structure
+    void BilinearDesign::Execute(float fc, Coefs c[], float& g)
+    {
+        Butterworth();
+        Bilinear(fc);
+        ToCascade();
+        GetGain();
+        GetCoefs(c, g);
+    }
+
+    // Get poles for Butterworth characteristics
+    void BilinearDesign::Butterworth()
+    {
+        float pi_2order = PI_/(2.0f*ORDER_);
+        for (int j=0; j<ORDER_/2; j++)  // Pole with imaginary part >= 0
+        {
+            float theta = (2.0f*j + 1.0f)*pi_2order;
+            sP_[j] = Complex(-cosf(theta), sinf(theta));
+        }
+    }
+    
+    // Bilinear transform
+    //      fc: Cutoff frequency
+    void BilinearDesign::Bilinear(float fc)
+    {
+        float wc = tanf(fc*PI_FS_);
+        for (int k=0; k<ORDER_/2; k++)
+            zP_[k] = (1.0f + wc*sP_[k])/(1.0f - wc*sP_[k]);
+    }
+    
+    // Convert to coefficients for cascade structure
+    void BilinearDesign::ToCascade()
+    {
+        for (int j=0; j<ORDER_/2; j++)
+        {
+            ck_[j].a1 = 2.0f*real(zP_[j]);              // a1m
+            ck_[j].a2 = -norm(zP_[j]);                  // a2m
+            ck_[j].b1 = (PB_ == LPF) ? 2.0f : -2.0f;    // b1m
+        }
+    }
+
+    // Calculate gain factor
+    void BilinearDesign::GetGain(){
+        float u = (PB_ == LPF) ? 1.0f : -1.0f;
+        float g0 = 1.0f;
+        for (int k=0; k<ORDER_/2; k++)
+            g0 = g0*(1.0f - (ck_[k].a1 + ck_[k].a2*u)*u)/
+                    (1.0f + (ck_[k].b1 + u)*u);
+        gain_ = g0;
+    }
+    
+    // Get coefficients
+    void BilinearDesign::GetCoefs(Coefs c[], float& gain)
+    {
+        for (int k=0; k<ORDER_/2; k++)
+        {
+            c[k].a1 = ck_[k].a1;
+            c[k].a2 = ck_[k].a2;
+            c[k].b1 = ck_[k].b1;
+        }
+        gain = gain_;
+    }
+}
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/BilinearDesignLpfHpf/BilinearDesignLH.hpp	Fri Sep 11 09:54:45 2015 +0000
@@ -0,0 +1,62 @@
+//------------------------------------------------------------------------------
+//  Design of Butterworth LPF and HPF using bilinear transform -- Header
+//
+//   2014/06/29, Copyright (c) 2014 MIKAMI, Naoki
+//------------------------------------------------------------------------------
+
+#ifndef BILINEAR_BUTTERWORTH_HPP
+#define BILINEAR_BUTTERWORTH_HPP
+
+#include "mbed.h"
+#include <complex>  // requisite
+
+namespace Mikami
+{
+    typedef complex<float> Complex; // define "Complex"
+
+    class BilinearDesign
+    {
+    public:
+        struct Coefs { float a1, a2, b1; };
+        enum Type { LPF, HPF };
+
+        // Constructor
+        BilinearDesign(int order, float fs, Type pb)
+            : PI_FS_(PI_/fs), ORDER_(order), PB_(pb)
+        {
+            sP_ = new Complex[order/2];
+            zP_ = new Complex[order/2];
+            ck_ = new Coefs[order/2];
+        }
+
+        // Destractor
+        ~BilinearDesign()
+        {
+            delete[] sP_;
+            delete[] zP_;
+            delete[] ck_;
+        }
+
+        // Execution of design
+        void Execute(float fc, Coefs c[], float& g);
+
+    private:
+        static const float PI_ = 3.1415926536f;
+        const float PI_FS_;
+        const int ORDER_;
+        const Type PB_;
+
+        Complex* sP_;   // Poles on s-plane
+        Complex* zP_;   // Poles on z-plane
+        Coefs* ck_;     // Coefficients of transfer function for cascade form
+        float gain_;    // Gain factor for cascade form
+
+        void Butterworth();
+        void Bilinear(float fc);
+        void ToCascade();
+        void GetGain();
+        void GetCoefs(Coefs c[], float& gain);
+    };
+}
+#endif  // BILINEAR_BUTTERWORTH_HPP
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/UITDSP_ADDA.lib	Fri Sep 11 09:54:45 2015 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/MikamiUitOpen/code/UITDSP_ADDA/#543daa087bd5
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/UIT_ACM1602NI.lib	Fri Sep 11 09:54:45 2015 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/MikamiUitOpen/code/UIT_ACM1602NI/#b7c761c179c9
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/UIT_AQM1602.lib	Fri Sep 11 09:54:45 2015 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/MikamiUitOpen/code/UIT_AQM1602/#01336babcccd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/UIT_IIR_Filter.lib	Fri Sep 11 09:54:45 2015 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/MikamiUitOpen/code/UIT_IIR_Filter/#2533ed0150e7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Fri Sep 11 09:54:45 2015 +0000
@@ -0,0 +1,119 @@
+//--------------------------------------------------------------
+// Cutoff frequency variable LPF and HPF by IIR 8th-order filter
+//      A0: Signal to be filtered
+//      A2: Value which controls cutoff frequency
+//
+//      sw: 0, 2  Through
+//          1: LPF
+//          3: HPF
+// 2015/09/11, Copyright (c) 2015 MIKAMI, Naoki
+//--------------------------------------------------------------
+
+#include "ADC_Interrupt.hpp"    // for ADC using interrupt
+#include "DAC_MCP4921.hpp"      // for DAC MCP4921, MCP4922
+using namespace Mikami;
+
+#include "BilinearDesignLH.hpp" // for design of IIR filter
+#include "IIR_Cascade.hpp"      // for IIR filter of cascade structure
+
+// ACM1602Ni を使う場合は次の define 文をコメントにすること
+#define AQM1602
+
+#ifdef AQM1602
+#include "AQM1602.hpp"
+Aqm1602 Lcd_;
+#else
+#include "ACM1602NI.hpp"
+Acm1602Ni Lcd_;
+#endif
+
+const int FS_ = 24000;          // Sampling frequency: 24 kHz
+ADC_Intr myAdc_(A0, FS_, A2);
+DAC_MCP4921 myDac_;
+
+const int ORDER_ = 8;
+IirCascade<ORDER_> iirLH_;      // IIR filter object
+
+DigitalIn sw1_(D2, PullDown);   // 0: disable filter
+                                // 1: enable filter
+DigitalIn sw2_(D3, PullDown);                                
+
+uint16_t a2_ = 0;   // Inputted data from A2 pin
+
+// Interrupt service routine for ADC
+void AdcIsr()
+{   
+    float xn = myAdc_.Read();   // Read from A0
+
+    myAdc_.Select2ndChannel();  // Select A2   
+    myAdc_.SoftStart();         // ADC start for A2 input
+
+    // Execute IIR filter
+    float yn = iirLH_.Execute(xn);
+
+    if (sw1_ == 0) myDac_.Write(xn);    // Using no filter
+    else           myDac_.Write(yn);    // Using filter
+
+    // Read value which controls cutoff frequency
+    a2_ = myAdc_.ReadWait_u16();
+
+    myAdc_.Select1stChannel();      // Select A0
+    myAdc_.ClearPending_EnableIRQ();// Clear pending interrupt
+                                    // and enable ADC_IRQn
+}
+
+int main()
+{
+    myDac_.ScfClockTim3(1000000);   // cutoff frequency: 10 kHz
+
+    BilinearDesign lpfDsgn(ORDER_, FS_, BilinearDesign::LPF);
+    BilinearDesign hpfDsgn(ORDER_, FS_, BilinearDesign::HPF);
+
+    myAdc_.SetIntrVec(AdcIsr);  // Assign ISR for ADC interrupt
+
+    float fc1 = 0;
+    while (true)
+    {
+        // fc: cutoff frequency, 100 -- 2000 Hz
+        float fc = 1900.0f*(a2_/4095.6f) + 100.0f;
+
+        if (sw1_ == 0)
+        {
+            printf("Through\r\n");
+            Lcd_.WriteStringXY("Through         ", 0, 0);
+            wait(0.2f);
+            Lcd_.ClearLine(0);
+            fc1 = 0;
+        }
+        else
+        {
+            if (fabs(fc - fc1) > 10.0f)
+            {
+                char str[18];
+                if (sw2_ == 0) sprintf(str, "LPF: fc=%4d Hz", int(fc+0.5f));
+                else           sprintf(str, "HPF: fc=%4d Hz", int(fc+0.5f));
+
+                printf("%s\r\n", str);
+                Lcd_.WriteStringXY(str, 0, 0);
+                fc1 = fc;
+
+                // Design new coefficients based on new fc
+                BilinearDesign::Coefs coefs[ORDER_/2];
+                float g0;
+                if (sw2_ == 0) lpfDsgn.Execute(fc, coefs, g0);
+                else           hpfDsgn.Execute(fc, coefs, g0);
+                Biquad::Coefs coefsIir[ORDER_/2];
+                // Update new coefficients
+                for (int n=0; n<ORDER_/2; n++)
+                {
+                    coefsIir[n].a1 = coefs[n].a1;
+                    coefsIir[n].a2 = coefs[n].a2;
+                    coefsIir[n].b1 = coefs[n].b1;
+                    coefsIir[n].b2 = 1.0f;
+                }
+                iirLH_.SetCoefs(g0, coefsIir);
+            }
+        }
+        wait(0.1f);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed.bld	Fri Sep 11 09:54:45 2015 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/mbed_official/code/mbed/builds/ba1f97679dad
\ No newline at end of file