nuttx/drivers/analog/lmp92001.c

1563 lines
43 KiB
C

/****************************************************************************
* drivers/analog/lmp92001.c
*
* Copyright (C) 2018 Abdelatif Guettouche. All rights reserved.
* Author: Abdelatif Guettouche <abdelatif.guettouche@gmail.com>
*
* This file is a part of NuttX:
*
* Copyright (C) 2018 Gregory Nutt. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name NuttX nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <stdio.h>
#include <sys/types.h>
#include <stdint.h>
#include <stdbool.h>
#include <semaphore.h>
#include <errno.h>
#include <debug.h>
#include <nuttx/arch.h>
#include <nuttx/analog/adc.h>
#include <nuttx/analog/dac.h>
#include <nuttx/ioexpander/ioexpander.h>
#include <nuttx/i2c/i2c_master.h>
#include <nuttx/analog/lmp92001.h>
/****************************************************************************
* Preprocessor definitions
****************************************************************************/
#if !defined(CONFIG_I2C)
# error I2C Support Required.
#endif
#if defined(CONFIG_LMP92001)
#define LMP92001_REG_TEST 0x01U
#define LMP92001_REG_ID 0x0EU
#define LMP92001_REG_VER 0x0FU
#define LMP92001_REG_SGEN 0x10U
#define LMP92001_REG_SGPI 0x11U
#define LMP92001_REG_SHIL 0x12U
#define LMP92001_REG_SLOL 0x13U
#define LMP92001_REG_CGEN 0x14U
#define LMP92001_REG_CDAC 0x15U
#define LMP92001_REG_CGPO 0x16U
#define LMP92001_REG_CINH 0x17U
#define LMP92001_REG_CINL 0x18U
#define LMP92001_REG_CAD1 0x19U
#define LMP92001_REG_CAD2 0x1AU
#define LMP92001_REG_CAD3 0x1BU
#define LMP92001_REG_CTRIG 0x1CU
#define LMP92001_REG_ADC1 0x20U
#define LMP92001_REG_ADC2 0x21U
#define LMP92001_REG_ADC3 0x22U
#define LMP92001_REG_ADC4 0x23U
#define LMP92001_REG_ADC5 0x24U
#define LMP92001_REG_ADC6 0x25U
#define LMP92001_REG_ADC7 0x26U
#define LMP92001_REG_ADC8 0x27U
#define LMP92001_REG_ADC9 0x28U
#define LMP92001_REG_ADC10 0x29U
#define LMP92001_REG_ADC11 0x2AU
#define LMP92001_REG_ADC12 0x2BU
#define LMP92001_REG_ADC13 0x2CU
#define LMP92001_REG_ADC14 0x2DU
#define LMP92001_REG_ADC15 0x2EU
#define LMP92001_REG_ADC16 0x2FU
#define LMP92001_REG_ADC17 0x30U
#define LMP92001_REG_LIH1 0x40U
#define LMP92001_REG_LIH2 0x41U
#define LMP92001_REG_LIH3 0x42U
#define LMP92001_REG_LIH9 0x43U
#define LMP92001_REG_LIH10 0x44U
#define LMP92001_REG_LIH11 0x45U
#define LMP92001_REG_LIL1 0x46U
#define LMP92001_REG_LIL2 0x47U
#define LMP92001_REG_LIL3 0x48U
#define LMP92001_REG_LIL9 0x49U
#define LMP92001_REG_LIL10 0x4AU
#define LMP92001_REG_LIL11 0x4BU
#define LMP92001_REG_CREF 0x66U
#define LMP92001_REG_DAC1 0x80U
#define LMP92001_REG_DAC2 0x81U
#define LMP92001_REG_DAC3 0x82U
#define LMP92001_REG_DAC4 0x83U
#define LMP92001_REG_DAC5 0x84U
#define LMP92001_REG_DAC6 0x85U
#define LMP92001_REG_DAC7 0x86U
#define LMP92001_REG_DAC8 0x87U
#define LMP92001_REG_DAC9 0x88U
#define LMP92001_REG_DAC10 0x89U
#define LMP92001_REG_DAC11 0x8AU
#define LMP92001_REG_DAC12 0x8BU
#define LMP92001_REG_DALL 0x90U
#define LMP92001_REG_BLK0 0xF0U
#define LMP92001_REG_BLK1 0xF1U
#define LMP92001_REG_BLK2 0xF2U
#define LMP92001_REG_BLK3 0xF3U
#define LMP92001_REG_BLK4 0xF4U
#define LMP92001_REG_BLK5 0xF5U
#define LMP92001_SGEN_BUSY (1 << 7U)
#define LMP92001_SGEN_RDYN (1 << 6U)
#define LMP92001_SGEN_HV (1 << 2U)
#define LMP92001_SGEN_LV (1 << 1U)
#define LMP92001_SGEN_GPI (1 << 0U)
#define LMP92001_SGPI_GPI7 (1 << 7U)
#define LMP92001_SGPI_GPI6 (1 << 6U)
#define LMP92001_SGPI_GPI5 (1 << 5U)
#define LMP92001_SGPI_GPI4 (1 << 4U)
#define LMP92001_SGPI_GPI3 (1 << 3U)
#define LMP92001_SGPI_GPI2 (1 << 2U)
#define LMP92001_SGPI_GPI1 (1 << 1U)
#define LMP92001_SGPI_GPI0 (1 << 0U)
#define LMP92001_CGEN_RST (1 << 7U)
#define LMP92001_CGEN_TOD (1 << 2U)
#define LMP92001_CGEN_LCK (1 << 1U)
#define LMP92001_CGEN_STRT (1 << 0U)
#define LMP92001_CDAC_GANG (1 << 2U)
#define LMP92001_CDAC_OLVL (1 << 1U)
#define LMP92001_CDAC_OFF (1 << 0U)
#define LMP92001_CDAC_ON (0U)
#define LMP92001_CGPO_GPO7 (1 << 7U)
#define LMP92001_CGPO_GPO6 (1 << 6U)
#define LMP92001_CGPO_GPO5 (1 << 5U)
#define LMP92001_CGPO_GPO4 (1 << 4U)
#define LMP92001_CGPO_GPO3 (1 << 3U)
#define LMP92001_CGPO_GPO2 (1 << 2U)
#define LMP92001_CGPO_GPO1 (1 << 1U)
#define LMP92001_CGPO_GPO0 (1 << 0U)
#define LMP92001_CTRIG_SNGL (1 << 0U)
#define LMP92001_CREF_AEXT (1 << 2U)
#define LMP92001_CREF_DEXT (1 << 1U)
#define LMP92001_ADC_MAX_CHANNELS 17U
#define LMP92001_DAC_MAX_CHANNELS 12U
#define LMP92001_GPIO_MAX_PINS 8U
/****************************************************************************
* Private Types
****************************************************************************/
struct lmp92001_dev_s
{
FAR struct i2c_master_s *i2c; /* I2C interface */
uint8_t addr; /* I2C address */
#ifdef CONFIG_ADC
FAR const struct adc_callback_s *cb;
uint8_t adc_channels_enabled;
uint8_t adc_channels[LMP92001_ADC_MAX_CHANNELS];
#endif
#ifdef CONFIG_IOEXPANDER
struct ioexpander_dev_s gpio_dev;
#endif
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
/* LMP92001 Helpers. */
static int lmp92001_i2c_write(FAR struct lmp92001_dev_s *priv,
FAR const uint8_t *buffer, int buflen);
static int lmp92001_i2c_read(FAR struct lmp92001_dev_s *priv,
uint8_t reg, FAR uint8_t *buffer, int buflen);
static int lmp92001_dac_setref(FAR struct lmp92001_dev_s *priv,
enum lmp92001_ref_e ref);
static int lmp92001_dac_updateall(FAR struct lmp92001_dev_s *priv,
uint16_t value);
static int lmp92001_adc_setref(FAR struct lmp92001_dev_s *priv,
enum lmp92001_ref_e ref);
static void lmp92001_adc_extractchannel(FAR struct lmp92001_dev_s *priv,
enum lmp92001_adc_enable_e channels);
static int lmp92001_adc_enablechannel(FAR struct lmp92001_dev_s *priv,
enum lmp92001_adc_enable_e channels);
static int lmp92001_adc_singleshot(FAR struct lmp92001_dev_s *priv);
static int lmp92001_adc_continuousconv(FAR struct lmp92001_dev_s *priv);
static int lmp92001_adc_readchannel(FAR struct lmp92001_dev_s *priv,
FAR struct adc_msg_s *msg);
/* DAC Interface. */
#ifdef CONFIG_DAC
static void lmp92001_dac_reset(FAR struct dac_dev_s *dev);
static int lmp92001_dac_setup(FAR struct dac_dev_s *dev);
static void lmp92001_dac_shutdown(FAR struct dac_dev_s *dev);
static void lmp92001_dac_txint(FAR struct dac_dev_s *dev, bool enable);
static int lmp92001_dac_send(FAR struct dac_dev_s *dev,
FAR struct dac_msg_s *msg);
static int lmp92001_dac_ioctl(FAR struct dac_dev_s *dev, int cmd,
unsigned long arg);
#endif
/* ADC Interface. */
#ifdef CONFIG_ADC
static int lmp92001_adc_bind(FAR struct adc_dev_s *dev,
FAR const struct adc_callback_s *callback);
static void lmp92001_adc_reset(FAR struct adc_dev_s *dev);
static int lmp92001_adc_setup(FAR struct adc_dev_s *dev);
static void lmp92001_adc_shutdown(FAR struct adc_dev_s *dev);
static void lmp92001_adc_rxint(FAR struct adc_dev_s *dev, bool enable);
static int lmp92001_adc_ioctl(FAR struct adc_dev_s *dev, int cmd,
unsigned long arg);
#endif
/* I/O Expander Interface. */
#ifdef CONFIG_IOEXPANDER
static int lmp92001_gpio_direction(FAR struct ioexpander_dev_s *dev,
uint8_t pin, int dir);
static int lmp92001_gpio_option(FAR struct ioexpander_dev_s *dev, uint8_t pin,
int opt, void *regval);
static int lmp92001_gpio_writepin(FAR struct ioexpander_dev_s *dev,
uint8_t pin, bool value);
static int lmp92001_gpio_readpin(FAR struct ioexpander_dev_s *dev,
uint8_t pin, FAR bool *value);
#ifdef CONFIG_IOEXPANDER_MULTIPIN
static int lmp92001_gpio_multiwritepin(FAR struct ioexpander_dev_s *dev,
FAR uint8_t *pins, FAR bool *values,
int count);
static int lmp92001_gpio_multireadpin(FAR struct ioexpander_dev_s *dev,
FAR uint8_t *pins, FAR bool *values,
int count);
#endif
#ifdef CONFIG_IOEXPANDER_INT_ENABLE
static FAR void *lmp92001_gpio_attach(FAR struct ioexpander_dev_s *dev,
ioe_pinset_t pinset,
ioe_callback_t callback, FAR void *arg);
static int lmp92001_gpio_detach(FAR struct ioexpander_dev_s *dev,
FAR void *handle);
#endif
#endif
/****************************************************************************
* Private Data
****************************************************************************/
static struct lmp92001_dev_s g_devpriv;
#ifdef CONFIG_DAC
static const struct dac_ops_s g_dacops =
{
.ao_reset = lmp92001_dac_reset,
.ao_setup = lmp92001_dac_setup,
.ao_shutdown = lmp92001_dac_shutdown,
.ao_txint = lmp92001_dac_txint,
.ao_send = lmp92001_dac_send,
.ao_ioctl = lmp92001_dac_ioctl,
};
static struct dac_dev_s g_dacdev =
{
.ad_ops = &g_dacops,
.ad_priv = &g_devpriv,
};
#endif
#ifdef CONFIG_ADC
static const struct adc_ops_s g_adcops =
{
.ao_bind = lmp92001_adc_bind,
.ao_reset = lmp92001_adc_reset,
.ao_setup = lmp92001_adc_setup,
.ao_shutdown = lmp92001_adc_shutdown,
.ao_rxint = lmp92001_adc_rxint,
.ao_ioctl = lmp92001_adc_ioctl
};
static struct adc_dev_s g_adcdev =
{
.ad_ops = &g_adcops,
.ad_priv = &g_devpriv,
};
#endif
#ifdef CONFIG_IOEXPANDER
static const struct ioexpander_ops_s g_gpio_ops =
{
lmp92001_gpio_direction,
lmp92001_gpio_option,
lmp92001_gpio_writepin,
lmp92001_gpio_readpin,
lmp92001_gpio_readpin
#ifdef CONFIG_IOEXPANDER_MULTIPIN
, lmp92001_gpio_multiwritepin
, lmp92001_gpio_multireadpin
, lmp92001_gpio_multireadpin
#endif
#ifdef CONFIG_IOEXPANDER_INT_ENABLE
, lmp92001_gpio_attach
, lmp92001_gpio_detach
#endif
};
#endif
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: lmp92001_i2c_write
*
* Description:
* Transmits a buffer.
*
****************************************************************************/
static int lmp92001_i2c_write(FAR struct lmp92001_dev_s *priv,
FAR const uint8_t *buffer, int buflen)
{
struct i2c_msg_s msg;
int ret;
/* Setup for the transfer */
msg.frequency = CONFIG_LMP92001_I2C_FREQUENCY,
msg.addr = priv->addr;
msg.flags = 0;
msg.buffer = (FAR uint8_t *)buffer; /* Override const */
msg.length = buflen;
/* Then perform the transfer. */
ret = I2C_TRANSFER(priv->i2c, &msg, 1);
if (ret < 0)
{
aerr("LMP92001 I2C transfer failed: %d", ret);
return ret;
}
return (ret >= 0) ? OK : ret;
}
/****************************************************************************
* Name: lmp92001_i2c_read
*
* Description:
* Reads a buffer.
*
****************************************************************************/
static int lmp92001_i2c_read(FAR struct lmp92001_dev_s *priv,
uint8_t reg, FAR uint8_t *buffer, int buflen)
{
struct i2c_msg_s msg[2];
int ret;
/* Setup for the transfer */
msg[0].frequency = CONFIG_LMP92001_I2C_FREQUENCY,
msg[0].addr = priv->addr,
msg[0].flags = I2C_M_NOSTOP;
msg[0].buffer = &reg;
msg[0].length = 1;
msg[1].frequency = CONFIG_LMP92001_I2C_FREQUENCY,
msg[1].addr = priv->addr,
msg[1].flags = I2C_M_READ;
msg[1].buffer = buffer;
msg[1].length = buflen;
/* Then perform the transfer. */
ret = I2C_TRANSFER(priv->i2c, msg, 2);
if (ret < 0)
{
aerr("LMP92001 I2C transfer failed: %d", ret);
return ret;
}
return (ret >= 0) ? OK : ret;
}
/****************************************************************************
* Name: lmp92001_dac_setref
*
* Description:
* Sets the DACs reference (internal or external).
*
****************************************************************************/
static int lmp92001_dac_setref(FAR struct lmp92001_dev_s *priv,
enum lmp92001_ref_e ref)
{
uint8_t value = 0;
uint8_t const BUFFER_SIZE = 2U;
uint8_t buffer[BUFFER_SIZE];
int ret = OK;
ret = lmp92001_i2c_read(priv, LMP92001_REG_CREF, &value, 1);
if (ret < 0)
{
aerr("LMP92001 DAC set reference failed: %d", ret);
return ret;
}
if (ref == LMP92001_REF_EXTERNAL)
{
value |= LMP92001_CREF_DEXT;
}
else
{
value &= ~LMP92001_CREF_DEXT;
}
buffer[0] = LMP92001_REG_CREF;
buffer[1] = value;
ret = lmp92001_i2c_write(priv, buffer, BUFFER_SIZE);
if (ret < 0)
{
aerr("LMP92001 DAC set reference failed: %d", ret);
return ret;
}
return ret;
}
/****************************************************************************
* Name: lmp92001_dac_updateall
*
* Description:
* Write to all DACx registers simultaneously.
*
****************************************************************************/
static int lmp92001_dac_updateall(FAR struct lmp92001_dev_s *priv,
uint16_t value)
{
uint8_t const BUFFER_SIZE = 3U;
uint8_t buffer[BUFFER_SIZE];
int ret = OK;
buffer[0] = LMP92001_REG_DALL;
buffer[1] = (uint8_t)(value >> 8U);
buffer[2] = (uint8_t)(value & 0xFFU);
ret = lmp92001_i2c_write(priv, buffer, BUFFER_SIZE);
if (ret < 0)
{
aerr("LMP92001 DAC update all failed: %d", ret);
return ret;
}
return ret;
}
/****************************************************************************
* Name: lmp92001_dac_reset
*
* Description:
* Reset the DAC device. Called early to initialize the hardware. This
* is called, before ao_setup() and on error conditions.
*
****************************************************************************/
#ifdef CONFIG_DAC
static void lmp92001_dac_reset(FAR struct dac_dev_s *dev)
{
}
/****************************************************************************
* Name: lmp92001_dac_setup
*
* Description:
* Configure the DAC. This method is called the first time that the DAC
* device is opened. This will occur when the port is first opened.
* This setup includes configuring and attaching DAC interrupts. Interrupts
* are all disabled upon return.
*
****************************************************************************/
static int lmp92001_dac_setup(FAR struct dac_dev_s *dev)
{
FAR struct lmp92001_dev_s *priv = (FAR struct lmp92001_dev_s *)dev->ad_priv;
uint8_t const BUFFER_SIZE = 2U;
uint8_t buffer[BUFFER_SIZE];
int ret = OK;
/* Enable DAC channels. */
buffer[0] = LMP92001_REG_CDAC;
buffer[1] = LMP92001_CDAC_ON;
ret = lmp92001_i2c_write(priv, buffer, BUFFER_SIZE);
if (ret < 0)
{
aerr("LMP92001 DAC setup failed: %d", ret);
return ret;
}
return ret;
}
/****************************************************************************
* Name: lmp92001_dac_shutdown
*
* Description:
* Disable the DAC. This method is called when the DAC device is closed.
* This method reverses the operation the setup method.
*
****************************************************************************/
static void lmp92001_dac_shutdown(FAR struct dac_dev_s *dev)
{
}
/****************************************************************************
* Name: lmp92001_dac_txint
*
* Description:
* Call to enable or disable TX interrupts
*
****************************************************************************/
static void lmp92001_dac_txint(FAR struct dac_dev_s *dev, bool enable)
{
}
/****************************************************************************
* Name: lmp92001_dac_send
*
* Description:
* This method will send one message on the DAC.
*
****************************************************************************/
static int lmp92001_dac_send(FAR struct dac_dev_s *dev,
FAR struct dac_msg_s *msg)
{
FAR struct lmp92001_dev_s *priv = (FAR struct lmp92001_dev_s *)dev->ad_priv;
uint8_t const BUFFER_SIZE = 3U;
uint8_t buffer[BUFFER_SIZE];
int ret;
/* Sanity check */
DEBUGASSERT(priv->i2c != NULL);
ainfo("Channel: %d - Value: %08x\n", msg->am_channel, msg->am_data);
/* Set up the message to send.
* We follow the same conventions as the datasheet.
* ie. channels number start from 1 not from zero.
*/
buffer[0] = (msg->am_channel - 1) + LMP92001_REG_DAC1;
buffer[1] = (uint8_t)(msg->am_data >> 8U);
buffer[2] = (uint8_t)(msg->am_data & 0xFFU);
ret = lmp92001_i2c_write(priv, buffer, BUFFER_SIZE);
if (ret < 0)
{
aerr("LMP92001 DAC send failed: %d", ret);
return ret;
}
dac_txdone(&g_dacdev);
return ret;
}
/****************************************************************************
* Name: lmp92001_dac_ioctl
*
* Description:
* All ioctl calls will be routed through this method.
*
****************************************************************************/
static int lmp92001_dac_ioctl(FAR struct dac_dev_s *dev, int cmd,
unsigned long arg)
{
FAR struct lmp92001_dev_s *priv = (FAR struct lmp92001_dev_s *)dev->ad_priv;
int ret = OK;
switch (cmd)
{
case ANIOC_LMP92001_DAC_SET_REF:
{
ret = lmp92001_dac_setref(priv, (enum lmp92001_ref_e)(arg));
}
break;
case ANIOC_LMP92001_DAC_UPDATEALL:
{
ret = lmp92001_dac_updateall(priv, (uint16_t)(arg));
}
break;
/* Command was not recognized */
default:
aerr("LMP92001 ERROR: Unrecognized cmd: %d\n", cmd);
ret = -ENOTTY;
break;
}
return ret;
}
#endif /* CONFIG_DAC */
/****************************************************************************
* Name: lmp92001_adc_setref
*
* Description:
* Sets the ADCs reference (internal or external).
*
****************************************************************************/
static int lmp92001_adc_setref(FAR struct lmp92001_dev_s *priv,
enum lmp92001_ref_e ref)
{
uint8_t value = 0;
uint8_t const BUFFER_SIZE = 2U;
uint8_t buffer[BUFFER_SIZE];
int ret = OK;
ret = lmp92001_i2c_read(priv, LMP92001_REG_CREF, &value, 1);
if (ret < 0)
{
aerr("LMP92001 ADC set reference failed: %d", ret);
return ret;
}
if (ref == LMP92001_REF_EXTERNAL)
{
value |= LMP92001_CREF_AEXT;
}
else
{
value &= ~LMP92001_CREF_AEXT;
}
buffer[0] = LMP92001_REG_CREF;
buffer[1] = value;
ret = lmp92001_i2c_write(priv, buffer, BUFFER_SIZE);
if (ret < 0)
{
aerr("LMP92001 ADC set reference failed: %d", ret);
return ret;
}
return ret;
}
/****************************************************************************
* Name: lmp92001_adc_extractchannel
*
* Description:
* Extract channels from the enum.
*
****************************************************************************/
static void lmp92001_adc_extractchannel(FAR struct lmp92001_dev_s *priv,
enum lmp92001_adc_enable_e channels)
{
uint8_t i;
uint8_t j;
uint16_t tmp;
i = 0U;
j = priv->adc_channels_enabled;
tmp = channels;
/* Channels to be enabled are the bits set in the argument channels. */
while (tmp != 0U)
{
if (tmp & 1U)
{
priv->adc_channels_enabled++;
priv->adc_channels[j] = i + 1;
ainfo("Channel %d to be enabled\n", priv->adc_channels[j]);
j++;
}
tmp >>= 1U;
i++;
}
ainfo("Number of channels to be enabled: %d\n",
priv->adc_channels_enabled);
}
/****************************************************************************
* Name: lmp92001_adc_enablechannel
*
* Description:
* Enables the given ADC channels.
*
****************************************************************************/
static int lmp92001_adc_enablechannel(FAR struct lmp92001_dev_s *priv,
enum lmp92001_adc_enable_e channels)
{
uint8_t cad1 = 0U;
uint8_t cad2 = 0U;
uint8_t cad3 = 0U;
uint8_t const BUFFER_SIZE = 2U;
uint8_t buffer[BUFFER_SIZE];
int ret = OK;
if (priv->adc_channels_enabled < LMP92001_ADC_MAX_CHANNELS && channels != 0)
{
lmp92001_adc_extractchannel(priv, channels);
ret = lmp92001_i2c_read(priv, LMP92001_REG_CAD1, &cad1, 1);
if (ret < 0)
{
aerr("LMP92001 ADC enable channel failed %d\n", ret);
return ret;
}
ret = lmp92001_i2c_read(priv, LMP92001_REG_CAD2, &cad2, 1);
if (ret < 0)
{
aerr("LMP92001 ADC enable channel failed %d\n", ret);
return ret;
}
ret = lmp92001_i2c_read(priv, LMP92001_REG_CAD3, &cad3, 1);
if (ret < 0)
{
aerr("LMP92001 ADC enable channel failed %d\n", ret);
return ret;
}
cad1 |= channels & 0x000FFU;
cad2 |= (channels >> 8U) & 0x000FFU;
cad3 |= (channels >> 16U) & 0x1U;
if (cad1 > 0)
{
buffer[0] = LMP92001_REG_CAD1;
buffer[1] = cad1;
ret = lmp92001_i2c_write(priv, buffer, BUFFER_SIZE);
if (ret < 0)
{
aerr("LMP92001 ADC enable channel failed %d\n", ret);
return ret;
}
}
if (cad2 > 0)
{
buffer[0] = LMP92001_REG_CAD2;
buffer[1] = cad2;
ret = lmp92001_i2c_write(priv, buffer, BUFFER_SIZE);
if (ret < 0)
{
aerr("LMP92001 ADC enable channel failed %d\n", ret);
return ret;
}
}
if (cad3 > 0)
{
buffer[0] = LMP92001_REG_CAD3;
buffer[1] = cad3;
ret = lmp92001_i2c_write(priv, buffer, BUFFER_SIZE);
if (ret < 0)
{
aerr("LMP92001 ADC enable channel failed %d\n", ret);
return ret;
}
}
}
return ret;
}
/****************************************************************************
* Name: lmp92001_adc_singleshot
*
* Description:
* Starts a single shot conversion.
*
****************************************************************************/
static int lmp92001_adc_singleshot(FAR struct lmp92001_dev_s *priv)
{
uint8_t const BUFFER_SIZE = 2U;
uint8_t buffer[BUFFER_SIZE];
uint8_t sgen_value = 0;
int ret = OK;
/* Lock registers, this will also force CGEN.STRT to 0. */
buffer[0] = LMP92001_REG_CGEN;
buffer[1] = LMP92001_CGEN_LCK;
ret = lmp92001_i2c_write(priv, buffer, BUFFER_SIZE);
if (ret < 0)
{
aerr("LMP92001 ADC Single shot failed %d\n", ret);
return ret;
}
/* Trigger a single shot conversion. */
buffer[0] = LMP92001_REG_CTRIG;
buffer[1] = LMP92001_CTRIG_SNGL;
ret = lmp92001_i2c_write(priv, buffer, BUFFER_SIZE);
if (ret < 0)
{
aerr("LMP92001 ADC Single shot failed %d\n", ret);
return ret;
}
/* Wait for the BUSY flag. */
do
{
lmp92001_i2c_read(priv, LMP92001_REG_SGEN, &sgen_value, 1);
}
while ((sgen_value & LMP92001_SGEN_BUSY) != 0);
/* Unlock registers.
* It's not needed to read the registers first and verify if some
* bits were set. All the other bits should already be cleared.
*/
buffer[0] = LMP92001_REG_CGEN;
buffer[1] = 0;
ret = lmp92001_i2c_write(priv, buffer, BUFFER_SIZE);
if (ret < 0)
{
aerr("LMP92001 ADC Single shot failed %d\n", ret);
return ret;
}
return ret;
}
/****************************************************************************
* Name: lmp92001_adc_continuousconv
*
* Description:
* Starts the continuous conversion.
*
****************************************************************************/
static int lmp92001_adc_continuousconv(FAR struct lmp92001_dev_s *priv)
{
uint8_t const BUFFER_SIZE = 2U;
uint8_t buffer[BUFFER_SIZE];
uint8_t sgen_value = 0U;
int ret = OK;
/* Set CGEN.STRT bit and lock registers */
buffer[0] = LMP92001_REG_CGEN;
buffer[1] = LMP92001_CGEN_STRT | LMP92001_CGEN_LCK;
ret = lmp92001_i2c_write(priv, buffer, BUFFER_SIZE);
if (ret < 0)
{
aerr("LMP92001 ADC Continuous conversion failed %d\n", ret);
return ret;
}
/* Wait for the BUSY flag. */
do
{
lmp92001_i2c_read(priv, LMP92001_REG_SGEN, &sgen_value, 1);
}
while ((sgen_value & LMP92001_SGEN_BUSY) != 0);
/* Unlock the registers and keep STRT set. */
buffer[0] = LMP92001_REG_CGEN;
buffer[1] = LMP92001_CGEN_STRT;
buffer[1] &= ~LMP92001_CGEN_LCK;
ret = lmp92001_i2c_write(priv, buffer, BUFFER_SIZE);
if (ret < 0)
{
aerr("LMP92001 ADC Continuous conversion failed %d\n", ret);
return ret;
}
return ret;
}
/****************************************************************************
* Name: lmp92001_adc_readchannel
*
* Description:
* Read from an ADC channel.
*
****************************************************************************/
static int lmp92001_adc_readchannel(FAR struct lmp92001_dev_s *priv,
FAR struct adc_msg_s *msg)
{
uint8_t buffer[2];
uint8_t channel;
int ret = OK;
/* Adding LMP92001_REG_ADC1 to a channel gives the channel's register.
* Reminder: Channels numbering is the same as the datasheet.
* First channel is called Channel1.
* Note: ADC registers are 16-bit wide.
*/
channel = LMP92001_REG_ADC1 + msg->am_channel - 1;
ret = lmp92001_i2c_read(priv, channel, buffer, 2);
if (ret < 0)
{
aerr("LMP92001 ADC read failed: %d\n", ret);
return ret;
}
msg->am_data = (buffer[0] << 8U) | buffer[1];
return ret;
}
#ifdef CONFIG_ADC
/****************************************************************************
* Name: lmp92001_adc_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 lmp92001_adc_bind(FAR struct adc_dev_s *dev,
FAR const struct adc_callback_s *callback)
{
FAR struct lmp92001_dev_s *priv = (FAR struct lmp92001_dev_s *)dev->ad_priv;
DEBUGASSERT(priv != NULL);
priv->cb = callback;
return OK;
}
/****************************************************************************
* Name: lmp92001_adc_reset
*
* Description:
* Reset the ADC device. Called early to initialize the hardware. This
* is called, before ao_setup() and on error conditions.
*
****************************************************************************/
static void lmp92001_adc_reset(FAR struct adc_dev_s *dev)
{
FAR struct lmp92001_dev_s *priv =
(FAR struct lmp92001_dev_s *)dev->ad_priv;
priv->adc_channels_enabled = 0;
}
/****************************************************************************
* Name: lmp92001_adc_setup
*
* Description:
* Configure the ADC. This method is called the first time that the ADC
* device is opened. This will occur when the port is first opened.
* This setup includes configuring and attaching ADC interrupts.
* Interrupts are all disabled upon return.
*
****************************************************************************/
static int lmp92001_adc_setup(FAR struct adc_dev_s *dev)
{
return OK;
}
/****************************************************************************
* Name: lmp92001_adc_shutdown
*
* Description:
* Disable the ADC. This method is called when the ADC device is closed.
* This method reverses the operation the setup method.
*
****************************************************************************/
static void lmp92001_adc_shutdown(FAR struct adc_dev_s *dev)
{
}
/****************************************************************************
* Name: lmp92001_adc_rxint
*
* Description:
* Call to enable or disable RX interrupts
*
****************************************************************************/
static void lmp92001_adc_rxint(FAR struct adc_dev_s *dev, bool enable)
{
}
/****************************************************************************
* Name: lmp92001_adc_ioctl
*
* Description:
* All ioctl calls will be routed through this method
*
****************************************************************************/
static int lmp92001_adc_ioctl(FAR struct adc_dev_s *dev, int cmd,
unsigned long arg)
{
FAR struct lmp92001_dev_s *priv =
(FAR struct lmp92001_dev_s *)dev->ad_priv;
int ret = OK;
switch (cmd)
{
case ANIOC_LMP92001_ADC_ENABLE:
{
ret = lmp92001_adc_enablechannel(priv,
(enum lmp92001_adc_enable_e)arg);
}
break;
case ANIOC_LMP92001_ADC_SET_REF:
{
ret = lmp92001_adc_setref(priv, (enum lmp92001_ref_e)(arg));
}
break;
case ANIOC_TRIGGER:
case ANIOC_LMP92001_ADC_SINGLESHOT_CONV:
{
struct adc_msg_s msg;
int i;
ret = lmp92001_adc_singleshot(priv);
if (ret < 0)
{
break;
}
for (i = 0; i < priv->adc_channels_enabled; i++)
{
msg.am_channel = priv->adc_channels[i];
ret = lmp92001_adc_readchannel(priv, &msg);
priv->cb->au_receive(&g_adcdev, priv->adc_channels[i],
msg.am_data);
}
}
break;
case ANIOC_LMP92001_ADC_READ_CHANNEL:
{
FAR struct adc_msg_s *msg =
(FAR struct adc_msg_s *)((uintptr_t)arg);
ret = lmp92001_adc_singleshot(priv);
if (ret < 0)
{
break;
}
ret = lmp92001_adc_readchannel(priv, msg);
}
break;
case ANIOC_LMP92001_ADC_CONTINUOUS_CONV:
{
struct adc_msg_s msg;
int i;
ret = lmp92001_adc_continuousconv(priv);
if (ret < 0)
{
break;
}
for (i = 0; i < priv->adc_channels_enabled; i++)
{
msg.am_channel = priv->adc_channels[i];
ret = lmp92001_adc_readchannel(priv, &msg);
priv->cb->au_receive(&g_adcdev, priv->adc_channels[i],
msg.am_data);
}
}
break;
/* Command was not recognized */
default:
aerr("LMP92001 ERROR: Unrecognized cmd: %d\n", cmd);
ret = -ENOTTY;
break;
}
return ret;
}
#endif /* CONFIG_ADC */
/****************************************************************************
* Name: lmp92001_gpio_direction
*
* Description:
* Set the direction of an ioexpander pin. Required.
*
* Input Parameters:
* dev - Device-specific state data
* pin - The index of the pin to alter in this call
* dir - One of the IOEXPANDER_DIRECTION_ macros
*
* Returned Value:
* 0 on success, else a negative error code
*
****************************************************************************/
#ifdef CONFIG_IOEXPANDER
static int lmp92001_gpio_direction(FAR struct ioexpander_dev_s *dev,
uint8_t pin, int direction)
{
FAR struct lmp92001_dev_s *priv = (FAR struct lmp92001_dev_s *)dev;
uint8_t regval;
uint8_t const BUFFER_SIZE = 2U;
uint8_t buffer[BUFFER_SIZE];
int ret;
DEBUGASSERT(priv != NULL && pin < LMP92001_GPIO_MAX_PINS &&
(direction == IOEXPANDER_DIRECTION_IN ||
direction == IOEXPANDER_DIRECTION_OUT));
gpioinfo("I2C addr=%02x pin=%u direction=%s\n",
priv->addr, pin,
(direction == IOEXPANDER_DIRECTION_IN) ? "IN" : "OUT");
ret = lmp92001_i2c_read(priv, LMP92001_REG_CGPO, &regval, 1);
if (ret < 0)
{
gpioerr("LMP92001 GPIO set direction failed: %d\n", ret);
return ret;
}
buffer[0] = LMP92001_REG_CGPO;
if (direction == IOEXPANDER_DIRECTION_IN)
{
buffer[1] = regval | (1 << pin);
}
else
{
buffer[1] = regval & ~(1 << pin);
}
ret = lmp92001_i2c_write(priv, buffer, BUFFER_SIZE);
if (ret < 0)
{
gpioerr("LMP92001 GPIO set direction failed: %d\n", ret);
}
return ret;
}
/****************************************************************************
* Name: lmp92001_gpio_option
*
* Description:
* Set pin options. Required.
*
* Input Parameters:
* dev - Device-specific state data
* pin - The index of the pin to alter in this call
* opt - One of the IOEXPANDER_OPTION_ macros
* val - The option's value
*
* Returned Value:
* 0 on success, else a negative error code
*
****************************************************************************/
static int lmp92001_gpio_option(FAR struct ioexpander_dev_s *dev, uint8_t pin,
int opt, void *regval)
{
return OK;
}
/****************************************************************************
* Name: lmp92001_gpio_writepin
*
* Description:
* Set the pin level. Required.
*
* Input Parameters:
* dev - Device-specific state data
* pin - The index of the pin to alter in this call
* val - The pin level. Usually TRUE will set the pin high,
* except if OPTION_INVERT has been set on this pin.
*
* Returned Value:
* 0 on success, else a negative error code
*
****************************************************************************/
static int lmp92001_gpio_writepin(FAR struct ioexpander_dev_s *dev,
uint8_t pin, bool value)
{
FAR struct lmp92001_dev_s *priv = (FAR struct lmp92001_dev_s *)dev;
uint8_t regval;
uint8_t const BUFFER_SIZE = 2U;
uint8_t buffer[BUFFER_SIZE];
int ret;
ret = lmp92001_i2c_read(priv, LMP92001_REG_CGPO, &regval, 1);
if (ret < 0)
{
gpioerr("LMP92001 GPIO write pin failed: %d\n", ret);
return ret;
}
buffer[0] = LMP92001_REG_CGPO;
if (value)
{
buffer[1] = regval | (1 << pin);
}
else
{
buffer[1] = regval & ~(value << pin);
}
ret = lmp92001_i2c_write(priv, buffer, BUFFER_SIZE);
if (ret < 0)
{
gpioerr("LMP92001 GPIO write pin failed: %d\n", ret);
}
return ret;
}
/****************************************************************************
* Name: lmp92001_gpio_readpin
*
* Description:
* Read the actual PIN level. This can be different from the last value
* written to this pin. Required.
*
* Input Parameters:
* dev - Device-specific state data
* pin - The index of the pin
* value - Pointer to a buffer where the pin level is stored. Usually TRUE
* if the pin is high.
*
* Returned Value:
* 0 on success, else a negative error code
*
****************************************************************************/
static int lmp92001_gpio_readpin(FAR struct ioexpander_dev_s *dev,
uint8_t pin, FAR bool *value)
{
FAR struct lmp92001_dev_s *priv = (FAR struct lmp92001_dev_s *)dev;
uint8_t regval;
int ret;
DEBUGASSERT(priv != NULL && pin < LMP92001_GPIO_MAX_PINS &&
value != NULL);
gpioinfo("I2C addr=%02x, pin=%u\n", priv->addr, pin);
ret = lmp92001_i2c_read(priv, LMP92001_REG_SGPI, &regval, 1);
if (ret < 0)
{
gpioerr("LMP92001 GPIO read pin failed: %d\n", ret);
return ret;
}
*value = (bool)(regval >> pin) & 1U;
return ret;
}
/****************************************************************************
* Name: lmp92001_gpio_multiwritepin
*
* Description:
* Set the pin level for multiple pins. This routine may be faster than
* individual pin accesses. Optional.
*
* Input Parameters:
* dev - Device-specific state data
* pins - The list of pin indexes to alter in this call
* val - The list of pin levels.
*
* Returned Value:
* 0 on success, else a negative error code
*
****************************************************************************/
#ifdef CONFIG_IOEXPANDER_MULTIPIN
static int lmp92001_gpio_multiwritepin(FAR struct ioexpander_dev_s *dev,
FAR uint8_t *pins, FAR bool *values,
int count)
{
}
/****************************************************************************
* Name: lmp92001_gpio_multireadpin
*
* Description:
* Read the actual level for multiple pins. This routine may be faster than
* individual pin accesses. Optional.
*
* Input Parameters:
* dev - Device-specific state data
* pin - The list of pin indexes to read
* values - Pointer to a buffer where the pin levels are stored.
*
* Returned Value:
* 0 on success, else a negative error code
*
****************************************************************************/
static int lmp92001_gpio_multireadpin(FAR struct ioexpander_dev_s *dev,
FAR uint8_t *pins, FAR bool *values,
int count)
{
}
#endif
/****************************************************************************
* Name: lmp92001_gpio_attach
*
* Description:
* Attach and enable a pin interrupt callback function.
*
* Input Parameters:
* dev - Device-specific state data
* pinset - The set of pin events that will generate the callback
* callback - The pointer to callback function. NULL will detach the
* callback.
* arg - User-provided callback argument
*
* Returned Value:
* A non-NULL handle value is returned on success. This handle may be
* used later to detach and disable the pin interrupt.
*
****************************************************************************/
#ifdef CONFIG_IOEXPANDER_INT_ENABLE
static FAR void *lmp92001_gpio_attach(FAR struct ioexpander_dev_s *dev,
ioe_pinset_t pinset,
ioe_callback_t callback, FAR void *arg)
{
}
/****************************************************************************
* Name: lmp92001_gpio_detach
*
* Description:
* Detach and disable a pin interrupt callback function.
*
* Input Parameters:
* dev - Device-specific state data
* handle - The non-NULL opaque value return by lmp92001_gpio_attach()
*
* Returned Value:
* 0 on success, else a negative error code
*
****************************************************************************/
static int lmp92001_gpio_detach(FAR struct ioexpander_dev_s *dev,
FAR void *handle)
{
}
#endif
#endif /* CONFIG_IOEXPANDER */
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: lmp92001_dac_initialize
*
* Description:
* Initialize DAC
*
* Input Parameters:
* I2C Port number
* Device address
*
* Returned Value:
* Valid LMP92001 device structure reference on success; a NULL on failure
*
****************************************************************************/
#ifdef CONFIG_DAC
FAR struct dac_dev_s *lmp92001_dac_initialize(FAR struct i2c_master_s *i2c,
uint8_t addr)
{
FAR struct lmp92001_dev_s *priv;
/* Sanity check */
DEBUGASSERT(i2c != NULL);
/* Initialize the LMP92001 device structure */
priv = (FAR struct lmp92001_dev_s *)g_dacdev.ad_priv;
priv->i2c = i2c;
priv->addr = addr;
return &g_dacdev;
}
#endif
/****************************************************************************
* Name: lmp92001_adc_initialize
*
* Description:
* Initialize ADC
*
* Input Parameters:
* I2C Port number
* Device address
*
* Returned Value:
* Valid LMP92001 device structure reference on success; a NULL on failure
*
****************************************************************************/
#ifdef CONFIG_ADC
FAR struct adc_dev_s *lmp92001_adc_initialize(FAR struct i2c_master_s *i2c,
uint8_t addr)
{
FAR struct lmp92001_dev_s *priv;
/* Sanity check */
DEBUGASSERT(i2c != NULL);
/* Initialize the LMP92001 device structure */
priv = (FAR struct lmp92001_dev_s *)g_adcdev.ad_priv;
priv->cb = NULL;
priv->i2c = i2c;
priv->addr = addr;
return &g_adcdev;
}
#endif
#ifdef CONFIG_IOEXPANDER
FAR struct ioexpander_dev_s *
lmp92001_gpio_initialize(FAR struct i2c_master_s *i2c, uint8_t addr)
{
FAR struct lmp92001_dev_s *priv;
/* Sanity check */
DEBUGASSERT(i2c != NULL);
/* Initialize the LMP92001 device structure */
priv = &g_devpriv;
priv->gpio_dev.ops = &g_gpio_ops;
priv->i2c = i2c;
priv->addr = addr;
return &priv->gpio_dev;
}
#endif
#endif /* CONFIG_LMP92001 */