Alternatives to timer interupt

Go To Last Post
54 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello,

 

I've tried writing a 4th order IIR filter for the ATXMega256a3u.

 

Code shown below.

https://pastebin.com/35dSEeJ6

It works at 32MHz and should interupt 44.1K times every second as that is the sample rate of said filter.

However i've been told that using a timer interupt is basically a waste of time as it will never reach the speed that i need.

 

So i'm wondering what would be some alternatives to the timer interup to implement the sample rate of my filter?

Would an adc interupt work perhaps?

 

Kind regards Chris

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Poll? Use a timer flag and check it's state regularly?

 

Jim

 

 

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Maybe what Jim is stating is

 

volatile uint8_t go_for_it;

ISR(TCE0_OVF_vect) {
    go_for_it = 1;
}


void foo(void) {
{
    double  voltage ;                                               // contains read in voltage (mVolts)
    int16_t res;                                                    // contains read in adc value
    int16_t BinaryValue;                                            // contains value to write to DAC

    ...
    
    while (!DACB.STATUS & DAC_CH0DRE_bm);
}

int main() {
    go_for_it = 0;
    ... set up timer ...
    while (1) {
        while (! go_for_it) { } /* wait */
        foo();
        go_for_it = 0;
    }
}

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

No that is not what Jim mean !

 

If you have a ISR then just put the code there!

 

the wait while should be on the timer overflow bit.  (but not with timer ISR bit enabled).

(when you do it this way remember that you have to clear the flag your self (where the ISR normally do it for you)) 

 

Last Edited: Sun. Jun 9, 2019 - 09:13 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

However i've been told that using a timer interupt is basically a waste of time as it will never reach the speed that i need.

Welcome to the Forum.

 

I didn't look at your program as I don't read C.

 

That said, processing data at 44.1 K Samples/Sec is challenging with a 32 MHz micro.

Whether or not it will work depends upon how complex the filter calculations are, plus the overhead for the User Interface, and finally doing something with the data, (export or whatever). 

 

Typically, with a digital filter you truly want a precise and fixed sampling rate, (what all the digital filter algorithms are based upon).

So Using an ISR to read an ADC sample at a fixed rate is a good approach.

ISR fires, you read the last ADC sample, stuff it in a register or variable for the Main Loop to use for processing, and then trigger the ADC to obtain the next sample, and then exit the ISR.

The ADCc is then busy processing the next sample in hardware while the Main Loop is processing the data in software.

 

The kicker is that 44.1 K S/sec gives about 725 clock cycles between each ISR firing.

Most of the micro's instructions take 1 or 2 clock cycles.

The ISR will usually have to do some pushes and pops at the start and end of the ISR, to save and restore some registers, so each ISR has some overhead associated with it, which will use up some of the 725 clock cycles available.

 

So you need to be cognizant of how long it takes, (i.e. how many clock cycles), to process the data obtained for each ISR firing.

Fortunately, an IIR filter often isn't too computationally intensive, so you can likely pull this off just fine.

 

Recall, however, that you still need a few clock cycles for the UI and then doing something constructive with the filter output once you have it

 

You can use the simulator to count clock cycles for blocks of code.

 

Or you can set an I/O pin high on entering the ISR, and low on exiting the ISR, and see visually how much time the micro is spending within the ISR.

(In this case, with a fixed rate firing ISR, the ISR time is analogous to a PWM duty cycle.)

With this approach it is easy to see what proportion of the periodic interrupt cycle is devoted to the ISR, vs to the Main loop, but note that the ISR's pushes and pops are not included in the ISR pin high time.

The ISR does the initial pushes, then sets the pin high, runs your code, then sets the pin low, and then does the pops.

 

Before you can decide that you need to use some other approach, I think it is wise to determine the performance of your current approach, and see if it needs to be improved or not.

Know, also, that there are obviously different IIR filter implementations, depending upon whether it is mimicking a Butterworth Filter, Cheby Filter, or something else.

Sometimes there are good reasons for selecting a particular approach, sometimes you can look at the ease of filter implementation as a factor of filter selection.

 

If you find your self processor throughput limited with the Xmega, then know that MicroChip has their line of DSP Pics that are optimized for digital signal processing, and with which one might be confronted with less throughput bottlenecks.

 

JC 

 

Edit:Typo

 

Last Edited: Sun. Jun 9, 2019 - 10:04 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

This might be for a Mega328 Timer1 OCR1A compare event and one way to poll.

 

int main( void )  {
    .....
    
    while (1) {
        ....
        if (TIFR1 & (1<<OCF1A)) {

            //do the timer event stuff

        }  //end OCRA1 compare event

        ...

    }  //end while(1)

} //end main()

Another way might be

 

int main( void )  {
    .....
    
    while (1) {
        ....
        while (!(TIFR1 & (1<<OCF1A))) {}  //wait for compare event
        
        //do the timer event stuff
        
        ...

    }  //end while(1)

}  //end main()

 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

@docjc it's an iir filter that uses the chebyshev response.
As for wether it reaches the required speed right now while using a timer overflow interupt i was told to just discard that option i could however still test it but i doubt it will work due to the advice ive been given

Polling might be an option. I havent done this before so i will look into it

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Filters often require floating point, and that will be bad, at any speed, for a device with no math processing unit.

 

Jim

 

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Its done in fixed point if you look at the code. As for what it does, is read adc apply the filter and output it on the dac. Nothing else

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

@ka7hehk

 

With you polling solution how would i decide the sample rate?

 

    while (1) {
        ....
        if (TIFR1 & (1<<OCF1A)) {

            //do the timer event stuff

        }  //end OCRA1 compare event

I'm guessing TIFR1 is a timer interupt flag on R1 ? and outputcompare flag on 1A? 

Would you mind adding to your explanation?

- Chris

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If I understand the alternative being suggested, you would process your data and then sit in a loop watching for the Timer flag to signal that it is time to get the next sample.

The Timer/Counter again determines the sampling rate, although instead of it triggering an ISR, the code watches for it and then reads the sample and triggers the next ADC read, and then processes the data.

 

That would save you the overhead inherent to the ISR, the register pushes and pops.

 

That saves you a "few" clock cycles per ISR.

(How many likely depends upon your specific ISR code and the specific compiler being used.)

 

I mentioned before, it isn't clear to me why you are changing methodology when you have no hard data measurement upon which to determine if changing is necessary or not.

Someone "told you" to change, but they may or may not have a solid foundation for making that recommendation.

 

There have been other Threads about how much overhead, (how many clock cycles), it takes to enter and exit an ISR.

Looking at the program listing should give a good idea, or using the simulator to count the clock cycles would work.

 

If your current method almost works then saving 40ish clock cycles (?) might make a difference and make the alternative mode work.

 

But if the timing is that tight, then you don't have many clock cycles for the User Interface and stuffing the ADC.

 

JC

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The person that told me is familiar with the software and also the one that fabricated the board for me. Which is why i trust it when he says that it will not work with a timer overflow interup which i am using right now.

 

Also the only thing the Xmega needs to do is read the adc and apply the filter then put it on the dac. there will be nothing else. No interface or anything.

 

So if i understand correctly it would like something like this in pseudo code:

 

While (1)

  if counter is some value

  Do filter stuff

  reset counter value to do next stuff

 

- Chris

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Polling may be somewhat jittery & give poor results...you want the sample time as exact as possible.  Doing all the compares to see if it is the correct time must be have the same lag for each "passing" compare.

You can minimize the amount of pushing & popping waste, if you are careful.  Let only the sample register be shared with the main, (or the results value if the filter is part of the irq).  Truly, only the status register needs saved & any shared registers.  Telling the compiler your intentions can  be the headache.

 

====

notice you duplicate some variables & array element moves  (yA & xB)....you can speed things up by doing this once, instead of doing the same thing twice with different arrays.

 

  1.  yA[2] = yA[1];

  2.     yA[1] = yA[0];

  3.     yA[0] = y2;    // >> 14

  4.     // x_in of filter 2 is output of  filter 1.

  5.     xB[2] = xB[1];

  6.     xB[1] = xB[0];

  7.     xB[0] = y2; 

 

It may be the compiler is smart enough to detect this duplication & apply some optimization for you (it would have to go & research the initial values).....but I never count on that, if I can do it myself & assure is was done.

 

===============

 

 

Do you absolutely need such a fast sampling rate?

 

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Option 1:

Using Interrupts at a fixed rate, (44.1 K Samples/Sec)

 

Do
 	Do
		Read Flag that indicates there is a new value
		(Micro spends its spare time here)
	Loop
	Reset Flag
	Run Filter Algo
 	Update DAC with new filter output value
Loop

ISR:
	(Invisible to you, done by compiler:  Push Register X, Push Register Y, Push Register Z...)
	Read last ADC value and assign to a variable
	Trigger next ADC reading, (start ADC)
	Set a SW Flag that indicates the ISR generated a new data value to process
	(Invisible to you, done by compiler: Pop Z, Pop Y, Pop X...)
	Return

  

Option 2:

Using a Timer/Counter, again set for 44.1 K events/Second, but don't actually trigger an ISR

Do
 	Run Filter Algo
	Update DAC with new filter output value
	Do
		Poll, (Continuously read), the T/C register awaiting it to indicate
		that it is time to get another ADC value
		(Micro spends its spare time here)
	Loop
	Read last ADC value and assign to a variable
	Trigger next ADC reading, (start ADC)
Loop

 

 

You will have to look at the T/C options you have.

Sometimes Overflow isn't the best, as it might be hard to set it to 44.1 K Events/Sec.

You can use a different method to obtain more precise timing if needed.

Details depend upon the specific T/C being used.  

 

Alternatively, one could read the T/C value, if > X then time to get the next value

Then reset the T/C to 0

 

Alternatively, one could read the T/C value, if > X then time to get the next value

Calculate the next T/C value, (instead of resetting it to 0)

 

Other options exist, also.

 

Both techniques are actually very similar.

 

JC

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Not to be critical, because II am not, but I really do wonder why you need such a complex filter. What does Chebyshev give you in your application that, lets say, a 4-sample running average does not? N-sample running average just takes N bins (or a circular buffer). Sum N, right shift N times. Done. Take those N stored samples, shift the oldest one out, shift the newest one in, copy the sum to a working variable, Right shift N times. Done. Wash and repeat. You could easily do that in well less than 750 clock cycles. 

 

Yes, I know that Chebyshev gives you the steepest rate of attenuation with frequency, but what do you get from that?

 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

@ka7ehk 

 

This filter is to learn how to make a digital filter more or less the same as the analog counter part and to learn timing critical implementations.

I've previously made other filters like the one you suggested but sadly that is not part of the assignment this time.

 

@docjc 

 

I've previously used timers like this simple example.

#define  F_CPU 2000000UL
#include <avr/io.h>

int main(void)
{
  PORTC.DIRSET  = PIN0_bm;

  TCE0.CTRLA    = TC_CLKSEL_DIV8_gc;    // prescaling 8
  TCE0.PER      = 0XFFFF;               // maximal value

  while (1) {
    if ( TCE0.CNT >= 62500 ) {          // 8*62500/2000000 = 0.25 s
      PORTC.OUTTGL = PIN0_bm;
      TCE0.CNT = 0;
    }
  }
}

Having the timer count to a certain value which would correlate with the samplerate and then resetting it to 0 might be an option to reach the desired samplerate.

I'll have to look into the accuracy of the timer then.

 

- Chris

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

What do you mean when you say im duplicating values?

As xB,xA,yA,yB are the delay arrays for the filter coefficients and these values change. 

Im useing 4 arrays as its a cascaded filter using 2 2nd order sections.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

What do you mean when you say im duplicating values?

I'm using lazy typing here, but you have a 3 value "shift register:

 ya2=ya1  ya1=ya0  ya0=y2     y2 is the input

 xb2=xb1  xb1=xb0  xb0=y2     y2 is the input

 

After 3 samples they will have the exact same values (regardless of any initial starting values)...hence rather than waste time manipulating 2 sets of values (doing exact same thing twice)-- use only one set of values.   I am sad you didn't see this when I mentioned it.

 

Admittedly, this would be more important if you had other big business going on & had to shave time used up by the filtering.   Anything that is repeating that often can rapidly eat  into 32 million instructions a second.  Use your grains of sand wisely!

 

==========

You may as well put your filter in the IRQ & spit out the new DAC value there...since that is all you are doing & it makes things rather straightforward.  My mention for polling was this (an extreme example)...say it took 15 instructions to extract the timer bytes, do multi-compares, branches & reset the timer).  Since you don't know where you are in this 15 instruction loop at the moment of the event, there will be up to xx instruction jitter in the response. The worst if you just barely barely missed it while in the midst of the compares & don't detect until the next poll loop.  The irq doesn't have this issue, but does have a very slight latency uncertainty. 

 

 

 

 

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

CHrYZoR wrote:
Polling might be an option.
and DMA and events (XMEGA DMAC, XMEGA EVSYS)

AN2535 AVR1300: Using the XMEGA ADC | Application Notes | Microchip Technology Inc.

(PDF, page 25)

3. Advanced Features

...

3.1 DMA Controller

...

3.2 Event System

...

via ATxmega256A3U - 8-bit AVR Microcontrollers

 

"Dare to be naïve." - Buckminster Fuller

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

DocJC wrote:
If you find your self processor throughput limited with the Xmega, then know that MicroChip has their line of DSP Pics that are optimized for digital signal processing, and with which one might be confronted with less throughput bottlenecks.
likewise for PIC32 and SAM.

PIC32 Audio Decoders | Microchip Technology

Opus Codec is somewhat recent to PIC32; Opus is in C89 so it's mostly portable.

On what platforms does Opus run?

 

Infinite Impulse Response (IIR) Lattice Filters (arm)

 

"Dare to be naïve." - Buckminster Fuller

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

CHrYZoR wrote:
As for what it does, is read adc apply the filter and output it on the dac. Nothing else

 

Nothing else? Ever?

 

If your finished code is only going to do is this then you don't need interrupts at all.

 

Set your ADC conversion rate to 44k14 and in main() you then...

 

poll on ADC ready

output previous result to DAC

calculate new DAC value via the filter

#1 This forum helps those that help themselves

#2 All grounds are not created equal

#3 How have you proved that your chip is running at xxMHz?

#4 "If you think you need floating point to solve the problem then you don't understand the problem. If you really do need floating point then you have a problem you do not understand." - Heater's ex-boss

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Jup thats all it does. Aparr from the very first time where it will initialize the dac and adc and maybe a timer.. Afterwards it does only that.

I didn't know you could set the adc conversion rate like that. Do you happen to have an example?

 

 

 

EDIT: This is how i've done it right now

 

void init_adc(void)
{
    PORTA.DIRCLR     = PIN2_bm|PIN3_bm;
    ADCA.CH0.MUXCTRL = ADC_CH_MUXPOS_PIN2_gc |                // PA2 (PIN A2) to + channel 0
    ADC_CH_MUXNEG_GND_MODE3_gc;                                // GND to - channel 0
    ADCA.CH0.CTRL    = ADC_CH_INPUTMODE_DIFF_gc;            // channel 0 differential
    ADCA.REFCTRL     = ADC_REFSEL_INTVCC_gc;
    ADCA.CTRLB       = ADC_RESOLUTION_12BIT_gc |
    ADC_CONMODE_bm;                                            // signed conversion
    ADCA.PRESCALER   = ADC_PRESCALER_DIV16_gc;
    ADCA.CTRLA       = ADC_ENABLE_bm;
}

int16_t read_adc(void)            // return a signed
{
    int16_t res;                                           

    ADCA.CH0.CTRL |= ADC_CH_START_bm;
    while ( !(ADCA.CH0.INTFLAGS & ADC_CH_CHIF_bm) ) ;
    res = ADCA.CH0.RES;
    ADCA.CH0.INTFLAGS |= ADC_CH_CHIF_bm;

    return res;
}

 

I believe to set the frequency to 44.1K, i would need to use the adc free run mode as well.

 

These are the features:  Features

 12-bit resolution

 Up to two million samples per second

 Two inputs can be sampled simultaneously using ADC and 1x gain stage

 Four inputs can be sampled within 1.5µs

 Down to 2.5µs conversion time with 8-bit resolution

 Down to 3.5µs conversion time with 12-bit resolution

 

Using 32mhz and a prescaler of 256 would give me an ADC frequency of 32.000.000/256 = 125.000

32000000*(3.5*10^-6) would give me 112 samples per 3.5µs

 

125 000 / 112 would then be about 1100 samples

 

This seems a little low so i dont think my math is right

 

 

 

Last Edited: Mon. Jun 10, 2019 - 10:02 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

CHrYZoR wrote:
It works at 32MHz and should interupt 44.1K times every second

 

No of Cycles Available = 32E+6 / 44100 = 725 cy

 

I broke down your code into operatons:

 

5 x 32bit MultiplyAccumulate (MAC) {Y}

5 x 32bit MAC {Y2}

12 x 32bit move

4 x 32bit shift

fp_add + fp_mult + fp_div {voltage}

fp_mult {BinaryValue}

 

Oh Dear - I didn't complie it and count cycles but even without the floating point stuff I think you are slightly beyond the cycle count limit. The floating point stuff will kill performance massively.

 

Minor tinkering to replace the floats with integer maths may not solve this one. Time to move up to a 32-bit processor with hardware floating point.

 

{Edit}

Looking again, your delay pipelines may not need 32-bits but that only affects the moves. It's something to try though.

 

Last Edited: Mon. Jun 10, 2019 - 11:15 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yes i noticed that as well after ur comment after having a closer look i've changed them into 16bits. It might save a few cycles.

Also another thing that might help is overclocking the xmega to say 50MHz

Last Edited: Mon. Jun 10, 2019 - 01:29 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm confused in #12 you wrote :

the only thing the Xmega needs to do is read the adc and apply the filter then put it on the dac.

That sounds like one analog input and one analog output but : 

 

in #22 it sounds

 

a hole lot of things, are you planning on using external DAC's or are the inputs mixed to one DAC or ?

 

 

And about the use of chebyshev IIR filter, there is a reason for the use of FIR filters (other than the pure filter part)that  is that they are much easier to scale, perhaps there are many multiplications but that's normally not the problem for modern chips. (and if you want to optimize often many filter coefficients can be 0. (often people forget that when they just use a program to find coefficients) 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Basically the pastebin code is all it needs to do. The way it is now which is what needs to be changed is the timer overflow interupt.

 

It initializes the adc, dac and a timer through the use of functions.

Then it executes the math for the filter by using the adc read value from 1 input pin.

Then after the filter has done the math it outputs the answer to a dac pin in voltage.

 

 

 

- Chris

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

This is just an example of the topology I use for fast ADC with many calculations to perform, besides the user interface functions.

(But, the best MCU I am able to get is ATmega8. So the example below explores the general structure of the code only.)
 

; MCU= ATmega8, Xtal= 8 MHz

; initialized to read the first sample
    CLR   curSmpl               ; counter for the indiect jump [IJMP]

; ADC is set for free running, prescaler 1/64 ==> 125 KHz
; ADC reading time = 13 ADC clocks / 125 KHz ==> 104 us ==> 832 MCU cycles
; sampling rate 1 / 104 us ==> 9615 KHz

MAIN_lp:
; loop till ADC ends reading [free running mode)
    SBIS  ADCSRA, ADIF
    RJMP  MAIN_lp               ; no need to use ADC interrupt

; 'optional' to monitor the busy time of every function
    SBI   TIMEpin

    SBI   ADCSRA, ADIF          ; to clear ADIF

; Beginning common code
    IN    tmpHiL, ADCL
    STS   ADCrd_L, tmpHiL

    IN    tmpHiL, ADCH
    STS   ADCrd_H, tmpHiL

;.................
; more common code
;.................

    INC   curSmpl

    MOV   tmpHiL, curSmpl

    CPI   tmpHiL, SMPLnmbr      ; SMPLnmbr=8 here, could be up to 255
    BRLO  MAINend

; if curSmpl >= SMPLnmbr , reset curSmpl

    CLR   curSmpl

MAINend:
; Indirect jump via ZH:ZL [MCU flash word address]
    LDI   ZH, high(SMPL000)     ; SMPL000: is index of IJMP table, 0x??00
    MOV   ZL, curSmpl

    IJMP                        ; see table below, here ??=0F (highest value for IJMP)

;==============================================

func_00:
;...
    RJMP  END_act

func_01:
;...
    RJMP  END_act

func_02:
;...
    RJMP  END_act

;...
;...

func_07:
;...
    RJMP  END_act

;==============================================

END_act:

;...................
; Ending common code
;...................

; 'optional' to monitor the busy time of every function
    CBI   TIMEpin

    RJMP  MAIN_lp

;-----------------------
; *** Indirect jumps ***
;-----------------------

    .org 0x0F00

SMPL000: RJMP  func_00
SMPL001: RJMP  func_01
SMPL002: RJMP  func_02
SMPL003: RJMP  func_03
SMPL004: RJMP  func_04
SMPL005: RJMP  func_05
SMPL006: RJMP  func_06
SMPL007: RJMP  func_07

Sorry if the assembly code above doesn't help.

I use C for PC programs only, not for MCU firmware.

 

Last Edited: Mon. Jun 10, 2019 - 05:06 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You can divide all your constants by 2,4,8,16,etc so the final total result will be no more than 16 bits.  That will save a lot of calc steps.  As mentioned, you don't need xb2/1/0 (same as ya2/1/0), saving even more steps.

Make sure you put an RC filter ahead of the adc to cut off any high freq that might become aliased into the sample/filter.

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

@kerim thanks for the input but my assembler knowledge is about 2 lines i'm sorry.

 

@avrcandies Wouldn't dividing cause even more errors? just like the fixed point, which could potentially alter the filter characteristics such as the cutoff.

 

 

On a side note i've managed to get 48MHz using PLL which could give me 1.5times the clock cycles as before.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

On a side note i've managed to get 48MHz using PLL

Yes, but...

 

It seems you are doing this mostly for learning:

This filter is to learn how to make a digital filter more or less the same as the analog counter part and to learn timing critical implementations.

I've previously made other filters like the one you suggested but sadly that is not part of the assignment this time.

but it is probably worth mentioning that although the digital logic within the Xmega might well run at 48 MHz, (3V, Room Temp), there is some doubt as to how well the analog portions of the chip will function at the higher, (out of spec), clock speeds.

 

So, for learning the process, no big deal.

For a real project, it might matter a lot if the ADC or the DAC wasn't operating as expected.

 

JC

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I think you are slightly beyond the cycle count limit

I didn't see any hard filter spec's in the OP, and it subsequently appears that its a 2nd order filter.  ( " as its a cascaded filter using 2 2nd order sections.")

 

I decided to try it on an Xmega32E5, 3V, 32 MHz.

I (think) I implemented at 2nd order, Butterworth, IIR LPF, (Samp rate 44.1 KHz, Cutoff Freq: 10 KHz).

 

I "read" the ADC, process the filter, and feed the DAC.

But I haven't hooked up a signal generator to see if the filter actually works...

 

From some rough O'scope measurement:

Timing wise, my ISR is firing at ~ every 24.2 uSec, for an ISR rate of 41.322 KHz.

(Not quite the original spec of 44.1 KHz).

 

My ISR and Main Loop calcs take a variable amount of time, presumably based upon the (nosie as signal) ADC values.

 

I end up with, on the O'scope, about 1 uSec of "free time" before the next ISR fires.

 

I think I'd break it cascading a second stage..., (2, 2nd order filters).

 

But clearly a 1st order or a 2nd order (Butterworth) IIR is doable, in spec.

 

JC

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Wouldn't dividing cause even more errors? just like the fixed point, which could potentially alter the filter characteristics such as the cutoff.

Yes, but you will almost certainly NOT notice.  Is your head calibrated to match a certain spectrum? 

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I understand it's fun to try some stuff out though.

 

I've attempted to make something of the suggestions in the thread but i'm now stuck on event syncing also im not sure if at this point i should make a new thread for this?

 

Here is the code snippet.

https://pastebin.com/KREBLJZS

 

I'm trying to trigger the adc conversion by a timer to set a specified sample rate as suggested. I did this by linking the overflow to channel 0 like so EVSYS.CH0MUX = EVSYS_CHMUX_TCC0_OVF_gc;

 

Then on the adc side i'm stuck on these 2 lines:

Im trying to sweep channel 0 of the adc as that is my only input and then do this by the event 0 overflow from the timer.

ADCA.EVCTRL = ADC_SWEEP_0_gc | ADC_EVSEL_0123_gc | ADC_EVACT_SYNCSWEEP_gc;        /* Sweep channel 0, trigger using event channel 0, sync sweep on event */

 

Here im trying to trigger the conversion finish interupt after which my filter will use this sample to do its math so my interupt for the main loop is on channel 0 of the ADC
ADCA.CH0.INTCTRL = ADC_CH_INTMODE_COMPLETE_gc | ADC_CH_INTLVL_LO_gc;            /* Trigger a low-level interrupt on completion of the CH0 conversion */

 

 

Both ADC and timer can be seen in the pastebin.

Also the timer is a bit bloated i believe.

 

- Chris
 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 im not sure if at this point i should make a new thread for this?

 

I'd suggest that if you have further questions concerning the methodology of implementing your filter, ISR vs no ISR, etc., that that discussion stay here.

 

If you want to discuss the operation of the Xmega Event System, then I'd think that would be fair game for a new Thread.

(I am using Xmega xyz, I want to use the Event System to automatically trigger …)

 

I think learning to efficiently integrate the Event System into one's project can be a rather steep learning curve.

Doesn't mean you shouldn't tackle it, it just means you need to be prepared to spend a lot of hours at the bench with your favorite debugging tools.

 

JC

 

  

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

CHrYZoR wrote:
Here is the code snippet.
Why do you keep using pastebin that some of us cannot access? The editor here is quite capable of showing C code:

#include <avr/io.h>

int main(void) {
	PORTB = 0x55;
}

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm sorry i was unaware of people being unable to acces pastebin it was meant so that my post wouldnt get cluttered.

 

This is the code van from the pastebin:

 

void init_adc(void)
{
    PORTA.DIRCLR     = PIN2_bm|PIN3_bm;
    ADCA.CH0.MUXCTRL = ADC_CH_MUXPOS_PIN2_gc |                                      // PA2 (PIN A2) to + channel 0
    ADC_CH_MUXNEG_GND_MODE3_gc;                                                     // GND to - channel 0
    ADCA.CH0.CTRL = ADC_CH_GAIN_1X_gc | ADC_CH_INPUTMODE_DIFF_gc;                   /* Diff in, not using the gainblock */
    ADCA.REFCTRL     = ADC_REFSEL_INTVCC_gc;
    ADCA.CTRLB = ADC_CURRLIMIT_NO_gc | ADC_RESOLUTION_12BIT_gc | ADC_CONMODE_bm;    /* Enable signed mode, 12-bit conversion */
    ADCA.EVCTRL = ADC_SWEEP_0_gc | ADC_EVSEL_0123_gc | ADC_EVACT_SYNCSWEEP_gc;      /* Sweep channel 0, trigger using event channel 0, sync sweep on event */

    ADCA.PRESCALER = ADC_PRESCALER_DIV32_gc;                                        // 48MHz/32 = 1.5M
    ADCA.CTRLA       = ADC_ENABLE_bm;
    PMIC.CTRL |= PMIC_LOLVLEN_bm;                                                   // low level interupt for adc conversion
    ADCA.CH0.INTCTRL = ADC_CH_INTMODE_COMPLETE_gc | ADC_CH_INTLVL_LO_gc;            /* Trigger a low-level interrupt on completion of the CH0 conversion */

}

void init_timer_test(void){
    /* Init the timer/PWM and associated I/Os */
    TCC0.CTRLB = TC0_CCAEN_bm | TC_WGMODE_SINGLESLOPE_gc; /* PWM on CCA */
    TCC0.CTRLD = TC_EVACT_OFF_gc | TC_EVSEL_OFF_gc; /* No events */
    TCC0.CTRLE = TC_BYTEM_NORMAL_gc; /* No byte mode */
    TCC0.PER = 2400;    // 48MHz/2400 = 20KHz
    TCC0.INTCTRLA = TC_ERRINTLVL_OFF_gc | TC_OVFINTLVL_OFF_gc; /* All timer interrupts off */
    TCC0.INTCTRLB = TC_CCAINTLVL_OFF_gc | TC_CCBINTLVL_OFF_gc | TC_CCCINTLVL_OFF_gc | TC_CCDINTLVL_OFF_gc; /* Disable Compare/Capture interrupts */
    TCC0.CNT = 0;
    TCC0.CTRLA = TC_CLKSEL_DIV1_gc; /* Start the timer with a clock divider of 1 */

    EVSYS.CH0MUX = EVSYS_CHMUX_TCC0_OVF_gc; // Connect TCC0 overflow to event channel 0, allowing us to trigger on this interupt  http://asf.atmel.com/docs/latest/xmegaa/html/adc_use_case_2.html

    PORTC.DIRSET = PIN0_bm;
    PORTC.OUTCLR = PIN0_bm;

    PORTF.DIRSET = PIN0_bm | PIN1_bm;
    PORTF.OUTCLR = PIN0_bm | PIN1_bm;
}

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

As DocJC wrote in #5

You need to do cycle counting of your filter routine.

4 tap IIR filter in some 750 cycles is a high enough CPU load to make this a serious issue.

Applying the filter to some values may or may not be fixed time.

It could be that the number of CPU cycles needed to do a calculation depends on your data, for example the number of one's in your data input, or intermediate results.

 

Also, jitter is a serious issue with Audio.

It's easy to understand, that outputting the wrong value to the DAC is bad for audio quality (Rounding errors etc.), but outputting the right value at the wrong time is just as bad.

Just draw a sine wave on a piece of paper, and look at the area near a zero crossing.

For a value point of the DAC output, shifting in amplitude has exactly the same result as shifting in time.

 

So if the execution time of your IIR filter is variable, and you do it in an ISR and output the calculated value at the end of your ISR, then you've added (a lot of?) jitter to the timing.

The best solution for this is to put a (small) buffer inbetween.

This way you can calcualte the filter asynchrounous with the audio timing.

It also means your ISR is very small (just read a value from the buffer and output it to the DAC) and because of it's smallness it has to push and pop a lot less registers.

Even better, I think the Xmega's have DMA, which you can probably use to transfer data to the DAC (Maybe also for reading from the ADC).

 

The amount of time an ISR needs for entry and exit (push and pop instructions) depends on the complexity of the code.

When doing some complicated calculation in your ISR your AVR will push (and pop) most of it's registers.

Again: You need to do some (approximate) cycle counting here.

 

A technique I've used is to use a simple Logic Analyser (see my signatrue) and then continuously toggle an I/O bit in an idle loop in the main function.

Whenever an ISR triggers the bit stops toggling. Then in your ISR, set another I/O pin on entry, and clear it again as the last instruction in the ISR.

 

When timing gets tight, you should have an idea of how much time is needed for all of the sub tasks (Reading from ADC, writing to DAC, IIR fitler, User interface, etc).

You do not have to calcuate it cycle accurate, but you do have to know wheter a task tak takes 50 or 150 F_CPU cycles.

Doing magic with a USD 7 Logic Analyser: https://www.avrfreaks.net/comment/2421756#comment-2421756

Bunch of old projects with AVR's: http://www.hoevendesign.com

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

i'm not sure if i truly understand but what you are saying is that i should have a rough estimate of the ISR time for my filter?

 

And then make it so that every ISR is the same time to reduce timing issues?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

In the ISR simply output the dac value first (using the previously calculated filter values from the last time)..this will be a known, constant delay.

Next, take your new ADC reading & calculate the filter values.  Any input or output jitter is simply the timer IRQ latency response time, since the dac write/adc read is fixed with respect to the irq.

Since nothing is done in main, there are no variables needing shared with main.  While it is recommended to make IRQs as quick as possible, in this case there is nothing else to do so it seems a reasonable tradeoff for easy implementation.

 

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

This is what i've made of it now. I've changed the interupt to your suggestion and do the dac first and i've added a port so that i can check with my oscilloscope for the actual timing of the interupt.

It's a little messy but i hope this is what you meant.

#define F_CPU 48000000
#include "clksys_driver.h"
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

//ADC_voltage:
#define MAX_VALUE_ADC   2047                               // only 11 bits are used
#define VCC         3.30
#define VREF        (((double) VCC) / 1.6)                 // maximal value is 2.06125 V
//DAC
#define MAX_VALUE_DAC 4095
#define CAL_DAC 1.000  // Calibration value DAC

int16_t xA[3] = {0};
int16_t yA[3] = {0};
int16_t xB[3] = {0};
int16_t yB[3] = {0};
int32_t y2 = 0;
int32_t y = 0;

// Times 2^14 for fixed point
int32_t a10 = 16384;
int32_t a11 = -28286;
int32_t a12 = 12253;
int32_t b10 = 88;
int32_t b11 = 176;
int32_t b12 = 88;

int32_t a20 = 16384;
int32_t a21 = -30541;
int32_t a22 = 14537;
int32_t b20 = 95;
int32_t b21 = 190;
int32_t b22 = 95;

double  voltage ;                                                                                            // contains read in voltage (mVolts)
int16_t res;                                                                                                // contains read in adc value
int16_t BinaryValue;                                                                                        // contains value to write to DAC

void init_timer_test(void){
    /* Init the timer/PWM and associated I/Os */
    TCC0.CTRLB = TC0_CCAEN_bm | TC_WGMODE_SINGLESLOPE_gc;                                                    /* PWM on CCA */
    TCC0.CTRLD = TC_EVACT_OFF_gc | TC_EVSEL_OFF_gc;                                                            /* No events */
    TCC0.CTRLE = TC_BYTEM_NORMAL_gc;                                                                        /* No byte mode */
    TCC0.PER = 2400;                                                                                        // 48MHz/2400 = 20KHz
    TCC0.INTCTRLA = TC_ERRINTLVL_OFF_gc | TC_OVFINTLVL_OFF_gc;                                                /* All timer interrupts off */
    TCC0.INTCTRLB = TC_CCAINTLVL_OFF_gc | TC_CCBINTLVL_OFF_gc | TC_CCCINTLVL_OFF_gc | TC_CCDINTLVL_OFF_gc;    /* Disable Compare/Capture interrupts */
    TCC0.CNT = 0;
    TCC0.CTRLA = TC_CLKSEL_DIV1_gc;                                                                            /* Start the timer with a clock divider of 1 */
    EVSYS.CH0MUX = EVSYS_CHMUX_TCC0_OVF_gc;                                                                    // Connect TCC0 overflow to event channel 0, allowing us to trigger on this interupt  http://asf.atmel.com/docs/latest/xmegaa/html/adc_use_case_2.html
    PORTC.DIRSET = PIN0_bm;
    PORTC.OUTCLR = PIN0_bm;
    PORTF.DIRSET = PIN0_bm | PIN1_bm;
    PORTF.OUTCLR = PIN0_bm | PIN1_bm;
}

void init_dac(void){
    
    DACB.CTRLC = DAC_REFSEL_AVCC_gc;
    DACB.CTRLB = DAC_CHSEL_SINGLE_gc;
    DACB.CTRLA = DAC_CH0EN_bm | DAC_ENABLE_bm;
}

void init_adc(void){
    
    PORTA.DIRCLR     = PIN2_bm|PIN3_bm;
    ADCA.CH0.MUXCTRL = ADC_CH_MUXPOS_PIN2_gc |                                        // PA2 (PIN A2) to + channel 0
    ADC_CH_MUXNEG_GND_MODE3_gc;                                                        // GND to - channel 0
    ADCA.CH0.CTRL = ADC_CH_GAIN_1X_gc | ADC_CH_INPUTMODE_DIFF_gc;                    /* Diff in, not using the gainblock */
    ADCA.REFCTRL     = ADC_REFSEL_INTVCC_gc;
    ADCA.CTRLB = ADC_CURRLIMIT_NO_gc | ADC_RESOLUTION_12BIT_gc | ADC_CONMODE_bm;    /* Enable signed mode, 12-bit conversion */
    ADCA.EVCTRL = ADC_SWEEP_0_gc | ADC_EVSEL_0123_gc | ADC_EVACT_SYNCSWEEP_gc;        /* Sweep channel 0, trigger using event channel 0, sync sweep on event */

    ADCA.PRESCALER = ADC_PRESCALER_DIV32_gc;                                        // 48MHz/32 = 1.5M
    ADCA.CTRLA       = ADC_ENABLE_bm;
    PMIC.CTRL |= PMIC_LOLVLEN_bm;                                                    // low level interupt for adc conversion
    ADCA.CH0.INTCTRL = ADC_CH_INTMODE_COMPLETE_gc | ADC_CH_INTLVL_LO_gc;            /* Trigger a low-level interrupt on completion of the CH0 conversion */
}

static void InitClocks(void){
    
    CLKSYS_XOSC_Config( OSC_FRQRANGE_12TO16_gc, 0, OSC_XOSCSEL_XTAL_16KCLK_gc );
    CLKSYS_Enable( OSC_XOSCEN_bm );
    do {} while ( CLKSYS_IsReady( OSC_XOSCRDY_bm ) == 0 );
    CLKSYS_PLL_Config( OSC_PLLSRC_XOSC_gc, 3);
    CLKSYS_Enable( OSC_PLLEN_bm );
    do {} while ( CLKSYS_IsReady( OSC_PLLRDY_bm ) == 0 );
    CLKSYS_Main_ClockSource_Select( CLK_SCLKSEL_PLL_gc );
    CLKSYS_Disable( OSC_RC2MEN_bm );
    CLKSYS_Disable( OSC_RC32MEN_bm );
    
    } 

ISR(ADCA_CH0_vect){                                                                // TIMER Interupt for 20k samples/s triggers on each sample.
    PORTD.DIRCLR = PIN7_bm;
    while (!DACB.STATUS & DAC_CH0DRE_bm);
    DACB.CH0DATA = BinaryValue ;                                                //write &USBDataIn to DAC (PIN A10)
    res =   ADCA.CH0.RES;                                                        // Read out the ADC (PIN A2)
    
    // b(1)* (x(n)+(x(n-1)<<1)+x(n-2)) simplification: b(1)=b(3)=0.5*b(2)
    y2 = b10* xA[0]+ b11 *xA[1]+ b12*xA[2]+ a11*yA[1] + a12*yA[2];                // 1st 2nd order y2 = b10* xA[0]+ b11 *xA[1]+ b12*xA[2]- a11*yA[1] - a12*yA[2];
    
    y = b20* xB[0]+ b21 *xB[1]+ b22*xB[2]+ a21*yB[1] + a22*yB[2];                //2nd 2nd order
    
    // 1st 2nd order delays
    //x_in = res (output value of adc)
    xA[2] = xA[1];
    xA[1] = xA[0];
    xA[0] = res;
    
    yA[2] = yA[1];                                                                // this still needs to be changed
    yA[1] = yA[0];
    yA[0] = y2;    // >> 14
    
    // x_in of filter 2 is output of  filter 1.
    xB[2] = xB[1];
    xB[1] = xB[0];
    xB[0] = y2;                                                                    // 2d 2nd order delays  this was res(input) at first now its the output of filter 1 aka y2
    
    yB[2] = yB[1];
    yB[1] = yB[0];
    yB[0] = y;
    res = (short)(y >>14);                                                        // divided by 16384 or 2^14     voltage = y>>14  not sure if this is right.
    voltage = res * 1000.0 * VREF / (MAX_VALUE_ADC + 1.0);                        // Measured voltage in Volts.
    BinaryValue =  voltage*((MAX_VALUE_DAC)/(VCC))*0.001*CAL_DAC ;                // Bitvalue
    PORTD.DIRSET = PIN7_bm;
}

int main(void)
{
    PORTD.DIRSET = PIN7_bm;
    InitClocks();
    init_timer_test();        
    init_dac();                
    init_adc();                
//    PORTCFG.CLKEVOUT = PORTCFG_CLKOUT_PD7_gc; // output CLK value on pin D7

    sei();                                                                    //Enable interrupts
    while (1)
    {
        
    }
}

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You still duplicate the YA & XB values, so one set can be eliminated, if you are close to running low on time. 

 

 

Why do you have to wait on the dac?  Hopefully, you don't so that the timing is not varied 

while (!DACB.STATUS & DAC_CH0DRE_bm);

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yes i know its still duplicated thats why i commented that it still needs to be changed. It was just to make the math clear for myself.

 

And i could probably remove the while loop as the dac should be ready at that point.

 

I'm still not quite sure if this line is right though 

    ADCA.EVCTRL = ADC_SWEEP_0_gc | ADC_EVSEL_0123_gc | ADC_EVACT_SYNCSWEEP_gc;        /* Sweep channel 0, trigger using event channel 0, sync sweep on event */

I was unable to really find much about ADC sweeping for 1 channel and linking it to a timer overflow.

 

- Chris

 

 

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 

 

CHrYZoR wrote:
i'm not sure if i truly understand but what you are saying is that i should have a rough estimate of the ISR time for my filter?

Yes. If timing is a concern then simply do the measurements to confirm whether it is or is not an issue.

Put your measured timing in comment in the code. It is important enough to document, and others see you've gone through some trouble to do it properly.

When your code changes in the relevant part (or when using vastly different compiler settings) you should redo the measurement to verify your timing (Depending on how critical it is).

Instead of listening to your "friend" wo claims you should not use ISR's you can very simply measure it with an oscilloscope or Logic Analyser as I described before, and then show him the numbers you've measured.

 

avrcandies wrote:
In the ISR simply output the dac value first (using the previously calculated filter values from the last time)..this will be a known, constant delay.

This gets as close as an AVR can get you (Xmega's DMA might be better)

 

The trouble though is that for (high quality) audio even a single F_CPU cycle is unacceptable.

(But for voice processing, and doorbells it does not matter much).

 

To give you an idea of the required timing:

Assume a 44.1kHz sampled Full scale sinewave.

The derivative of a sine is a cosine, same shape.

 

For a normal 16-bit audio dac you have a voltage resolution of 1/65535 of the full scale of the sample.

Because the derivative (cosine) has a maximum slope of 1, you also want a time resolution of that same single bit.

 

This boils down to:

1 / 44100 / 65535 = 346.01 ps (Or preferably 1/2 of that or 1/10th to make sure it's "negligable").

which would be a single clock cycle of a 2.89GHz clock.

A single clock of a 20MHz AVR is 2 orders of magnitude bigger, and this effectively turns your precious 16-bit DAC into:

octave:10> log(power(2,16)/100) / log(2)
ans =  9.3561

a 9.4 bit DAC.

 

And that's from a single F_CPU delay. If your ISR() gets delayed because of another ISR() is running, you can easily get 100 or so F_CPU cycles extra delay.

 

So you have to ask yourself:

Do I want to build a simple audio gadget with this chip (doable), or do I want to build a serious piece of HiFi, (Which would require a uC,DSP or FPGA with dedicated hardware such as I2S.

 

I'm not familiar with the Xmega's. What does this do:
 

 PORTD.DIRSET = PIN7_bm;

Is that changing a direction register? That may or may not be visible on your Oscilloscope.

But the basic idea is right. On that output pin you can measure how much time the code in the ISR() needs to execute.

 

Now if you also continuously toggle a pin in your while(1) loop:

    while (1)
    {
      PORTD.DIRSET = PIN4_asdf;     // No Idea what I'm doing.
      PORTD.DIRCLR = PIN4_asdf;     // Just pseudo code with OP's format.
    }

Then you can use it to measure the ISR() overhead.

When the Processor gets pulled out of the while(1) loop, it stops toggling PIN4_asdf, and after the preamble of the ISR() it sets PIN7_bm.

A PUSH or a POP takes 2 F_CPU cycles on an AVR (also on Xmega?) so if your ISR needs to push 10 registers, you can expect a delay of roughly 20 F_CPU cycles.

Another very good way to get an overview of the ISR overhead is to have a look at the .lss output listings of the GCC compiler.

 

Also: You can do these measurements on a 2 channel oscilloscope ( Even an analog scope will do if the ISR() is triggered at regular intervals), however A EUR8 Logic Analyser is a better instrument for things like these. You have more channels for more measurements, and if you buy some decent clips with it (which are easily more expensive than the LA itself (about EUR20 for a set of 10)) they are also easier to fix to your board.

I have pulled the pins out of a bunch of old IDC headers, and sometimes solder tese bare pins to a test PCB as test points, and then simply put "dupont wires" between the test points and my LA.

 

Doing magic with a USD 7 Logic Analyser: https://www.avrfreaks.net/comment/2421756#comment-2421756

Bunch of old projects with AVR's: http://www.hoevendesign.com

Last Edited: Tue. Jun 11, 2019 - 04:04 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thank you for the reply this is very informative.

 

My application is just to test a few things and to learn stuff.

 

So the specs i've made for my filter are 1% THD in the passband of the filter so from 1 to 600hz and some dB gain for the filter other than that it needs the sample rate so i can get my filter shape with the nyquist theorem in mind.

 

and i see that i made a mistake with the port checking for the interupt time it should be like this :

        PORTD.OUTSET = PIN7_bm; // make high
        PORTD.OUTCLR = PIN7_bm; // make low
 

 PORTD.DIRSET = PIN7_bm;

And this enables the output of pin 7 

 

Also, i  have a 4channel oscilloscope to do measurements.

 

 

 

EDIT: i have perhaps found an alternative to using the interupt by using  a while loop and checking for the interupt flags and then clearing them at the end. Which i found in AVR document 1517 and is used in this example  https://github.com/Synapseware/xmega-intro/blob/master/avr1517-adc/code/Solutions/task4.c Not sure if this is actually faster though, so that will have to be tested.

- Chris

 

Last Edited: Tue. Jun 11, 2019 - 05:12 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

by using  a while loop and checking for the interupt flags and then clearing them at the end.

Why on earth would you do that?  Just set up a timer to do an IRQ at 44.1 KHz (every 22.676us)

 

a) Write the dac using the latest filter value

b) Read/Remember the latest adc value (is ready & available) & Start a new adc conversion (will/must be finished before the next irq)*

c) Calc a new filter value from the remembered adc value

d) Exit irq & wait for next irq.

 

