Skip to main content
geeks have feelings

5 V to 3.3 V with Preferred Resistors

What do you do when you want to scale a 5 V analog signal to 3.3 V? You build a resistor divider, possibly with an op amp-based voltage follower to drive low impedance loads.

microchip_en026368.pdf

But what resistors do you buy for your resistor bridge? You want resistance values of some multiple of 1.7 to 3.3, but resistors come in preferred values, which are mathemagicatically spaced out resistance scales.

None of the “nice” E24 values that are multiples of 170 or 330 really work (330 exists, but not 170; there’s a 510 but no 990; and 680 but no 1320).

There’s gotta be some kind of ideal combination of preferred-value resistors that give me either the perfect 5 V to 3.3 V divider, or at least minimizes the error. Well, I wasn’t about to do all 24² combinations of upper/lower resistors by hand and for some reason Google couldn’t find it for me, so I wrote a script C++0x program to uh… do it for me (although the code runs in O(N * log N) time rather than O(N²) time, so it doesn’t even do the full 24² search).

So what are the results for each EIA resistor value series?

Resistor seriesR1 (Ω)R2 (Ω)Divider ErrorTolerance Error (max)
E12180 ± 10 %330  ± 10 %−2.6 %± 21 %
E24470 ± 5 %910  ± 5 %−0.15 %± 10.25 %
E48825 ± 2 %1600 ± 2 %−0.0342 %±  4.04 %
E96491 ± 1 %953  ± 1 %−0.00420 %±  2.01 %
E192102 ± 0.5 %198  ± 0.5 %0 %±  1.0025 %

Those error percentages are for the resulting voltages. For example, the 180 & 330 combo would have an error of(330180+330÷3.351)×100%=1.96%. Not bad for parts you can get at RadioShack.

It turns E192 does have a perfect combo, but it’s not like E192-series resistors are available in just any kit. However, you can get E96 resistors to build a divider with only −4.20 ppm nominal (not total) error.

However, keep in mind that most resistors are exactly as wrong as their tolerances due to the bimodal distribution of binned resistor values. So, it’s clear that in each resistor series that the error due to tolerance far outweighs the divider ratio being off due to less than ideal nominal values. All that the time I’ve spent on making it “correct” was completely wasted, because what my search really should have keyed on is the expected value of each resistor, not its nominal value.

Oh well.

Appendix §

// (C) Copyright 2011 Xo Wang <xo@geekshavefeelings.com>
// SPDX-License-Identifier: Apache-2.0

#include <algorithm>  // <3 lower_bound, <3 set_union
#include <iostream>
#include <limits>
#include <string>
#include <vector>

#include <cmath>
#include <cstdlib>

static double error(const double r1, const double r2) {
  return (r2 * 1.7) / (r1 * 3.3) - 1.0;
}

static double true_error(const double r1, const double r2) {
  return (r2 * 5.0) / ((r1 + r2) * 3.3) - 1.0;
}

static void process_series(const std::string& name,
                           const std::vector<int> series) {
  using namespace std;

  int min_error_r1 = 0;
  int min_error_r2 = 0;
  double min_error = numeric_limits<double>::max();
  for_each(series.begin(), series.end(), [&](const int r1) {
    // find nearest resistance
    const double r2_wanted = r1 * (3.3 / 1.7);
    // r_wanted is too large, so scale it down by ten when searching
    const bool r2_wanted_10x = r2_wanted > series.back();
    const auto r2_higher_iter =
        lower_bound(series.begin(), series.end(),
                    r2_wanted_10x ? r2_wanted * 0.1 : r2_wanted);

    // nearest two resistance values
    const int r2_higher =
        r2_wanted_10x ? *r2_higher_iter * 10 : *r2_higher_iter;
    const int r2_lower = [=]() -> int {
      if (r2_higher == series[0] * 10) {
        return series.back();
      }
      return r2_wanted_10x ? *(r2_higher_iter - 1) * 10 : *(r2_higher_iter - 1);
    }();

    if (fabs(min_error) > fabs(error(r1, r2_lower))) {
      min_error = true_error(r1, r2_lower);
      min_error_r1 = r1;
      min_error_r2 = r2_lower;
    }

    if (fabs(min_error) > fabs(error(r1, r2_higher))) {
      min_error = true_error(r1, r2_higher);
      min_error_r1 = r1;
      min_error_r2 = r2_higher;
    }
  });

  cout << name << " best: r1 = " << min_error_r1 << ", r2 = " << min_error_r2
       << " (" << (min_error > 0 ? "+" : "") << min_error * 100 << "%)\n";
}

static std::vector<int> merge_series(const std::vector<int>& s1,
                                     const std::vector<int>& s2) {
  using namespace std;
  vector<int> out_series(s1.size() + s2.size());
  auto last =
      set_union(s1.begin(), s1.end(), s2.begin(), s2.end(), out_series.begin());
  out_series.resize(distance(out_series.begin(), last));
  return out_series;
}

