drivers/analog/ads1115: Add support for the Texas Instruments ADS1115

This patch adds initial support for the Texas Instruments ADS1115 ADC.
Please read the documentation for more details.

Signed-off-by: Tony Lin <99093620+justapotato213@users.noreply.github.com>
This commit is contained in:
Tony Lin 2025-03-21 21:48:33 -04:00 • committed by Alin Jerpelea
parent 85d9078a98
commit 1f2f910ccd
5 changed files with 1098 additions and 0 deletions

View file

@ -257,6 +257,109 @@ config ADC_MCP3008_DIFFERENTIAL
endif # ADC_MCP3008
config ADC_ADS1115
bool "ADS1115 support"
default n
select I2C
---help---
Enable driver support for the Texas Instruments ADS1115 16-bit ADC.
Currently support is experimental, especially the comparator and ALRT/RDY
pin functions.
if ADC_ADS1115
config ADC_ADS1115_I2C_FREQUENCY
int "ADS1115 I2C frequency"
default 100000
---help---
ADS1115 I2C frequency.
config ADC_ADS1115_ADDR
hex "ADS1115 I2C address"
default 0x48
---help---
ADS1115 I2C address. Default is 0x48.
config ADC_ADS1115_CHANNEL
int "ADS1115 channel"
default 0
range 0 7
---help---
Default ADS1115 channel/mux configuration.
0-3 are the differential channels.
4-7 are the single ended channels for A0 to A3
See the datasheet or ads1115.h for more info.
config ADC_ADS1115_PGA
int "ADS1115 PGA"
default 2
range 0 7
---help---
ADS1115 PGA configuration.
0 is +/-6.144V, 1 is +/-4.096V, 2 is +/-2.048V, 3 is +/-1.024V,
4 is +/-0.512V, 5 is +/-0.256V, 6 is +/-0.256V, 7 is +/-0.256V.
config ADC_ADS1115_CONTINOUS
bool "ADS1115 continuous mode"
default n
---help---
ADS1115 continuous mode. The ADS1115 will repeatedly take conversions.
If changing channels in this mode, there is a chance of old data being
read. To avoid this, use one shot mode or use the ALERT/RDY pin.
config ADC_ADS1115_DR
int "ADS1115 data rate"
default 4
range 0 7
---help---
ADS1115 data rate.
0 is 8sps, 1 is 16sps, 2 is 32sps, 3 is 64sps,
4 is 128sps, 5 is 250sps, 6 is 475sps, 7 is 860sps.
config ADC_ADS1115_COMP_MODE
bool "ADS1115 window comparator mode"
default n
---help---
ADS1115 window comparator mode.
config ADC_ADS1115_COMP_POL
bool "ADS1115 comparator polarity"
default n
---help---
ADS1115 comparator polarity. n is active low, y is active high.
config ADC_ADS1115_COMP_LAT
bool "ADS1115 comparator latching"
default n
---help---
ADS1115 comparator latching. n is non-latching, y is latching.
Note that this is cleared when reading from the conversion register.
config ADC_ADS1115_COMP_QUE
int "ADS1115 comparator queue"
default 3
range 0 3
---help---
ADS1115 comparator queue. 0 is assert after 1 conversion, 1, is
assert after 2 conversions, 2 is assert after 4 conversions, 3
is disable comparator, and set ALERT/RDY to high-impedence.
config ADC_ADS1115_HI_THRESH
int "ADS1115 high threshold"
default 32767
range 0 65536
---help---
ADS1115 high threshold.
config ADC_ADS1115_LO_THRESH
int "ADS1115 low threshold"
range 0 65536
default 32768
---help---
ADS1115 low threshold.
endif # ADC_ADS1115
endif # ADC
config COMP

View file

@ -113,6 +113,10 @@ endif
ifeq ($(CONFIG_ADC_MCP3008),y)
CSRCS += mcp3008.c
endif
ifeq ($(CONFIG_ADC_ADS1115),y)
CSRCS += ads1115.c
endif
endif
ifeq ($(CONFIG_LMP92001),y)

827
drivers/analog/ads1115.c Normal file
View file

