16.5.7 Channel Filter (Secondary Accumulator)

In Second Order Low Pass Filter Example, the second order low-pass filter is implemented using the second accumulator.

Second Order Low Pass Filter Example

#include <xc.h>
// VARIABLES OF THE SOFTWARE PART OF THE FILTER.
// These global variables are used in an interrupt.
// That's why they must be declared as "volatile".
// Input for the software part of the filter's first stage.
volatile long ch7_current_1 = 0;
// Input delayed for the first stage.
volatile long ch7_previous_1 = 0;
// Input for the software part of the filter's second stage.
volatile long ch7_current_2 = 0;
// Input delayed for the second stage.
volatile long ch7_previous_2 = 0;
// The filter output.
volatile long filtered_result = 0;
// Define peripheral clock frequency.
#define FCY (150000000UL) // 150MHz
// Define the CCP1 timer frequency.
#define TIMER_FREQUENCY (100UL) // 1kHz

int main()
{   
    //Set up clock for 40Msps operation
    clock_ADC_for_40Msps_from_PLL2();
    
    _TRISC8 = 0;
    _ANSELC8 = 0;
    
    // The device has 2 channels with the secondary
    // accumulator implemented: ## 6 and 7.
    // This example will use channel #7.
    // Enable accumulators roll-over to enable the secondary accumulator.
    AD1CH7CON2bits.ACCRO = 1;
    // Select integration sampling mode.
    AD1CH7CON1bits.MODE = 2;
    // CCP1 Timer starts conversions (1kHz frequency).
    AD1CH7CON1bits.TRG1SRC = 32;
    // CCP1 Timer re-triggers (1kHz frequency).
    AD1CH7CON1bits.TRG2SRC = 32;
    // Select the AN1 analog input/pin for the signal to be filtered.
    AD1CH7CON1bits.PINSEL = 1;
    // Select signal sampling time (6.5 TADs = 81nS).
    AD1CH7CON1bits.SAMC = 3;
    // Set number of conversions = 8 for the filter (sub-sampler).
    // The CH7RDY bit will be set after 8 conversions.
    // The conversions frequency is 1kHz defined by CCP1 Timer period.
    // The signal maximum frequency is in twice less = 500 Hz.
    // The filter cut-off frequency is 500kHz/8 = 62.5 Hz.
    AD1CH7CNT = 8;
    // Interrupt when AD1CH7DATA is updated
    AD1CH7CON1bits.IRQSEL = 1;
    // Set ADC to RUN mode.
    AD1CONbits.MODE = 2;
    // Enable ADC.
    AD1CONbits.ON = 1;
    // Wait when ADC will be ready/calibrated.
    while(AD1CONbits.ADRDY == 0);
    
    // Configure CCP1 Timer to trigger the channel # 7.
    CCP1CON1bits.MOD = 0;
    // Set 32-bit timer.
    CCP1CON1bits.T32 = 1;
    // Set period.
    CCP1PR = FCY/TIMER_FREQUENCY;
    // Run timer.
    CCP1CON1bits.ON = 1;
    
    // Enable channel # 7 interrupt.
    _AD1CH7IE = 1;
    // The AN1 pin filtered result is available in the channel # 19 interrupt.
    while(1);
    
    return 1;
}

// Channel # 7 interrupt.
// Called when integration is finished (every AD1CH7CNT = 8 conversions). 
void __attribute__((interrupt)) _AD1CH7Interrupt(){
    
    long primary_accumulator; 
    
    // Clear interrupt flag. If the interrupt is persistent then
    // to clear the flag it is required to read the ADC channel
    // result register first.
    primary_accumulator = AD1CH7DATA;
    _AD1CH7IF = 0; 
    
    // Process software part of the filter.
    ch7_current_1 = AD1CH7ACC;
    ch7_current_2 = ch7_previous_1 - ch7_current_1; 
    ch7_previous_1 = ch7_current_1;
    filtered_result = ch7_previous_2 - ch7_current_2;
    ch7_previous_2 = ch7_current_2;
    // Divide by 1:(8*8) or 1:64 or shift right by 6
    filtered_result >>= 6; 
    
    _LATC8 ^= 1;
}