Finer controll of DAC ?? - Page 1
Forum section: electronics / beginners

author: IamSynthetiC
author_rank: Regular Contributor
timestamp: July 04, 2020, 09:38:26 am
content: Greetings,i am working on a project in which i need to replace a 10-turn pot with a DAC, so the device is then a programable one. I planned touse a voltage divider oran OpAmp to drop the DAC's output from 5V (max) to about 0.48V (attenuation ~ 0.1). (10-bit DAC gives resolution of 0.0005V after attenuation).My question is :Are there any ways to better the resolution other than investing in more bits?and there any alternatives?Thank you for your time

author: dmills
author_rank: Super Contributor
timestamp: July 04, 2020, 09:48:53 am
content: Dither and lowpass the DAC output.

author: Kleinstein
author_rank: Super Contributor
timestamp: July 04, 2020, 10:16:36 am
content: If the adjustment is done iterative, one can use separate DACs for coarse and fine. So 2 x 8 bit in stead of 14 bits. It may need care with noise though.

author: daqq
author_rank: Super Contributor
timestamp: July 04, 2020, 11:07:06 am
content: You can PWM and then low pass the output - continuously make the DAC switch between two nearby states, separated by 1 LSB. And there's always the option of using a higher res DAC - they are pretty cheap these days.

author: Ground_Loop
author_rank: Frequent Contributor
timestamp: July 06, 2020, 01:18:46 am
content: How about using a 16-bit serial DAC like one of these MAX5541CSA. They're really easy to work with on SPI.

author: IamSynthetiC
author_rank: Regular Contributor
timestamp: July 06, 2020, 10:41:21 am
content: Thank you all for your replies,they have all proved very helpfull. it will take some time to research them all and see what fits my needs best.Another question i have though is this, what happens when the resolution you get in volts (2^bits/Voutmax) is a number such as 0.12345, which produces uneven steps? how do you deal with this ?Quote from: Ground_Loop on July 06, 2020, 01:18:46 amexcellent suggestion, i think i'll go with one of those.Quote from: daqq on July 04, 2020, 11:07:06 amI couldnt find any information about this. Why would you do that ?

author: daqq
author_rank: Super Contributor
timestamp: July 06, 2020, 10:49:47 am
content: QuoteClassic PWM DACs work by passing a two value (digital) signal through a low pass filter. Depending on the ratio of how much time you spend in either state you get a DC value. If you do the same, only with two different settings of the DAC you can move between them pretty fluently. Tricky on timing, but using DMA, Timers and hardware SPI it can be done.

author: brucehoult
author_rank: Super Contributor
timestamp: July 06, 2020, 10:37:54 pm
content: Quote from: IamSynthetiC on July 06, 2020, 10:41:21 amTo average them.This technique works well enough that about 30 years ago all the highest-end consumer CD players were suddenly advertising that they used "1-bit DAC". Which was true. They used a simple on/off signal modulated at a few MHz, followed by a low pass filter. It's very cheap to do, and with sufficient oversampling and filtering provides near perfect DC linearity, which is very very expensive to get using e.g. resistor networks.Most microcontrollers use PWM to implement analog outputs to this day.

author: brucehoult
author_rank: Super Contributor
timestamp: July 06, 2020, 10:38:53 pm
content: See also micro-stepping of stepper motors.

author: IamSynthetiC
author_rank: Regular Contributor
timestamp: July 07, 2020, 01:59:27 pm
content: ohhhhh, so i can use PWM to output an analog value "between" the digitalToAnalog values of the DAC ? I never would have though of that.But what about errors ?from my understanding the analog_value(Vout) would be Amplitude(A)*DutyCycle(D), therefore ΔVout=sqrt((ΔA*D)^2+(A*ΔD)^2), which doesn't seem that bad.Doing the rest of the math (with ΔΑ=ΔD=1%) the results are more than satisfactory, but how is it in practice ?Finaly, it seems i would have to come up with a table of Duty Cycles for each available programmable value (10.000 values) and also test them, which seems time consuming.are there any smarter ways of doing it ?Thanks again for your input.

author: Kleinstein
author_rank: Super Contributor
timestamp: July 08, 2020, 08:41:44 am
content: Modulation the DAC to produce extra resolution may work, but it depends. If the DNL is relatively poor, as with some cheap ones, the interpolation between the poor starting point is of limited use. In this case one may even choose to ignore the lowest bit of the DAC and better modulate a little higher up. Another point could be charge injection inside the DAC: frequent switching can add errors. Also just the digital traffic can cause extra interference.A big plus of using PWM is that it is monotonous by design. This can be important in some iterative processes - though the low speed can also be a big downside.

