DDS creates noise frequencies

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

Hi at all,

 

I am using an Xmega128A4u to create with DMA+DAC an DDS. The Output is a waveform stored in a lookup table. It consist of 1024 values. To the the DAC0 Output is connected amplifier, a high-pass and low-pass filter.

 

First of all, my frequencies are correct up to a few hundrets the frequency value is in hundrets.  That means if wfreq = 44050 a  frequency of 440,5Hz is observable. The Output is very clean. Because of this I think my code is not wrong in basics but in its details.

 

I am using a 12 bit DDS at 128ksps. If the stepsize has no rouding error, the is NO noise. I read something about dithering, filtering and interpolation but i dont understand yet, how to use it.

 

Here is some sourcecode. First clock settings:

 

#define F_CPU 32000000UL
#define SPS 128000

TCC1_CTRLA = 1;
TCC1_PERBUF = F_CPU/SPS-1;	//every 128000th seconds

Followed by the  calculationg of the stepsize  for the lookuptable given for a frecuency.

struct s_Ton{
    BYTE cVol;              //volume 0 - 0xff
    WORD wPos;              //current position in the lookup table
    DWORD wCounter;         //current counter (addition of stepsize every frame)
    DWORD wStepSize;        //stepsize in the lookuptable eveery frame for a given frequency
    DWORD wFreqOld;
    DWORD wFreq;            //frequency
};

#define SAMPLE_SIZE 1024
struct s_Ton g_asToene[1];   
g_asToene[0].wStepSize = ((unsigned long long)65536 * SAMPLE_SIZE * g_asToene[i].wFreq) / (SPS*100);	

The configuration of the DMA:

 

#define DMA_BUFFER_SIZE 256
WORD g_DmaBuffer1[DMA_BUFFER_SIZE];                                                  
WORD g_DmaBuffer2[DMA_BUFFER_SIZE];

DMA.CH0.CTRLA = DMA_CH_SINGLE_bm | DMA_CH_BURSTLEN_2BYTE_gc;
	DMA.CH0.CTRLB = 0x02;
	DMA.CH0.ADDRCTRL = DMA_CH_SRCRELOAD_BLOCK_gc
	| DMA_CH_SRCDIR_INC_gc
	| DMA_CH_DESTRELOAD_BURST_gc
	| DMA_CH_DESTDIR_INC_gc;
	DMA.CH0.TRIGSRC = DMA_CH_TRIGSRC_EVSYS_CH0_gc;
	DMA.CH0.TRFCNT = DMA_BUFFER_SIZE*2;
	DMA.CH0.REPCNT = 0;
	DMA.CH0.SRCADDR0 = (((uint16_t) &g_DmaBuffer1)>>0*8) & 0xFF;
	DMA.CH0.SRCADDR1 = (((uint16_t) &g_DmaBuffer1)>>1*8) & 0xFF;
	DMA.CH0.SRCADDR2 = 0;
	DMA.CH0.DESTADDR0 = (((uint16_t)(&DACB.CH0DATAL))>>0)&0xFF;
	DMA.CH0.DESTADDR1 = (((uint16_t)(&DACB.CH0DATAL))>>8)&0xFF;
	DMA.CH0.DESTADDR2 = 0;

	DMA.CH1.CTRLA = DMA_CH_SINGLE_bm | DMA_CH_BURSTLEN_2BYTE_gc;
	DMA.CH1.CTRLB = 0x02;
	DMA.CH1.ADDRCTRL = DMA_CH_SRCRELOAD_BLOCK_gc
	| DMA_CH_SRCDIR_INC_gc
	| DMA_CH_DESTRELOAD_BURST_gc
	| DMA_CH_DESTDIR_INC_gc;
	DMA.CH1.TRIGSRC = DMA_CH_TRIGSRC_EVSYS_CH0_gc;
	DMA.CH1.TRFCNT = DMA_BUFFER_SIZE*2;
	DMA.CH1.REPCNT = 0;
	DMA.CH1.SRCADDR0 = (((uint16_t) &g_DmaBuffer2)>>0*8) & 0xFF;
	DMA.CH1.SRCADDR1 = (((uint16_t) &g_DmaBuffer2)>>1*8) & 0xFF;
	DMA.CH1.SRCADDR2 = 0;
	DMA.CH1.DESTADDR0 = (((uint16_t)(&DACB.CH0DATAL))>>0)&0xFF;
	DMA.CH1.DESTADDR1 = (((uint16_t)(&DACB.CH0DATAL))>>8)&0xFF;
	DMA.CH1.DESTADDR2 = 0;

