ccircular-bufferdigital-filter

Circular Buffer Implementation for FIR Filter in C


I am programming on an embedded microcontroller (TMS320F28069) a 32-bit floating point MCU. I was going over some of the example projects, and one of them implements a simple FIR filter on ADC sampled data.

Block diagram here

Let's say the ADC buffer has 10 elements. And let's say the filter has length 3 (FILTER_LEN=3). The filter implementation is pretty straightforward, it starts at the end of the delay chain and moves its way to the beginning.

float32 ssfir(float32 *x, float32 *a, Uint16 n)
{

Uint16 i;                                   // general purpose
float32 y;                                  // result
float32 *xold;                              // delay line pointer

/*** Setup the pointers ***/
    a = a + (n-1);                      // a points to last coefficient
    x = x + (n-1);                      // x points to last buffer element
    xold = x;                           // xold points to last buffer element

/*** Last tap has no delay line update ***/
    y = (*a--)*(*x--);

/*** Do the other taps from end to beginning ***/
    for(i=0; i<n-1; i++)
    {
        y = y + (*a--)*(*x);            // filter tap
        *xold-- = *x--;                 // delay line update
    }

/*** Finish up ***/
    return(y);

}

Now, here is how the ADC circular buffer is implemented.

//---------------------------------------------------------------------
interrupt void ADCINT1_ISR(void)                // PIE1.1 @ 0x000D40  ADCINT1
{
static float32 *AdcBufPtr = AdcBuf;                 // Pointer to ADC data buffer
static float32 *AdcBufFilteredPtr = AdcBufFiltered; // Pointer to ADC filtered data buffer

    PieCtrlRegs.PIEACK.all = PIEACK_GROUP1;     // Must acknowledge the PIE group

//--- Manage the ADC registers     
    AdcRegs.ADCINTFLGCLR.bit.ADCINT1 = 1;       // Clear ADCINT1 flag

//--- Read the ADC result:
    *AdcBufPtr = ADC_FS_VOLTAGE*(float32)AdcResult.ADCRESULT0;

//--- Call the filter function
    xDelay[0] = *AdcBufPtr++;                   // Add the new entry to the delay chain
    *AdcBufFilteredPtr++ = ssfir(xDelay, coeffs, FILTER_LEN);

//--- Brute-force the circular buffer
    if( AdcBufPtr == (AdcBuf + ADC_BUF_LEN) )
    {
        AdcBufPtr = AdcBuf;                     // Rewind the pointer to the beginning
        AdcBufFilteredPtr = AdcBufFiltered;     // Rewind the pointer to the beginning
    }

}

xDelay is a float32 array of length FILTER_LEN initialized with 0s and coeffs is a float32 array of length FILTER_LEN initialized with the filter coefficients.

My question is, what is going on here:

//--- Call the filter function
    xDelay[0] = *AdcBufPtr++;                   // Add the new entry to the delay chain
    *AdcBufFilteredPtr++ = ssfir(xDelay, coeffs, FILTER_LEN);

How do values ever get stored in xDelay[1] or xDelay[2]? Their implementation seems to work fine, but I'm not following how old data gets pushed back in the xDelay array.


Solution

  • in the ssfir() function the following line shuffles the elements in the xDelay array

        *xold-- = *x--;                 // delay line update
    

    the line is in the for loop so the [1] element is copied to the [2] then the [0] element is copied to the [1] since the x and xold pointers decrement in spite of the for loop counting up