In this tutorial I’ll show you how to acquire analog data with the BeagleBone Black using the ADS1248 analog-to-digital converter IC. Although the BeagleBone has an onboard 8-channel 12 bit ADC, you might need either more channels or a better resolution. The ADS1248 has 4 differential/7 single-ended inputs and a resolution of 24 bits. Furthemore, the ADS1248 has a built-in current source which can be multiplexed to an analog input or a dedicated pin making a ratiometric resistance measurement pretty straightforward (however, in this introductory post I will not make use of this).
The schematic is self-explanatory. We get both the +5 V and the +3.3V for the analog and digital circuitry of ADS1248 from the P9 header of the BeagleBone. Keep in mind that the pins P9-5&6 (labeled as VDD +5V in the documentation) are just connections to the DC jack of the board, in case the BeagleBone is powered through USB they will not supply any power. I hooked up the START pin to P9-15 which I use as a GPIO to start the conversion. The CLK is wired to GND in order to activate the internal oscillator. I pulled up the RESET line to +3.3V with a 100k resistor. The rest of the lines going to the jumper header are the ones used for the communication via SPI, that is, MOSI, MISO, SCK and CS. I put an LED as well on P9-14 just to have some visual feedback. Here I don’t use the DRDY (data ready) output pin of the IC, you might want to wire it as well to one of the GPIOs to be able to detect a rising/falling edge.
I built the circuit on a breadboard. To make things easier I soldered the chip into a TSSOP 28 breakout board which I can plug into a receptacle on the board.
Get the Adafruit BBIO Python library from here: https://github.com/adafruit/adafruit-beaglebone-io-python. In a nutshell, if you have the Angstrom distribution on your BeagleBone, you can simply do that by executing the following commands:
/usr/bin/ntpdate -b -s -u pool.ntp.org opkg update && opkg install python-pip python-setuptools pip install Adafruit_BBIO
Apart from the Python classes it contains a really useful thing: the device overlay for SPI communication. As you have probably noticed in the BeagleBone documentation, the pins of the P9 header can function in different modes, depending on the state of the on-board multiplexer. In newer versions of Linux (from 3.8.x) the switching between different modes is managed via the device tree. Derek Molloy has an excellent video tutorial on device trees, but the good thing is, that you don’t have to worry about the details, because the library will automatically load in the necessary overlay for you.
You can download the minimal sample code I wrote for this tutorial from my GitHub repository:
The current version of the file can be expanded below. The class ADS1248 contains some register addresses and commands, see the documentation of the IC for more information. After initializing the ADC unit it reads the ADC value every second. In principle the output voltage of the LM335 is proportional to the absolute temperature, and the slope is 10 mV/°K, after correcting the ADC reference voltage to 3.37 V from 3.3 V it gives a pretty plausible result.
#!/usr/bin/python # -*- coding: utf-8 -*- #import the library from Adafruit_BBIO.SPI import SPI import Adafruit_BBIO.GPIO as GPIO import time class ADS1248: MUX0 = 0x00 MUX1 = 0x02 VBIAS = 0x01 SYS0 = 0x03 OFC0 = 0x04 OFC1 = 0x05 OFC2 = 0x06 FSC0 = 0x07 FSC1 = 0x08 FSC2 = 0x09 IDAC0 = 0x0a IDAC1 = 0x0b GPIOCFG = 0x0c GPIODIR = 0x0d GPIODAT = 0x0e NOP = 0xff WREG = 0x40 RREG = 0x20 RDATA = 0x12 # custom settings STARTPIN = "P9_15" def RegWrite(reg,val): spi.xfer2([ADS1248.WREG+(reg & 0xF),0x00,val]); return False def RegRead(reg): spi.xfer2([ADS1248.RREG+(reg & 0xF),00]); r = spi.xfer2([0x00]); # dummy return r def ReadADC(): spi.writebytes([ADS1248.RDATA]) # RDATA (read data once, page 49) a=spi.readbytes(3) spi.writebytes([ADS1248.NOP]) # sending NOP return a def ADCinit(): RegWrite(ADS1248.MUX0, 0b00000001); # MUX0: Pos. input: AIN0, Neg. input: AIN1 (Burnout current source off) RegWrite(ADS1248.MUX1, 0b00100000); # MUX1: REF0, normal operation RegWrite(ADS1248.SYS0, 0b00000000); # SYS0: PGA Gain = 1, 5 SPS RegWrite(ADS1248.IDAC0,0b00000000); # IDAC0: off RegWrite(ADS1248.IDAC1,0b11001100); # IDAC1: n.c. RegWrite(ADS1248.VBIAS,0b00000000); # VBIAS: BIAS voltage disabled RegWrite(ADS1248.OFC0, 0b00000000); # OFC0: 0 => reset offset calibration RegWrite(ADS1248.OFC1, 0b00000000); # OFC1: 0 => reset offset calibration RegWrite(ADS1248.OFC2, 0b00000000); # OFC2: 0 => reset offset calibration RegWrite(ADS1248.GPIOCFG, 0b00000000); # GPIOCFG: all used as analog inputs RegWrite(ADS1248.GPIODIR, 0b00000000); # GPIODIR: - RegWrite(ADS1248.GPIODAT, 0b00000000); # GPIODAT: - spi = SPI(0,0) #/dev/spidev1.0 spi.msh=10000 # SPI clock set to 10 kHz spi.bpw = 8 # bits/word spi.threewire = False spi.lsbfirst = False spi.mode = 1 spi.cshigh = False # ADS1248 chip select (active low) spi.open(0,0) GPIO.setup("P9_14", GPIO.OUT) # drive START high to start conversion GPIO.setup(ADS1248.STARTPIN, GPIO.OUT) GPIO.output(ADS1248.STARTPIN,GPIO.HIGH) time.sleep(0.02) ADCinit() while True: GPIO.output("P9_14",GPIO.HIGH) a=ReadADC() V=(a<<16)+(a<<8)+a print ("Integer reading: %d"%(V)) volts=1.0*V/(pow(2,23)-1)*3.37 print ("U = %.3f V"%(volts)) T=volts*100-273.15; print ("T = %.2f °C"%(T)) GPIO.output("P9_14",GPIO.LOW) time.sleep(1)
See a sample output below.
root@beaglebone:~/ADS1248# ./ADS1248_minimal.py Integer reading: 7348562 U = 2.952 V T = 22.07 °C
If you have issues on the software side, I recommend to start by checking out the device tree. If you check for the existence of the spidev, you should see something similar to this:
root@beaglebone:~/py-spidev# ls /dev/spidev* /dev/spidev1.0 /dev/spidev1.1
Another idea is to check the overlays. Before running the script for the first time, this is what I get when checking /sys/devices/bone_capemgr.9/slots.
root@beaglebone:~# cat /sys/devices/bone_capemgr.9/slots 0: 54:PF--- 1: 55:PF--- 2: 56:PF--- 3: 57:PF--- 4: ff:P-O-L Bone-LT-eMMC-2G,00A0,Texas Instrument,BB-BONE-EMMC-2G 5: ff:P-O-L Bone-Black-HDMI,00A0,Texas Instrument,BB-BONELT-HDMI
After running the script there is a new slot appearing.
root@beaglebone:~# cat /sys/devices/bone_capemgr.9/slots 0: 54:PF--- 1: 55:PF--- 2: 56:PF--- 3: 57:PF--- 4: ff:P-O-L Bone-LT-eMMC-2G,00A0,Texas Instrument,BB-BONE-EMMC-2G 5: ff:P-O-L Bone-Black-HDMI,00A0,Texas Instrument,BB-BONELT-HDMI 7: ff:P-O-L Override Board Name,00A0,Override Manuf,ADAFRUIT-SPI0
You can also check the output of dmesg, this is what you should see:
[ 117.025860] bone-capemgr bone_capemgr.9: part_number 'ADAFRUIT-SPI0', version 'N/A' [ 117.025938] bone-capemgr bone_capemgr.9: slot #7: generic override [ 117.025957] bone-capemgr bone_capemgr.9: bone: Using override eeprom data at slot 7 [ 117.025975] bone-capemgr bone_capemgr.9: slot #7: 'Override Board Name,00A0,Override Manuf,ADAFRUIT-SPI0' [ 117.026108] bone-capemgr bone_capemgr.9: slot #7: Requesting part number/version based 'ADAFRUIT-SPI0-00A0.dtbo [ 117.026127] bone-capemgr bone_capemgr.9: slot #7: Requesting firmware 'ADAFRUIT-SPI0-00A0.dtbo' for board-name 'Override Board Name', version '00A0' [ 117.035859] bone-capemgr bone_capemgr.9: slot #7: dtbo 'ADAFRUIT-SPI0-00A0.dtbo' loaded; converting to live tree [ 117.036070] bone-capemgr bone_capemgr.9: slot #7: #2 overlays
Finally, check the mode of a given pin, for example P9-22, which is the SCK (use the table on the GitHub repo of Derek Molloy to decypher the pin number). From this output you can conclude that the pin is in mode 0 (last digit), which is correct.
root@beaglebone:~/ADS1248# cat /sys/kernel/debug/pinctrl/44e10800.pinmux/pins | grep "pin 84" pin 84 (44e10950) 00000030 pinctrl-single
If you have problems with wget and https (which I had on my original Angstrom installation), the easiest solution is to compile your own wget with ssl support:
wget http://files.directadmin.com/services/wget-1.8.2.tar.gz tar xvzf wget-1.8.2.tar.gz cd wget-1.8.2 ./configure --prefix=/usr --with-ssl make make install
To test your SPI interface, you can use the spidev_text.c sample program.
wget https://raw.githubusercontent.com/raspberrypi/linux/rpi-3.10.y/Documentation/spi/spidev_test.c gcc -o spidev_test spidev_test.c
Before running the executable, short the pins 18 and 21 of the P9 header, that is, the MOSI and MISO lines. Then you should see the following output.
root@beaglebone:~# ./spidev_test /dev/spidev1.0 spi mode: 0 bits per word: 8 max speed: 500000 Hz (500 KHz) FF FF FF FF FF FF 40 00 00 00 00 95 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF DE AD BE EF BA AD F0 0D
In the absence of the shorting wire you get:
root@beaglebone:~# ./spidev_test /dev/spidev1.0 spi mode: 0 bits per word: 8 max speed: 500000 Hz (500 KHz) FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
Forum topic with example C code interfacing the ADS1248 to an AVR uC: http://www.mikrocontroller.net/topic/281205