JOVANA
Library Glossary Getting Started Three Levels Fields How it works Mission
Join the mission
All guides

Reading Real Signals: ADC, DAC and Sampling

Your [[ee-microcontroller|microcontroller]] thinks in clean integers, but temperature, sound and light arrive as smooth, wobbly voltages. The bridge between those two worlds is the **analog-to-digital converter** — and its mirror image, the DAC, going the other way. In this guide you will see exactly how a sensor's 1.65 V becomes the number 512, why a 10-bit converter can only see in 3.2 mV steps, and how a single PWM pin plus one capacitor can fake an analog output. Get this layer right and your firmware stops lying about what the real world is doing.

Two worlds that don't speak the same language

Imagine handing a thermometer to a computer that can only count on its fingers. The thermometer's mercury rises *continuously* — it can sit at 23.4°C, 23.47°C, or any value in between, with infinite shades. The computer, on the other hand, can only ever hold whole numbers in a register. Somewhere between the smooth physical world and the discrete digital one, a translation has to happen. That translator is the analog-to-digital converter (ADC), and getting to know it is the moment embedded systems stop being abstract code and start touching reality.

Almost every sensor you will ever wire up speaks analog [[voltage|voltage]]. A photodiode brightens and its output climbs from 0.2 V to 2.9 V. A microphone wiggles a few tens of millivolts around a quiet midpoint. A potentiometer knob sweeps a wiper voltage smoothly across its travel. None of these are numbers yet — they are physical voltages on a wire. The ADC's job is to look at that wire and answer one question, over and over: *how high is this voltage, as a fraction of a known maximum?*

Resolution, reference, and the size of one step

An ADC has two numbers stamped on it that decide everything about its precision. The first is resolution, given in bits. An *n*-bit converter slices the input range into 2ⁿ levels — an 8-bit ADC has 256 levels (0…255), a 10-bit ADC has 1024 (0…1023), a 12-bit ADC has 4096. The second is the reference voltage, V_ref: the input voltage that produces the maximum code. Feed in V_ref and you read full-scale; feed in 0 V and you read zero. Everything in between is divided up evenly.

The single most important quantity you can derive from these two is the step size, sometimes called the *LSB voltage* — the voltage change that bumps the output code up by exactly one. It is simply the reference divided by the number of steps:

step_size = V_ref / 2^n

Worked example — 10-bit ADC, V_ref = 3.3 V
--------------------------------------------
  levels    = 2^10        = 1024
  step_size = 3.3 V / 1024 = 0.003222 V
            ≈ 3.2 mV per code

