One Bit Sigma Delta DAC

XCore Project reviews, ideas, videos and proposals.
User avatar
Andy
Respected Member
Posts: 279
Joined: Fri Dec 11, 2009 1:34 pm

One Bit Sigma Delta DAC

Post by Andy »

Does anyone have expertise in the area of Sigma Delta modulation on DSPs? I'm looking to improve the quality of a one bit DAC in software on the XMOS but not sure where to start.

My understanding is that the process involves upsampling the PCM and applying a noise shaping filter - unfortunately I'm finding it difficult to find any sources to suggest how to do this algorithmically in software.


User avatar
Woody
XCore Addict
Posts: 165
Joined: Wed Feb 10, 2010 2:32 pm

Post by Woody »

Andy wrote:My understanding is that the process involves upsampling the PCM and applying a noise shaping filter - unfortunately I'm finding it difficult to find any sources to suggest how to do this algorithmically in software.
Yes you want to increase the sample frequency, then do the noise shaping. There are two stages to increasing the frequency: upsampling and then filtering. Upsamping is just adding extra zero samples to get to the new sample rate. However high frequencies are introduced in the upsampling process that need to be filtered out.

This web page has useful information about increasing the sample frequency (interpolation) http://www.dspguru.com/dsp/faqs/multirate/interpolation. This website is useful for designing the filter you need http://www.dsptutor.freeuk.com/remez/Re ... esign.html. You need to know what characteristics your filter needs to be. I can help you with this if you need it.

I have written a sigma delta DAC in C (code below) which produces two sine waves. The output here was printing to a file with sample values and times. Because it's a sigma delta converter the samples values are either 0 or 2^31. I used this format because I was doing simulations and the output file formed the input stream to an FFT. You should be able to analyze it to understand how it works.

This sigma delta has first order noise shaping. I don't know how to implement higher order noise shaping, but I'm keen to find out! If anyone knows please post!

Code: Select all

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
//#include <xs1.h>
//#include <print.h>

#define TEST_FREQ        1000.0
#define TEST_WAVELENGTHS   10.0
#define TEST_TWIST_FREQ   800.0
#define TEST_TWIST_RATIO    2.0

#define SIGMA_DELTA_SAMPLE_STEP 1000000
#define SIGMA_DELTA_TIME_STEP   (100/10) // in units of 10ns

#define AMPLITUDE_DB 0 //-60.0 // e.g. 0 or -60



int sineWave(float time) { // Time step is 10ns
//  return floor(0.5 + 900000000 * sin(time * 2 * M_PI * TEST_FREQ / 100000000));
    return floor(0.5 +
                 300000000 * sin(time * 2 * M_PI * TEST_FREQ       / 100000000)
               + 300000000 * sin(time * 2 * M_PI * TEST_TWIST_FREQ / 100000000)/ TEST_TWIST_RATIO
                );
}


extern long lrand48(void);

#define printint(x) printf("%d", (int)(x));
#define printstr(x) printf("%s", x);

void doDac() {

    // If the sample is greater than the integral, output a 1
    float sample;
    float sampleError;              //     Difference between input signal & integral of prev samples
    float integral = (1 << 30);     //     Sum of all the outputs weighted.
    float time = 0;         // Time step is 10ns
    int   output = 0;               // Current output
    int   nextOutput;

    float volume = pow(10.0, (AMPLITUDE_DB/20.0)) ;

    printf("0 0\n"); //  Show output is 0

    for (time = 0; time < (TEST_WAVELENGTHS *100000000/(float)TEST_FREQ); time +=SIGMA_DELTA_TIME_STEP ) { // Time step is 10ns

        // Get Sample for this time
        sample = ((unsigned)(sineWave((float)time) * volume + (1 << 30))) ;

        sampleError = sample - integral;
        if ( sampleError>= 0) {
            if ( output == 0) {
                // Drive output to 1 for first cycle
                output = 1;
                printf("%.1f",(time)*10);
                printstr(" 0\n");
                printf("%.1f",(time)*10);
                printstr(" 2147483648\n");
            }
            integral += SIGMA_DELTA_SAMPLE_STEP;

        } else {
            // sampleError < 0
            if ( output == 1) {
                // Drive output to 0 for first cycle
                output = 0;
                printf("%.1f",(time)*10);
                printstr(" 2147483648\n");
                printf("%.1f",(time)*10);
                printstr(" 0\n");
            }
            integral -= SIGMA_DELTA_SAMPLE_STEP;
        }

//printf("time %6.1f sample %6.1f sampleError %6.1f output %d integral %6.1f \n", time*10, sample, sampleError, output, integral);


    }
}

int main(void) {
    doDac();
    return 0;
}
User avatar
lilltroll
XCore Expert
Posts: 956
Joined: Fri Dec 11, 2009 3:53 am
Location: Sweden, Eskilstuna

Post by lilltroll »

Is it just a software thing, or do you aim to use it on a XMOS pin out?

If its include hardware: First of all, you need an buffered output with low jitter.
If you look at the 3.3V rail you will proably se that it is a noisy one since it is used to supply the digital part. I guess you need to use a low noise LDO which feeds the output-buffer (or a one chip solution).
The next thing is timing accuricy. Depending on the slew rate and noise, I guess that you might need to resample the XMOS output whith a stable clock source. Even if the XMOS output doesn't have jitter problem, and noisy output due to a noisy rail will introduce a timing error. Resampling is very easy in this case - take a look at the S/PDIF output in the schematics for the USB Audio 2.0.
When you have good accuricy both in the x-axis time and y-axis amplitude you have a foundation to improve things. You can calulate the need of precision in time and amplitude if you know your design goal of efficient bits. I guess its more than 7 or 8 bits if you consider noise shaping. You can also use analog feedback to correct for the errors introduced in the output transistors. With a dual rail you can use a H-bridge to increase the output voltage.
Probably not the most confused programmer anymore on the XCORE forum.
User avatar
Andy
Respected Member
Posts: 279
Joined: Fri Dec 11, 2009 1:34 pm

