Go Down

Topic: Digital Filter Implementation (Read 2516 times) previous topic - next topic

bhargava

Hi All,

I am trying to use the arduino duemilanove board to perform some real time digital filtering. I intend to read-in an analog signal using the on-board ADC, perform some filtering operations on the digitized signal and then use an external serial DAC (LTC 1660CN) to output the filtered analog signal. Thus far, I have been able to read in an analog signal and use the DAC to output it back. In order to have a fixed sampling rate, I am using an interrupt based program to perform the ADC/DAC operations. Using the Timer2 Overflow Interrupt, I was able to get a sampling rate of 13.9 KHz. I would like to push this higher. I tried using the Timer in CTC mode, to enable faster triggering of the interrupts. I also tried to load TCNT2 register with a number <255 (eg. 128 so it takes half as much time to trigger the interrupt), but both approaches give me the same sampling rate. I am not sure what I am missing. Below is my code.



// Program Arduino board to sample an analog signal
// and output an analog signal after Digital to Analog Conversion

#define ANIN 2    // analog input
#define CS_LD 7    // Enable pin for DAC
#define SCK 8      // Clock input to DAC
#define DATA 9     // Serial data input to DAC

// defines for setting and clearing register bits

#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))

// Variables to hold sampled signal and digital number to write.

uint16_t valadc;
uint16_t valdac;

void setup()
{

 // DAC pin definitions
 
 pinMode(DATA, OUTPUT);
 pinMode(SCK,OUTPUT);
 pinMode(CS_LD,OUTPUT);
 digitalWrite(DATA,LOW);
 digitalWrite(SCK,LOW);
 digitalWrite(CS_LD,LOW);
 
 // Timer 2 settings
 
 TCCR2A = 0;

 // Timer2 Clock Prescaler to : 1

   sbi(TCCR2B,CS20);
   cbi(TCCR2B,CS21);
   cbi(TCCR2B,CS22);
   
   OCR2A = 2;
   
 //Timer2 Overflow Interrupt Enable  
 
 //TIMSK2 = 1<<TOIE2;
 
 // Timer compare interrupt enable
 
 TIMSK2 = (1<<OCIE2A);
   
 //Set counter value to 128
 
 //TCNT2 = 128;

 // set ADC prescale to 16 to enable fast sampling
 
 sbi(ADCSRA,ADPS2) ;
 cbi(ADCSRA,ADPS1) ;
 cbi(ADCSRA,ADPS0) ;

}

// Interrupt Service Routine for Timer2

//ISR(TIMER2_OVF_vect) {
 ISR(TIMER2_COMPA_vect) {
 
 valadc = analogRead(ANIN);
 valdac = valadc;
 
 // We perform digital read/write operations using PORT manipulations.
 // as described in http://www.arduino.cc/en/Reference/PortManipulation
 // Here the seventh bit of PORTD is the digital I/O pin 7 which is the CS_LD
 // pin of the DAC
 
 cbi(PORTD,7);
 
 //load address of DAC (4 bits)
 
 for (int i=2;i>=0;i--){

   // Pin 0 of PORTB is digital I/O 8 and Pin 1 is D I/O 9
   // which are clock and data inputs to the DAC respectively.
   
   cbi(PORTB,1);
   sbi(PORTB,0);
   cbi(PORTB,0);
   
   // Alternatively,
   
   //    PORTB &= B01;   //data = 0
   //    PORTB |= B01;   //clock = high
   //    PORTB &= B00;   //clock = low
   
   // or
   
   //digitalWrite(DATA,0);
   //digitalWrite(SCK,HIGH);
   //delayMicroseconds(HALF_CLOCK_PERIOD);
   //digitalWrite(SCK,LOW);
   //delayMicroseconds(HALF_CLOCK_PERIOD);
   
 }
   
   sbi(PORTB,1);
   sbi(PORTB,0);
   cbi(PORTB,0);
 
 //send the 10 bit data word to the DAC
 
 for(int i=9;i>=0;i--){

   if ( ((valdac&(1<<i)))>>i ){
     sbi(PORTB,1);
   }
   else{
     cbi(PORTB,1);
   }
   
   sbi(PORTB,0);
   cbi(PORTB,0);    
 }
 
 //write don't care bits (2 bits)
   
   cbi(PORTB,1);
   sbi(PORTB,0);
   cbi(PORTB,0);
   
   cbi(PORTB,1);
   sbi(PORTB,0);
   cbi(PORTB,0);

 //latch enable, DAC output is set

   sbi(PORTD,7);
   
   //TCNT2 = 128;
   OCR2A = 2;

}

void loop(){
 ;
}



Drogman

#1
Oct 15, 2009, 01:27 pm Last Edit: Oct 15, 2009, 01:28 pm by Drogman Reason: 1
Why not use the analogue conversion complete interrupt? That way it will run at the fastest possible speed.

You will need to write your own code to read the ADC hardware inside the interrupt code, but copy the few lines from wiring.c - and after you do a read, you need to trigger a conversion to get the next interrupt.

You'll also have to trigger a conversion to make it do the very first interrupt.

The default sample speed is Clock / 128 which is 125K samples a second. You can make it sample faster but it loses accuracy and gains noise.

So:

Setup interrupt handler.
Initialise analogue converters (actually already done in init())
Trigger first conversion. (with  ADCSRA |= _BV (ADSC) ; )

Inside the interrupt handler it:
 Reads the value (read low byte first, then high)
 Triggers the next conversion
 Processes the value, sends it to the DAC, etc.

That way the next conversion is happening behind the scenes while you are processing the current one.

If you wanted to eek out a few more cycles then you could put it into free-running mode, but you may not need it.

Good luck!

Go Up