Hello there, fellow Arduino Programmers.
I spent my day (well, part of it at least) programming a FIR (or Finite Impuls Response) filter on the Arduino.
It can be used as a building block for any program that requires some form of filtering. Because of its simple nature, you may use this for various purposes:
- filtering audio (duh...)
- smoothing of analog inputs
- smoothing of PWM outputs (ramp up, and down of motors, gradually turning lights/LED's on and off)
- taking a 'running average' on streaming data
- removing 'DC' or static components of a signal (requires a 'high pass' or 'band pass' filter
I've written this to sharpen my limited C-programming skills, so there is probably lots of room for improvement. I hope people will criticize my code, so I can learn to write proper code, and optimize it. Once optimized, I hope this code could make it into the 'playground'. I guess if it handled INT's it would be more useful there.
I've commented the code extensively, so people could understand what it going on, and change it to their own needs.
/*
Simple FIR filter implementation
This routine is just written as an excersize in Arduino programming.
The routine implements a Finite Impuls Response filter.
Finite Impuls Response filters can be used to create all kinds of filters.
Filters can be used to partially remove noise, remove 'DC' offsets, or 'shape' analogue values. See http://en.wikipedia.org/wiki/Finite_impulse_response to read more aboute FIR filters. Several online sources are available to generate the coefficients. Google for the various sources.
About the routine: it is not really optimized for anything. It accepts exactly one float, spits out one float, and should be called for every sample. For the filter to work as expected, the samples should be 'equidistant' in time, so taken at identical intervals.
Making this an 'integer' function (to make it more usable with the onboard analog inputs) can be done. This would also limit execution time and memory usage.
Talking performance: using floats a 5 tap cycle takes about 180µs, so that is pretty fast and allows about 3~4kHz sample rate, taking some overhead in the main loop into consideration. Using ints or even better bytes should fraction the looptime by at least 4 to 8 times!
Rene Knuvers, 14JUL2010, arduino@reneknuvers.nl
*/
#define FILTERTAPS 5 // The number of taps in your filter, or better the number of coefficients
// declare array for values
float values[FILTERTAPS] = {0, 0, 0, 0, 0}; // to have a nice start up, fill the array with 0's
// NOTE: this could be 1 shorter, as we already have the input in a variable. This would save stack size/memory
// declare input and output variables
float input = 1; // without a real input, looking at the step respons (input at unity, 1) would be nice to see
float output = 0; // output as a 0, but that doesn't really matter
// our counter, real life applications won't need this
byte n = 0;
void setup() {
Serial.begin(115200); // open the serial port, I like them fast ;-)
}
float fir(float in){
static byte k; // k stores a pointer to create a circular memory through the array. This variable remains its value the next time we call the fir routine (hence 'static')
byte i = 0; // i is a counter to step through the filter coefficients and stored values
float out = 0; // out is the return variable. It is set to 0 every time we call the filter!
values[k] = input; // store the input in the array at the current pointer position
// declare variables for coefficients
// these should be calculated by hand, or using a tool
// in case a phase linear filter is required, the coefficients are symmetric
// for time optimization it seems best to enter symmetric values like below
float coef[FILTERTAPS] = { 0.021, 0.096, 0.146, 0.096, 0.021};
//declare gain coefficient to scale the output back to normal
float gain = 0.38; // set to 1 and input unity to see what this needs to be
for (i=0; i<FILTERTAPS; i++) { // we step through the array
out += coef[i] * values[(i + k) % FILTERTAPS]; // ... and add and multiply each value to accumulate the output
// (i + k) % FILTERTAPS creates a cyclic way of getting through the array
}
out /= gain; // We need to scale the output (unless the coefficients provide unity gain in the passband)
k = (k+1) % FILTERTAPS; // k is increased and wraps around the FILTERTAPS, so next time we will overwrite the oldest saved sample in the array
return out; // we send the output value back to whoever called the routine
}
void loop() {
// This is the loop that takes care of calling the FIR filter for some samples
for (n = 0; n < FILTERTAPS + 2; n++) { // If you like to see the step response, take at least as many cycles as the length of your FIR filter (FILTERTAPS + 1 or 2)
Serial.print("n= "); // print the sample number
Serial.println(n, DEC);
Serial.println("Now calling fir...");
output = fir(input); // here we call the fir routine with the input. The value 'fir' spits out is stored in the output variable.
Serial.print("fir presented the following value= ");
Serial.println(output); // just for debugging or to understand what it does, print the output value
}
while (true) {}; // endless loop
}
Please, feel free to comment and post alternatives!
Rene Knuvers
Goirle, The Netherlands
arduino@reneknuvers.nl