So a voltage on the pin maps to a code:

  code = round( V_in / step_size )
       = round( V_in * 1024 / 3.3 )

  V_in = 1.65 V  ->  code = round(1.65/0.003222) = 512   (mid-scale)
  V_in = 0.10 V  ->  code = round(0.10/0.003222) =  31
  V_in = 3.30 V  ->  code = 1023                          (full-scale)
  V_in = 3.40 V  ->  code = 1023   (clipped! ADC can't see past V_ref)
The whole of basic ADC arithmetic on one screen: step size sets resolution, anything above V_ref simply clips to full-scale.

That ~3.2 mV is the *finest distinction this converter can make*. Two voltages that differ by less than 3.2 mV may land on the same code — the ADC literally cannot tell them apart. This is your first real engineering constraint: if your sensor's interesting signal swings only 5 mV, a 10-bit ADC on a 3.3 V reference gives you barely two codes of usable range, which is hopeless. The fix is either more bits, a smaller reference matched to the signal, or an amplifier (an instrumentation amplifier is the classic choice) to blow the signal up *before* it reaches the converter.

Quantization: the unavoidable rounding error

Because the output must be a whole number, the ADC is forced to *round* every continuous input to the nearest tick mark. That rounding is called quantization, and the small difference between the true voltage and the value the code represents is quantization error. It is not a bug — it is a fundamental, designed-in consequence of squeezing an infinite-precision world into a finite number of bits. The best you can ever do is keep that error to within ±½ a step.

Analog input (smooth ramp)        ADC output (staircase)

  V                                    code
  |            /                        |        ___
  |          /                          |      _|
  |        /          quantize          |    _|        each riser
  |      /         ===========>         |  _|          = one step (3.2 mV)
  |    /                                | _|
  |  /                                  ||             flat tread = a band of
  |/_____________ t                     |____________  inputs that all read alike

  The vertical gap between the smooth line and the nearest
  step edge, at any instant, is the quantization error: ≤ ±½ LSB.
Quantization turns a smooth ramp into a staircase; the error is the gap between line and tread, bounded by half a step.

Here is the elegant part. If we treat that rounding as random noise sprinkled on top of the true signal, it has a known size, and it lets us predict the *best possible* quality of an ideal converter with a famous rule of thumb: each extra bit of resolution buys you about 6 dB of dynamic range. The full result for a perfect *n*-bit ADC driven by a full-scale sine wave is SNR ≈ 6.02 n + 1.76 dB. A 10-bit converter therefore tops out near 62 dB; a 16-bit audio converter near 98 dB. No real chip beats this — it is a ceiling set purely by the act of rounding.

Sampling: how often, and the trap of going too slow

Resolution answers *how finely* we measure each value; sampling answers *how often*. An ADC doesn't watch the signal continuously — it takes a snapshot at regular intervals, the sampling rate f_s, measured in samples per second (Hz). Reading a slowly-drifting battery temperature, ten samples a second is luxurious. Capturing audio, you need tens of thousands; digitizing a radio signal, tens of millions. The rate you choose is dictated almost entirely by how fast your signal can wiggle.

And there is a hard law you cannot cheat. The Nyquist–Shannon sampling theorem says you must sample at *more than twice* the highest frequency present in your signal. Sample too slowly and a fast wave masquerades as a slow one — a phantom that was never really there. This counterfeit is called aliasing, and it is the single most common way beginners ruin their data. It is the same effect that makes wagon wheels appear to spin backwards in old films: the camera's frame rate is undersampling the spokes.

True 7 Hz sine, sampled at only 8 Hz (too slow — need > 14 Hz):

  real signal:   /\    /\    /\    /\    /\     (7 cycles / sec)
  sample points:  o        o        o        o
  what you see:    \______/ \______/           a fake ~1 Hz wave!

Rule:  f_s  >  2 * f_max          (Nyquist criterion)
Guard: real systems use an *anti-aliasing* low-pass filter BEFORE
       the ADC to kill anything above f_s/2 *before* it can alias.
Undersample a 7 Hz tone at 8 Hz and it folds down into a bogus low-frequency wave — aliasing in action.

The return trip: DAC, and faking one with PWM

Everything so far has been one-way: world → numbers. But often you need to go the other direction — numbers → world. To set a motor's speed reference, paint a pixel's grey level on an old display, or push audio out to a speaker, your code holds a number and needs a *voltage* on a pin. That is the job of the digital-to-analog converter (DAC), the ADC run in reverse: hand it the code 512 with a 3.3 V reference, and it drives the pin to 1.65 V.

Here is the practical catch: plenty of cheap microcontrollers ship with a built-in ADC but no DAC at all. The classic workaround — and the reason rung 2 taught you pulse-width modulation — is that a PWM pin plus a simple low-pass filter is a surprisingly good fake. PWM blasts out a square wave that is fully on for some fraction of each cycle (the *duty cycle*) and fully off for the rest. Average that square wave over time and you get a steady DC level proportional to the duty: 25% duty on a 3.3 V supply averages to ~0.825 V.

PWM + RC low-pass filter  =  a cheap 1-channel DAC

   PWM pin  o───[ R ]───┬───o  V_out  (smooth analog level)
                        │
                       === C
                        │
                       GND

Duty cycle  ->  average output voltage
   duty = D (0..1),  V_out ≈ D * V_supply

   25% duty, 3.3 V  ->  V_out ≈ 0.825 V
   50% duty, 3.3 V  ->  V_out ≈ 1.65  V
   75% duty, 3.3 V  ->  V_out ≈ 2.475 V

Design rule: make the filter's time constant RC much LONGER than the
PWM period (RC >> 1/f_pwm) so the square wave is smoothed to ripple,
but not so long that V_out can't keep up when you change the duty.
One resistor and one capacitor turn a digital PWM pin into a usable analog output — the workhorse poor-man's DAC.

Signal-to-noise: the quality limit of the whole chain

Resolution tells you the *finest step* the converter can take, but it does not tell you whether that step is *meaningful*. If your wiring picks up 20 mV of electrical hash, then on a 3.3 V / 10-bit ADC your bottom *six codes* are pure garbage — they're flapping on noise, not signal. The honest measure of usefulness is the signal-to-noise ratio (SNR): how big the signal you care about is compared to the noise riding under it, almost always quoted in decibels.

Two ceilings combine to set the SNR you actually get. The first is the quantization floor from the previous section (≈ 6.02 n + 1.76 dB) — you can never do better than your bit count allows. The second is everything else: thermal noise in the resistors, supply ripple, board layout, a noisy reference, the sensor itself. Buying a 16-bit ADC and then feeding it a signal buried in 12 bits' worth of noise is wasted money — the noise, not the bits, sets your real precision. This is why measurement instruments (rung 6) obsess over grounding, shielding, and reference quality.

  1. Match the reference to the signal. Choose V_ref so your real signal fills most of the input range — wasted range is wasted bits.
  2. Filter before you sample. An anti-aliasing low-pass filter ahead of the ADC stops fast junk from folding down into your band.
  3. Sample fast enough. Keep f_s above twice your highest real frequency, with margin for the filter's gentle roll-off.
  4. Average to dig out detail. Averaging N independent samples lowers random noise by about √N — a cheap way to recover sub-LSB precision on a slow signal.
  5. Mind the layout. Keep the analog input short, away from switching and PWM lines; a clean board can be worth two extra bits.