author: mikerj
author_rank: Super Contributor
timestamp: July 08, 2020, 11:20:48 am
content: Quote from: IamSynthetiC on July 07, 2020, 01:59:27 pmYou don't need a table, just some very simple maths. Treat the high resolution DAC setpoint as two parts, integer and fractional (i.e. DAC bits + PWM bits). At a regular interval (e.g. timer interrupt) add your DAC setpoint to an accumulator and extract the integer bits, set the DAC with it and then clear the integer bits. The fractional bits remain for the next time.This scheme is more suited for built in DACs that can be quickly updated via a register write or two rather than external DACs on an SPI or I2C bus since a timer interrupt can ensure a low(ish) jitter update rate at reasonable frequencies with low CPU overhead.Something like this, which provides a 14 bit resolution using a 10 bit DAC and 4 PWM bits. Please note this is untested, but shows the principle.Code:static volatile uint16_t dacHiResSet = 0;static volatile uint16_t dacAcc = 0;#define DAC_PWM_BITS 4#define DAC_RAW_BITS 10#define DAC_HIRES_MAX ((1U<<(DAC_PWM_BITS+DAC_RAW_BITS))-1)void SetHiResDac(uint16_t newDac){/* Limit maximum DAC value */if (newDac > DAC_HIRES_MAX ){newDac = DAC_HIRES_MAX;}__disable_timer_irq(); /* Disable timer interrupt whilst updating DAC variables */dacHiResSet = newDac; /* Set new hi-res DAC value */dacAcc = 0; /* Clear accumulator */__enable_timer_irq(); /* Re-enable interrupt */}void TimerISR(void){uint16_t dacOut;/* Add high resolution DAC setpoint to accumulator */dac1Acc += dacHiResSet;/* Extract integer DAC value from accumulator */dac1Out = (uint16_t)(dac1Acc>>DAC_PWM_BITS);/* Clear integer bits in accumulator, leave only fractional bits */dac1Acc &= (1U<<DAC_PWM_BITS)-1;/* Update the DAC with PWM value */SetDac(dacOut);}

author: IamSynthetiC
author_rank: Regular Contributor
timestamp: July 11, 2020, 12:49:43 pm
content: Sorry for taking so long to reply.So most of the improvements are software related? no special external components required?By the way the project aimed toward converting an analog controlled electronic load to a programable one.To sum up, I would need atleast one 14-bit DAC for a resolution of 1mA (using the PWM technique) (testing required !!!).A quick-to-react lowpas filter, and an OpAmp for protection.Hardware-wise it seems almost ....simple.I am not good with coding but thankfully I've got a programmer friend joining the project soon.So is it safe to build something like this now, and improve it later in software? Or am i missing something?Thanks again for your inputs, you have all been more than helpfull.

author: Kleinstein
author_rank: Super Contributor
timestamp: July 11, 2020, 01:22:50 pm
content: For low ripple the PWM DAC needs a relatively good low pass filter. Even than PWM + filter is a slow DAC:Starting from some 16 MHz, 14 bit PWM would be at some 1 kHz PWM frequency. For low ripple with a 2 nd order low pass filter one would need cross over in the <100 Hz range, which it where it starts to need relatively large caps. So depending on the ripple target one may want more than a 2 nd order filter.One can improve a little on this in software by using a sigma delta type modulation to start with less low frequency ripple, but this takes some time from the µC.

author: tooki
author_rank: Super Contributor
timestamp: July 11, 2020, 06:45:41 pm
content: How often do you need to update the value? If it's not very often, you can use high-resolution, low frequency PWM. For example, in an Arduino, you can change it from 8-bit/490Hz to 16-bit/244Hz, which is more than enough for lots of applications. Then just use a low-pass filter (like Kleinstein explains) to extract the analog value.

author: ogden
author_rank: Super Contributor
timestamp: July 11, 2020, 08:07:16 pm
content: Quote from: Kleinstein on July 11, 2020, 01:22:50 pmWith delta-sigma type modulation one can improve much. For near-DC DAC application all you need is RAM and means to output bitstream meaning fast and glitchless SPIperipheral, preferably with DMA capabilities. One calculate how many out of 2^14 bits shall be set to "1", then spread them evenly in 2^14 bit (2KB) array, output it in loop with SPI using DMA. Done. Simple RC filter is fine for such DAC in many applications.

author: brucehoult
author_rank: Super Contributor
timestamp: July 12, 2020, 01:04:10 am
content: Quote from: ogden on July 11, 2020, 08:07:16 pmYou mean using a digital output as 1-bit DAC?I don't think I'd call that SPI as you don't need any clock or handshaking, only shove the bits out a shift register.Calculating the bits is very simple:Code:#define MAXVAL (1<<14)int tot = MAXVAL/2;int nextBit(uint16_t val) {tot -= val;if (tot >= 0) {return 0;} else {tot += MAXVAL;return 1;}}The val argument can be constant or changing slowly (compared to the bitrate)This is simple enough that you can run pretty high bitrates just calculating the values on the fly, but yeah sure if you precalculate them into an array with MAXVAL 1-bit entries then you can use DMA to shove them out a shift register at very high speed.

author: ogden
author_rank: Super Contributor
timestamp: July 12, 2020, 05:22:18 am
content: Quote from: brucehoult on July 12, 2020, 01:04:10 amI did mean SPIperipheral. Original post adjusted.QuoteAre you sure your code will generateDelta-Sigmabitstream?