And at last the Interrupt of the DMA:

ISR(DMA_CH0_vect){
    DMA.CH1.CTRLA |= DMA_CH_ENABLE_bm;
    DMA.CH0.CTRLB |= (1<<4);	

    asm volatile(
	/*variablen laden*/
	"lds r2, %[AC0]				\n\t"					//Counter 0
	"lds r3, %[AC0]+1			\n\t"					//..
	"lds r16, %[AC0]+2			\n\t"					//..
	"lds r6, %[AS0]				\n\t"					//Stepsize 0
	"lds r7, %[AS0]+1			\n\t"					//..
	"lds r8, %[AS0]+2			\n\t"					//..
	"lds r12, %[AP0]			\n\t"					//Position 0
	"lds r18, %[AP0]+1			\n\t"
	"lds r14, %[M0]				\n\t"
	"ldi YH, hi8(%[DM])			\n\t"
	"ldi YL, lo8(%[DM])			\n\t"
	"ldi r24, lo8(%[SIZE])		\n\t"
	"ldi r25, hi8(%[SIZE])		\n\t"
	"ldi r23, 0					\n\t"
	"ldi r22, 0					\n\t"

	/*loop*/
	"REPEAT:					\n\t"
	"add r2, r6					\n\t"					//Stepsize add
	"adc r3, r7					\n\t"					//..
	"adc r16, r8				\n\t"					//... here witt carry
	"add r12, r16				\n\t"					//Addition to position
	"adc r18, r23				\n\t"
	"andi r18, 3				\n\t"
	"mov r16, r23				\n\t"					//Modulo 1024 Counter

	/*load value*/
	"ldi XH, hi8(%[SAMPLE])		\n\t"					//load value from lookup
	"ldi XL, lo8(%[SAMPLE])		\n\t"					//..
	"add XL, r12				\n\t"					//cPos add
	"adc XH, r18				\n\t"					//Carry add
	"add XL, r12				\n\t"					//cPos add
	"adc XH, r18				\n\t"					//Carry add
	"ld r20, X+					\n\t"					//load sample value
	"ld r21, X+					\n\t"					//..

	"st Y+,r20					\n\t"
	"st Y+,r21					\n\t"

	"sbiw r24, 1				\n\t"					//loop- 1
	"brne REPEAT				\n\t"

	/*variablen speichern*/
	"sts %[AC0],r2				\n\t"					//Counter 0 save
	"sts %[AC0]+1,r3			\n\t"					//..
	"sts %[AC0]+2,r16			\n\t"					//..
	"sts %[AP0],r12				\n\t"					//Position 0 save
	"sts %[AP0]+1,r18			\n\t"

	:
	: [AC0] "i" (&g_asToene[0].wCounter)
	, [AS0] "i" (&g_asToene[0].wStepSize)
	, [AP0] "i" (&g_asToene[0].wPos)
	, [M0] "i" (&g_asToene[0].cVol)
	, [DM] "i" (&g_DmaBuffer1[0])
	, [SAMPLE] "i" (&g_awSample[0])
	, [SIZE] "i" (DMA_BUFFER_SIZE)
	: "memory" ,"r2","r3","r4","r5","r6","r7","r8","r9","r10","r11","r12","r13","r14","r15","r16","r17","r18","r19","r20","r21","r22","r23","r24","r25","r26","r27","r28","r29","r30","r31"
	);
}

ISR(DMA_CH1_vect){
    DMA.CH0.CTRLA |= DMA_CH_ENABLE_bm;
    DMA.CH1.CTRLB |= (1<<4);		

    [...] // the same inlineassembler..
}

 

Here is a recorded sinewave at 123Hz and 125Hz. The stepsize of 125Hz has no noise - it has no rounding error. The steosize of 123 has a rounding error. At 250, 500, 1000, 2000Hz.... there is also no error.

 

https://soundcloud.com/user-5225...

https://soundcloud.com/user-5225...

 

It is going to be a Theremin: https://soundcloud.com/user-5225...

 

Does someone have any tips for me? Thank you very very much!

