*Note: I found this one in my drafts folder on Blogger, never quite completed. I seem to recall writing up examples of other data sheets containing gibberish, but as of 2022 I cannot find those notes. I think it makes a useful point, so I’m uploading it even in its unfinished state.*

I’ve recently started a new job in which I am again doing embedded software development, this time for a system built around an Atmel SAM4E microcontroller.

This is a nice chip with a lot of powerful peripherals and a modern programming model (for example, the use of separate set, clear, and read registers for certain peripheral control bits, which saves me cycles by allowing me to perform a single write to turn on and off specific bits without having to do a careful atomic read, change, and write). I have some history with ARM systems and in general I’m quite comfortable with them. We’re using FreeRTOS which has been very effective for us, providing some very nice amenities such as queues, binary sempahores, direct-to-task notifications, and event groups, all of which have simply just worked, usually on the first try. The only bugs I’ve found while working with FreeRTOS so far have been my own.

The hardware datasheets have been less impressive. It appears that the process of writing data sheets is entirely outsourced now, to developers in contract shops, probably overseas, and who do not have the programming expertise to understand that what they are writing and drawing is, simply, nonsense.

The datasheet for a DAC has one job: to explain how to use the part. It has to explain both its hardware aspects and its software aspects. I am not, strictly speaking, a qualified electrical engineer, so I can’t entirely vouch for the description of how to connect and interface the part, but if it is anything like the description of the software interface, I pity hardware designers.

A digital-to-analog converter, or DAC, receives a binary number of some defined number of bits and uses this number to set its voltage output. We are using the Analog Devices AD5734, which seems like a nice, capable part, although we were a bit surprised by the degree of output error in practice. Let’s assume we’ve gotten the SPI interfacing taken care of and look at how we need to encode the digital values to produce a given voltage. On page 20 of revision D of the datasheet, we are presented with a transfer function for unipolar output, and another for bipolar output. In the first version, we are told that:

`Vout = Vrefin x Gain\[D/2^N\]`

where **D** is the decimal equivalent of the binary code sent to the DAC, **N** is the bit resolution of the DAC, **Vrefin** is the reference voltage coming into the **REFIN** pin, and gain is a value from a lookup table, based on the selected output range.

This is nonsense.

Let’s say I want to use the 14-bit version of the part to generate a voltage in the range 0..10V. My gain value is 4 (from the chart), and my Vrefin is 2.5. 10 = 2.5 x 4 x [D / 2^14].

How about zero volts? Given that setup, this would give me 0 = 10 x D / 16384, or D = 0. So to produce zero volts (or some real-world value as close to zero volts as possible), we would feed a zero into the DAC. That seems sensible.

How about ten volts? That gives me 10 = 10 x D / 16384. Solving for D, I get D = 16384.

That number won’t fit in a 14-bit register. In other words, it’s nonsense.

Let’s review how numbers work, shall we? In decimal, a three-digit number can take on the values 000 through 999, yielding 1,000 possible values. (In practice, we leave off leading zeroes). In binary, a 14-digit number can take on the values 00000000000000 through 11111111111111. 11111111111111 is, in decimal, 16,383. So clearly that voltage expression needs to include a 2^N - 1. Not doing so is as dumb as saying “let’s pick a three-digit decimal number in the range from zero to 1,000.”

The second formula provided on page 10, for calculating voltage out when the chip is configured to use a bipolar output range, suffers from the same problem. If I followed it literally, and wanted to configure my DAC for voltages in the range -10..+10V, I would use a gain value of 8. With that same 2.5V reference input, this would give me Vout = 20 x D / 16384 - 10. That makes sense; it’s like the other one, but double the range. So if I want -10V, I’d feed it zero. If I want +10V, I’d feed it 16384. Again, nonsense.

Surely there must be some examples, right?

Let’s look at page 22. We’ll start with the simplest example, using what the datasheet calls “straight binary coding,” where zero represents zero and the highest possible binary number represents the highest possible voltage. Table 12, middle column, 0..10V range. The top of the chart shows our highest output value, fourteen ones. With the output voltage +4 x refin x (16,383/16,384). Using our 2.5V reference, giving the DAC its largest possible value, 9.9993896484375V, while its lowest value is 0V.

Nonsense.

This math would mean that the DAC is inherently incapable of mapping the highest input value to the highest output value. The range of fourteen-bit binary numbers is, by definition, divisible by two. It is not like the range 0..10V, which has a precise middle value of 5V. Any range of numbers representable by a fixed number of bits produces a number of values that is divisible by two. There is no middle value, unless the designers of this chip did something quite odd, and then entirely failed to clearly document it. Which, come to think of it, may have happened, but I doubt it.

To understand this it is valuable to consider the related case of an analog-to-digital converter. Let’s consider a very simple one, a two-bit ADC (I’m not sure such a part exists, but humor me).

An ADC is kind of like a game in which you throw balls into bins. By determining which bin the ball lands it, it ought to be possible to determine, roughly, the trajectory of the ball. Let’s not take that analogy too far, or I’ll have to talk about air resistance, gravity, ricochet, etc.

If we know in advance that our tosses will have a defined range of velocities, say, zero through ten miles per hour, and a given starting point, we could design a series of bins to catch the balls, with a backstop so that balls will always fall into one of the bins. If we wanted to be able to divide that range into four, we’d have four bins. How would we label the four bins?

It wouldn’t be accurate to label the first bin zero and the last bin ten. Those are the *endpoints* of the range we cover.

Statistically, each bin would cover a range of 2.5 mph. It would make the most sense to characterize the velocity of the balls falling into the first bin as *about* 1.25 mph, not zero, and the velocity of the balls falling into the last bin as *about* 8.75 mph, not 10. In other words, the characterized digital value should represent the midpoint of each “bin.”

Now imagine a two-bit DAC connected to this two-bit ADC. Shouldn’t the output values of the DAC be characterized the same as the output values of the ADC? If they aren’t, with this simple system, you could lose a quarter of your range. The highest input value going into the DAC would produce an output voltage that was likely to fall into the third bin, not the fourth.

The Analog Devices DAC can be configured to use signed twos complement coding instead of unsigned binary coding (with an implied offset). However, this does not really help the “no zero value” problem. In twos complement encoding, there is always one more negative value than there are non-negative values. To match the behavior of the offset binary coding, zero would be generated by -1, or binary 11111111111111. Instead the datasheet shows zero volts generated by zero. This would mean that negative voltages are produced in the range 0..8191, zero is produced at 8192, and positive voltages are produced in the range 8193 to 16383.

Let’s reduce this to the case of a two-bit DAC, capable of producing four values in the range -10..+10V. These values would not be -10, -5, 0, and +5, would they? Of course not. That would leave off the DAC’s actual maximum. The values would be split the 20V range into four “bins.” The nominal values of these four “bins” would be -7.5V, -2.5V, 2.5V, 7.5V.

There’s another option: to control the DAC using signed twos complement numbers. In this case, we’re trying to drive our DAC to produce one of four possible values, but our two-bit values can only specify -2, -1, 0, and 1. What now? Well, we’d need an implied offset.

Our real-world ADCs and DACs have exactly this problem with respect to their datasheets; it’s not at all subtle when an implementer tries to follow a formula which will produce a nonsensical result for one or more voltage values! And so, these datasheets must be taken with a grain of salt, and the resulting driver code should be *very* clearly commented.

This work by Paul R. Potts is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. The CSS framework is stylize.css, Copyright © 2014 by Jack Crawford.