Getting started with the ADS1248 on the BeagleBone Black

ADS1248_BBB_buildIn 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 hardware

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.

ADS1248_BBB_build

The software

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:

wget https://github.com/donfuge/ADS1248/blob/master/ADS1248_minimal.py 

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[0]<<16)+(a[1]<<8)+a[2]
	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

Troubleshooting

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

Further reading

Forum topic with example C code interfacing the ADS1248 to an AVR uC: http://www.mikrocontroller.net/topic/281205
https://github.com/jadonk/validation-scripts/tree/master/test-capemgr
http://elinux.org/BeagleBone_and_the_3.8_Kernel

Advertisements

4 thoughts on “Getting started with the ADS1248 on the BeagleBone Black

  1. Pingback: Data logging and graphing using RRDtool | Hack something today

  2. Pingback: First steps with the HC-05 on the BeagleBone Black | Hack something today

  3. You cannot tie one of the analog inputs( AIN1 in your schematic) to GND. With the input buffering, the GND voltage is just outside the analog input range of the device and needs to be 100mV higher (when the power supply for the analog section AVDD is 5V). It’s listed in the ADS1248 datasheet at page 3 as Common-mode input range.

    • Thank you for your comment. I have checked the datasheet, you are right. Furthermore, the schematic seems to violate another rule, “For VREF > 2.7V, the analog input differential voltage should not exceed 2.7V/PGA”. Since room temperature is around 300 K, even with PGA=1 the input voltage is around 3 V. Indeed, this circuit needs a minor rethinking.

      I think the easy way out is to use the internal current source of the ADC to bias the LM335Z and a bias resistor to shift the voltage on AIN1 to the allowed input range, like in the example “Two-Wire RTD Application” here: http://www.ti.com/lit/an/sbaa180/sbaa180.pdf?DCMP=hpa_contriubted_article&HQS=sbaa180-ca

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s