@ -0,0 +1,827 @@
/****************************************************************************
* drivers/analog/ads1115.c
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <nuttx/nuttx.h>
#include <nuttx/signal.h>
#include <assert.h>
#include <debug.h>
#include <endian.h>
#include <errno.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <sys/types.h>
#include <nuttx/analog/adc.h>
#include <nuttx/analog/ads1115.h>
#include <nuttx/analog/ioctl.h>
#include <nuttx/arch.h>
#include <nuttx/i2c/i2c_master.h>
/****************************************************************************
* Preprocessor definitions
****************************************************************************/
#if defined(CONFIG_ADC_ADS1115)
#ifndef CONFIG_ADC_ADS1115_I2C_FREQUENCY
#define CONFIG_ADC_ADS1115_I2C_FREQUENCY 100000
#endif
#define ADS1115_NUM_CHANNELS 8
#define ADS1115_OS_SHIFT (1 << 15)
#define ADS1115_MUX_SHIFT 12
#define ADS1115_MUX_MASK (7 << ADS1115_MUX_SHIFT)
#define ADS1115_PGA_SHIFT 9
#define ADS1115_PGA_MASK (7 << ADS1115_PGA_SHIFT)
#define ADS1115_MODE_MASK (1 << 8)
#define ADS1115_DR_SHIFT 5
#define ADS1115_DR_MASK (7 << ADS1115_DR_SHIFT)
#define ADS1115_COMP_MODE_MASK (1 << 4)
#define ADS1115_COMP_POL_MASK (1 << 3)
#define ADS1115_COMP_LAT_MASK (1 << 2)
#define ADS1115_COMP_QUE_SHIFT 0
#define ADS1115_COMP_QUE_MASK (3 << ADS1115_COMP_QUE_SHIFT)
/****************************************************************************
* Private Types
****************************************************************************/
struct ads1115_dev_s
{
FAR struct i2c_master_s *i2c;
FAR const struct adc_callback_s *cb;
uint8_t addr;
/* Current configuration of ADC. */
uint16_t cmdbyte;
};
enum ads1115_registers_e
{
ADS1115_CONVERSION_REGISTER = 0x00,
ADS1115_CONFIG_REGISTER = 0x01,
ADS1115_LO_THRESH_REGISTER = 0x02,
ADS1115_HI_THRESH_REGISTER = 0x03,
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
static int ads1115_bind(FAR struct adc_dev_s *dev,
FAR const struct adc_callback_s *callback);
static void ads1115_reset(FAR struct adc_dev_s *dev);
static int ads1115_setup(FAR struct adc_dev_s *dev);
static void ads1115_shutdown(FAR struct adc_dev_s *dev);
static void ads1115_rxint(FAR struct adc_dev_s *dev, bool enable);
static int ads1115_ioctl(FAR struct adc_dev_s *dev, int cmd,
unsigned long arg);
/****************************************************************************
* Private Data
****************************************************************************/
static const struct adc_ops_s g_ads1115ops =
{
.ao_bind = ads1115_bind,
.ao_reset = ads1115_reset,
.ao_setup = ads1115_setup,
.ao_shutdown = ads1115_shutdown,
.ao_rxint = ads1115_rxint,
.ao_ioctl = ads1115_ioctl,
};
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: ads1115_write_register
*
* Description:
* Writes a value to a register in the ADS1115.
*
* Input Parameters:
* priv - An ADS1115 device structure
* reg - The register to write to.
* value - The value to write to the register, in big endian.
*
****************************************************************************/
static int ads1115_write_register(FAR struct ads1115_dev_s *priv,
enum ads1115_registers_e reg,
uint16_t value)
{
int ret = OK;
if (priv == NULL)
{
ret = -EINVAL;
}
else
{
struct i2c_msg_s i2cmsg[2];
/* Write the register into the address pointer register. */
i2cmsg[0].frequency = CONFIG_ADC_ADS1115_I2C_FREQUENCY;
i2cmsg[0].addr = priv->addr;
i2cmsg[0].flags = 0;
i2cmsg[0].buffer = &reg;
i2cmsg[0].length = sizeof(reg);
/* Write the value into the register. */
i2cmsg[1].frequency = CONFIG_ADC_ADS1115_I2C_FREQUENCY;
i2cmsg[1].addr = priv->addr;
i2cmsg[1].flags = I2C_M_NOSTART;
i2cmsg[1].buffer = (FAR uint8_t *)&value;
i2cmsg[1].length = sizeof(value);
ret = I2C_TRANSFER(priv->i2c, i2cmsg, 2);
if (ret < 0)
{
aerr("ADS1115 I2C transfer failed: %d\n", ret);
}
}
return ret;
}
/****************************************************************************
* Name: ads1115_read_register
*
* Description:
* Reads a value from a register in the ADS1115 in big endian.
*
* Input Parameters:
* priv - An ADS1115 device structure
* reg - The register to read from.
* buf - A pointer to a buffer to store the value read from the register.
*
****************************************************************************/
static int ads1115_read_register(FAR struct ads1115_dev_s *priv, uint8_t reg,
uint16_t *buf)
{
int ret = OK;
if (priv == NULL || buf == NULL)
{
ret = -EINVAL;
}
else
{
struct i2c_msg_s i2cmsg[2];
/* Write the register into the address pointer register. */
i2cmsg[0].frequency = CONFIG_ADC_ADS1115_I2C_FREQUENCY;
i2cmsg[0].addr = priv->addr;
i2cmsg[0].flags = 0;
i2cmsg[0].buffer = (FAR uint8_t *)(&reg);
i2cmsg[0].length = sizeof(reg);
/* Read from the register. */
i2cmsg[1].frequency = CONFIG_ADC_ADS1115_I2C_FREQUENCY;
i2cmsg[1].addr = priv->addr;
i2cmsg[1].flags = I2C_M_READ;
i2cmsg[1].buffer = (FAR uint8_t *)(buf);
i2cmsg[1].length = sizeof(*buf);
ret = I2C_TRANSFER(priv->i2c, i2cmsg, 2);
if (ret < 0)
{
aerr("ADS1115 I2C transfer failed: %d\n", ret);
}
}
return ret;
}
/****************************************************************************
* Name: ads1115_read_current_register
*
* Description:
* Reads from the current register in the ADS1115 in big endian.
*
* Input Parameters:
* priv - An ADS1115 device structure
* buf - A pointer to a buffer to store the value read from the register.
*
****************************************************************************/
static int ads1115_read_current_register(FAR struct ads1115_dev_s *priv,
uint16_t *buf)
{
int ret = OK;
if (priv == NULL || buf == NULL)
{
ret = -EINVAL;
}
else
{
struct i2c_msg_s i2cmsg[1];
i2cmsg[0].frequency = CONFIG_ADC_ADS1115_I2C_FREQUENCY;
i2cmsg[0].addr = priv->addr;
i2cmsg[0].flags = I2C_M_READ;
i2cmsg[0].buffer = (FAR uint8_t *)(buf);
i2cmsg[0].length = sizeof(*buf);
ret = I2C_TRANSFER(priv->i2c, i2cmsg, 1);
if (ret < 0)
{
aerr("ADS1115 I2C transfer failed: %d\n", ret);
}
}
return ret;
}
/****************************************************************************
* Name: cmdbyte_init
*
* Description:
* Initializes the command byte with the default settings, and writes to the
* high and low threshold registers.
*
* Input Parameters:
* priv - An ADS1115 device structure
*
****************************************************************************/
static int cmdbyte_init(FAR struct ads1115_dev_s *priv)
{
int ret = OK;
/* Set the default channel. */
priv->cmdbyte = CONFIG_ADC_ADS1115_CHANNEL << ADS1115_MUX_SHIFT;
/* Set the default pga setting. */
priv->cmdbyte |= CONFIG_ADC_ADS1115_PGA << ADS1115_PGA_SHIFT;
/* Set the default mode. */
#ifndef CONFIG_ADC_ADS1115_CONTINOUS
priv->cmdbyte |= ADS1115_MODE_MASK;
#endif
/* Set the default data rate. */
priv->cmdbyte |= CONFIG_ADC_ADS1115_DR << ADS1115_DR_SHIFT;
/* Set the default comparator mode. */
#ifdef CONFIG_ADC_ADS1115_COMP_MODE
priv->cmdbyte |= ADS1115_COMP_MODE_MASK;
#endif
/* Set the default comparator polarity. */
#ifdef CONFIG_ADC_ADS1115_COMP_POL
priv->cmdbyte |= ADS1115_COMP_POL_MASK;
#endif
/* Set the default comparator latching. */
#ifdef CONFIG_ADC_ADS1115_COMP_LAT
priv->cmdbyte |= ADS1115_COMP_LAT_MASK;
#endif
/* Set the comparator queue. */
priv->cmdbyte |= CONFIG_ADC_ADS1115_COMP_QUE << ADS1115_COMP_QUE_SHIFT;
ret = ads1115_write_register(priv, ADS1115_HI_THRESH_REGISTER,
htobe16(CONFIG_ADC_ADS1115_HI_THRESH));
if (ret != OK)
{
aerr("Failed to write high threshold register\n");
return ret;
}
ret = ads1115_write_register(priv, ADS1115_LO_THRESH_REGISTER,
htobe16(CONFIG_ADC_ADS1115_LO_THRESH));
return ret;
}
/****************************************************************************
* Name: ads1115_readchannel
*
* Description:
* Reads a conversion from the ADC.
*
* Input Parameters:
* priv - An ADS1115 device structure
* msg - An ADC message struct where the am_channel member contains the
* channel number to be read, and where the am_data member is where the
* reading is stored.
*
* NOTE:
* When switching between channels in continous mode, old data may be read.
* Using the ALERT/RDY pin can avoid this.
*
* Each channel corrseponds to a differing mux configuration as defined in
* the datasheet.
*
* msg->am_channel MUX Configuration
* 0 AINP = AIN0, AINN = AIN1
* 1 AINP = AIN0, AINN = AIN3
* 2 AINP = AIN1, AINN = AIN3
* 3 AINP = AIN2, AINN = AIN3
* 4 AINP = AIN0, AINN = GND
* 5 AINP = AIN0, AINN = GND
* 6 AINP = AIN1, AINN = GND
* 7 AINP = AIN2, AINN = GND
* 8 AINP = AIN3, AINN = GND
****************************************************************************/
static int ads1115_readchannel(FAR struct ads1115_dev_s *priv,
FAR struct adc_msg_s *msg)
{
int ret = OK;
if (priv == NULL || msg == NULL || msg->am_channel >= ADS1115_NUM_CHANNELS)
{
ret = -EINVAL;
}
else
{
uint16_t buf;
uint16_t channel_bits = msg->am_channel << ADS1115_MUX_SHIFT;
bool one_shot =
((priv->cmdbyte & ADS1115_MODE_MASK) == ADS1115_MODE_MASK);
bool new_channel =
((priv->cmdbyte & ADS1115_MUX_MASK) != channel_bits);
/* check if our current channel is the same as the one that
* is set or if in one shot mode.
*/
if (new_channel || one_shot)
{
/* Clear the current mux bits. */
priv->cmdbyte &= ~(ADS1115_MUX_MASK);
/* Set the new mux bits. */
priv->cmdbyte |= channel_bits;
/* Set the OS bit to start conversion. */
priv->cmdbyte |= ADS1115_OS_SHIFT;
/* Write to the configuration register. */
ainfo("cmdbyte: %x\n", priv->cmdbyte);
ret = ads1115_write_register(priv, ADS1115_CONFIG_REGISTER,
htobe16(priv->cmdbyte));
}
if (one_shot)
{
/* Read the configuration register until OS has been set to 1. */
int count = 0;
do
{
/* ADS1115 takes ~25 usec to wake up */
nxsig_usleep(25);
ret = ads1115_read_current_register(priv, &buf);
count++;
}
while ((be16toh(buf) & ADS1115_OS_SHIFT) == 0 && ret == OK &&
count < 3);
ainfo("config register: %x\n", (be16toh(buf)));
if (count >= 3)
{
aerr("ADS1115 timed out waiting for conversion to finish\n");
return -ETIMEDOUT;
}
}
/* Read the conversion register. */
ret = ads1115_read_register(priv, ADS1115_CONVERSION_REGISTER, &buf);
ainfo("output: %d\n", (int16_t)betoh16(buf));
msg->am_data = (uint32_t)betoh16(buf);
priv->cmdbyte &= ~(ADS1115_OS_SHIFT); /* Clear the OS bit. */
}
return ret;
}
/****************************************************************************
* Name: ads1115_bind
*
* Description:
* Bind the upper-half driver callbacks to the lower-half implementation.
* This must be called early in order to receive ADC event notifications.
*
****************************************************************************/
static int ads1115_bind(FAR struct adc_dev_s *dev,
FAR const struct adc_callback_s *callback)
{
FAR struct ads1115_dev_s *priv = (FAR struct ads1115_dev_s *)dev->ad_priv;
DEBUGASSERT(priv != NULL);
priv->cb = callback;
return 0;
}
/****************************************************************************
* Name: ads1115_reset
*
* Description:
* Reset the ADC device. Called early to initialize the hardware. This
* is called, before ao_setup() and on error conditions.
*
****************************************************************************/
static void ads1115_reset(FAR struct adc_dev_s *dev)
{
FAR struct ads1115_dev_s *priv = (FAR struct ads1115_dev_s *)dev->ad_priv;
cmdbyte_init(priv);
}
/****************************************************************************
* Name: ads1115_setup
*
* Description:
* Configure the ADC. This method is called the first time that the ADC
* device is opened. This method will write the default configuration into
* the configuration register.
*
****************************************************************************/
static int ads1115_setup(FAR struct adc_dev_s *dev)
{
FAR struct ads1115_dev_s *priv = (FAR struct ads1115_dev_s *)dev->ad_priv;
int ret = ads1115_write_register(priv, ADS1115_CONFIG_REGISTER,
htobe16(priv->cmdbyte));
return ret;
}
/****************************************************************************
* Name: ads1115_shutdown
*
* Description:
* Disable the ADC. This method is called when the ADC device is closed.
* This method should reverse the operation of the setup method. The
* ADS1115 can be put into a power-down state by setting the mode bit to
* single-shot. This will stop the ADC from converting and reduce power
* consumption.
*
****************************************************************************/
static void ads1115_shutdown(FAR struct adc_dev_s *dev)
{
FAR struct ads1115_dev_s *priv = (FAR struct ads1115_dev_s *)dev->ad_priv;
priv->cmdbyte |= ADS1115_MODE_MASK;
ads1115_write_register(priv, ADS1115_CONFIG_REGISTER,
htobe16(priv->cmdbyte));
}
/****************************************************************************
* Name: ads1115_rxint
*
* Description:
* Needed for ADC upper-half compatibility but conversion interrupts
* are not supported by the ADS1115.
*
****************************************************************************/
static void ads1115_rxint(FAR struct adc_dev_s *dev, bool enable)
{
}
/****************************************************************************
* Name: ads1115_ioctl
*
* Description:
* All ioctl calls will be routed through this method.
*
****************************************************************************/
static int ads1115_ioctl(FAR struct adc_dev_s *dev, int cmd,
unsigned long arg)
{
FAR struct ads1115_dev_s *priv = (FAR struct ads1115_dev_s *)dev->ad_priv;
int ret = OK;
switch (cmd)
{
case ANIOC_TRIGGER:
{
struct adc_msg_s msg;
for (uint8_t i = 0; i < ADS1115_NUM_CHANNELS && (ret == 0); i++)
{
msg.am_channel = i;
ainfo("Channel: %d\n", i);
ret = ads1115_readchannel(priv, &msg);
if (ret == 0)
{
priv->cb->au_receive(dev, i, msg.am_data);
}
else
{
aerr("Error reading channel %d with error %d \n", i, ret);
}
}
}
break;
case ANIOC_ADS1115_SET_PGA:
{
if (arg >= ADS1115_PGA1 && arg <= ADS1115_PGA8)
{
priv->cmdbyte &= ~ADS1115_PGA_MASK;
priv->cmdbyte |= arg << ADS1115_PGA_SHIFT;
}
else
{
ret = -EINVAL;
break;
}
ret = ads1115_write_register(priv, ADS1115_CONFIG_REGISTER,
htobe16(priv->cmdbyte));
}
break;
case ANIOC_ADS1115_SET_MODE:
{
if (arg == ADS1115_MODE1)
{
priv->cmdbyte &= ~ADS1115_MODE_MASK;
}
else if (arg == ADS1115_MODE2)
{
priv->cmdbyte |= ADS1115_MODE_MASK;
}
else
{
ret = -EINVAL;
break;
}
ret = ads1115_write_register(priv, ADS1115_CONFIG_REGISTER,
htobe16(priv->cmdbyte));
}
break;
case ANIOC_ADS1115_SET_DR:
{
if (arg >= ADS1115_DR1 && arg <= ADS1115_DR8)
{
priv->cmdbyte &= ~ADS1115_DR_MASK;
priv->cmdbyte |= arg << ADS1115_DR_SHIFT;
}
else
{
ret = -EINVAL;
break;
}
ret = ads1115_write_register(priv, ADS1115_CONFIG_REGISTER,
htobe16(priv->cmdbyte));
}
break;
case ANIOC_ADS1115_SET_COMP_POL:
{
if (arg == ADS1115_COMP_POL1)
{
priv->cmdbyte &= ~ADS1115_COMP_POL_MASK;
}
else if (arg == ADS1115_COMP_POL2)
{
priv->cmdbyte |= ADS1115_COMP_POL_MASK;
}
else
{
ret = -EINVAL;
break;
}
ret = ads1115_write_register(priv, ADS1115_CONFIG_REGISTER,
htobe16(priv->cmdbyte));
}
break;
case ANIOC_ADS1115_SET_COMP_MODE:
{
if (arg == ADS1115_COMP_MODE1)
{
priv->cmdbyte &= ~ADS1115_COMP_MODE_MASK;
}
else if (arg == ADS1115_COMP_MODE2)
{
priv->cmdbyte |= ADS1115_COMP_MODE_MASK;
}
else
{
ret = -EINVAL;
break;
}
ret = ads1115_write_register(priv, ADS1115_CONFIG_REGISTER,
htobe16(priv->cmdbyte));
}
break;
case ANIOC_ADS1115_SET_COMP_LAT:
{
if (arg == ADS1115_COMP_LAT1)
{
priv->cmdbyte &= ~ADS1115_COMP_LAT_MASK;
}
else if (arg == ADS1115_COMP_LAT2)
{
priv->cmdbyte |= ADS1115_COMP_LAT_MASK;
}
else
{
ret = -EINVAL;
break;
}
ret = ads1115_write_register(priv, ADS1115_CONFIG_REGISTER,
htobe16(priv->cmdbyte));
}
break;
case ANIOC_ADS1115_SET_COMP_QUEUE:
{
if (arg >= ADS1115_COMP_QUEUE1 && arg <= ADS1115_COMP_QUEUE4)
{
priv->cmdbyte &= ~ADS1115_COMP_QUE_MASK;
priv->cmdbyte |= arg << ADS1115_COMP_QUE_SHIFT;
}
else
{
ret = -EINVAL;
break;
}
ret = ads1115_write_register(priv, ADS1115_CONFIG_REGISTER,
htobe16(priv->cmdbyte));
}
break;
case ANIOC_ADS1115_READ_CHANNEL:
{
FAR struct adc_msg_s *msg = (FAR struct adc_msg_s *)arg;
ret = ads1115_readchannel(priv, msg);
}
break;
case ANIOC_ADS1115_SET_HI_THRESH:
{
if (arg >= 0 && arg <= UINT16_MAX)
{
ret = ads1115_write_register(priv, ADS1115_HI_THRESH_REGISTER,
htobe16(arg));
}
else
{
ret = -EINVAL;
}
}
break;
case ANIOC_ADS1115_SET_LO_THRESH:
{
if (arg >= 0 && arg <= UINT16_MAX)
{
ret = ads1115_write_register(priv, ADS1115_LO_THRESH_REGISTER,
htobe16(arg));
}
else
{
ret = -EINVAL;
}
}
break;
case ANIOC_GET_NCHANNELS:
{
ret = ADS1115_NUM_CHANNELS;
}
break;
default:
ret = -ENOTTY;
break;
}
return ret;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: ads1115_initialize
*
* Description:
* Initialize the selected ADC
*
* Input Parameters:
* i2c - Pointer to a valid I2C master struct.
* addr - I2C device address.
*
* Returned Value:
* Valid ADC device structure reference on success; a NULL on failure
*
* **************************************************************************/
FAR struct adc_dev_s *ads1115_initialize(FAR struct i2c_master_s *i2c,
uint8_t addr)
{
FAR struct ads1115_dev_s *priv;
FAR struct adc_dev_s *adcdev;
DEBUGASSERT(i2c != NULL);
priv = kmm_malloc(sizeof(struct ads1115_dev_s));
if (priv == NULL)
{
aerr("ERROR: Failed to allocate ads1115_dev_s instance\n");
free(priv);
return NULL;
}
priv->cb = NULL;
priv->i2c = i2c;
priv->addr = addr;
priv->cmdbyte = 0;
int ret = cmdbyte_init(priv);
if (ret != OK)
{
aerr("Failed to initialize ADS1115\n");
free(priv);
return NULL;
}
adcdev = kmm_malloc(sizeof(struct adc_dev_s));
if (adcdev == NULL)
{
aerr("ERROR: Failed to allocate adc_dev_s instance\n");
return NULL;
}
memset(adcdev, 0, sizeof(struct adc_dev_s));
adcdev->ad_ops = &g_ads1115ops;
adcdev->ad_priv = priv;
return adcdev;
}
#endif /* CONFIG_ADC_ADS1115 */

View file

@ -0,0 +1,159 @@
/****************************************************************************
* include/nuttx/analog/ads1115.h
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
#ifndef __INCLUDE_NUTTX_ANALOG_ADS1115_H
#define __INCLUDE_NUTTX_ANALOG_ADS1115_H
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/analog/ioctl.h>
#include <nuttx/config.h>
#include <nuttx/i2c/i2c_master.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* IOCTL Commands
* Cmd: ANIOC_ADS1115_SET_PGA Arg: enum ads1115_pga_e
* Cmd: ANIOC_ADS1115_SET_MODE Arg: enum ads1115_mode_e
* Cmd: ANIOC_ADS1115_SET_DR Arg: enum ads1115_dr_e
* Cmd: ANIOC_ADS1115_SET_COMP_MODE Arg: enum ads1115_comp_mode_e
* Cmd: ANIOC_ADS1115_SET_COMP_POL Arg: enum ads1115_comp_pol_e
* Cmd: ANIOC_ADS1115_SET_COMP_LAT Arg: enum ads1115_comp_lat_e
* Cmd: ANIOC_ADS1115_SET_COMP_QUEUE Arg: enum ads1115_comp_queue_e
* Cmd: ANIOC_ADS1115_READ_CHANNEL Arg: struct adc_msg_s *channel
* Cmd: ANIOC_ADS1115_SET_HI_THRESH Arg: uint16_t value
* Cmd: ANIOC_ADS1115_SET_LO_THRESH Arg: uint16_t value
*/
#define ANIOC_ADS1115_SET_PGA _ANIOC(AN_ADS1115_FIRST + 0)
#define ANIOC_ADS1115_SET_MODE _ANIOC(AN_ADS1115_FIRST + 1)
#define ANIOC_ADS1115_SET_DR _ANIOC(AN_ADS1115_FIRST + 2)
#define ANIOC_ADS1115_SET_COMP_MODE _ANIOC(AN_ADS1115_FIRST + 3)
#define ANIOC_ADS1115_SET_COMP_POL _ANIOC(AN_ADS1115_FIRST + 4)
#define ANIOC_ADS1115_SET_COMP_LAT _ANIOC(AN_ADS1115_FIRST + 5)
#define ANIOC_ADS1115_SET_COMP_QUEUE _ANIOC(AN_ADS1115_FIRST + 6)
#define ANIOC_ADS1115_READ_CHANNEL _ANIOC(AN_ADS1115_FIRST + 7)
#define ANIOC_ADS1115_SET_HI_THRESH _ANIOC(AN_ADS1115_FIRST + 8)
#define ANIOC_ADS1115_SET_LO_THRESH _ANIOC(AN_ADS1115_FIRST + 9)
/****************************************************************************
* Public Types
****************************************************************************/
/* Input Multiplexer configuration. */
enum ads1115_mux_e
{
ADS1115_MUX1 = 0u, /* AINP = AIN0 and AINN = AIN1 (default) */
ADS1115_MUX2, /* AINP = AIN0 and AINN = AIN3 */
ADS1115_MUX3, /* AINP = AIN1 and AINN = AIN3 */
ADS1115_MUX4, /* AINP = AIN2 and AINN = AIN3 */
ADS1115_MUX5, /* AINP = AIN0 and AINN = GND */
ADS1115_MUX6, /* AINP = AIN1 and AINN = GND */
ADS1115_MUX7, /* AINP = AIN2 and AINN = GND */
ADS1115_MUX8, /* AINP = AIN3 and AINN = GND */
};
/* Programmable gain amplifier configuration */
enum ads1115_pga_e
{
ADS1115_PGA1 = 0u, /* FSR = ±6.144V */
ADS1115_PGA2, /* FSR = ±4.096V */
ADS1115_PGA3, /* FSR = ±2.048V (default) */
ADS1115_PGA4, /* FSR = ±1.024V */
ADS1115_PGA5, /* FSR = ±0.512V */
ADS1115_PGA6, /* FSR = ±0.256V */
ADS1115_PGA7, /* FSR = ±0.256V */
ADS1115_PGA8, /* FSR = ±0.256V */
};
/* Device Operating Mode */
enum ads1115_mode_e
{
ADS1115_MODE1 = 0u, /* Continuous conversion mode */
ADS1115_MODE2 /* Power-down single-shot mode (default) */
};
/* Data rate */
enum ads1115_dr_e
{
ADS1115_DR1 = 0u, /* 8 SPS */
ADS1115_DR2, /* 16 SPS */
ADS1115_DR3, /* 32 SPS */
ADS1115_DR4, /* 64 SPS */
ADS1115_DR5, /* 128 SPS (default) */
ADS1115_DR6, /* 250 SPS */
ADS1115_DR7, /* 475 SPS */
ADS1115_DR8, /* 860 SPS */
};
/* Comparator mode */
enum ads1115_comp_mode_e
{
ADS1115_COMP_MODE1 = 0u, /* Traditional comparator (default) */
ADS1115_COMP_MODE2 /* Window comparator */
};
/* Comparator Polarity */
enum ads1115_comp_pol_e
{
ADS1115_COMP_POL1 = 0u, /* Active low (default) */
ADS1115_COMP_POL2 /* Active high */
};
/* Latching comparator */
enum ads1115_comp_lat_e
{
ADS1115_COMP_LAT1 = 0u, /* Nonlatching comparator. The ALERT/RDY pin does
* not latch when asserted (default) */
ADS1115_COMP_LAT2 /* Latching comparator. See datasheet. */
};
/* Comparator queue and disable */
enum ads1115_comp_queue_e
{
ADS1115_COMP_QUEUE1 = 0u, /* Assert after one conversion */
ADS1115_COMP_QUEUE2, /* Assert after two conversions */
ADS1115_COMP_QUEUE3, /* Assert after four conversions */
ADS1115_COMP_QUEUE4, /* Disable comparator and set ALERT/RDY pin to
* high-impedance (default) */
};
/****************************************************************************
* Public Function Prototypes
****************************************************************************/
FAR struct adc_dev_s *ads1115_initialize(struct i2c_master_s *i2c,
uint8_t addr);
#endif /* __INCLUDE_NUTTX_ANALOG_ADS1115_H */

View file

@ -116,6 +116,11 @@
#define AN_MCP3008_FIRST (AN_SAMV7_AFEC_FIRST + AN_SAMV7_AFEC_NCMDS)
#define AN_MCP3008_NCMDS 1
/* See include/nuttx/analog/ads1115.h */
#define AN_ADS1115_FIRST (AN_MCP3008_FIRST + AN_MCP3008_NCMDS)
#define AN_ADS1115_NCMDS 10
/****************************************************************************
* Public Function Prototypes
****************************************************************************/