Hi, if anybody could assist with some issues i am having, whether that be code to try or a useful source, i would be very grateful. Here is the background information.
I am currently designing a DSP notch filter for use as a training aid for military electronics students. The idea is to have a conversation which cannot be heard due to a frequency/noise played at the same time, both audio signals will be input and the noise frequency will be taken out.
The audio signal goes into an anolgue circuit, is amplified and passed through three Low Pass filters to eliminate anything over 4KHz. Using a potential divider this signal is then set on a 2.5V centre.
The signal is read into the Arduino analogue pin 3, stored in a small array for use in the transfer function and output at digital pins 31-38.
The sampling rate has been changed and audio passes through to a speaker without error.
The issue is that the transfer function used does not remove the desired, or any other, frequency and i am unsure of the reason why.
Below is my setup code;
void setup() {
Serial.begin(115200); // setup serial
DDRC = 255; // set digital pins 31-38 as outputs
ADCSRA = 0; // clear ADCSRA register
ADCSRB = 0; // clear ADCSRB register
ADMUX |= (3 & 0x07); // set A3 analog input pin
ADMUX |= (1 << REFS0); // set reference voltage
ADMUX |= (1 << ADLAR); // left align ADC value to 8 bits from ADCH register
// sampling rate is [ADC clock] / [prescaler] / [conversion clock cycles]
// for Arduino Uno ADC clock is 16 MHz and a conversion takes 13 clock cycles
ADCSRA |= (1 << ADPS2) | (1 << ADPS0); // 32 prescaler for 38.5 KHz
// ADCSRA |= (1 << ADPS2); // 16 prescaler for 76.9 KHz
//ADCSRA |= (1 << ADPS1) | (1 << ADPS0); // 8 prescaler for 153.8 KHz
ADCSRA |= (1 << ADATE); // enable auto trigger
ADCSRA |= (1 << ADIE); // enable interrupts when measurement complete
ADCSRA |= (1 << ADEN); // enable ADC
ADCSRA |= (1 << ADSC); // start ADC measurements
}
And the following code is the interrupt i am using to read the input values and attempt the transfer function.
ISR(ADC_vect)
{
double in[3]; // Arrays for read values
double out[3];
numSamples++;
in[2]=in[1];
in[1]=in[0];
in[0] = ADCH; // read 8 bit value from ADC
out[2]=out[1];
out[1]=out[0];
out[0] = ((in[0] * b0) + (in[1] * b1) + (in[2] * b2) - (out[1] * a1) - (out[2] * a2)); //Transfer Function
//out[0] = in[0];
PORTC = out[0]; //Outputs digital signals on Port C
}
I have tried several sources which unfortunately as a beginner i do not have the background knowledge to understand or implement what i have seen.
Any help would be appreciated.
Thank you in advance.
The DSP book i have out but just confirms the maths i have done to find the multipliers for my transfer function using the z transform method.
Just looking into the line you pointed out ADMUX |= (3 & 0x07); checking against data sheets to check for potential problems although struggling to realise any as audio is sampled and output correctly.
(Apologies if this is difficult to interpret, the first time i touched programming and Arduino was two weeks ago)
Is not reading an 8 bit value, it is reading the top two bits from the 10 bit A/D converter.
Next all that floating point multiplication in the DSP function is going to take time, have you ensured that this is not slowing down the real sample rate you are getting? A DSP design needs a specific sample rate to work on.
Finally when implementing a DSP filter it is normal to use a ring buffer with a pointer to it rather than shuffle the numbers about in an array.
int analogPin = 3;
#define a1 -1.334517808
#define a2 0.959355045
#define b0 0.980128067
#define b1 -1.335418897
#define b2 0.980128067
// fixed multipliers for 15.3KHz
volatile int numSamples = 0;
long t, t0;
void setup() {
Serial.begin(115200); // setup serial
pinMode(13, OUTPUT);
DDRC = 255; // set digital pins 31-38 as outputs
ADCSRA = 0; // clear ADCSRA register
ADCSRB = 0; // clear ADCSRB register
ADMUX |= (3 & 0x07); // set A3 analog input pin
ADMUX |= (1 << REFS0); // set reference voltage
ADMUX |= (1 << ADLAR); // left align ADC value to 8 bits from ADCH register
// sampling rate is [ADC clock] / [prescaler] / [conversion clock cycles]
// for Arduino Uno ADC clock is 16 MHz and a conversion takes 13 clock cycles
ADCSRA |= (1 << ADPS2) | (1 << ADPS0); // 32 prescaler for 38.5 KHz
// ADCSRA |= (1 << ADPS2); // 16 prescaler for 76.9 KHz
//ADCSRA |= (1 << ADPS1) | (1 << ADPS0); // 8 prescaler for 153.8 KHz
ADCSRA |= (1 << ADATE); // enable auto trigger
ADCSRA |= (1 << ADIE); // enable interrupts when measurement complete
ADCSRA |= (1 << ADEN); // enable ADC
ADCSRA |= (1 << ADSC); // start ADC measurements
}
ISR(ADC_vect)
{
double in[3]; // Arrays for read values
double out[3];
numSamples++;
in[2] = in[1];
in[1] = in[0];
in[0] = ADCH; // read 8 bit value from ADC
out[2] = out[1];
out[1] = out[0];
//out[0] = ((2*in[0])+(0.2*in[1])+(0.6*out[1])); //Test TF for LPF
out[0] = ((in[0] * b0) + (in[1] * b1) + (in[2] * b2) - (out[1] * a1) - (out[2] * a2)); //Transfer Function
//out[0] = in[0];
PORTC = out[0]; //Outputs digital signals on Port C
//digitalWrite(13, !digitalRead(13));
}
void loop() {
// if (numSamples >= 1000) {
// t = micros() - t0; // calculate elapsed time
//
// Serial.print("Sampling frequency: ");
// Serial.print((float)1000000 / t);
// Serial.println(" KHz");
// delay(1000);
//
// // restart
// t0 = micros();
// numSamples = 0;
// }
}
I used the loop to check the sampling speed which constantly read 15.3kHz. Hopefully this is correct as i found the example along with the code to speed up the sampling rate at the following website. http://yaab-arduino.blogspot.com/2015/02/fast-sampling-from-analog-input.html
The audio passes through a signal conditioning circuit which consists of an amplifier and LPF, this is then passed into the arduino through analogue pin 3. After the Arduino output i have a DAC which is a R2R ladder which feeds into a buffer and then the speaker. We are also monitoring the input and output on an oscilloscope and spectrum analyser. A signal generator is used as an input for testing.
Hopefully this answers all of your questions, apologies for the ambiguity.
Appreciate the help.
Your filter coefficients were calculated for 15.3 KHz but you are doing your filter calculations in the main loop where your sample time is not defined. I would have kept the filter calculations in your ADC ISR and adjusted the ISR period (with the ADPS[0:2] bits) and your filter coefficients to match in frequency. A less elegant way to do it is to do the calculation in the main loop, record the time you do the calculation and constantly check if it is time to do the next calculation using micros().
Thanks for the tip, i'll give it a try Monday once i have access to the kit.
Don't worry about ADCH, since you set ADLAR:
ADMUX |= (1 << ADLAR); // left align ADC value to 8 bits from ADCH register
Everything else is wrong, your code is never gonna work as intended. Here is some steps, you need to take:
Sampling must be precise, there are two options - free running adc or adc conversion started by Timer. First option is easier to implement, but minimum frequency is 9.6 kHz, and if arduino can't do a math in the same speed, would be a problems.
You always can control sampling rate setting couple digitalWrite's in the ISR, than driving high/low any available digital pin and observing on a scope if it's correct.
Measure how long does it take for arduino to do a math. Put some dummy data array, than measure micros() before and after processing lines of code, see what you get. Basically, arduino is very slow with floating point math, better to re-write your filter in integer's.