nuttx/drivers/analog/lmp92001.c
Alin Jerpelea b84f45812e NuttX: Abdelatif Guettouche: update licenses to Apache
Abdelatif Guettouche has submitted the ICLA and we can migrate the licenses
 to Apache.

Gregory Nutt has submitted the SGA and we can migrate the licenses
 to Apache.

Signed-off-by: Alin Jerpelea <alin.jerpelea@sony.com>
2021-04-01 12:13:12 -05:00

1553 lines
42 KiB
C

/****************************************************************************
* drivers/analog/lmp92001.c
*
* 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 <stdio.h>
#include <sys/types.h>
#include <stdint.h>
#include <stdbool.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;
if (direction != IOEXPANDER_DIRECTION_IN &&
direction != IOEXPANDER_DIRECTION_OUT)
{
return -EINVAL;
}
DEBUGASSERT(priv != NULL && pin < LMP92001_GPIO_MAX_PINS);
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 */