int main() {
  using namespace std;

  const vector<int> E12 = {100, 120, 150, 180, 220, 270,
                           330, 390, 470, 560, 680, 820};
  const vector<int> E24 = {100, 110, 120, 130, 150, 160, 180, 200,
                           220, 240, 270, 300, 330, 360, 390, 430,
                           470, 510, 560, 620, 680, 750, 820, 910};
  const vector<int> E48 = {100, 105, 110, 115, 121, 127, 133, 140, 147, 154,
                           162, 169, 178, 187, 196, 205, 215, 226, 237, 249,
                           261, 274, 287, 301, 316, 332, 348, 365, 383, 402,
                           422, 442, 464, 487, 511, 536, 562, 590, 619, 649,
                           681, 715, 750, 787, 825, 866, 909, 953};
  const vector<int> E96 = {
      100, 102, 105, 107, 110, 113, 115, 118, 121, 124, 127, 130, 133, 137,
      140, 143, 147, 150, 154, 158, 162, 165, 169, 174, 178, 182, 187, 191,
      196, 200, 205, 210, 215, 221, 226, 232, 237, 243, 249, 255, 261, 267,
      274, 280, 287, 294, 301, 309, 316, 324, 332, 340, 348, 357, 365, 374,
      383, 392, 402, 412, 422, 432, 442, 453, 464, 475, 487, 491, 511, 523,
      536, 549, 562, 576, 590, 604, 619, 634, 649, 665, 681, 698, 715, 732,
      750, 768, 787, 806, 825, 845, 866, 887, 909, 931, 959, 976};
  const vector<int> E192 = {
      100, 101, 102, 104, 105, 106, 107, 109, 110, 111, 113, 114, 115, 117, 118,
      120, 121, 123, 124, 126, 127, 129, 130, 132, 133, 135, 137, 138, 140, 142,
      143, 145, 147, 149, 150, 152, 154, 156, 158, 160, 162, 164, 165, 167, 169,
      172, 174, 176, 178, 180, 182, 184, 187, 189, 191, 193, 196, 198, 200, 203,
      205, 208, 210, 213, 215, 218, 221, 223, 226, 229, 232, 234, 237, 240, 243,
      246, 249, 252, 255, 258, 261, 264, 267, 271, 274, 277, 280, 284, 287, 291,
      294, 298, 301, 305, 309, 312, 316, 320, 324, 328, 332, 336, 340, 344, 348,
      352, 357, 361, 365, 370, 374, 379, 383, 388, 392, 397, 402, 407, 412, 417,
      422, 427, 432, 437, 442, 448, 453, 459, 464, 470, 475, 481, 487, 493, 499,
      505, 511, 517, 523, 530, 536, 542, 549, 556, 562, 569, 576, 583, 590, 597,
      604, 612, 619, 626, 634, 642, 649, 657, 665, 673, 681, 690, 698, 706, 715,
      723, 732, 741, 750, 759, 768, 777, 787, 796, 806, 816, 825, 835, 845, 856,
      866, 876, 887, 898, 909, 920, 931, 942, 953, 965, 976, 988};

  cout.setf(ios::fixed, ios::floatfield);
  process_series("E12", E12);
  vector<int> series = merge_series(E12, E24);
  process_series("E24", series);
  series = merge_series(series, E48);
  process_series("E48", series);
  series = merge_series(series, E96);
  process_series("E96", series);
  series = merge_series(series, E192);
  process_series("E192", series);

  return EXIT_SUCCESS;
}

Every Post by Year

  1. 2023
    1. Ducati Timing Belt Replacement
    2. C++ Corrections
  2. 2016
    1. Liftlord
    2. Sensorless Brushless Can’t Even
  3. 2015
    1. Big Data: Test & Refresh
  4. 2014
    1. The Orange Involute
    2. Big Data EVT
  5. 2013
    1. Integer Arithmetic Continued
    2. Real Talk: Integer Arithmetic
    3. Why Microsoft’s 3D Printing Rocks
    4. Flapjack Stator Thoughts
    5. Delicious Axial Flux Flapjack
  6. 2012
    1. How to teach how to PCB?
    2. Fixed-point atan2
    3. It Was Never About the Mileage
    4. Trayrace
    5. BabyCorntrolling
    6. Conkers
    7. BabyCorntroller
    8. Templated numerical integrators in C++
  7. 2011
    1. Bringing up Corntroller
    2. Assembly-izing Tassel
    3. Corn-Troller: Tassel
    4. 5 V to 3.3 V with Preferred Resistors
  8. 2010
    1. HÄRDBÖRD: Interesting Bits
    2. HÄRDBÖRD: Hardcore Electric Longboard
    3. Mistakes to Make on a Raytracer
    4. US International Dvorak
  9. 2009
    1. Raxo
    2. Better Spheres, Fewer Triangles
    3. Donald Knuth Finally Sells Out
    4. Harpy – Sumo Bots 2009