Before reading any further, to give you on some insight on the output level of the oscilloscope picture's the ~340mV green signal is normal. If you know me I posted a question as to why its 340mV but for sake of simplicity 340mV is normal in this case. So attenuation is not being seen here.

trying to design some digital filters directly from matlab to be implemented in the STM32H7 using the CMSIS DSP library

I tried first doing a butterworth low pass filter with some criteria I made listed in the picture

enter image description here

Converted it to Direct-Form 1 and into second - order sections. Then generated to a .h header in single precision form as I am working with floats and not doubles.

Here are the coefficients

float32_t iir_coeffs[20] = {0.344617784f, 0.689235568f, 0.344617784f, 0.2994394898f, -0.6779106259f, // Biquad Stage 1
                            0.265586704f, 0.531173408f, 0.265586704f, 0.2307691127f, -0.2931158841f, // Biquad Stage 2
                            0.2259309888f, 0.4518619776f, 0.2259309888f, 0.1963121295f, -0.1000360623f, // Biquad Stage 3
                            0.2090389729f, 0.4180779458f, 0.2090389729f, 0.1816346198f, -0.01779049635f // Biquad Stage 4}; 

Once the coefficients where obtained, all I had to do in code was change the state variable length to 4*4 = 16 and number of stages to 4 as this is a 4 stage biquad filter implementation.

However this doesnt work! I have no idea as to why? There's no attenuation @ 20kHz should be -3dB down and at 40kHz should be a nice -100dB down but the picture says otherwise.

Oscilloscope pictures enter image description here enter image description here

enter image description here


    #define ARM_MATH_CM7

#include "main.h"

#include "arm_math.h"

void init_Clock(void);
void init_I2S(void);
void init_Debugging(void);
void init_Interrupt(void);
void init_SpeedTest(void);

int32_t RxBuff[4];
int32_t TxBuff[4];
uint8_t TC_Callback = 0;
uint8_t HC_Callback = 0;

char uartBuff[8];
float32_t iir_coeffs[20] = {0.344617784f, 0.689235568f, 0.344617784f, 0.2994394898f, -0.6779106259f,
                            0.265586704f, 0.531173408f, 0.265586704f, 0.2307691127f, -0.2931158841f,
                            0.2259309888f, 0.4518619776f, 0.2259309888f, 0.1963121295f, -0.1000360623f,
                            0.2090389729f, 0.4180779458f, 0.2090389729f, 0.1816346198f, -0.01779049635f};

float32_t iir_mono_state[16];

float32_t inSample[4];
float32_t outSample[4];

arm_biquad_casd_df1_inst_f32 monoChannel;

void DMA1_Stream0_IRQHandler(void) {

    if (((DMA1 -> LISR) & (DMA_LISR_TCIF0)) != 0){
        DMA1 -> LIFCR |= DMA_LIFCR_CTCIF0;
        TC_Callback = 1;

    else if (((DMA1 -> LISR) & (DMA_LISR_HTIF0)) != 0){
         DMA1 -> LIFCR |= DMA_LIFCR_CHTIF0;
         HC_Callback = 1;


int main(void) {

    arm_biquad_cascade_df1_init_f32(&monoChannel, 4, iir_coeffs, iir_mono_state);

  while (1)

      if (HC_Callback == 1){

         // GPIOA->BSRR |= GPIO_BSRR_BS3_HIGH;

          for (int i = 0; i < 2; i++){
              inSample[i] = (float32_t)RxBuff[i];

          arm_biquad_cascade_df1_f32(&monoChannel, inSample, outSample, 2);

          for (int i = 0; i < 2; i++){
                TxBuff[i] = outSample[i];

          HC_Callback = 0;

      } else  if (TC_Callback == 1){

        //  GPIOA->BSRR |= GPIO_BSRR_BR3_LOW;

          for (int i = 2; i < 4; i++){
              inSample[i] = (float32_t)RxBuff[i];

                  arm_biquad_cascade_df1_f32(&monoChannel, &inSample[2], &outSample[2], 2);

                  for (int i = 2; i < 4; i++){
                        TxBuff[i] =  outSample[i];

          TC_Callback = 0;




Update 1: To show you how the coefficients are presented

enter image description here

UPDATE 2: Tried doing it one biquad at a time comparing it to the bode plot in matlab as I go up a biquad and it works up till the 4th stage. Not sure if that helps.

  • 1
    \$\begingroup\$ Please don't paste same question to two sites without remark: dsp.stackexchange.com/questions/70965/… \$\endgroup\$ Oct 20, 2020 at 20:32
  • \$\begingroup\$ Are you using 96 kHz sampling frequency? If you try other, higher frequencies, can you see some hint of a corner frequency? \$\endgroup\$ Oct 20, 2020 at 20:32
  • \$\begingroup\$ @MarcusMüller sorry, thought that was allowed. What do you mean without remark? \$\endgroup\$
    – Leoc
    Oct 20, 2020 at 20:35
  • \$\begingroup\$ @aconcernedcitizen Correct Iam using a 96kHz sampling. 44kHz I see an attenuation \$\endgroup\$
    – Leoc
    Oct 20, 2020 at 20:36
  • \$\begingroup\$ How did you account for frequency warping between S and Z domains? Bilinear transform? \$\endgroup\$
    – user16324
    Oct 20, 2020 at 20:52

2 Answers 2


Did you try scaling the input and the output?
The documentation of the arm_biquad_cascade_df1_f32 mentions:

The frequency response of a Biquad filter is a function of its coefficients. It is possible for the gain through the filter to exceed 1.0 meaning that the filter increases the amplitude of certain frequencies. This means that an input signal with amplitude < 1.0 may result in an output > 1.0 and these are saturated or overflowed based on the implementation of the filter. To avoid this behavior the filter needs to be scaled down such that its peak gain < 1.0 or the input signal must be scaled down so that the combination of input and filter are never overflowed.

Depending on your input / output format, maybe you can use arm_q31_to_float / arm_float_to_q31.

  • \$\begingroup\$ Welcome! :-) (a) I think you meant arm_biquad_cascade_df1_f32 not arm_biquad_cascade_df1_f3 - yes? I've fixed that typo. (b) To comply with site rules here, a link must be given to any text / images / photos which are included from elsewhere. I think I've found the best link for the documentation you quoted. If you think another source is better, you can edit your answer to change the link I used. (c) FYI, the site standard here for quoted text is the "blockquote" (>) so I used that. Some editing docs are here. I hope that helps :-) \$\endgroup\$
    – SamGibson
    Oct 21, 2020 at 11:53
  • \$\begingroup\$ Your answer not only does not address OP's problems (why there is no corner frequency, not why there is attenuation), but you are also pointing towards a function that is not relevant to OP's case, since what you are talking about is for fixed-point arithmetic (_q15 and _q31 versions), not floating-point, (_f32, which is what OP is using). \$\endgroup\$ Oct 21, 2020 at 12:15

Found the solution,

The sampling rate is actually somehow ~192kHz, I'll have to take a look at this as to how it became that.

Tried creating Biquad coefficients for 192kHz Sampling rate and everything worked as I noticed which was pointed out by 'a concerned citizen' that my Fc was at ~40kHz which is doubled used 192kHz instead of 96kHz and boom.

Currently working with a 8th Order butterworth IIR low pass filter.

enter image description here enter image description here

  • \$\begingroup\$ Just double checked the code, I should be on 96kHz unless the Fws is actually = Fw*2. Right now the SPI is clocked at 98.34004MHz. SPI Clock / (256*(2*2)+0) =~96kHz according to data sheet, but clearly something is off here \$\endgroup\$
    – Leoc
    Oct 22, 2020 at 7:30
  • \$\begingroup\$ It may be your I2S config. A 2 channel stereo 96kHz sampling rate gives 192kHz samples per second, so you better be sure that you are receiving and processing a single channel instead of 2 channel stereo buffer. \$\endgroup\$
    – Justme
    Oct 22, 2020 at 7:43
  • \$\begingroup\$ @Justme I dont know where in the I2S config it says mono though, I believe its doing 2 channel. Its just kinda deceiving in retro spec with the data sheet as it says FWS = Sampling rate. I setup my FWS = 96kHz shouldnt that be the sampling rate? I guess its 192kHz. I gotta bring it down Fws = 48kHz I guess.. \$\endgroup\$
    – Leoc
    Oct 22, 2020 at 7:48
  • \$\begingroup\$ @Leoc When I said "check your fs", I mean really check your fs, not just by looking at what you think you had to set in order to get the correct prescaler, but with the oscilloscope, as well. And now the question comes: why did the single 2nd order stages work? \$\endgroup\$ Oct 22, 2020 at 7:50
  • \$\begingroup\$ @aconcernedcitizen no, you're right I oversaw it. Kinda confusing thinking in the datasheet it tells you Fws = is sampling rate however thats for the stereo channel. I do apologize big over sight by me \$\endgroup\$
    – Leoc
    Oct 22, 2020 at 8:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.