/**************************************************************************** * drivers/analog/lmp92001.c * * Copyright (C) 2018 Abdelatif Guettouche. All rights reserved. * Author: Abdelatif Guettouche * * 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 #include #include #include #include #include #include #include #include #include #include #include #include /**************************************************************************** * 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 = ® 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, ®val, 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, ®val, 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, ®val, 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 */