AnsweredAssumed Answered

What is wrong with my biquad filter?

Question asked by Abdullah Kahraman on Feb 10, 2015
Latest reply on Feb 13, 2015 by Abdullah Kahraman

Hello,

 

I am trying to implement "Biquad Cascade IIR Low Pass Filter Using Direct Form I Structure" using ARM CMSIS-DSP library on FRDM-KL25Z board. The filter's properties can be seen in the first screenshot below.

 

What I am doing is sampling the ADC, converting it to Q15 format, storing it, filtering, converting back to decimal and playing it back using PWM (with TPM module) and filtering the PWM with a capacitor and a resistor to have the analog output.

 

I am giving a sinusoidal signal with an offset of VCC/2 to the ADC input. When I store it and play back what I store without any modification, without any filtering and such, I get an almost perfect mach between input and output. I can also do some small modifications like removing offset and multiplying the signal amplitude. You can see the results of these operations in the oscilloscope shots.

 

When I apply the filtering function arm_biquad_cascade_df1_q15( ), I get strange results with different coefficient settings, but nothing near what I like. I am using Eli Hughes' wonderful tool for calculating the coefficients.

 

What am I doing wrong?

 

filter.png

CMSIS.png

double.png

remove_offset.png

 

/* ###################################################################
**     Filename    : main.c
**     Project     : CMSIS_example
**     Processor   : MKL25Z128VLK4
**     Version     : Driver 01.01
**     Compiler    : GNU C Compiler
**     Date/Time   : 2015-02-07, 14:30, # CodeGen: 0
**     Abstract    :
**         Main module.
**         This module contains user's application code.
**     Settings    :
**     Contents    :
**         No public methods
**
** ###################################################################*/
/*!
** @file main.c
** @version 01.01
** @brief
**         Main module.
**         This module contains user's application code.
*/     
/*!
**  @addtogroup main_module main module documentation
**  @{
*/     
/* MODULE main */


/* Including needed modules to compile this module/procedure */
#include "Cpu.h"
#include "Events.h"
#include "ADC0.h"
#include "TPM2.h"
#include "PIT.h"
#include "PTA.h"
#include "TPM1.h"
/* Including shared modules, which are used for whole project */
#include "PE_Types.h"
#include "PE_Error.h"
#include "PE_Const.h"
#include "IO_Map.h"

/* User includes (#include below this line is not maintained by Processor Expert) */
void main_loop(void);
/*lint -save  -e970 Disable MISRA rule (6.3) checking. */
int main(void)
/*lint -restore Enable MISRA rule (6.3) checking. */
{
  /* Write your local variable definition here */

  /*** Processor Expert internal initialization. DON'T REMOVE THIS CODE!!! ***/
  PE_low_level_init();
  /*** End of Processor Expert internal initialization.                    ***/

  /* Write your code here */
  /* For example: for(;;) { } */

  main_loop();

  /*** Don't write any code pass this line, or it will be deleted during code generation. ***/
  /*** RTOS startup code. Macro PEX_RTOS_START is defined by the RTOS component. DON'T MODIFY THIS CODE!!! ***/
  #ifdef PEX_RTOS_START
    PEX_RTOS_START();                  /* Startup of the selected RTOS. Macro is defined by the RTOS component. */
  #endif
  /*** End of RTOS startup code.  ***/
  /*** Processor Expert end of main routine. DON'T MODIFY THIS CODE!!! ***/
  for(;;){}
  /*** Processor Expert end of main routine. DON'T WRITE CODE BELOW!!! ***/
} /*** End of main routine. DO NOT MODIFY THIS TEXT!!! ***/

/* END main */
/*!
** @}
*/
/*
** ###################################################################
**
**     This file was created by Processor Expert 10.3 [05.09]
**     for the Freescale Kinetis series of microcontrollers.
**
** ###################################################################
*/





 

/*
* main_loop.c
*
*  Created on: Feb 7, 2015
*      Author: Abdullah
*/
#include "Cpu.h"
#include "Events.h"
#include "ADC0.h"
#include "TPM2.h"
/* Including shared modules, which are used for whole project */
#include "PE_Types.h"
#include "PE_Error.h"
#include "PE_Const.h"
#include "IO_Map.h"

#include "arm_math.h"

#define FILTER_NUM_STAGES    1
#define FILTER_BLOCK_SIZE    32

uint8_t FIR_src_index = 0;
uint8_t enable_output = 0;

arm_biquad_casd_df1_inst_q15 filter_struct;

q15_t FIR_src[32] =
{ 0 };
q15_t FIR_src_buf[32] =
{ 0 };
q15_t FIR_dest[32] =
{ 0 };

q15_t FIR_coefficients[FILTER_NUM_STAGES * 6] =
{ 797, 0, 1594, 797, 28007, 1574 };

