Beefy Boxes and Bandwidth Generously Provided by pair Networks
The stupid question is the question not asked
 
PerlMonks  

comment on

( [id://3333]=superdoc: print w/replies, xml ) Need Help??

I needed to generate resistor pairings for a resistor voltage divider to generate voltages from 0 to full scale in 256 steps for use with configuration on a PCB. The following script parses a CSV file dumped out from component supplier DigiKey and from the over 500 resistor values selects 58 that can be used to generate the 254 pairings needed (255 and 0 are special cases that aren't included).

use strict; use warnings; use Text::CSV qw(csv); use List::BinarySearch; my $maxInValue = 255; # Generate 8 bit +ADC values my $minResistance = 300; my $maxResistance = 100e3; my %unitMul = (kOhms => 1.0e3, Ohms => 1.0,); my $rFileName = 'resistorSeries.csv'; my @rows = @{csv(in => $rFileName, headers => "auto")}; my @resistorValues = @rows; my %rowByValue; # Pull out resistor values for my $rowIdx (0 .. $#rows) { my $row = $rows[$rowIdx]; my ($resistance, $units) = split ' ', $row->{Resistance}; die "Can't scale by $units\n" if !exists $unitMul{$units}; $resistorValues[$rowIdx] = $resistance * $unitMul{$units}; $rowByValue{$resistorValues[$rowIdx]} //= $row; } # Make sure we only have one instance of each resistor value my %unique = map {$_ => 1} @resistorValues; @resistorValues = keys %unique; @resistorValues = grep {$_ >= $minResistance && $_ < $maxResistance} @resistorValues +; @resistorValues = sort {$a <=> $b} @resistorValues; # Target values 0 and 255 are special cases so exclude those. my @targets = map {{target => $_, pairs => []}} 1 .. $maxInValue + - 1; my %targetByValue = map {$targets[$_]->{target} => $_} 0 .. $#targets; my %usedCounts; my $worstBestErr = 0.0; # Find all the ways we can get close to each target value for my $wanted (@targets) { my $values = $wanted->{pairs} //= []; my $usedValues = $wanted->{usedValues} //= {}; # $ratio = Vin / Vout = 255 / target my $ratio = $maxInValue / $wanted->{target}; for my $r2 (@resistorValues) { my $tryR1 = ($ratio - 1) * $r2; next if $tryR1 < $resistorValues[0]; last if $tryR1 > $resistorValues[-1]; my $r1 = nearestR($tryR1); my $actValue = 255 * $r2 / ($r2 + $r1); my $err = abs($wanted->{target} - $actValue); push @$values, {r1 => $r1, r2 => $r2, err => $err, value => $a +ctValue}; $usedValues->{$r1} = $values->[-1]; $usedValues->{$r2} = $values->[-1]; } # Sort values by error @$values = sort {$a->{err} <=> $b->{err}} @$values; $worstBestErr = $values->[0]{err} if $worstBestErr < $values->[0]{ +err}; } # Count number of uses of each resistor value used for my $target (@targets) { my $values = $target->{pairs}; # Eliminate any pairings with a worse error than $worstBestErr @$values = grep {$_->{err} <= $worstBestErr} @$values; for my $value (@$values) { ++$usedCounts{$value->{r1}}; ++$usedCounts{$value->{r2}}; } } # Select resistor pairings my $valuesCount = keys %usedCounts; my %missingTargetIdx = map {$_ => 1} 0 .. $maxInValue - 2; my @rByCount = sort {$usedCounts{$b} <=> $usedCounts{$a}} keys %usedCo +unts; my %usedR; my %targetPairs; while (keys %missingTargetIdx) { if (!@rByCount) { die "Can't generate pairings for: @{[keys %missingTargetIdx]}\ +n"; } my $targetR = shift @rByCount; my @matches; # Find all target values that use the current target resistor for my $targetIdx (keys %missingTargetIdx) { my $target = $targets[$targetIdx]; my $value = $target->{target}; for my $pair (@{$target->{pairs}}) { next if $pair->{r1} != $targetR && $pair->{r2} != $targetR +; push @matches, {targetIdx => $targetIdx, r1 => $pair->{r1}, r2 => $pa +ir->{r2}}; $targetPairs{$target->{target}} = [$pair->{r1}, $pair->{r2}, $pair->{value}, $pair->{err +}]; last; } } next if !@matches; # Remove targets we've just found from %missingTargets and add bot +h # resistors in each target pair to %usedR for my $match (@matches) { ++$usedR{$match->{r1}}; ++$usedR{$match->{r2}}; delete $missingTargetIdx{$match->{targetIdx}}; } # See if there are any other targets that are satisfied by %usedR +values for my $targetIdx (keys %missingTargetIdx) { my $target = $targets[$targetIdx]; my $used = $target->{usedValues}; for my $pairKey (keys %$used) { next if !exists $usedR{$used->{$pairKey}{r1}} || !exists $usedR{$used->{$pairKey}{r2}}; $targetPairs{$target->{target}} = [ $used->{$pairKey}{r1}, $used->{$pairKey}{r2}, $used->{$pairKey}{value}, $used->{$pairKey}{err} ]; ++$usedR{$used->{$pairKey}{r1}}; ++$usedR{$used->{$pairKey}{r2}}; delete $missingTargetIdx{$targetIdx}; last; } } } # BOM report my $bomCount = keys %usedR; my @bomValues = sort {$a <=> $b} keys %usedR; print "$bomCount values used excluding 0 Ohm and 'DNF'\n"; for my $value (@bomValues) { my $row = $rowByValue{$value}; printf "%5d: %15s\n", $value, $row->{"Manufacturer Part Number"}; } # Resistor pairings report my $maxZ = 0; my $minR = 100000; for my $pair (sort {$a <=> $b} keys %targetPairs) { my $r1 = $targetPairs{$pair}[0]; my $r2 = $targetPairs{$pair}[1]; my $r1t = 1.001 * $targetPairs{$pair}[0]; my $r1b = 0.999 * $targetPairs{$pair}[0]; my $r2t = 1.001 * $targetPairs{$pair}[1]; my $r2b = 0.999 * $targetPairs{$pair}[1]; my $topValue1 = 255 * $r2t / ($r2t + $r1b); my $botValue1 = 255 * $r2b / ($r2b + $r1t); my $topValue2 = 255 * $r2b / ($r2b + $r1b); my $botValue2 = 255 * $r2t / ($r2t + $r1t); my $topValue = $topValue1 > $topValue2 ? $topValue1 : $topValue2; my $botValue = $botValue1 < $botValue2 ? $botValue1 : $botValue2; my $span = $topValue - $botValue; my $z = 1 / (1 / $r1 + 1 / $r2); my $r = $r1 + $r2; $maxZ = $z if $maxZ < $z; $minR = $r if $minR > $r; printf "%3d: R1 %5d, R2 %5d = %6.2f (err %5.3f: %6.2f - %6.2f = %4.2f +), z = %.0f\n", $pair, @{$targetPairs{$pair}}, $botValue, $topValue, $span, $z +; } printf "Max Z: %.0f, min R: %.0f\n", $maxZ, $minR; sub nearestR { my ($target) = @_; my $insertPoint = List::BinarySearch::binsearch_pos {$a <=> $b} $target, @resistorValues; if ($insertPoint > 0) { my ($prev, $next) = @resistorValues[$insertPoint - 1, $insertP +oint]; if ($target - $prev < $next - $target) { --$insertPoint; } } return $resistorValues[$insertPoint]; }

Prints:

58 values used excluding 0 Ohm and 'DNF' 301: ERJ-PB3B3010V 340: ERJ-PB3B3400V ... 90900: ERJ-PB3B9092V 95300: ERJ-PB3B9532V 1: R1 90900, R2 357 = 1.00 (err 0.002: 1.00 - 1.00 = 0.00), +z = 356 2: R1 95300, R2 750 = 1.99 (err 0.009: 1.99 - 2.00 = 0.01), +z = 744 3: R1 49900, R2 590 = 2.98 (err 0.020: 2.97 - 2.99 = 0.01), +z = 583 4: R1 80600, R2 1270 = 3.96 (err 0.044: 3.95 - 3.96 = 0.02), +z = 1250 5: R1 80600, R2 1620 = 5.02 (err 0.024: 5.01 - 5.03 = 0.02), +z = 1588 ... 253: R1 340, R2 43200 = 253.01 (err 0.009: 253.00 - 253.01 = 0.01), +z = 337 254: R1 357, R2 90900 = 254.00 (err 0.002: 254.00 - 254.00 = 0.00), +z = 356 Max Z: 36437, min R: 602

The report shows the actual values generated with ideal resistors and the range of values taking resistor tolerances into account. The z value is the effective impedance the ADC input sees. min R is the minimum series resistance of any resistor pair which relates to the maximum current through the resistors.

Optimising for fewest key strokes only makes sense transmitting to Pluto or beyond

In reply to Calculating resistor pairs to generate a range of voltages by GrandFather

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post; it's "PerlMonks-approved HTML":



  • Are you posting in the right place? Check out Where do I post X? to know for sure.
  • Posts may use any of the Perl Monks Approved HTML tags. Currently these include the following:
    <code> <a> <b> <big> <blockquote> <br /> <dd> <dl> <dt> <em> <font> <h1> <h2> <h3> <h4> <h5> <h6> <hr /> <i> <li> <nbsp> <ol> <p> <small> <strike> <strong> <sub> <sup> <table> <td> <th> <tr> <tt> <u> <ul>
  • Snippets of code should be wrapped in <code> tags not <pre> tags. In fact, <pre> tags should generally be avoided. If they must be used, extreme care should be taken to ensure that their contents do not have long lines (<70 chars), in order to prevent horizontal scrolling (and possible janitor intervention).
  • Want more info? How to link or How to display code and escape characters are good places to start.
Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others imbibing at the Monastery: (4)
As of 2024-04-23 22:21 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found