Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

DDS Interpolation - 8bit Atmel AVR ASM to 12 bit DAC

I just completed a DDS project, on an Atmel AVR employing ASM, and have come to the conclusion that an 8 bit look-up table and 8 bit DAC create too much quantization distortion at low frequencies; for lack of better wording, I'm getting a sine wave with a ladder effect on my oscilloscope.

Obviously, If I smooth out the waveform with a big LPF, I have problems with amplitude in higher frequencies.

In theory, upgrading from an 8 bit to 12 bit DAC and employing interpolation with the 4 least significant bits should allow me to raise the cutoff point of my filter a significant enough amount to alleviate problems with waveform amplitude at higher frequencies. My problem is that I don't have a clue how to do this or if there is an easier way to remove the zipper effect.. perhaps 12 bit look-up tables?

So far, I've created an infinite loop and every time the loop completes a cycle, a value is sent to the DAC based on the position of the pointer related to the look-up table. Here's where I get confused. I've read tons of info on this and still haven't found a working example. If I have an infinite loop, how am I supposed to stuff the interpolation values between the table look-up values? About the best thing that I can think of is (a + b) /2; I can probably implement this and get an extra bit or the equivalent of a 512 point look-up table, but I'd like to think that there is an easier way or something that could potentially deliver better results. I don't know C or how to employ it, but I will give it a try if it is prudent.

Currently, my clock is at 1MHZ and I could probably go to 16MHZ if necessary.

Here is a sample of my code:

; Set sinewave output as default

    ldi     ZH, High(sine*2); setup Z pointer hi
    ldi     ZL, Low(sine*2) ; setup Z pointer lo

; Clear accumulator

    clr     r29             ; clear accumulator

; Setup adder registers

    ldi     r24,0x50        ; Fine adder value change register
    ldi     r25,0x08        ; Middle adder value change register
    ldi     r26,0x00        ; Coarse adder value change register

LOOP1:

    add     r28,r24         ; 1  Adder values carry over to higher registers.  Higher registers raise freq. in larger steps 
    adc     r29,r25         ; 1
    adc     r30,r26         ; 1  r30 is database address pointer for Z register
    lpm     r0, Z           ; 3  (Load Program Memory) Reads byte from database into the destination register based on Z pointer
    out     PORTD,r0


    rjmp    LOOP1           ; 2 => 9 cycles
like image 851
Ted LED Avatar asked Nov 03 '22 10:11

Ted LED


1 Answers

If your LUT has 256 entries, you can first use the register r29 (that apparently goes from 0 to 255) as the scaling factor between two successive samples.

Output = (LUT[r30] * (256 - r29) + LUT[r30+1] * r29) >> 8;

Also this thread discusses many practical alternatives to sine wave generation.

EDIT The formula implements text book linear interpolation

y = a*(1-t) + b*t, with 0<=t<1

So that y=a, when t=0 and y=b, when t=1. Shifting by 8 means that the interpolation term t is divided by 256 after the multiplication. In the expression LUT[r30+1] I'm assuming an implicit modulo 256 arithmetic, as r30 is 8-bit (isn't it?).

LUT expansion to 12 bit has to be done separately, as dividing by 4 instead would just increase the quantization error in the LUT.

enter image description here

The interpolation happens always relative to integer indices in the LUT, regardless if many samples fall to the same range eg. LUT2 and LUT[3]. Mathematically Lut[R], LUT[R+1] is more correct than [R-1], [R], but in real life there is no difference, as human audatory system doesn't have absolute reference phase.

like image 95
Aki Suihkonen Avatar answered Nov 09 '22 07:11

Aki Suihkonen