# How to properly implement an IIR High-Pass filter with a cut off frequency ~20Hz

I am trying to implement a digital High-Pass filter (IIR) with a cut off frequency around ~20Hz on a STM32H753ZI with the CMSIS DSP API. Every time I try to go test it, the output is no longer recognizable below the Fc but looks fine above it.

The way I'm creating the filter is by having MATLAB FDATOOL do most of the work then using the Direct II - Transposed form with a precision of a double.

Before anyone asked, yes the STM32H7 is equipped with a DP-FPU (Double Precision - FPU)

I was doing float precision before and could see why I couldn't go low that frequencies, but I would imagine with double precision I would have no problems.

Any suggestions?

Coefficients for HP Filter with Fc ~ 20Hz:

The order of the filters is : 11

The number of section of the filter is: 6

    0.99982445967420, -1.99964891934840, 0.99982445967420, 1.99964816177700, -0.99964967691960,
0.99948849907220, -1.99897699814440, 0.99948849907220, 1.99897624082800, -0.99897775546090,
0.99919412499170, -1.99838824998340, 0.99919412499170, 1.99838749289000, -0.99838900707690,
0.99896509406540, -1.99793018813080, 0.99896509406540, 1.99792943121100, -0.99793094505080,
0.99881986128070, -1.99763972256140, 0.99881986128070, 1.99763896575200, -0.99764047937130,
0.99938486903600, -0.99938486903600, 0.00000000000000, 0.99876973807200, -0.00000000000000


Code:

/*
* DSP_FACTORY.c
*
*  Created on: May 7, 2021
*      Author: Christopher
*/

#define ARM_MATH_CM7
#include "main.h"
#include "arm_math.h"
#include "FILTER.h"
#include "CONSTANTS.h"
#include "OPCODE.h"
#include "I2S_FACTORY.h"

static double inputCompensator = 1.0;
static uint8_t volumeCurrent = 100;

void INIT_FILTER(uint8_t sourceMode) {

switch(sourceMode) {

case INPUT_INLINE:

break;

case INPUT_BLUETOOTH:

break;
}
}

void I2S_HALFCOMPLETE_CALLBACK() {

int * I2S1_RxBUFF = getI2S1_RxBUFF();
int * I2S1_TxBUFF = getI2S1_TxBUFF();

double INSAMPLE_I2S_L[1024];
double INSAMPLE_I2S_R[1024];

double OUTSAMPLE_I2S_L[1024];
double OUTSAMPLE_I2S_R[1024];

for (int i = 0; i < 2048; i ++){

if ( i % 2 == 0){ // R Samples

INSAMPLE_I2S_L[i-(i >> 1)] = (double) (I2S1_RxBUFF[i] << ADC_COMPENSATOR) * (0.01 * (double) volumeCurrent) * inputCompensator;

} else if ( i % 2 == 1){ // R Samples

INSAMPLE_I2S_R[i-1-((i-1)>>1)] = (double) (I2S1_RxBUFF[i] << ADC_COMPENSATOR) * (0.01 * (double) volumeCurrent) * inputCompensator;

}
}

for (int i = 0; i < 2048; i ++){

if (i % 2 == 0) { // L Samples

I2S1_TxBUFF[i] = (int)OUTSAMPLE_I2S_L[i-(i >> 1)];

} else if ( i % 2 == 1) { // R Samples

I2S1_TxBUFF[i] = (int)OUTSAMPLE_I2S_R[i-1-((i-1)>>1)];

}
}

}

