One of the most important things your microcontroller can do is read analog voltages. It is important particularly in interfacing with sensors where most throw varying voltage levels that represent varying physical quantities (temperature, pressure, etc.). Microcontrollers, including the Beaglebone Black, have analog to digital converters to do this. Here I will show you how to use the Beaglebone Black ADC module.
If this is your first time reading my site, you must know that:
My BBB runs on Ubuntu 16.04 LTS with kernel 4.4.9-ti-r25. There is a slight difference on this distro and with others. The differences will be included in this tutorial
Some ADC Math
The BBB has a 12-bit ADC module with a 1.8 V reference. The AM335x, which is the BBB’s CPU, uses successive approximation and can take 200,000 samples per second. The digital value can be calculated using the formula:
This means the Beaglebone Black can read voltage levels from 439.6 uV to 1.8 V. If your sensor goes above 1.8, then you need additional circuits to scale down the voltage.
The Device Tree
The Beaglebone Black ADC function is not enabled by default. You need this device tree to enable it:
/* * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ /dts-v1/; /plugin/; / { compatible = "ti,beaglebone", "ti,beaglebone-black", "ti,beaglebone-green"; // identification part-number = "BB-ADC"; version = "00A0"; // resources this cape uses exclusive-use = "P9.39", // AIN0 "P9.40", // AIN1 "P9.37", // AIN2 "P9.38", // AIN3 "P9.33", // AIN4 "P9.36", // AIN5 "P9.35", // AIN6 "tscadc"; // hardware ip used fragment@0 { target = <&tscadc>; __overlay__ { status = "okay"; adc { ti,adc-channels = <0 1 2 3 4 5 6>; ti,chan-step-avg = <0x16 0x16 0x16 0x16 0x16 0x16 0x16>; ti,chan-step-opendelay = <0x98 0x98 0x98 0x98 0x98 0x98 0x98>; ti,chan-step-sampledelay = <0x0 0x0 0x0 0x0 0x0 0x0 0x0>; }; }; }; };
As you can see above, the pins P9.39, P9.40, P9.37, P9.38, P9.33, P9.36, P9.35 are your analog input pins. Compile the above .dts file to a .dtbo binary object:
dtc -O dtb -o BB-ADC-00A0.dtbo -b 0 -@ BB-ADC-00A0.dts
The compiled .dtbo object must then be moved to /lib/firmware:
mv BB-ADC-00A0.dtbo /lib/firmware
Both the .dts and .dtbo files are already in the /lib/firmware in the latest debian and angstrom distro.
The next step is to load the device tree fragment into the cape manager using:
echo BB-ADC > /sys/devices/platform/bone_capemgr/slots
For debian and ubuntu, the slots location is:
/sys/devices/bone_capemgr.8/slots
You can check if it’s loaded by doing:
root@arm:~# cat /sys/devices/platform/bone_capemgr/slots 0: PF---- -1 1: PF---- -1 2: PF---- -1 3: PF---- -1 4: P-O-L- 0 Override Board Name,00A0,Override Manuf,BB-ADC
I don’t have any other device tree overlay loaded so it’s the only thing you can see.
Shameless plug but you have to grab this board. It’s very much like the Beaglebone Black only smaller:
If the above steps are successful then the /sys/bus/iio/devices folder will now have been created.
root@arm:/sys/bus/iio/devices# cd iio:device0 root@arm:/sys/bus/iio/devices/iio:device0# ls buffer in_voltage0_raw in_voltage2_raw in_voltage4_raw in_voltage6_raw of_node scan_elements uevent dev in_voltage1_raw in_voltage3_raw in_voltage5_raw name power subsystem root@arm:/sys/bus/iio/devices/iio:device0#
The five analog channels (AIN0 to AIN5) are represented by the in_voltagex_raw files you see above. To read the input on a particular channel, just do:
root@arm:/sys/bus/iio/devices/iio:device0# cat in_voltage0_raw 3914
I didn’t connect anything so mine reads 3914 (which I believe is due to noise voltages).
Beaglebone Black ADC Continuous Reading
So far we are reading analog voltages in one-shot mode. Most applications need continuous reading of input values. For continuous mode, we need a buffer to capture the input values while the BBB is doing something else. The buffer can be enabled by doing:
root@arm:~# echo 1 > /sys/bus/iio/devices/iio\:device0/scan_elements/in_voltage0_en root@arm:~# echo 100 > /sys/bus/iio/devices/iio\:device0/buffer/length root@arm:~# echo 1 > /sys/bus/iio/devices/iio\:device0/buffer/enable
The first line tells which analog channel to scan. Just change the x in in_voltagex_en if you need a different analog channel. The second line specifies the length of the buffer and the last line enables the buffer.
The kernel source has a sample application located at drivers/staging/iio/Documentation/ named generic_buffer.c. If you’re like me who’s lazy to download the kernel source, here’s the generic_buffer.c file.
Open the nano file editor
nano generic_buffer.c
And copy-paste the code above.
However, this code uses a hardware trigger to fire up continuous ADC. You need the patch file to disable the hardware trigger.
Again, open the nano editor
nano generic_buffer.patch
and copy-paste the above code.
Run the patch command using
patch < generic_buffer.patch
You’ll also need the iio_utils.c file and the iio_utils.h file and must be saved to the same directory as the generic_buffer.c file. Compile all the files using:
gcc --static generic_buffer.c iio_utils.c -o generic_buffer
Now we have an executable command to print the analog values! To use the file, just type:
root@arm:~# ./generic_buffer -n TI-am335x-adc -l 5 -c 3
Where 5 is the buffer length and 3 is the number of iterations. The above command will print 5 readings three times:
root@arm:~# ./generic_buffer -n TI-am335x-adc -l 5 -c 3 iio device number being used is 0 /sys/bus/iio/devices/iio:device0 3921.000000 3909.000000 3897.000000 3888.000000 3881.000000 3873.000000 3866.000000 3863.000000 3123.000000 3125.000000 3126.000000 3124.000000 3124.000000 3111.000000 3110.000000
If you need to go back to one-shot mode then just do:
echo 0 > /sys/bus/iio/devices/iio\:device0/buffer/enable
That’s all to it. Next, I’m planning to use the analog readings and put them into a web page hosted by the BBB. Come back to this site for updates!
nice to read…
The math is wrong in the ‘Some ADC Math’. 1.8V / 4096 = 439.5uV not 43.96uV.