Last Edited: Sat. Feb 18, 2017 - 06:29 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

That is inherent in the DDS algorithm. It is also why the IC DDS implementations use phase accumulators that have MANY bits (way more than 12). You will never reach zero "noise" when the desired frequency is not an integer multiple. You can only reduce it to some low level that is hopefully acceptable.

 

Jim

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

Last Edited: Sat. Feb 18, 2017 - 06:41 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Just saying: Doing a linear interpolation between every sampling point there is no noise anymore. It works with 16bits accumulators und 512 sample size.

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

That stands to reason doesn't it? Although I wouldn't say 'no noise' - as there always is a degree of noise. Something like a harmonic oscillator might be easier though.

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

 Doing a linear interpolation between every sampling point there is no noise anymore

I was following the discussion right up to this point.

Surely, at the end of the day, you end up picking the next sample as either X or X+ 1, regardless of where your linear interpolation value falls, the output of the decision is binary.

The accumulator gives you long term frequency control, (sample selection), but already "interpolates" the next data sample, (if you are interpolating what I think you are).

 

Lastly as a digitally generated signal, (think stair steps in the output), all DDS signal generators need a good LPF on the output for noise reduction.

 

JC 

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

Linear interpolation or no, the same harmonics will be present. In one case, the waveform is a series of linked steps (which is what you get from any true DAC) while the other case is a series of linked slopes. The amplitudes of the harmonics will certainly be different but the same ones will still be there. If there are N+1 sample points per cycle (counting one at the first zero crossing and the N+1th at the second zero crossing of the same slope), that is N intervals, then there will be a spurious signal at N times the fundamental. There may be other (lower) harmonics of the fundamental if the data to the DAC is not sinusoidal.

 

But, in the digital world of DDS, if those slopes are generated digitally, then THEY will consist of a series of very small amplitude steps and THOSE steps will also generate a spurious signal. If those "slopes" are generated by analog filtering, then this effect is absent.

 

Jim

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

Last Edited: Fri. May 26, 2017 - 09:02 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Curby wrote:

I am using an Xmega128A4u to create with DMA+DAC an DDS. The Output is a waveform stored in a lookup table. It consist of 1024 values. To the the DAC0 Output is connected amplifier, a high-pass and low-pass filter.

I'm not sure how DMA helps, as usually DDS is a fast adder that the MSBs index into a Sine Table, and then feed a DAC.

 

Curby wrote:

First of all, my frequencies are correct up to a few hundrets the frequency value is in hundrets.  That means if wfreq = 44050 a  frequency of 440,5Hz is observable. The Output is very clean. Because of this I think my code is not wrong in basics but in its details.

 

I am using a 12 bit DDS at 128ksps. If the stepsize has no rouding error, the is NO noise. I read something about dithering, filtering and interpolation but i dont understand yet, how to use it.

Here is a recorded sinewave at 123Hz and 125Hz. The stepsize of 125Hz has no noise - it has no rounding error. The steosize of 123 has a rounding error. At 250, 500, 1000, 2000Hz.... there is also no error.

128k / 1024 is 125Hz, that means 125Hz will use every single sine entry, just once, so of course that will have the best performance.

any other value close to 125Hz, will either have to duplicate some value, some of the time, or skip some value, some of the time.

That will degrade the sine quality, and the nature of the dual or skip, is to add noise.

Those errors are usually small enough to be tolerated in a design.

 

Just what noise levels are you hoping to achieve, and what are you measuring ?

 

I'm not sure sure you are using 12b DDS, as that is quite low. (that has a 31.25Hz step size)  More common is a 24b or 32b adder, which would give freq LSBs of 7.629394531 milli Hz or 29.80232239 micro Hz

 

Those are the numeric average frequency resolutions, the Sine still has to be built by scanning your 1024 table, and the only means to vary f is to do that dual or skip I mentioned.

Those points will not be in the same place every sine cycle, and even the points chosen from the table will vary at higher freq out.

eg If you ask for 1250 Hz, one in every 10 entries in that 1024 table will be output, on average, for 102.4 samples points per whole cycle out..

That 102.4 will actually deliver as something like a repeating  sample set of  (102+103+102+103+102)/5  = 102.4 average.

 

 

Last Edited: Sat. May 27, 2017 - 02:15 AM