 Note: my Beagleboard/Beaglebone pages begin here!

So, let's do some analog input on the BeagleBone Black (data below applies to other BeagleBoards and BeagleBones as well, because they all have 1.8V analog inputs).

My BeagleBoneBlack runs off a DC-DC converter attached to a series to a 12V battery. So I have the DC-DC input (sometime larger than 14V) and its 5V output goingo to the BBB. I want to measure these two values.

The ADC (analog-to-digital converter) pins of the BeagleBoneBlack have these properties:

• voltage range: 0 to 1.8V (voltage out of range may permanently damage the ADC; this means that we will need to use a voltage divider)
• resolution: "12 bits" (that is, 2**12 different values, from 0 to 4095; our precision is definitely limited to 4096 different values... for example, using a 40960 volts source, our best effort can lead to no more than 10 volts accuracy!)
• sampling time: "125ns" (this is the actual time needed to the ADC to "secure" a value; software sampling may get a few millions values per second... but then you will have no more processor time to store them!)
• current flow: "2uA" (negligible: thus we don't need to worry about maximum power rating of the voltage divider resistors).

In order to read a DC (discrete current) value higher than the 1.8V range, we need a voltage divider (just two resistors; see the hand-written schema in the photo below: V-in means "voltage input", that is, the "original" voltage to be read; V-out means "voltage output", that is, the proportionally "adjusted to 1.8V" output voltage to be read).

We will need at least 0.1% precision resistors because we do not want to introduce macroscopic errors in reading (what about "11.56 volts with +-10% accuracy"? arrrgh: it's something between 10.4V and 12.71V, between an almost fully discharged battery and and an almost fully charged one!)

Alas, 0.01% and 0.005% precision resistors will set you off up to 40-50 bucks each. So we choose the (still affordable) 0.1% from our local store: this means that we will be able to say "11.56 volts with +-0.1% accuracy" - that is, if our "11.56V" reported value will be actually something between 11.548 and 11.571V, which is somewhat an acceptable error margin.

We will only consider resistors of 1000 Ohm or less because of the internal impedance of some analog devices (here it is safe to use R2 always under 1k Ohm).

Let's open the web catalog of our local store and select the cheapest 80 Ohm to 999 Ohm "0.1%" resistors (I chose 80 because I need some extra resistors around that value). Yay! 11 to 17 cents each. But... values like 357, 953, 133 ohm... how can we select the a couple of them to use for a voltage divider?

First we decide a maximum. My 12V battery pack, when fully charged, shows something above 14V without load. I arbitrarily choose 14.444V.

Then I asked my best friend (the Ruby programming language) to do all the math for all the combinations of the resistor values that I found to be cheap:
#!/usr/bin/env ruby

fail "!--uso: #{\$0} vin vout r1 r2..."  if ARGV.size < 4

val = ARGV.collect { |i| i.to_f }
vin = val.delete_at 0
vout = val.delete_at 0
v = {}

prec = 0.1/100.0      # resistori precisi allo 0.1%
adc = 2**12           # l'ADC della BBB e' a 12 bit

while val.size > 1
r1 = val.delete_at 0

val.each do |r2|
v.update( { (vin-(vout*(r1+r2)/r2)).abs => [ r1, r2 ] } )
v.update( { (vin-(vout*(r2+r1)/r1)).abs => [ r2, r1 ] } )
end
end

puts
puts "obiettivo: #{vin}v === precisione: +-#{'%.3f' % [vin*prec*1000.0]}mv === soluzioni migliori per #{vout}v:"
v.sort[0..9].each do |rvout, r|
r1, r2 = r.first, r.last
rvin = vout*(r1+r2)/r2
vdiff = rvin-vin
verr = (vdiff/vin*1000.0)/10.0
next if verr>=100.0
puts "%8.2fv (%+8.3f %+5.1f%%)  res:%6.3fmv  ===  r1:%9.1f    r2:%9.1f" % [ rvin, vdiff, verr, vres, r1, r2 ]
end
puts

And then I ask for: ./calcolo-resistenze-da-comprare.rb 14.444 1.8 115 95 357 215 324 205 383 649 365 576 182 392 590 634 953 887 340 237 261 309 226 280 604 124 133

That is: please calculate how to convert "up to 14.444V" to "max 1.8V" using two resistors chosen between the cheapest 0.1% 115 Ohm one, the second-cheapest 0.1% 95 Ohm one, and so on.

In a split second Ruby tells me:
obiettivo: 14.444v === precisione: +-14.444mv === soluzioni migliori per 1.8v:
14.68v (  +0.232  +1.6%)  res: 3.583mv  ===  r1:    887.0    r2:    124.0
14.70v (  +0.254  +1.8%)  res: 3.588mv  ===  r1:    953.0    r2:    133.0
14.10v (  -0.347  -2.4%)  res: 3.442mv  ===  r1:    649.0    r2:     95.0
13.81v (  -0.631  -4.4%)  res: 3.372mv  ===  r1:    634.0    r2:     95.0
13.80v (  -0.639  -4.4%)  res: 3.370mv  ===  r1:    887.0    r2:    133.0
15.63v (  +1.190  +8.2%)  res: 3.817mv  ===  r1:    953.0    r2:    124.0
13.24v (  -1.200  -8.3%)  res: 3.233mv  ===  r1:    604.0    r2:     95.0
15.68v (  +1.239  +8.6%)  res: 3.829mv  ===  r1:    887.0    r2:    115.0
12.98v (  -1.465 -10.1%)  res: 3.169mv  ===  r1:    590.0    r2:     95.0
12.71v (  -1.730 -12.0%)  res: 3.104mv  ===  r1:    576.0    r2:     95.0

Target is 14.444V; precision "0.1%" means some 14.444 millivolt approximation, best solution found is a voltage divider for 14.68v maximum (+0.232 volts, that is 1.6% higher than my request) in which the ADC resolution will be some 3.583 millivolt (that is: if my ADC value is "4000 out of 4095" after one of "3999 out of 4095", then I am measuring an input voltage of 3.583 millivolt higher than the previous). To get this, I have to buy an 887 Ohm for R1 and a 124 Ohm for R2: the voltage divider will take down a 14.68V input to an 1.8V output for the BBB ADC.

Note that if we arbitraly change a little the arbitrary value chosen as "maximum input voltage to be expected to convert to 1.8V", we can choose different R1 and R2 values; for example, choosing 14.70V, we will use the cheap 953 and 133 Ohm ones (+1.8% more than I initially selected). I don't want to go below 14.5V, and don't need values larger than 14.5V, so I'll stick with one of the first two values.

Now, let's say we want to measure some 5V on another ADC pin. We will have to search for suitable R1 and R2 between the same cheap values listed above. Choosing a "common" value for the above 14V and this 5V will mean a few cents extra saving.

So I ask to my lovely Ruby what about 5.3V (the DC-DC should output no more than 5.2V but it's better to be on the safe side) down to 1.8V: ./calcolo-resistenze-da-comprare.rb 5.3 1.8 115 95 357 215 324 205 383 649 365 576 182 392 590 634 953 887 340 237 261 309 226 280 604 124 133

obiettivo: 5.3v === precisione: +-5.300mv === soluzioni migliori per 1.8v:
5.32v (  +0.018  +0.3%)  res: 1.298mv  ===  r1:    604.0    r2:    309.0
5.32v (  +0.022  +0.4%)  res: 1.299mv  ===  r1:    634.0    r2:    324.0
5.33v (  +0.031  +0.6%)  res: 1.301mv  ===  r1:    357.0    r2:    182.0
5.33v (  +0.032  +0.6%)  res: 1.302mv  ===  r1:    261.0    r2:    133.0
5.34v (  +0.037  +0.7%)  res: 1.303mv  ===  r1:    226.0    r2:    115.0
5.25v (  -0.052  -1.0%)  res: 1.281mv  ===  r1:    182.0    r2:     95.0
5.24v (  -0.058  -1.1%)  res: 1.280mv  ===  r1:    392.0    r2:    205.0
5.24v (  -0.060  -1.1%)  res: 1.279mv  ===  r1:    237.0    r2:    124.0
5.24v (  -0.063  -1.2%)  res: 1.279mv  ===  r1:    590.0    r2:    309.0
5.24v (  -0.064  -1.2%)  res: 1.278mv  ===  r1:    649.0    r2:    340.0

That is, I could go for 604 and 309 Ohm for a 5.32V converted to 1.8V.

And -yeah!- we have common values: to convert from 14.7V to 1.8V I can choose R1=953 and R2=133, while to convert from 5.33V to 1.8V I can choose R1=261 and R2=133 again, so that I will need only 3 kinds of resistors! (yes, I like having lots of differently-rated resistors, but this time I'm buying them only to measure some 5V and 12V DC sources with my BeagleBoneBlack!)

And now, let's go reading!

Older Linux kernels have the analog inputs mapped in the omap/tsc directory, but using incorrect naming: the actual AIN0 input pin is mapped to the ain1 file, AIN1 to ain2, and so on: for example, let's read the AIN0 value (pin 39 of the P9 connector on the BBB):
cd /sys/devices/platform/omap/tsc/
cat ain1

Newer kernels (from 3.8 series up) have correct naming but require to be enabled by cape manager at boot (using something like echo cape-bone-iio > /sys/devices/bone_capemgr.*/slots to enable them after booting Linux) and then enter the active ocp helper directory; for example, let's read the AIN0 value (pin 39 of P9):
cd /sys/devices/ocp.*/helper.*
cat AIN0

Sorry: I cannot yet figure how to enable correct support for latest Angstrom/ArchLinux distributions. 