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
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With