Andrew B. Wright, S. M. ’88, Ph. D.
The archive <- coming soon
The analog input system is part of the touchscreen interface. This is not an obvious place to find it. Here is TI’s guide.
As an aside, many sensors are being provided which use the I2C interface or the USB interface. It is becoming less critical to use the analog input interface. But, in embedded applications, having a solid analog interface can get you past obstacles, especially if you cannot find an engineered solution that meets your needs.
The system operates on a separate ground and voltage source, so input conditioning to analog signals is important. Protecting the ADC modules and avoiding saturation are key concerns.
Although the PRU can sample the ADC at a fixed (fast) timing, the ADC can be accessed through the ARM side of the Beaglebone. The ADC can be set up to automatically sample at a fixed rate and put samples into a buffer.
For initial testing, the ARM will be used. The ADC can be accessed once the appropriate dtbo file has been loaded. BB-ADC-00A0.dtbo has been provided in /lib/firmware.See the TI wiki on ADC for much info.
Use the command
echo BB-ADC > /sys/devices/platform/bone_capemgr/slots
to load the fragment and gain access to the ADCs.
The ADCs will be available at
The files, in_voltagex_raw, where x = 0…6, contain the 12 bit binary code for each analog input.
Connect a 1k resistor in series with a 10k potentiometer or a voltage divider (1k and 5k resistors) between the analog power pin (1.8v, P9-32), the analog ground pin (P9-34), and one of the analog input pins (P9-33, 35, 36, 37, 38, 39 [AIN0], and 40). A circuit diagram can be seen in Figure <insert figure here>. Varying the potentiometer wiper will vary the code in /sys/bus/iio/devices/iio:device0/in_voltagex_raw. The voltage divider will give a fixed value that is different from the open circuit reading.
Continuous readings from the ARM:
echo 1 > /sys/bus/iio/devices/iio:device0/scan_elements/in_voltagex_en
echo num > /sys/bus/iio/devices/iio:device0/buffer/length
echo 1 > /sys/bus/iio/devices/iio:device0/buffer/enable (this is now handled by the generic_buffer program)
There is a utility called generic_buffer which makes this easy. It can be found in the kernel source under tools/iio. It requires the files, iio_utils.c, iio_utils.h, also found in that directory. Compile with gcc -o gen generic_buffer.c iio_utils.c. Run with ./gen -n “TI-am335x-adc -g”. The ‘-g’ flag makes this triggerless. In earlier versions of the program, you had to patch out the trigger stuff. This is no longer necessary.
If all goes well, you should get a stream of floating point numbers, which are actually the 12 bit binary codes read from the ADC4 (if that’s the one you enabled above).
NOTE: P9.33 is connected to AIN4 (in_voltage4_raw).
The PRU side took a bit of work. The TRM’s description of the registers, bits, and what everything does is cryptic. However, by running the ADC driver above and then looking at the registers from the PRU side, a pretty good starting point can be deduced. The ADC driver code (available in the kernel sources) gives a lot more help in getting things going.
The main discovery that I made was that you need to wait a while between setting bits in registers. The PRU is really fast, so you can override your own commands. You can over-sample the ADC, so you need to run the PRU’s sample rate under the ADC’s conversion rate.
I created a directory under root, adc_pru. The archive of the code is here. <- coming soon.
This code samples three channels, including the channel wired above. You can wire the other two channels if you want.
The program looks at the STEPENABLE to determine if conversions are complete. This may not be the only or best flag for this purpose.
It looks at the count in the FIFO buffer to determine how many times to read the FIFO. NOTE: each read of the FIFO reduces the COUNT by 1 and pushes the next value in the FIFO up the stack. I have not done any testing to determine if you can read the FIFO too fast. Reading the FIFO can be done by assigning the FIFO buffer to a variable (e.g. x = FIFO reads the FIFO).
It sets the STEPID bit, so that the ID can be included in the data byte. Even though the register description in the TRM calls this the channel ID, it is the step ID. So, if you connect analog input 4 to step 1, the value in the channel ID in the FIFO DATA will be 0 (step 1 is labeled 0, step 2 is labeled 1, …). Consequently, if you only use one step, and that step is step 1, you cannot determine from the channel ID if anything is going on. It is up to the programmer to remember the mapping between inputs and step.
The arm side program, which starts and stops the PRU (can also be done from the command line with echo “afS” > /dev/rpmsg_pru3x and echo “afs” > /dev/rpmsg_pru3x) is called adc.c
The PRU code:
The linker command file (renamed to adc.cmd) and the resource table are the same from earlier projects.
A new header file, tsADCss.h, is required by this project. It is located with the other header files.
If you want to see what is going on in BB-ADC, copy /lib/firmware/BB-ADC-00A0.dtbo to your local directory and run
dtc -I dtb -O dts BB-ADC-00A0.dtbo > BB-ADC-00A0.dts
at the command line. The dts file can be viewed in a text editor (gedit or nano).
The registers governing the ADC are located in section xx of the TRM <insert link>. A header file to access the registers has been set up here. <insert link to header page once header file has been created and debugged>
To access the ADC from the PRU, follow the procedure to load code onto the PRU <insert makefile, code snippet for ARM, code snippet for PRU>.
The Touchscreen Controller – Analog to Digital Converter (TSC_ADC) control registers are located at 0x44e0d000 (see Table 2-2. L4_WKUP Peripheral Memory Map in the TRM).
The TSC_ADC DMA section is located at 0x54C0_0000 to 0x54FF_FFFF (see Table 2-1. L3 Memory Map in the TRM).
The CM_WKUP_ADC_TSC_CLKCTRL register (offset 0xbc, reset = 0x30000) needs to be turned on to make the adc work at all (MODULEMODE=2). The CM_WKUP registers start at 0x44E0_0400 (see Table 2-2 in the TRM).
Adding an anti-aliasing filter and input protection:
Aliasing is a problem in analog to digital conversion. An analog low pass filter is necessary no matter what. The order and cut-off frequency require design and testing. See here for some filter information (in progress). See here for some info on FFT on the beaglebone (in progress). What I’d like to try is to use a fairly high sample rate on the ADC with a cheap and dirty input section to provide buffering and filtering. Then, I’d like to implement a digital filter bank to provide multiple cut-off frequencies and downsampling. I’ve read something about this in Oppenheim and Shafer but have not had the opportunity to test it out. FIR low pass filters would be straight-forward to design and implement. The math seems to be workable. Does the PRU have the computational capabilities?
Add to /boot/uEnv.txt …
(Not needed with the PRU)