NucleoでFIRフィルタ


STM32マイコンを使用したNucleoボードにmbedを使って書いたプログラムでFIRフィルタを実装しました。

はじめに

研究室で信号処理の研究をしていて、マイコンを使ってエフェクターやシンセサイザーのように音声信号を処理できるような物が作れたら面白そうだなぁと思って作りました。

使った物

  • STM32 Nucleo Board STM32F303K8
    いつだか買って積みボードになっていたもの
  • 抵抗やコンデンサ少々

回路

ADCの入力が負電圧に振れると困るのでVcc(3.3V)/2のバイアスをかけていますが基本的にはPCの音声出力をNucleoのA0ピンにつなげただけです。

結果

$Fs = 8000{\rm[Hz]},Fp=0.2(1600{\rm [Hz]}),Fs=0.21(1680{\rm [Hz]})$のLPFを200次で作りました。

$1.6 {\rm [kHz]}$入力時 (黄:入力 青:出力)

$1.7{\rm[kHz]}$入力時

しっかり阻止域以上の信号を減衰させることができています。

プログラム

サンプリングレートを決めてその周期でタイマー割り込みを発生させ、ADCによる読み込みと畳み込みによるフィルタの適用、DACによる出力を行っています。

#include "mbed.h"
#include <stdlib.h>

#define BUFF_SIZE 201 //フィルタ長 = バッファサイズ
#define F_ORD BUFF_SIZE

Ticker toggle_led_ticker;

DigitalOut led1(LED1);
AnalogIn v_read(A0);
AnalogOut v_out(A3);

const float SampleRate = 1.0/8000.0;

float buff[BUFF_SIZE] = {0};
int index = 0;

//LPF 100次 フィルタ長201 Type1 Fp=0.2 Fs=0.21
float h[F_ORD] = {
 /*フィルタ係数省略*/
};

float sum;
float out;


void toggle_led() {

    //バッファの値をインデックス+1分ずらす
    //buff[] = 1,2,3,4 -> newval,1,2,3 
    for(int  i = 0; i<BUFF_SIZE - 1; i++)
    {
        buff[(BUFF_SIZE-1) - i] = buff[(BUFF_SIZE-1) - i - 1];
    }
    //buff[0](最新の読み取り)
    buff[0] = v_read.read();

    //畳み込み
    sum = 0.0;
    for(int i = 0; i<F_ORD; i++)
    {
        sum += h[i]*buff[BUFF_SIZE - i];
    }

    v_out = sum;
}

int main() {
    // Init the ticker with the address of the function (toggle_led) to be attached and the interval (100 ms)
    toggle_led_ticker.attach(&toggle_led, SampleRate);
    while (true) {
        // Do other things...
    }
}

まとめ

ADCで入力を読み取り、インパルス応答との畳み込みを行うことでディジタルLPFを実装することに成功。
mbedでSTMマイコンをいじるとクロックが$8{\rm[MHz]}$で悲しいのでCubeIDEを使って上限の$72{\rm[MHz]}$まで早くして一般的なオーディオ信号を扱えるようにしたい。(この状態で割り込みを$44.1{\rm[kHz}$]にしたら動かなかった...)