16.6.8 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 ch6_current_1 = 0;
// Input delayed for the first stage.
volatile long ch6_previous_1 = 0;
// Input for the software part of the filter's second stage.
volatile long ch6_current_2 = 0;
// Input delayed for the second stage.
volatile long ch6_previous_2 = 0;
// The filter output.
volatile long filtered_result = 0;
// Define peripheral clock frequency.
#define FCY (4000000UL) // 4MHz
// Define the CCP1 timer frequency.
#define TIMER_FREQUENCY (1000UL) // 1kHz
int main() {
//Set up clock for 40Msps operation
clock_ADC_for_40Msps_from_PLL2();
_TRISC8 = 0;
// The device has 2 channels with the secondary
// accumulator implemented: ## 5 and 6.
// This example will use channel #6.
// Enable accumulators roll-over to enable the secondary accumulator.
AD1CH6CON2bits.ACCRO = 1;
// Select integration sampling mode.
AD1CH6CON1bits.MODE = 2;
// CCP1 Timer starts conversions (1kHz frequency).
AD1CH6CON1bits.TRG1SRC = 0b010011;
// CCP1 Timer re-triggers (1kHz frequency).
AD1CH6CON1bits.TRG2SRC = 0b010011;
// Select the AN1 analog input/pin for the signal to be filtered.
AD1CH6CON1bits.PINSEL = 1;
// Select signal sampling time (6.5 TADs = 81nS).
AD1CH6CON1bits.SAMC = 3;
// Set number of conversions = 8 for the filter (sub-sampler).
// The CH6RDY 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 500Hz/8 = 62.5 Hz.
AD1CH6CNT = 8;
// Interrupt when AD1CH6DATA is updated
AD1CH6CON1bits.IRQSEL = 1;
// Enable ADC.
AD1CONbits.ON = 1;
// Wait when ADC will be ready/calibrated.
while (AD1CONbits.ADRDY == 0);
// Configure CCP1 Timer to trigger the channel # 6.
CCP1CON1bits.MOD = 0;
// Set 32-bit timer.
CCP1CON1bits.T32 = 1;
// Set period.
CCP1PR = FCY / TIMER_FREQUENCY;
// Run timer.
CCP1CON1bits.ON = 1;
// Enable channel # 6 interrupt.
_AD1CH6IE = 1;
// The AN1 pin filtered result is available in the channel # 6 interrupt.
while (1);
return 1;
}
// Channel # 6 interrupt.
// Called when integration is finished (every AD1CH6CNT = 8 conversions).
void __attribute__((interrupt)) _AD1CH6Interrupt() {
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 = AD1CH6DATA;
_AD1CH6IF = 0;
// Process software part of the filter.
ch6_current_1 = AD1CH6ACC;
ch6_current_2 = ch6_previous_1 - ch6_current_1;
ch6_previous_1 = ch6_current_1;
filtered_result = ch6_previous_2 - ch6_current_2;
ch6_previous_2 = ch6_current_2;
// Divide by 1:(8*8) or 1:64 or shift right by 6
filtered_result >>= 6;
_LATC8 ^= 1;
}
