294 lines
8.7 KiB
C
294 lines
8.7 KiB
C
/*
|
|
* ads1115.c:
|
|
* Extend wiringPi with the ADS1115 I2C 16-bit ADC
|
|
* Copyright (c) 2016 Gordon Henderson
|
|
***********************************************************************
|
|
* This file is part of wiringPi:
|
|
* https://projects.drogon.net/raspberry-pi/wiringpi/
|
|
*
|
|
* wiringPi is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Lesser General Public License as
|
|
* published by the Free Software Foundation, either version 3 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* wiringPi is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with wiringPi.
|
|
* If not, see <http://www.gnu.org/licenses/>.
|
|
***********************************************************************
|
|
*/
|
|
|
|
/*
|
|
*********************************************************************************
|
|
* We're going to work in a hybrid mode to fit in with the wiringPi way of
|
|
* doing things, so there will be 4 analog pin which read the 4 single-ended
|
|
* channels as usual, also some fake digitalOutputs - these are the control
|
|
* registers that allow the user to put it into single/diff mode, set the
|
|
* gain and data rates.
|
|
*********************************************************************************
|
|
*/
|
|
|
|
#include <byteswap.h>
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
|
|
#include <wiringPi.h>
|
|
#include <wiringPiI2C.h>
|
|
|
|
#include "ads1115.h"
|
|
|
|
// Bits in the config register (it's a 16-bit register)
|
|
|
|
#define CONFIG_OS_MASK (0x8000) // Operational Status Register
|
|
#define CONFIG_OS_SINGLE (0x8000) // Write - Starts a single-conversion
|
|
// Read 1 = Conversion complete
|
|
|
|
// The multiplexor
|
|
|
|
#define CONFIG_MUX_MASK (0x7000)
|
|
|
|
// Differential modes
|
|
|
|
#define CONFIG_MUX_DIFF_0_1 (0x0000) // Pos = AIN0, Neg = AIN1 (default)
|
|
#define CONFIG_MUX_DIFF_0_3 (0x1000) // Pos = AIN0, Neg = AIN3
|
|
#define CONFIG_MUX_DIFF_1_3 (0x2000) // Pos = AIN1, Neg = AIN3
|
|
#define CONFIG_MUX_DIFF_2_3 (0x3000) // Pos = AIN2, Neg = AIN3 (2nd differential channel)
|
|
|
|
// Single-ended modes
|
|
|
|
#define CONFIG_MUX_SINGLE_0 (0x4000) // AIN0
|
|
#define CONFIG_MUX_SINGLE_1 (0x5000) // AIN1
|
|
#define CONFIG_MUX_SINGLE_2 (0x6000) // AIN2
|
|
#define CONFIG_MUX_SINGLE_3 (0x7000) // AIN3
|
|
|
|
// Programmable Gain Amplifier
|
|
|
|
#define CONFIG_PGA_MASK (0x0E00)
|
|
#define CONFIG_PGA_6_144V (0x0000) // +/-6.144V range = Gain 2/3
|
|
#define CONFIG_PGA_4_096V (0x0200) // +/-4.096V range = Gain 1
|
|
#define CONFIG_PGA_2_048V (0x0400) // +/-2.048V range = Gain 2 (default)
|
|
#define CONFIG_PGA_1_024V (0x0600) // +/-1.024V range = Gain 4
|
|
#define CONFIG_PGA_0_512V (0x0800) // +/-0.512V range = Gain 8
|
|
#define CONFIG_PGA_0_256V (0x0A00) // +/-0.256V range = Gain 16
|
|
|
|
#define CONFIG_MODE (0x0100) // 0 is continuous, 1 is single-shot (default)
|
|
|
|
// Data Rate
|
|
|
|
#define CONFIG_DR_MASK (0x00E0)
|
|
#define CONFIG_DR_8SPS (0x0000) // 8 samples per second
|
|
#define CONFIG_DR_16SPS (0x0020) // 16 samples per second
|
|
#define CONFIG_DR_32SPS (0x0040) // 32 samples per second
|
|
#define CONFIG_DR_64SPS (0x0060) // 64 samples per second
|
|
#define CONFIG_DR_128SPS (0x0080) // 128 samples per second (default)
|
|
#define CONFIG_DR_475SPS (0x00A0) // 475 samples per second
|
|
#define CONFIG_DR_860SPS (0x00C0) // 860 samples per second
|
|
|
|
// Comparator mode
|
|
|
|
#define CONFIG_CMODE_MASK (0x0010)
|
|
#define CONFIG_CMODE_TRAD (0x0000) // Traditional comparator with hysteresis (default)
|
|
#define CONFIG_CMODE_WINDOW (0x0010) // Window comparator
|
|
|
|
// Comparator polarity - the polarity of the output alert/rdy pin
|
|
|
|
#define CONFIG_CPOL_MASK (0x0008)
|
|
#define CONFIG_CPOL_ACTVLOW (0x0000) // Active low (default)
|
|
#define CONFIG_CPOL_ACTVHI (0x0008) // Active high
|
|
|
|
// Latching comparator - does the alert/rdy pin latch
|
|
|
|
#define CONFIG_CLAT_MASK (0x0004)
|
|
#define CONFIG_CLAT_NONLAT (0x0000) // Non-latching comparator (default)
|
|
#define CONFIG_CLAT_LATCH (0x0004) // Latching comparator
|
|
|
|
// Comparitor queue
|
|
|
|
#define CONFIG_CQUE_MASK (0x0003)
|
|
#define CONFIG_CQUE_1CONV (0x0000) // Assert after one conversions
|
|
#define CONFIG_CQUE_2CONV (0x0001) // Assert after two conversions
|
|
#define CONFIG_CQUE_4CONV (0x0002) // Assert after four conversions
|
|
#define CONFIG_CQUE_NONE (0x0003) // Disable the comparator (default)
|
|
|
|
#define CONFIG_DEFAULT (0x8583) // From the datasheet
|
|
|
|
|
|
static const uint16_t dataRates [8] =
|
|
{
|
|
CONFIG_DR_8SPS, CONFIG_DR_16SPS, CONFIG_DR_32SPS, CONFIG_DR_64SPS, CONFIG_DR_128SPS, CONFIG_DR_475SPS, CONFIG_DR_860SPS
|
|
} ;
|
|
|
|
static const uint16_t gains [6] =
|
|
{
|
|
CONFIG_PGA_6_144V, CONFIG_PGA_4_096V, CONFIG_PGA_2_048V, CONFIG_PGA_1_024V, CONFIG_PGA_0_512V, CONFIG_PGA_0_256V
|
|
} ;
|
|
|
|
|
|
/*
|
|
* analogRead:
|
|
* Pin is the channel to sample on the device.
|
|
* Channels 0-3 are single ended inputs,
|
|
* channels 4-7 are the various differential combinations.
|
|
*********************************************************************************
|
|
*/
|
|
|
|
static int myAnalogRead (struct wiringPiNodeStruct *node, int pin)
|
|
{
|
|
int chan = pin - node->pinBase ;
|
|
int16_t result ;
|
|
uint16_t config = CONFIG_DEFAULT ;
|
|
|
|
chan &= 7 ;
|
|
|
|
// Setup the configuration register
|
|
|
|
// Set PGA/voltage range
|
|
|
|
config &= ~CONFIG_PGA_MASK ;
|
|
config |= node->data0 ;
|
|
|
|
// Set sample speed
|
|
|
|
config &= ~CONFIG_DR_MASK ;
|
|
config |= node->data1 ;
|
|
|
|
// Set single-ended channel or differential mode
|
|
|
|
config &= ~CONFIG_MUX_MASK ;
|
|
|
|
switch (chan)
|
|
{
|
|
case 0: config |= CONFIG_MUX_SINGLE_0 ; break ;
|
|
case 1: config |= CONFIG_MUX_SINGLE_1 ; break ;
|
|
case 2: config |= CONFIG_MUX_SINGLE_2 ; break ;
|
|
case 3: config |= CONFIG_MUX_SINGLE_3 ; break ;
|
|
|
|
case 4: config |= CONFIG_MUX_DIFF_0_1 ; break ;
|
|
case 5: config |= CONFIG_MUX_DIFF_2_3 ; break ;
|
|
case 6: config |= CONFIG_MUX_DIFF_0_3 ; break ;
|
|
case 7: config |= CONFIG_MUX_DIFF_1_3 ; break ;
|
|
}
|
|
|
|
// Start a single conversion
|
|
|
|
config |= CONFIG_OS_SINGLE ;
|
|
config = __bswap_16 (config) ;
|
|
wiringPiI2CWriteReg16 (node->fd, 1, config) ;
|
|
|
|
// Wait for the conversion to complete
|
|
|
|
for (;;)
|
|
{
|
|
result = wiringPiI2CReadReg16 (node->fd, 1) ;
|
|
result = __bswap_16 (result) ;
|
|
if ((result & CONFIG_OS_MASK) != 0)
|
|
break ;
|
|
delayMicroseconds (100) ;
|
|
}
|
|
|
|
result = wiringPiI2CReadReg16 (node->fd, 0) ;
|
|
result = __bswap_16 (result) ;
|
|
|
|
// Sometimes with a 0v input on a single-ended channel the internal 0v reference
|
|
// can be higher than the input, so you get a negative result...
|
|
|
|
if ( (chan < 4) && (result < 0) )
|
|
return 0 ;
|
|
else
|
|
return (int)result ;
|
|
}
|
|
|
|
|
|
/*
|
|
* digitalWrite:
|
|
* It may seem odd to have a digital write here, but it's the best way
|
|
* to pass paramters into the chip in the wiringPi way of things.
|
|
* We have 2 digital registers:
|
|
* 0 is the gain control
|
|
* 1 is the data rate control
|
|
*********************************************************************************
|
|
*/
|
|
|
|
static void myDigitalWrite (struct wiringPiNodeStruct *node, int pin, int data)
|
|
{
|
|
int chan = pin - node->pinBase ;
|
|
chan &= 3 ;
|
|
|
|
if (chan == 0) // Gain Control
|
|
{
|
|
if ( (data < 0) || (data > 6) ) // Use default if out of range
|
|
data = 2 ;
|
|
node->data0 = gains [data] ;
|
|
}
|
|
else // Data rate control
|
|
{
|
|
if ( (data < 0) || (data > 7) ) // Use default if out of range
|
|
data = 4 ;
|
|
node->data1 = dataRates [data] ; // Bugfix 0-1 by "Eric de jong (gm)" <ericdejong@gmx.net> - Thanks.
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
* analogWrite:
|
|
* We're using this to write to the 2 comparitor threshold registers.
|
|
* We could use a digitalWrite here but as it's an analog comparison
|
|
* then it feels better to do it this way.
|
|
*********************************************************************************
|
|
*/
|
|
|
|
static void myAnalogWrite (struct wiringPiNodeStruct *node, int pin, int data)
|
|
{
|
|
int chan = pin - node->pinBase ;
|
|
int reg ;
|
|
int16_t ndata ;
|
|
|
|
chan &= 3 ;
|
|
|
|
reg = chan + 2 ;
|
|
|
|
/**/ if (data < -32767)
|
|
ndata = -32767 ;
|
|
else if (data > 32767)
|
|
ndata = 32767 ;
|
|
else
|
|
ndata = (int16_t)data ;
|
|
|
|
ndata = __bswap_16 (ndata) ;
|
|
wiringPiI2CWriteReg16 (node->fd, reg, data) ;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* ads1115Setup:
|
|
* Create a new wiringPi device node for an ads1115 on the Pi's
|
|
* I2C interface.
|
|
*********************************************************************************
|
|
*/
|
|
|
|
int ads1115Setup (const int pinBase, int i2cAddr)
|
|
{
|
|
struct wiringPiNodeStruct *node ;
|
|
int fd ;
|
|
|
|
if ((fd = wiringPiI2CSetup (i2cAddr)) < 0)
|
|
return FALSE ;
|
|
|
|
node = wiringPiNewNode (pinBase, 8) ;
|
|
|
|
node->fd = fd ;
|
|
node->data0 = CONFIG_PGA_4_096V ; // Gain in data0
|
|
node->data1 = CONFIG_DR_128SPS ; // Samples/sec in data1
|
|
node->analogRead = myAnalogRead ;
|
|
node->analogWrite = myAnalogWrite ;
|
|
node->digitalWrite = myDigitalWrite ;
|
|
|
|
return TRUE ;
|
|
}
|