NRF25 using Butterworth bandpass filter for ECG by SAADC

HI everyone, i have a problem when implementing Butterworth bandpass filter in SAADC module to cancel out the noise of our ECG module.

I used " nrf_saadc_value_t m_buffer_pool[2][SAMPLE_BUFFER_LEN] " and " nrfx_saadc_buffer_convert(m_buffer_pool[0], SAMPLE_BUFFER_LEN) "

As far as i had known, the VOLTAGE signal of the analog input will be transfered to the memory of MCU and converted to an appropriate level of BITS, then these value will be stored in registers in terms of BITS. I used a software filter based on the code on this Github link: github.com/.../filter-c

My question is: How can the Butterworth filter cancel out the noises and only select the band of desirable frequency for my project. Does it work only on frequency generated by the electrical voltage Or it can also filter the BITS value in the memory?

Thanks in advance.

  • Hello,

    according to the example.c on the github page that you linked it should work like this:

    1. you create a filter with the desired parameters like this:

    BWBandPass* filter = create_bw_band_pass_filter(4, 250, 2, 45);

    (in this example it would be a 4th order band pass filter with a sampling rate of 250 Hz with a frequency band of 2 ... 45 Hz)

    2. assuming you have one buffer for the unfiltered data from the ADC (let's call that buffer "unfiltered") and one buffer to store the filtered data in (called "filtered"), and both are SAMPLE_BUFFER_LEN long, you can apply the filter to the data like this:

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

       filtered[i] = bw_band_pass(filter, unfiltered[i]);

    }

    3. when you are done with filtering you can dispose of the filter with:

    free_bw_band_pass(filter);

    4. Now the "filtered" array will contain the resulting signal, without noise.

    I tried the filter above with a synthetic test signal that contains a 25Hz sine wave with some 100 Hz noise and a DC offset. The result looks like this: (upper half is the original signal with distortions, lower half is the output of the filter)

    As you can see, the noise and the DC offset is gone in the filtered signal, so the filter seems to be doing what it's supposed to do.

    Best regards

    Frank

  • one buffer to store the filtered data

    can you please explain more about this one? Do i have to create another timer handler to fill the filtered data into?

  • Well, that was just an example how the filter can be used. If you really need another buffer depends on what you want to do with the filtered data. Do you need to store it, or do you just need to do some calculations on the result?.

    Can you be a bit more specific on how you want to process the data? Is there some interrupt or event triggered when the sampling is finished? Maybe you even have some example code that you can share?

    Best regards

    Frank

  • Hi,


    I think you will have to contact the author for the filter you are using. 

    But since the filter seems to be only a software filter then there is some computation here that will change the digital values, and not the analog values. So the filter that you are pointing to is not a physical filter that effects the voltage level or frequency of the incoming signal before it is translated to digital values and stored in the memory. 

    Regards,
    Jonathan

  • My project is to read data from ECG sensor, i used butterworth filter to cancel the high and low noises frequency, i mean not the bandpass but the low pass first to cancel out the 0.5Hz noise, then i the high pass to cancel out the 45Hz noises.

    i have some troubles when doing my code part

    this is my code:

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    #define SAMPLE_BUFFER_LEN 5 


    static nrf_saadc_value_t m_buffer_pool[2][SAMPLE_BUFFER_LEN];

    static const nrf_drv_timer_t m_timer = NRF_DRV_TIMER_INSTANCE(1);

    static nrf_ppi_channel_t m_ppi_channel;

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    void timer_handler(nrf_timer_event_t event_type, void * p_context)
    { }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


    void timer_with_ppi_init(void)
    {
    ret_code_t err_code;

    err_code = nrf_drv_ppi_init();
    APP_ERROR_CHECK(err_code);

    nrf_drv_timer_config_t timer_cfg = NRF_DRV_TIMER_DEFAULT_CONFIG;
    timer_cfg.bit_width = NRF_TIMER_BIT_WIDTH_32;

    err_code = nrf_drv_timer_init(&m_timer, &timer_cfg, timer_handler);
    APP_ERROR_CHECK(err_code);

    uint32_t ticks = nrf_drv_timer_ms_to_ticks(&m_timer, 2);

    nrf_drv_timer_extended_compare(&m_timer, NRF_TIMER_CC_CHANNEL0, ticks, NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK, false);

    nrf_drv_timer_enable(&m_timer);

    uint32_t timer_compare_event_addr = nrf_drv_timer_compare_event_address_get(&m_timer, NRF_TIMER_CC_CHANNEL0);

    uint32_t saadc_sample_task_addr = nrf_drv_saadc_sample_task_get();

    err_code = nrf_drv_ppi_channel_alloc(&m_ppi_channel);
    APP_ERROR_CHECK(err_code);

    err_code = nrf_drv_ppi_channel_assign(m_ppi_channel, timer_compare_event_addr, saadc_sample_task_addr);
    APP_ERROR_CHECK(err_code);
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


    void saadc_callback_handler(nrf_drv_saadc_evt_t const * p_event)
    {
    float val;

    if(p_event -> type == NRFX_SAADC_EVT_DONE)
    {
    ret_code_t err_code;

    err_code = nrfx_saadc_buffer_convert(p_event -> data.done.p_buffer, SAMPLE_BUFFER_LEN);
    APP_ERROR_CHECK(err_code);

    for(int i = 0; i<SAMPLE_BUFFER_LEN; i++)
    {
     NRF_LOG_RAW_INFO("%d\n\r", p_event -> data.done.p_buffer[i]);

    }

    }
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


    void saadc_init(void)
    {
    ret_code_t err_code;

    nrf_saadc_channel_config_t channel_config = NRFX_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN2);

    err_code = nrf_drv_saadc_init(NULL, saadc_callback_handler);
    APP_ERROR_CHECK(err_code);

    err_code = nrfx_saadc_channel_init(0, &channel_config);
    APP_ERROR_CHECK(err_code);

    err_code = nrfx_saadc_buffer_convert(m_buffer_pool[0], SAMPLE_BUFFER_LEN);
    APP_ERROR_CHECK(err_code);

    err_code = nrfx_saadc_buffer_convert(m_buffer_pool[1], SAMPLE_BUFFER_LEN);
    APP_ERROR_CHECK(err_code);
    }

Related