q15_t FIR_state[FILTER_NUM_STAGES * 4] =
{ 0 };

void main_loop(void)
{
    static uint8_t ctr = 0;

    filter_struct.numStages = FILTER_NUM_STAGES;
    filter_struct.pState = FIR_state;
    filter_struct.pCoeffs = FIR_coefficients;
    filter_struct.postShift = 0;

    arm_biquad_cascade_df1_init_q15(&filter_struct, filter_struct.numStages,
            filter_struct.pCoeffs, filter_struct.pState,
            filter_struct.postShift);

    while (1)
    {
        if ((FIR_src_index == 32) && (enable_output == 0))
        {
            for (ctr = 0; ctr < 32; ctr--)
            {
                FIR_src_buf[ctr] = FIR_src[ctr];
            }
            ADC0_SC1A |= ADC_SC1_AIEN_MASK;
            PIT_TCTRL0 |= ( PIT_TCTRL_TIE_MASK | PIT_TCTRL_TEN_MASK);
            FIR_src_index = 0;
            enable_output = 1;
            arm_biquad_cascade_df1_q15(&filter_struct, FIR_src_buf, FIR_dest,
                    FILTER_BLOCK_SIZE);
        }
    }
}





 

/* ###################################################################
**     Filename    : Events.c
**     Project     : CMSIS_example
**     Processor   : MKL25Z128VLK4
**     Component   : Events
**     Version     : Driver 01.00
**     Compiler    : GNU C Compiler
**     Date/Time   : 2015-02-07, 14:30, # CodeGen: 0
**     Abstract    :
**         This is user's event module.
**         Put your event handler code here.
**     Settings    :
**     Contents    :
**         Cpu_OnNMIINT - void Cpu_OnNMIINT(void);
**
** ###################################################################*/
/*!
** @file Events.c
** @version 01.00
** @brief
**         This is user's event module.
**         Put your event handler code here.
*/
/*!
**  @addtogroup Events_module Events module documentation
**  @{
*/
/* MODULE Events */

#include "Cpu.h"
#include "Events.h"
#include "arm_math.h"

#ifdef __cplusplus
extern "C"
{
#endif

/* User includes (#include below this line is not maintained by Processor Expert) */

extern uint8_t FIR_src_index;
extern uint8_t enable_output;
extern q15_t FIR_src[32];
extern q15_t FIR_dest[32];

PE_ISR(ADC0_Interrupt)
{
    static uint8_t dummy;

    // GPIOA_PTOR = (1<<2);

    // Normalize 0 <-> 255 to 65536 <-> 32768 (or in Q15: -0.99999 <-> 0.9999)
    dummy = ADC0_RA ;
    if (dummy > 126)
    {
        dummy -= 127;
        FIR_src[FIR_src_index] = (q15_t) (((int32_t) (dummy)) << 8);
    }
    else
    {
        dummy = 127 - dummy;
        FIR_src[FIR_src_index] = (q15_t) (((int32_t) (dummy)) << 8);
        FIR_src[FIR_src_index] += 0x8000;
    }

    FIR_src_index++;
    if (FIR_src_index >= 32)
    {
        ADC0_SC1A &= ~(ADC_SC1_AIEN_MASK);
        dummy = ADC0_RA;
        FIR_src_index = 32;
    }
}

PE_ISR(PIT_Interrupt)
{
    static uint8_t block_index = 0;
    static uint16_t dummy = 0;

    // GPIOA_PTOR = (1<<2);

    if (enable_output == 1)
    {
        // To modify the input signal, bypass the filter:
        //dummy = (uint16_t)(FIR_src[block_index]);
        // To use the filter, use filter output:
        dummy = (uint16_t)(FIR_dest[block_index]);
        if (dummy >= 0x8000)
        {
            dummy -= 0x8000;
            dummy >>= 8;
            dummy = (uint8_t) (127 - dummy);
        }
        else
        {
            dummy = (uint8_t) (dummy >> 8);
            dummy += 127;
        }
        // Uncomment below to double input signal amplitude.
        //dummy = dummy * 2 - 127;
        if (dummy > 255)
            dummy = 255;
        while (dummy != TPM1_C0V )TPM1_C0V = dummy;
        block_index++;
        if (block_index == 32)
        {
            block_index = 0;
            enable_output = 0;
            PIT_TCTRL0 &= (PIT_TCTRL_TIE_MASK);
        }
    }
    PIT_TFLG0 |= PIT_TFLG_TIF_MASK;
}

/* END Events */

#ifdef __cplusplus
} /* extern "C" */
#endif

/*!
** @}
*/
/*
** ###################################################################
**
**     This file was created by Processor Expert 10.3 [05.09]
**     for the Freescale Kinetis series of microcontrollers.
**
** ###################################################################
*/




Outcomes