void I2S_TRANSFERCOMPLETE_CALLBACK() {

int * I2S1_RxBUFF = getI2S1_RxBUFF();
int * I2S1_TxBUFF = getI2S1_TxBUFF();

double INSAMPLE_I2S_L[1024];
double INSAMPLE_I2S_R[1024];

double OUTSAMPLE_I2S_L[1024];
double OUTSAMPLE_I2S_R[1024];

for (int i = 2048; i < 4096; i ++){

if (i % 2 == 0){ // L Samples

INSAMPLE_I2S_L[i-(i >> 1)-1024] = (double) (I2S1_RxBUFF[i] << ADC_COMPENSATOR) * (0.01 * (double) volumeCurrent) * inputCompensator;

} else if ( i % 2 == 1){ // R Samples

INSAMPLE_I2S_R[i-1-((i-1)>>1)-1024] = (double) (I2S1_RxBUFF[i] << ADC_COMPENSATOR) * (0.01 * (double) volumeCurrent) * inputCompensator;

}
}

for (int i = 2048; i < 4096; i ++){

if (i % 2 == 0) { // L Samples

I2S1_TxBUFF[i] = (int)OUTSAMPLE_I2S_L[i-(i >> 1)-1024];

} else if ( i % 2 == 1) { // R Samples

I2S1_TxBUFF[i] = (int)OUTSAMPLE_I2S_R[i-1-((i-1)>>1)-1024];

}
}

}

void MUTE(uint8_t INPUT_AUDIO_SELECTOR) {

if((INPUT_AUDIO_SELECTOR == INPUT_INLINE) | (INPUT_AUDIO_SELECTOR == INPUT_BLUETOOTH))

SPI1->CR1 |= SPI_CR1_CSUSP;
while (!((SPI1->CR1) & (SPI_CR1_CSTART)));
DMA1_Stream0->CR &= ~DMA_SxCR_EN;
DMA1_Stream1->CR &= ~DMA_SxCR_EN;
SPI1->CR1 &= ~SPI_CR1_SPE;
}

void UNMUTE(uint8_t INPUT_AUDIO_SELECTOR) {

if((INPUT_AUDIO_SELECTOR == INPUT_INLINE) | (INPUT_AUDIO_SELECTOR == INPUT_BLUETOOTH))

DMA1_Stream0->CR |= DMA_SxCR_EN;
DMA1_Stream1->CR |= DMA_SxCR_EN;
SPI1->CR1 |= SPI_CR1_SPE;
SPI1->CR1 |= SPI_CR1_CSTART;
}


UPDATE 1:

Fc = 15Hz

Fc = 40Hz

Fc = 400Hz

Frequency Response

• "Every time I try to go test it, the output is no longer recognizable below the Fc but looks fine above it". Can you post sample input and output waveforms for sinusoidal waves at, say, 10Hz, 20Hz and 30Hz? Perhaps, being a high pass filter, this is expected? – AJN Jun 8 at 6:24
• @AJN I see what you mean, ill post a picture to describe what I am saying with more clarity. – Leoc Jun 8 at 6:25
• Seeing so many coefficients, wha is the order of the filter? Please post a frequency response from the Matlab tool used to create the coefficients. – AJN Jun 8 at 6:26
• What sampling rates are you using? If the ratio is too great (looks like it), then you'll run into numerical problems. See this on dsp.ee for some tricks. – a concerned citizen Jun 8 at 6:30
• Is there a typo at the line ... init_f64(&audioStream_R, ..., pState_L_ ... – AJN Jun 8 at 11:33

I don't understand the code;

I guessed that the problem is with improper state transfer between batches being processed. The sudden dips usually indicate that. The reason why it is not seen for higher frequency input may be because you are viewing at magnified time scale for those inputs. The scope is not showing you the boundaries of the 1024 samples (by chance).

These lines supports my suspicion. Is there a typo ? Should an L be an R ?

arm_biquad_cascade_df2T_init_f64(&audioStream_L, 6, initFilter_96kHz_inline,
pState_
L
_96kHz_inline);


• If this guess is correct INPUT_BLUETOOTH should work correctly. Also, I wasn't able to recreate the frequency response plots from the coefficients you posted. Verify it using a software like Matlab / Octave to be doubly sure. – AJN Jun 8 at 11:37