diff --git a/drivers/analog/Kconfig b/drivers/analog/Kconfig index 282e9879efc..9b8f2f10022 100644 --- a/drivers/analog/Kconfig +++ b/drivers/analog/Kconfig @@ -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 diff --git a/drivers/analog/Make.defs b/drivers/analog/Make.defs index fe2e4e1211b..cf0063ebca8 100644 --- a/drivers/analog/Make.defs +++ b/drivers/analog/Make.defs @@ -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) diff --git a/drivers/analog/ads1115.c b/drivers/analog/ads1115.c new file mode 100644 index 00000000000..c19c27b3a15 --- /dev/null +++ b/drivers/analog/ads1115.c @@ -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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/**************************************************************************** + * 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 = ® + 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 *)(®); + 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 */ diff --git a/include/nuttx/analog/ads1115.h b/include/nuttx/analog/ads1115.h new file mode 100644 index 00000000000..1870e89fc4d --- /dev/null +++ b/include/nuttx/analog/ads1115.h @@ -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 +#include +#include + +/**************************************************************************** + * 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 */ diff --git a/include/nuttx/analog/ioctl.h b/include/nuttx/analog/ioctl.h index e3c29f6fcad..92d30e1f1a6 100644 --- a/include/nuttx/analog/ioctl.h +++ b/include/nuttx/analog/ioctl.h @@ -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 ****************************************************************************/