Note there is NO checking of any flags, since that adds a timing uncertainty.

 

* note 44 KHz is too fast for the standard mega series, which has about 15.5KHz max sampling, at 10 bits.

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Allright i will stick to the code i posted earlier then and test it somewhere tommorow, thank you.

 

 

 

-Chris

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Allright i will stick to the code i posted earlier then and test it somewhere tommorow, thank you.

Testing it in your chip would be the best place!!

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Having tested it, it doesnt work sadly

The interupt routine works but the filter doesnt.. It seems to oscilate even without an input signal which seems odd to me.

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

CHrYZoR wrote:
... but the filter doesnt.. It seems to oscilate even without an input signal which seems odd to me.
FIR filters are unconditionally stable; IIR filters are conditionally stable.

Is the initial condition set?

 

"Dare to be naïve." - Buckminster Fuller

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello,

 

Im not sure what you mean by this.

 

The input is a 1v sine wave.

And the formula is the same as the code posted a few replies above.

 

EDIT: The interupt works as having the ADC input directly on the dac gives the same signal.

However once the filter code gets added it does what can be seen in the picture posted earlier. It does this with or without an input and it behaves the same. Which is why im pretty sure it's the fault of the filter code.

 

- Chris

Last Edited: Fri. Jun 14, 2019 - 05:57 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

CHrYZoR wrote:
Im not sure what you mean by this.
The initial values are correct; zero may or may not be a correct value.

Recursive Filter Design | The Scientist and Engineer's Guide to Digital Signal Processing

...

Don't use random numbers here, or the initial filter will be unstable. 

...

(more than the filter's coefficients)

 

More causes of oscillation : overflow, underflow, imprecision

Using Integers | The Scientist and Engineer's Guide to Digital Signal Processing

...

... or even make it unstable. 

...

 

"Dare to be naïve." - Buckminster Fuller

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Appreciate seeing the references to my favorite DSP book.

 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The input can  be irrelevant---meaning you may have designed a filter feedback that is unstable (an oscillator)...

 

Have you tried your coefficints in a filter simulation/analysis package to measure the gain margin & phase margin?  You could use matlab, matrixx, and many other packages.

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The input can  be irrelevant---meaning you may have designed a filter feedback that is unstable (an oscillator)...

 

Have you tried your coefficints in a filter simulation/analysis package to measure the gain margin & phase margin?  You could use matlab, matrixx, and many other packages.

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!