Post by Andy »

Thanks for your replies. My assumption is that by doing the processing in software, I can reduce harmonic distortion and only need a simple LPF arrangement on the XMOS pin out, thus reducing the BOM and cost.

I should point out that I only need acceptable quality for casual listening to MP3 compressed audio. The one bit DAC floating around here (as used in the WAV player project) isn't bad but seems to suffer from noise which is noticeable without a filter with a fairly low cut off. I'm not sure if this is solely harmonic or related to jitter on the XMOS output (or both?).

Any suggestions?
User avatar
lilltroll
XCore Expert
Posts: 956
Joined: Fri Dec 11, 2009 3:53 am
Location: Sweden, Eskilstuna

Post by lilltroll »

The first limitation.

Suppose that you would like to make a DC-genrator with 2^16 steps (16-bit)

To create 0V on the output you would only put 0 to the DAC.
If you would like to go one bit higher, you would use 2^16-1 zeros and 1 one. The meanvalue of that serie would be that one bit higher. Running at 100 MHz, you can only output around 1500 such 2^16 long series / s. So even with a "perfect output filter" and everything else ideal, you will not be able to reproduce frequencies above 750 Hz in 16 bits.

The output is not fast enough to provide both large bandwith and bitdepth.

With 12 bits the series length would be 4096 - creating 24k "words" / s, with a need of a perfect LP filter at 12 kHz.
Reduce it to 11 bits, and you can use the human ear as a steep lowpass filter, but any nonlinear parts in the chain may create intermodulation distortion that will be heard below 20 kHz.

But - how to get the best perceptual sound out of the XMOS with very fey external parts is a good quest.
Probably not the most confused programmer anymore on the XCORE forum.
User avatar
Andy
Respected Member
Posts: 279
Joined: Fri Dec 11, 2009 1:34 pm

Post by Andy »

Very interesting lilltroll - I see the limitation.

Here's the current PWM code:

Code: Select all

void PWM_slave(chanend c_samples, port pin)
{
	unsigned int time;
	unsigned int duty_cycle;
	unsigned int state;
	unsigned int pwm_frequency = 100000;
	unsigned int period = (XS1_TIMER_HZ / pwm_frequency);
	short sample;
	timer t_pwm;

	state = 0;
	t_pwm :> time;
	while(1)
	{
		select
		{
		case c_samples :> sample:
			duty_cycle = ((sample+(1<<15)) * period)>>16;
			break;
		case t_pwm when timerafter(time) :> int _:
			pin <: state;
			if(state)
				time += duty_cycle;
			else
				time += period - duty_cycle;
			state = !state;
			break;
		}
	}
}
I'm not sure where the noise is coming from and hence I'm having difficulty improving the algorithm.
Last edited by Andy on Sat Feb 13, 2010 1:02 am, edited 1 time in total.
User avatar
shawn
XCore Addict
Posts: 238
Joined: Thu Dec 17, 2009 5:15 am

Post by shawn »

User avatar
lilltroll
XCore Expert
Posts: 956
Joined: Fri Dec 11, 2009 3:53 am
Location: Sweden, Eskilstuna

Post by lilltroll »

It doesn't need to be a living hell to place a Sigma-Delta converter outside the chip. This on has a stereo output in a 8 SOIC.

http://www.wolfsonmicro.com/products/WM8762

You can also get them with an internal power amplifier for headphones, speakers up to 1 W RMS or both, then you can have internal mixers, EQ and so on.

To reduce the number of external clocks, either you use a MCLK that can be used to clock the XMOS with help of the internal PLL in the XMOS, or you can use a DAC with an internal PLL. So even if you need a 8.00 MHz clock for USB 1.0, you should be able to use the same clock for the XMOS and DAC as well. Good for a "Lo-Fi" solution :mrgreen:
Probably not the most confused programmer anymore on the XCORE forum.
lurcher
Member
Posts: 14
Joined: Sun Jan 24, 2010 5:53 pm
Contact:

Post by lurcher »

To create 0V on the output you would only put 0 to the DAC.
If you would like to go one bit higher, you would use 2^16-1 zeros and 1 one. The meanvalue of that serie would be that one bit higher. Running at 100 MHz, you can only output around 1500 such 2^16 long series / s. So even with a "perfect output filter" and everything else ideal, you will not be able to reproduce frequencies above 750 Hz in 16 bits.
Not sure its entirely as you say there, I think what you are describing is PWM not sigma delta. For example the DSD stream from a SACD uses a 1 bit 2.8224 MHz sample rate.

I think this may be enough to get started

http://www.beis.de/Elektronik/DeltaSigm ... Delta.html

I should read more about this stuff :-)
User avatar
lilltroll
XCore Expert
Posts: 956
Joined: Fri Dec 11, 2009 3:53 am
Location: Sweden, Eskilstuna

Post by lilltroll »

Yes it is PWM, since he wants to 'listen' to the XMOS pin-output directly as I understood it (ok maybe some first order filtering) The problem is that the time-resolution that can be achieve is (only) 10 ns.

The example with convering a signal from analouge to PCM, by first running a second order DeltaSigma converter and thereafter use sinc3 (can be made by logic without multiplication) filters is something else, but the PWM limitation will still be there, it he want's to 'listen' to the output directly.

I'm not sure that the first input to the system is, and the final output - but if the goal is to listen to MP3 from one of the XMOS pin, the 10ns limitation is a heavy one.
Probably not the most confused programmer anymore on the XCORE forum.
Post Reply