From 57623d42ebb04f0a0b9e6eb7c0847a3ece2aa0ff Mon Sep 17 00:00:00 2001 From: patacongo Date: Mon, 17 Sep 2012 18:18:44 +0000 Subject: Resync new repository with old repo r5166 git-svn-id: http://svn.code.sf.net/p/nuttx/code/trunk@5153 42af7a65-404d-4744-a932-0658087f49c3 --- nuttx/drivers/analog/Kconfig | 77 ++++ nuttx/drivers/analog/Make.defs | 87 +++++ nuttx/drivers/analog/ad5410.c | 205 +++++++++++ nuttx/drivers/analog/adc.c | 423 ++++++++++++++++++++++ nuttx/drivers/analog/ads1255.c | 335 +++++++++++++++++ nuttx/drivers/analog/dac.c | 499 ++++++++++++++++++++++++++ nuttx/drivers/analog/pga11x.c | 793 +++++++++++++++++++++++++++++++++++++++++ 7 files changed, 2419 insertions(+) create mode 100644 nuttx/drivers/analog/Kconfig create mode 100644 nuttx/drivers/analog/Make.defs create mode 100644 nuttx/drivers/analog/ad5410.c create mode 100644 nuttx/drivers/analog/adc.c create mode 100644 nuttx/drivers/analog/ads1255.c create mode 100644 nuttx/drivers/analog/dac.c create mode 100644 nuttx/drivers/analog/pga11x.c (limited to 'nuttx/drivers/analog') diff --git a/nuttx/drivers/analog/Kconfig b/nuttx/drivers/analog/Kconfig new file mode 100644 index 000000000..ebed79c78 --- /dev/null +++ b/nuttx/drivers/analog/Kconfig @@ -0,0 +1,77 @@ +# +# For a description of the syntax of this configuration file, +# see misc/tools/kconfig-language.txt. +# + +config ADC + bool "Analog-to-Digital Conversion" + default n + ---help--- + Select to enable support for analog input device support. This includes + not only Analog-to-Digital Converters (ADC) but also amplifiers and + analog multiplexers. + +config ADC_ADS125X + bool "TI ADS1255/ADS1256 support" + default n + depends on ADC + select SPI + +config ADS1255_FREQUENCY + int "ADS1255/ADS1256 SPI frequency" + default 1000000 + depends on ADC_ADS125X + +config ADC_PGA11X + bool "TI PGA112/3/6/7 support" + default n + depends on ADC + select SPI + ---help--- + Enables support for the PGA112, PGA113, PGA116, PGA117 Zerø-Drift + PROGRAMMABLE GAIN AMPLIFIER with MUX + +config PGA11X_SPIFREQUENCY + int "TI PGA112/3/6/7 SPI frequency" + default 1000000 + depends on ADC_PGA11X + ---help--- + PGA11x SPI frequency. + +config PGA11X_SPIMODE + int "TI PGA112/3/6/7 SPI mode" + default 0 + depends on ADC_PGA11X + ---help--- + PGA11x SPI mode. The specification says that the device operates in Mode 0 or + Mode 3. But sometimes you need to tinker with this to get things to work + correctly. Default: Mode 0 + +config PGA11X_DAISYCHAIN + bool "TI PGA112/3/6/7 daisy chain mode" + default n + depends on ADC_PGA11X + ---help--- + Enable support to use two PGA116/7's in Daisy Chain configuration. + +config PGA11X_MULTIPLE + bool "Multiple TI PGA112/3/6/7 support" + default n + depends on ADC_PGA11X && !PGA11X_DAISYCHAIN + ---help--- + Can be defined to support multiple PGA11X devices on board with separate + chip selects (not daisy chained). Each device will require a customized + SPI interface to distinguish them when SPI_SELECT is called with + devid=SPIDEV_MUX. + +config DAC + bool "Digital-to-Analog Conversion" + default n + ---help--- + Select to enable support for Digital-to-Analog Converters (DACs). + +config DAC_AD5410 + bool "AD5410 support" + default n + depends on DAC + select SPI diff --git a/nuttx/drivers/analog/Make.defs b/nuttx/drivers/analog/Make.defs new file mode 100644 index 000000000..d94e39758 --- /dev/null +++ b/nuttx/drivers/analog/Make.defs @@ -0,0 +1,87 @@ +############################################################################ +# drivers/analog/Make.defs +# +# Copyright (C) 2011 Gregory Nutt. All rights reserved. +# Author: Gregory Nutt +# +# 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. +# +############################################################################ + +# Don't build anything if there is no support for analog devices + +# Check for DAC devices + +ifeq ($(CONFIG_DAC),y) + +# Include the common DAC character driver + +CSRCS += dac.c + +# Include DAC device drivers + +ifeq ($(CONFIG_DAC_AD5410),y) + CSRCS += ad5410.c +endif +endif + +# Check for ADC devices + +ifeq ($(CONFIG_ADC),y) + +# Include the common ADC character driver + +CSRCS += adc.c + +# Amplifiers/multiplexers + +ifeq ($(CONFIG_ADC_PGA11X),y) + CSRCS += pga11x.c +endif + +# Include ADC device drivers + +ifeq ($(CONFIG_ADC_ADS125X),y) + CSRCS += ads1255.c +endif +endif + +# Add analog driver build support (the nested if-then-else implements an OR) + +ifeq ($(CONFIG_DAC),y) + DEPPATH += --dep-path analog + VPATH += :analog + CFLAGS += ${shell $(TOPDIR)/tools/incdir.sh $(INCDIROPT) "$(CC)" $(TOPDIR)/drivers/analog} +else +ifeq ($(CONFIG_ADC),y) + DEPPATH += --dep-path analog + VPATH += :analog + CFLAGS += ${shell $(TOPDIR)/tools/incdir.sh $(INCDIROPT) "$(CC)" $(TOPDIR)/drivers/analog} +endif +endif + diff --git a/nuttx/drivers/analog/ad5410.c b/nuttx/drivers/analog/ad5410.c new file mode 100644 index 000000000..3e925a3a9 --- /dev/null +++ b/nuttx/drivers/analog/ad5410.c @@ -0,0 +1,205 @@ +/************************************************************************************ + * arch/drivers/analog/ad5410.c + * + * Copyright (C) 2011 Li Zhuoyi. All rights reserved. + * Author: Li Zhuoyi + * History: 0.1 2011-08-05 initial version + * + * This file is a part of NuttX: + * + * Copyright (C) 2010 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. + * + ************************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#if defined(CONFIG_DAC_AD5410) + +#define AD5410_REG_NOP 0x00 +#define AD5410_REG_WR 0x01 +#define AD5410_REG_RD 0x02 +#define AD5410_REG_CMD 0x55 +#define AD5410_REG_RST 0x56 + +#define AD5410_CMD_REXT (1<<13) +#define AD5410_CMD_OUTEN (1<<12) +#define AD5410_CMD_SRCLK(x) (x<<8) +#define AD5410_CMD_SRSTEP(x) (x<<5) +#define AD5410_CMD_SREN (1<<4) +#define AD5410_CMD_DCEN (1<<3) +#define AD5410_CMD_420MA 0x05 +#define AD5410_CMD_020MA 0x06 +#define AD5410_CMD_024MA 0x07 + +/**************************************************************************** + * ad_private Types + ****************************************************************************/ +struct up_dev_s +{ + int devno; + FAR struct spi_dev_s *spi; /* Cached SPI device reference */ +}; + +/**************************************************************************** + * ad_private Function Prototypes + ****************************************************************************/ + +/* DAC methods */ + +static void dac_reset(FAR struct dac_dev_s *dev); +static int dac_setup(FAR struct dac_dev_s *dev); +static void dac_shutdown(FAR struct dac_dev_s *dev); +static void dac_txint(FAR struct dac_dev_s *dev, bool enable); +static int dac_send(FAR struct dac_dev_s *dev, FAR struct dac_msg_s *msg); +static int dac_ioctl(FAR struct dac_dev_s *dev, int cmd, unsigned long arg); +static int dac_interrupt(int irq, void *context); + +/**************************************************************************** + * ad_private Data + ****************************************************************************/ + +static const struct dac_ops_s g_dacops = +{ + .ao_reset =dac_reset, + .ao_setup = dac_setup, + .ao_shutdown = dac_shutdown, + .ao_txint = dac_txint, + .ao_send = dac_send, + .ao_ioctl = dac_ioctl, +}; + +static struct up_dev_s g_dacpriv; +static struct dac_dev_s g_dacdev = +{ + .ad_ops = &g_dacops, + .ad_priv= &g_dacpriv, +}; + +/**************************************************************************** + * ad_private Functions + ****************************************************************************/ + +/* Reset the DAC device. Called early to initialize the hardware. This +* is called, before ao_setup() and on error conditions. +*/ +static void dac_reset(FAR struct dac_dev_s *dev) +{ + +} + +/* 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 dac_setup(FAR struct dac_dev_s *dev) +{ + FAR struct up_dev_s *priv = (FAR struct up_dev_s *)dev->ad_priv; + FAR struct spi_dev_s *spi = priv->spi; + SPI_SELECT(spi, priv->devno, true); + SPI_SEND(spi,AD5410_REG_CMD); + SPI_SEND(spi,(AD5410_CMD_OUTEN|AD5410_CMD_420MA)>>8); + SPI_SEND(spi,AD5410_CMD_OUTEN|AD5410_CMD_420MA); + SPI_SELECT(spi, priv->devno, false); + return OK; +} + +/* Disable the DAC. This method is called when the DAC device is closed. +* This method reverses the operation the setup method. +*/ +static void dac_shutdown(FAR struct dac_dev_s *dev) +{ +} + +/* Call to enable or disable TX interrupts */ +static void dac_txint(FAR struct dac_dev_s *dev, bool enable) +{ +} + +static int dac_send(FAR struct dac_dev_s *dev, FAR struct dac_msg_s *msg) +{ + FAR struct up_dev_s *priv = (FAR struct up_dev_s *)dev->ad_priv; + FAR struct spi_dev_s *spi = priv->spi; + SPI_SELECT(spi, priv->devno, true); + SPI_SEND(spi,AD5410_REG_WR); + SPI_SEND(spi,(uint8_t)(msg->am_data>>24)); + SPI_SEND(spi,(uint8_t)(msg->am_data>>16)); + SPI_SELECT(spi, priv->devno, false); + dac_txdone(&g_dacdev); + return 0; +} + +/* All ioctl calls will be routed through this method */ +static int dac_ioctl(FAR struct dac_dev_s *dev, int cmd, unsigned long arg) +{ + dbg("Fix me:Not Implemented\n"); + return 0; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: up_ad5410cinitialize + * + * Description: + * Initialize the selected DAC port + * + * Input Parameter: + * Port number (for hardware that has mutiple DAC interfaces) + * + * Returned Value: + * Valid ad5410 device structure reference on succcess; a NULL on failure + * + ****************************************************************************/ + +FAR struct dac_dev_s *up_ad5410initialize(FAR struct spi_dev_s *spi, unsigned int devno) +{ + FAR struct up_dev_s *priv = (FAR struct up_dev_s *)g_dacdev.ad_priv; + priv->spi=spi; + priv->devno=devno; + return &g_dacdev; +} +#endif + diff --git a/nuttx/drivers/analog/adc.c b/nuttx/drivers/analog/adc.c new file mode 100644 index 000000000..84070f162 --- /dev/null +++ b/nuttx/drivers/analog/adc.c @@ -0,0 +1,423 @@ +/**************************************************************************** + * drivers/analog/adc.c + * + * Copyright (C) 2011 Li Zhuoyi. All rights reserved. + * Author: Li Zhuoyi + * History: 0.1 2011-08-04 initial version + * + * Derived from drivers/can.c + * + * Copyright (C) 2008-2009 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * 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 + +#include + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int adc_open(FAR struct file *filep); +static int adc_close(FAR struct file *filep); +static ssize_t adc_read(FAR struct file *, FAR char *, size_t); +static ssize_t adc_write(FAR struct file *filep, FAR const char *buffer, size_t buflen); +static int adc_ioctl(FAR struct file *filep,int cmd,unsigned long arg); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct file_operations adc_fops = +{ + adc_open, /* open */ + adc_close, /* close */ + adc_read, /* read */ + adc_write, /* write */ + 0, /* seek */ + adc_ioctl /* ioctl */ +#ifndef CONFIG_DISABLE_POLL + , 0 /* poll */ +#endif +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ +/************************************************************************************ + * Name: adc_open + * + * Description: + * This function is called whenever the ADC device is opened. + * + ************************************************************************************/ + +static int adc_open(FAR struct file *filep) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct adc_dev_s *dev = inode->i_private; + uint8_t tmp; + int ret = OK; + + /* If the port is the middle of closing, wait until the close is finished */ + + if (sem_wait(&dev->ad_closesem) != OK) + { + ret = -errno; + } + else + { + /* Increment the count of references to the device. If this the first + * time that the driver has been opened for this device, then initialize + * the device. + */ + + tmp = dev->ad_ocount + 1; + if (tmp == 0) + { + /* More than 255 opens; uint8_t overflows to zero */ + + ret = -EMFILE; + } + else + { + /* Check if this is the first time that the driver has been opened. */ + + if (tmp == 1) + { + /* Yes.. perform one time hardware initialization. */ + + irqstate_t flags = irqsave(); + ret = dev->ad_ops->ao_setup(dev); + if (ret == OK) + { + /* Mark the FIFOs empty */ + + dev->ad_recv.af_head = 0; + dev->ad_recv.af_tail = 0; + + /* Finally, Enable the CAN RX interrupt */ + + dev->ad_ops->ao_rxint(dev, true); + + /* Save the new open count on success */ + + dev->ad_ocount = tmp; + } + irqrestore(flags); + } + } + sem_post(&dev->ad_closesem); + } + return ret; +} + +/************************************************************************************ + * Name: adc_close + * + * Description: + * This routine is called when the ADC device is closed. + * It waits for the last remaining data to be sent. + * + ************************************************************************************/ + +static int adc_close(FAR struct file *filep) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct adc_dev_s *dev = inode->i_private; + irqstate_t flags; + int ret = OK; + + if (sem_wait(&dev->ad_closesem) != OK) + { + ret = -errno; + } + else + { + /* Decrement the references to the driver. If the reference count will + * decrement to 0, then uninitialize the driver. + */ + + if (dev->ad_ocount > 1) + { + dev->ad_ocount--; + sem_post(&dev->ad_closesem); + } + else + { + /* There are no more references to the port */ + + dev->ad_ocount = 0; + + /* Free the IRQ and disable the ADC device */ + + flags = irqsave(); /* Disable interrupts */ + dev->ad_ops->ao_shutdown(dev); /* Disable the ADC */ + irqrestore(flags); + + sem_post(&dev->ad_closesem); + } + } + return ret; +} + +/**************************************************************************** + * Name: adc_read + ****************************************************************************/ + +static ssize_t adc_read(FAR struct file *filep, FAR char *buffer, size_t buflen) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct adc_dev_s *dev = inode->i_private; + size_t nread; + irqstate_t flags; + int ret = 0; + int msglen; + + avdbg("buflen: %d\n", (int)buflen); + + if (buflen % 5 == 0) + msglen = 5; + else if (buflen % 4 == 0) + msglen = 4; + else if (buflen % 3 == 0) + msglen = 3; + else if (buflen % 2 == 0) + msglen = 2; + else if (buflen == 1) + msglen = 1; + else + msglen = 5; + + if (buflen >= msglen) + { + /* Interrupts must be disabled while accessing the ad_recv FIFO */ + + flags = irqsave(); + while (dev->ad_recv.af_head == dev->ad_recv.af_tail) + { + /* The receive FIFO is empty -- was non-blocking mode selected? */ + + if (filep->f_oflags & O_NONBLOCK) + { + ret = -EAGAIN; + goto return_with_irqdisabled; + } + + /* Wait for a message to be received */ + + dev->ad_nrxwaiters++; + ret = sem_wait(&dev->ad_recv.af_sem); + dev->ad_nrxwaiters--; + if (ret < 0) + { + ret = -errno; + goto return_with_irqdisabled; + } + } + + /* The ad_recv FIFO is not empty. Copy all buffered data that will fit + * in the user buffer. + */ + + nread = 0; + do + { + FAR struct adc_msg_s *msg = &dev->ad_recv.af_buffer[dev->ad_recv.af_head]; + + /* Will the next message in the FIFO fit into the user buffer? */ + + if (nread + msglen > buflen) + { + /* No.. break out of the loop now with nread equal to the actual + * number of bytes transferred. + */ + + break; + } + + /* Copy the message to the user buffer */ + + if (msglen == 1) + { + /* Only one channel,read highest 8-bits */ + + buffer[nread] = msg->am_data >> 24; + } + else if (msglen == 2) + { + /* Only one channel, read highest 16-bits */ + + *(int16_t *)&buffer[nread] = msg->am_data >> 16; + } + else if (msglen == 3) + { + /* Read channel highest 16-bits */ + + buffer[nread] = msg->am_channel; + *(int16_t *)&buffer[nread + 1] = msg->am_data >> 16; + } + else if (msglen == 4) + { + /* read channel highest 24-bits */ + + *(int32_t *)&buffer[nread] = msg->am_data; + buffer[nread] = msg->am_channel; + } + else + { + /* Read all */ + + *(int32_t *)&buffer[nread + 1] = msg->am_data; + buffer[nread] = msg->am_channel; + } + nread += msglen; + + /* Increment the head of the circular message buffer */ + + if (++dev->ad_recv.af_head >= CONFIG_ADC_FIFOSIZE) + { + dev->ad_recv.af_head = 0; + } + } + while (dev->ad_recv.af_head != dev->ad_recv.af_tail); + + /* All on the messages have bee transferred. Return the number of bytes + * that were read. + */ + + ret = nread; + +return_with_irqdisabled: + irqrestore(flags); + } + + avdbg("Returning: %d\n", ret); + return ret; +} + +/************************************************************************************ + * Name: adc_write + ************************************************************************************/ + +static ssize_t adc_write(FAR struct file *filep, FAR const char *buffer, size_t buflen) +{ + return 0; +} + +/************************************************************************************ + * Name: adc_ioctl + ************************************************************************************/ + +static int adc_ioctl(FAR struct file *filep, int cmd, unsigned long arg) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct adc_dev_s *dev = inode->i_private; + int ret = OK; + + ret = dev->ad_ops->ao_ioctl(dev, cmd, arg); + return ret; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int adc_receive(FAR struct adc_dev_s *dev, uint8_t ch, int32_t data) +{ + FAR struct adc_fifo_s *fifo = &dev->ad_recv; + int nexttail; + int err = -ENOMEM; + + /* Check if adding this new message would over-run the drivers ability to enqueue + * read data. + */ + + nexttail = fifo->af_tail + 1; + if (nexttail >= CONFIG_ADC_FIFOSIZE) + { + nexttail = 0; + } + + /* Refuse the new data if the FIFO is full */ + + if (nexttail != fifo->af_head) + { + /* Add the new, decoded CAN message at the tail of the FIFO */ + + fifo->af_buffer[fifo->af_tail].am_channel = ch; + fifo->af_buffer[fifo->af_tail].am_data = data; + + /* Increment the tail of the circular buffer */ + + fifo->af_tail = nexttail; + + if (dev->ad_nrxwaiters > 0) + { + sem_post(&fifo->af_sem); + } + err = OK; + } + return err; +} + +int adc_register(FAR const char *path, FAR struct adc_dev_s *dev) +{ + /* Initialize the ADC device structure */ + + dev->ad_ocount = 0; + + sem_init(&dev->ad_recv.af_sem, 0, 0); + sem_init(&dev->ad_closesem, 0, 1); + + dev->ad_ops->ao_reset(dev); + + return register_driver(path, &adc_fops, 0444, dev); +} diff --git a/nuttx/drivers/analog/ads1255.c b/nuttx/drivers/analog/ads1255.c new file mode 100644 index 000000000..374decc54 --- /dev/null +++ b/nuttx/drivers/analog/ads1255.c @@ -0,0 +1,335 @@ +/************************************************************************************ + * arch/drivers/analog/ads1255.c + * + * Copyright (C) 2011 Li Zhuoyi. All rights reserved. + * Author: Li Zhuoyi + * History: 0.1 2011-08-05 initial version + * 0.2 2011-08-25 fix bug in g_adcdev (cd_ops -> ad_ops,cd_priv -> ad_priv) + * + * This file is a part of NuttX: + * + * Copyright (C) 2010 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. + * + ************************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#if defined(CONFIG_ADC_ADS1255) + +#define ADS125X_BUFON 0x02 +#define ADS125X_BUFOFF 0x00 + +#define ADS125X_PGA1 0x00 +#define ADS125X_PGA2 0x01 +#define ADS125X_PGA4 0x02 +#define ADS125X_PGA8 0x03 +#define ADS125X_PGA16 0x04 +#define ADS125X_PGA32 0x05 +#define ADS125X_PGA64 0x06 + +#define ADS125X_RDATA 0x01 //Read Data +#define ADS125X_RDATAC 0x03 //Read Data Continuously +#define ADS125X_SDATAC 0x0F //Stop Read Data Continuously +#define ADS125X_RREG 0x10 //Read from REG +#define ADS125X_WREG 0x50 //Write to REG +#define ADS125X_SELFCAL 0xF0 //Offset and Gain Self-Calibration +#define ADS125X_SELFOCAL 0xF1 //Offset Self-Calibration +#define ADS125X_SELFGCAL 0xF2 //Gain Self-Calibration +#define ADS125X_SYSOCAL 0xF3 //System Offset Calibration +#define ADS125X_SYSGCAL 0xF4 //System Gain Calibration +#define ADS125X_SYNC 0xFC //Synchronize the A/D Conversion +#define ADS125X_STANDBY 0xFD //Begin Standby Mode +#define ADS125X_RESET 0xFE //Reset to Power-Up Values +#define ADS125X_WAKEUP 0xFF //Completes SYNC and Exits Standby Mode + +#ifndef CONFIG_ADS1255_FREQUENCY +#define CONFIG_ADS1255_FREQUENCY 1000000 +#endif +#ifndef CONFIG_ADS1255_MUX +#define CONFIG_ADS1255_MUX 0x01 +#endif +#ifndef CONFIG_ADS1255_CHMODE +#define CONFIG_ADS1255_CHMODE 0x00 +#endif +#ifndef CONFIG_ADS1255_BUFON +#define CONFIG_ADS1255_BUFON 1 +#endif +#ifndef CONFIG_ADS1255_PGA +#define CONFIG_ADS1255_PGA ADS125X_PGA2 +#endif +#ifndef CONFIG_ADS1255_SPS +#define CONFIG_ADS1255_SPS 50 +#endif + +/**************************************************************************** + * ad_private Types + ****************************************************************************/ + +struct up_dev_s +{ + uint8_t channel; + uint32_t sps; + uint8_t pga; + uint8_t buf; + const uint8_t *mux; + int irq; + int devno; + FAR struct spi_dev_s *spi; /* Cached SPI device reference */ +}; + +/**************************************************************************** + * ad_private Function Prototypes + ****************************************************************************/ + +/* ADC methods */ + +static void adc_reset(FAR struct adc_dev_s *dev); +static int adc_setup(FAR struct adc_dev_s *dev); +static void adc_shutdown(FAR struct adc_dev_s *dev); +static void adc_rxint(FAR struct adc_dev_s *dev, bool enable); +static int adc_ioctl(FAR struct adc_dev_s *dev, int cmd, unsigned long arg); +static int adc_interrupt(int irq, void *context); + +/**************************************************************************** + * ad_private Data + ****************************************************************************/ + +static const struct adc_ops_s g_adcops = +{ + .ao_reset = adc_reset, /* ao_reset */ + .ao_setup = adc_setup, /* ao_setup */ + .ao_shutdown = adc_shutdown, /* ao_shutdown */ + .ao_rxint = adc_rxint, /* ao_rxint */ + .ao_ioctl = adc_ioctl /* ao_read */ +}; + +static struct up_dev_s g_adcpriv = +{ + .mux = (const uint8_t []) + { + CONFIG_ADS1255_MUX,0 + }, + .sps = CONFIG_ADS1255_SPS, + .channel = 0, + .irq = CONFIG_ADS1255_IRQ, +}; + +static struct adc_dev_s g_adcdev = +{ + .ad_ops = &g_adcops, + .ad_priv= &g_adcpriv, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static uint8_t getspsreg(uint16_t sps) +{ + static const unsigned short sps_tab[]= + { + 3,7,12,20,27,40,55,80,300,750,1500,3000,5000,10000,20000,65535, + }; + static const unsigned char sps_reg[]= + { + 0x03,0x13,0x23,0x33,0x43,0x53,0x63,0x72,0x82,0x92,0xa1,0xb0,0xc0,0xd0,0xe0,0xf0, + }; + int i; + for (i=0; i<16; i++) + { + if (spsad_priv; + FAR struct spi_dev_s *spi = priv->spi; + + SPI_SETMODE(spi, SPIDEV_MODE1); + SPI_SETBITS(spi, 8); + SPI_SETFREQUENCY(spi, CONFIG_ADS1255_FREQUENCY); + usleep(1000); + SPI_SELECT(spi, priv->devno, true); + SPI_SEND(spi,ADS125X_WREG+0x03); //WRITE SPS REG + SPI_SEND(spi,0x00); //count=1 + SPI_SEND(spi,0x63); + SPI_SELECT(spi, priv->devno, false); +} + +/* 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 adc_setup(FAR struct adc_dev_s *dev) +{ + FAR struct up_dev_s *priv = (FAR struct up_dev_s *)dev->ad_priv; + FAR struct spi_dev_s *spi = priv->spi; + int ret = irq_attach(priv->irq, adc_interrupt); + if (ret == OK) + { + SPI_SELECT(spi, priv->devno, true); + SPI_SEND(spi,ADS125X_WREG); //WRITE REG from 0 + SPI_SEND(spi,0x03); //count=4+1 + if (priv->buf) + SPI_SEND(spi,ADS125X_BUFON); //REG0 STATUS BUFFER ON + else + SPI_SEND(spi,ADS125X_BUFOFF); + SPI_SEND(spi,priv->mux[0]); + SPI_SEND(spi,priv->pga); //REG2 ADCON PGA=2 + SPI_SEND(spi,getspsreg(priv->sps)); + usleep(1000); + SPI_SEND(spi,ADS125X_SELFCAL); + SPI_SELECT(spi, priv->devno, false); + up_enable_irq(priv->irq); + } + return ret; +} + +/* Disable the ADC. This method is called when the ADC device is closed. +* This method reverses the operation the setup method. +*/ + +static void adc_shutdown(FAR struct adc_dev_s *dev) +{ + FAR struct up_dev_s *priv = (FAR struct up_dev_s *)dev->ad_priv; + up_disable_irq(priv->irq); + irq_detach(priv->irq); +} + +/* Call to enable or disable RX interrupts */ + +static void adc_rxint(FAR struct adc_dev_s *dev, bool enable) +{ + FAR struct up_dev_s *priv = (FAR struct up_dev_s *)dev->ad_priv; + if (enable) + up_enable_irq(priv->irq); + else + up_disable_irq(priv->irq); +} + +/* All ioctl calls will be routed through this method */ + +static int adc_ioctl(FAR struct adc_dev_s *dev, int cmd, unsigned long arg) +{ + dbg("Fix me:Not Implemented\n"); + return 0; +} + +static int adc_interrupt(int irq, void *context) +{ + uint32_t regval; + FAR struct up_dev_s *priv = (FAR struct up_dev_s *)g_adcdev.ad_priv; + FAR struct spi_dev_s *spi = priv->spi; + unsigned char buf[4]; + unsigned char ch; + + SPI_SELECT(spi, priv->devno, true); + SPI_SEND(spi,ADS125X_RDATA); + up_udelay(10); + buf[3]=SPI_SEND(spi,0xff); + buf[2]=SPI_SEND(spi,0xff); + buf[1]=SPI_SEND(spi,0xff); + buf[0]=0; + + priv->channel++; + ch = priv->mux[priv->channel]; + if ( ch == 0 ) + { + priv->channel=0; + ch = priv->mux[0]; + } + + SPI_SEND(spi,ADS125X_WREG+0x01); + SPI_SEND(spi,0x00); + SPI_SEND(spi,ch); + SPI_SEND(spi,ADS125X_SYNC); + up_udelay(2); + SPI_SEND(spi,ADS125X_WAKEUP); + SPI_SELECT(spi, priv->devno, false); + + adc_receive(&g_adcdev,priv->channel,*(int32_t *)buf); + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: up_ads1255initialize + * + * Description: + * Initialize the selected adc port + * + * Input Parameter: + * Port number (for hardware that has mutiple adc interfaces) + * + * Returned Value: + * Valid can device structure reference on succcess; a NULL on failure + * + ****************************************************************************/ + +FAR struct adc_dev_s *up_ads1255initialize(FAR struct spi_dev_s *spi, unsigned int devno) +{ + FAR struct up_dev_s *priv = (FAR struct up_dev_s *)g_adcdev.ad_priv; + + /* Driver state data */ + + priv->spi = spi; + priv->devno = devno; + return &g_adcdev; +} +#endif + diff --git a/nuttx/drivers/analog/dac.c b/nuttx/drivers/analog/dac.c new file mode 100644 index 000000000..e1fc3049f --- /dev/null +++ b/nuttx/drivers/analog/dac.c @@ -0,0 +1,499 @@ +/**************************************************************************** + * drivers/analog/dac.c + * + * Copyright (C) 2011 Li Zhuoyi. All rights reserved. + * Author: Li Zhuoyi + * History: 0.1 2011-08-04 initial version + * + * Derived from drivers/can.c + * + * Copyright (C) 2008-2009Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * 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 + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define HALF_SECOND_MSEC 500 +#define HALF_SECOND_USEC 500000L + + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int dac_open(FAR struct file *filep); +static int dac_close(FAR struct file *filep); +static ssize_t dac_read(FAR struct file *, FAR char *, size_t); +static ssize_t dac_write(FAR struct file *filep, FAR const char *buffer, size_t buflen); +static int dac_ioctl(FAR struct file *filep,int cmd,unsigned long arg); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct file_operations dac_fops = +{ + dac_open, + dac_close, + dac_read, + dac_write, + 0, + dac_ioctl +#ifndef CONFIG_DISABLE_POLL + , 0 +#endif +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ +/************************************************************************************ + * Name: dac_open + * + * Description: + * This function is called whenever the DAC device is opened. + * + ************************************************************************************/ + +static int dac_open(FAR struct file *filep) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct dac_dev_s *dev = inode->i_private; + uint8_t tmp; + int ret = OK; + + /* If the port is the middle of closing, wait until the close is finished */ + + if (sem_wait(&dev->ad_closesem) != OK) + { + ret = -errno; + } + else + { + /* Increment the count of references to the device. If this the first + * time that the driver has been opened for this device, then initialize + * the device. + */ + + tmp = dev->ad_ocount + 1; + if (tmp == 0) + { + /* More than 255 opens; uint8_t overflows to zero */ + + ret = -EMFILE; + } + else + { + /* Check if this is the first time that the driver has been opened. */ + + if (tmp == 1) + { + /* Yes.. perform one time hardware initialization. */ + + irqstate_t flags = irqsave(); + ret = dev->ad_ops->ao_setup(dev); + if (ret == OK) + { + /* Mark the FIFOs empty */ + + dev->ad_xmit.af_head = 0; + dev->ad_xmit.af_tail = 0; + + /* Save the new open count on success */ + + dev->ad_ocount = tmp; + } + irqrestore(flags); + } + } + sem_post(&dev->ad_closesem); + } + return ret; +} + +/************************************************************************************ + * Name: dac_close + * + * Description: + * This routine is called when the DAC device is closed. + * It waits for the last remaining data to be sent. + * + ************************************************************************************/ + +static int dac_close(FAR struct file *filep) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct dac_dev_s *dev = inode->i_private; + irqstate_t flags; + int ret = OK; + + if (sem_wait(&dev->ad_closesem) != OK) + { + ret = -errno; + } + else + { + /* Decrement the references to the driver. If the reference count will + * decrement to 0, then uninitialize the driver. + */ + + if (dev->ad_ocount > 1) + { + dev->ad_ocount--; + sem_post(&dev->ad_closesem); + } + else + { + /* There are no more references to the port */ + + dev->ad_ocount = 0; + + /* Now we wait for the transmit FIFO to clear */ + + while (dev->ad_xmit.af_head != dev->ad_xmit.af_tail) + { +#ifndef CONFIG_DISABLE_SIGNALS + usleep(HALF_SECOND_USEC); +#else + up_mdelay(HALF_SECOND_MSEC); +#endif + } + + /* Free the IRQ and disable the DAC device */ + + flags = irqsave(); /* Disable interrupts */ + dev->ad_ops->ao_shutdown(dev); /* Disable the DAC */ + irqrestore(flags); + + sem_post(&dev->ad_closesem); + } + } + return ret; +} + +/**************************************************************************** + * Name: dac_read + ****************************************************************************/ + +static ssize_t dac_read(FAR struct file *filep, FAR char *buffer, size_t buflen) +{ + return 0; +} + +/************************************************************************************ + * Name: dac_xmit + * + * Description: + * Send the message at the head of the ad_xmit FIFO + * + * Assumptions: + * Called with interrupts disabled + * + ************************************************************************************/ + +static int dac_xmit(FAR struct dac_dev_s *dev) +{ + bool enable = false; + int ret = OK; + + /* Check if the xmit FIFO is empty */ + + if (dev->ad_xmit.af_head != dev->ad_xmit.af_tail) + { + /* Send the next message at the head of the FIFO */ + + ret = dev->ad_ops->ao_send(dev, &dev->ad_xmit.af_buffer[dev->ad_xmit.af_head]); + + /* Make sure the TX done interrupts are enabled */ + + enable = (ret == OK ? true : false); + } + dev->ad_ops->ao_txint(dev, enable); + return ret; +} + +/************************************************************************************ + * Name: dac_write + ************************************************************************************/ + +static ssize_t dac_write(FAR struct file *filep, FAR const char *buffer, size_t buflen) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct dac_dev_s *dev = inode->i_private; + FAR struct dac_fifo_s *fifo = &dev->ad_xmit; + FAR struct dac_msg_s *msg; + bool empty = false; + ssize_t nsent = 0; + irqstate_t flags; + int nexttail; + int msglen; + int ret = 0; + + /* Interrupts must disabled throughout the following */ + + flags = irqsave(); + + /* Check if the TX FIFO was empty when we started. That is a clue that we have + * to kick off a new TX sequence. + */ + + empty = (fifo->af_head == fifo->af_tail); + + /* Add the messages to the FIFO. Ignore any trailing messages that are + * shorter than the minimum. + */ + + if (buflen % 5 ==0 ) + msglen=5; + else if (buflen % 4 ==0 ) + msglen=4; + else if (buflen % 3 ==0 ) + msglen=3; + else if (buflen % 2 ==0 ) + msglen=2; + else if (buflen == 1) + msglen=1; + else + msglen=5; + + while ((buflen - nsent) >= msglen ) + { + /* Check if adding this new message would over-run the drivers ability to enqueue + * xmit data. + */ + + nexttail = fifo->af_tail + 1; + if (nexttail >= CONFIG_DAC_FIFOSIZE) + { + nexttail = 0; + } + + /* If the XMIT fifo becomes full, then wait for space to become available */ + + while (nexttail == fifo->af_head) + { + /* The transmit FIFO is full -- was non-blocking mode selected? */ + + if (filep->f_oflags & O_NONBLOCK) + { + if (nsent == 0) + { + ret = -EAGAIN; + } + else + { + ret = nsent; + } + goto return_with_irqdisabled; + } + + /* If the FIFO was empty when we started, then we will have + * start the XMIT sequence to clear the FIFO. + */ + + if (empty) + { + dac_xmit(dev); + } + + /* Wait for a message to be sent */ + + do + { + ret = sem_wait(&fifo->af_sem); + if (ret < 0 && errno != EINTR) + { + ret = -errno; + goto return_with_irqdisabled; + } + } + while (ret < 0); + + /* Re-check the FIFO state */ + + empty = (fifo->af_head == fifo->af_tail); + } + + /* We get here if there is space at the end of the FIFO. Add the new + * CAN message at the tail of the FIFO. + */ + + if (msglen==5) + { + msg = (FAR struct dac_msg_s *)&buffer[nsent]; + memcpy(&fifo->af_buffer[fifo->af_tail], msg, msglen); + } + else if(msglen == 4) + { + fifo->af_buffer[fifo->af_tail].am_channel=buffer[nsent]; + fifo->af_buffer[fifo->af_tail].am_data=*(uint32_t *)&buffer[nsent]; + fifo->af_buffer[fifo->af_tail].am_data&=0xffffff00; + } + else if(msglen == 3) + { + fifo->af_buffer[fifo->af_tail].am_channel=buffer[nsent]; + fifo->af_buffer[fifo->af_tail].am_data=(*(uint16_t *)&buffer[nsent+1]); + fifo->af_buffer[fifo->af_tail].am_data<<=16; + } + else if(msglen == 2) + { + fifo->af_buffer[fifo->af_tail].am_channel=0; + fifo->af_buffer[fifo->af_tail].am_data=(*(uint16_t *)&buffer[nsent]); + fifo->af_buffer[fifo->af_tail].am_data<<=16; + } + else if(msglen == 1) + { + fifo->af_buffer[fifo->af_tail].am_channel=0; + fifo->af_buffer[fifo->af_tail].am_data=buffer[nsent]; + fifo->af_buffer[fifo->af_tail].am_data<<=24; + } + /* Increment the tail of the circular buffer */ + + fifo->af_tail = nexttail; + + /* Increment the number of bytes that were sent */ + + nsent += msglen; + } + + /* We get here after all messages have been added to the FIFO. Check if + * we need to kick of the XMIT sequence. + */ + + if (empty) + { + dac_xmit(dev); + } + + /* Return the number of bytes that were sent */ + + ret = nsent; + +return_with_irqdisabled: + irqrestore(flags); + return ret; +} + +/************************************************************************************ + * Name: dac_ioctl + ************************************************************************************/ + +static int dac_ioctl(FAR struct file *filep, int cmd, unsigned long arg) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct dac_dev_s *dev = inode->i_private; + int ret = OK; + + ret = dev->ad_ops->ao_ioctl(dev, cmd, arg); + return ret; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/************************************************************************************ + * Name: dac_txdone + * + * Description: + * Called from the DAC interrupt handler at the completion of a send operation. + * + * Return: + * OK on success; a negated errno on failure. + * + ************************************************************************************/ + +int dac_txdone(FAR struct dac_dev_s *dev) +{ + int ret = -ENOENT; + + /* Verify that the xmit FIFO is not empty */ + + if (dev->ad_xmit.af_head != dev->ad_xmit.af_tail) + { + /* Remove the message at the head of the xmit FIFO */ + + if (++dev->ad_xmit.af_head >= CONFIG_DAC_FIFOSIZE) + { + dev->ad_xmit.af_head = 0; + } + + /* Send the next message in the FIFO */ + + ret = dac_xmit(dev); + if (ret == OK) + { + /* Inform any waiting threads that new xmit space is available */ + + ret = sem_post(&dev->ad_xmit.af_sem); + } + } + return ret; +} + +int dac_register(FAR const char *path, FAR struct dac_dev_s *dev) +{ + /* Initialize the DAC device structure */ + + dev->ad_ocount = 0; + + sem_init(&dev->ad_xmit.af_sem, 0, 0); + sem_init(&dev->ad_closesem, 0, 1); + + dev->ad_ops->ao_reset(dev); + + return register_driver(path, &dac_fops, 0555, dev); +} + diff --git a/nuttx/drivers/analog/pga11x.c b/nuttx/drivers/analog/pga11x.c new file mode 100644 index 000000000..7bb4a11bb --- /dev/null +++ b/nuttx/drivers/analog/pga11x.c @@ -0,0 +1,793 @@ +/**************************************************************************** + * drivers/analog/pga11x.c + * + * Copyright (C) 2012 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * References: + * "PGA112, PGA113, PGA116, PGA117: Zerø-Drift PROGRAMMABLE GAIN AMPLIFIER + * with MUX", SBOS424B, March 2008, Revised September 2008, Texas + * Instruments Incorporated" + * + * 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 + +#if defined(CONFIG_ADC) && defined(CONFIG_ADC_PGA11X) + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ +/* The PGA112/PGA113 have a three-wire SPI digital interface; the + * PGA116/PGA117 have a four-wire SPI digital interface. The PGA116/117 also + * have daisy-chain capability (The PGA112/PGA113 can be used as the last device + * in a daisy-chain as shown if write-only communication is acceptable). + */ + +/* PGA11x commands (PGA112/PGA113) */ + +#define PGA11X_CMD_READ 0x6a00 +#define PGA11X_CMD_WRITE 0x2a00 +#define PGA11X_CMD_NOOP 0x0000 +#define PGA11X_CMD_SDN_DIS 0xe100 +#define PGA11X_CMD_SDN_EN 0xe1f1 + +/* SPI Daisy-Chain Commands (PGA116/PGA117) */ + +#define PGA11X_DCCMD_SELECTOR 0x8000 +#define PGA11X_DCCMD_NOOP (PGA11X_DCCMD_SELECTOR | PGA11X_CMD_NOOP) +#define PGA11X_DCCMD_SDN_DIS (PGA11X_DCCMD_SELECTOR | PGA11X_CMD_SDN_DIS) +#define PGA11X_DCCMD_SDN_EN (PGA11X_DCCMD_SELECTOR | PGA11X_CMD_SDN_EN) +#define PGA11X_DCCMD_READ (PGA11X_DCCMD_SELECTOR | PGA11X_CMD_READ) +#define PGA11X_DCCMD_WRITE (PGA11X_DCCMD_SELECTOR | PGA11X_CMD_WRITE) + +/* Write command Gain Selection Bits (PGA112/PGA113) + * + * the PGA112 and PGA116 provide binary gain selections (1, 2, 4, 8, 16, 32, + * 64, 128); the PGA113 and PGA117 provide scope gain selections (1, 2, 5, 10, + * 20, 50, 100, 200). + */ + +#define PGA11X_GAIN_SHIFT (4) /* Bits 4-7: Gain Selection Bits */ +#define PGA11X_GAIN_MASK (15 << PGA11X_GAIN_SHIFT) + +/* Write command Mux Channel Selection Bits + * + * The PGA112/PGA113 have a two-channel input MUX; the PGA116/PGA117 have a + * 10-channel input MUX. + */ + +#define PGA11X_CHAN_SHIFT (0) /* Bits 0-3: Channel Selection Bits */ +#define PGA11X_CHAN_MASK (15 << PGA11X_CHAN_SHIFT) + +/* Other definitions ********************************************************/ + +#define SPI_DUMMY 0xff + +/* Debug ********************************************************************/ +/* Check if (non-standard) SPI debug is enabled */ + +#ifndef CONFIG_DEBUG +# undef CONFIG_DEBUG_VERBOSE +# undef CONFIG_DEBUG_SPI +#endif + +#ifdef CONFIG_DEBUG_SPI +# define spidbg dbg +# ifdef CONFIG_DEBUG_VERBOSE +# define spivdbg dbg +# else +# define spivdbg(x...) +# endif +#else +# define spidbg(x...) +# define spivdbg(x...) +#endif + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: pga11x_configure + * + * Description: + * Configure the SPI bus as needed for the PGA11x device. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void pga11x_configure(FAR struct spi_dev_s *spi) +{ + spivdbg("MODE: %d BITS: 8 Frequency: %d\n", + CONFIG_PGA11X_SPIMODE, CONFIG_PGA11X_SPIFREQUENCY); + + /* Call the setfrequency, setbits, and setmode methods to make sure that + * the SPI is properly configured for the device. + */ + + SPI_SETMODE(spi, CONFIG_PGA11X_SPIMODE); + SPI_SETBITS(spi, 8); + (void)SPI_SETFREQUENCY(spi, CONFIG_PGA11X_SPIFREQUENCY); +} + +/**************************************************************************** + * Name: pga11x_lock + * + * Description: + * Lock the SPI bus and configure it as needed for the PGA11x device. + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifndef CONFIG_SPI_OWNBUS +static void pga11x_lock(FAR struct spi_dev_s *spi) +{ + spivdbg("Locking\n"); + + /* On SPI busses where there are multiple devices, it will be necessary to + * lock SPI to have exclusive access to the busses for a sequence of + * transfers. The bus should be locked before the chip is selected. + * + * This is a blocking call and will not return until we have exclusiv access to + * the SPI buss. We will retain that exclusive access until the bus is unlocked. + */ + + SPI_LOCK(spi, true); + + /* After locking the SPI bus, the we also need call the setfrequency, setbits, and + * setmode methods to make sure that the SPI is properly configured for the device. + * If the SPI buss is being shared, then it may have been left in an incompatible + * state. + */ + + pga11x_configure(spi); +} +#else +# define pga11x_lock(spi) +#endif + +/**************************************************************************** + * Name: pga11x_unlock + * + * Description: + * Lock the SPI bus and configure it as needed for the PGA11x device. + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifndef CONFIG_SPI_OWNBUS +static inline void pga11x_unlock(FAR struct spi_dev_s *spi) +{ + spivdbg("Unlocking\n"); + + SPI_LOCK(spi, false); +} +#else +# define pga11x_unlock(spi) +#endif + +/**************************************************************************** + * Name: pga11x_send16 + * + * Description: + * Send 16 bits of data, ignoring any returned data + * + * Input Parameters: + * spi - PGA11X driver instance + * word - The data to send + * + * Returned Value: + * None + * + * Assumptions: + * The bus is locked and the device is selected + * + ****************************************************************************/ + +static void pga11x_send16(FAR struct spi_dev_s *spi, uint16_t word) +{ + spivdbg("Send %04x\n", word); + + /* The logical interface is 16-bits wide. However, this driver uses a + * 8-bit configuration for greaer portability. + * + * Send the MS byte first. Then the LS byte. + */ + + SPI_SEND(spi, word >> 8); + SPI_SEND(spi, word & 0xff); +} + +/**************************************************************************** + * Name: pga11x_recv16 + * + * Description: + * Receive 16 bits of data. + * + * Input Parameters: + * spi - PGA11X driver instance + * + * Returned Value: + * The received 16-bit value + * + * Assumptions: + * The bus is locked and the device is selected + * + ****************************************************************************/ + +static uint16_t pga11x_recv16(FAR struct spi_dev_s *spi) +{ + uint8_t msb; + uint8_t lsb; + + /* The logical interface is 16-bits wide. However, this driver uses a + * 8-bit configuration for greaer portability. + * + * Send a dummy byte and receive MS byte first. Then the LS byte. + */ + + msb = SPI_SEND(spi, SPI_DUMMY); + lsb = SPI_SEND(spi, SPI_DUMMY); + spivdbg("Received %02x %02x\n", msb, lsb); + + return ((uint16_t)msb << 8) | (uint16_t)lsb; +} + +/**************************************************************************** + * Name: pga11x_write + * + * Description: + * Send a 16-bit command. + * + * Input Parameters: + * spi - PGA11X driver instance + * cmd - PGA11X command (non-daisy chained) + * u1cmd - PGA11X U1 command (daisy chained) + * u2cmd - PGA11X U2 command (daisy chained) + * + * Returned Value: + * The received 16-bit value + * + * Assumptions: + * The device is NOT selected and the NOT bus is locked. + * + ****************************************************************************/ + +#ifndef CONFIG_PGA11X_DAISYCHAIN +static void pga11x_write(FAR struct spi_dev_s *spi, uint16_t cmd) +{ + spivdbg("cmd %04x\n", cmd); + + /* Lock, select, send the 16-bit command, de-select, and un-lock. */ + + pga11x_lock(spi); + SPI_SELECT(spi, SPIDEV_MUX, true); + pga11x_send16(spi, cmd); + SPI_SELECT(spi, SPIDEV_MUX, false); + pga11x_unlock(spi); +} +#else +static void pga11x_write(FAR struct spi_dev_s *spi, uint16_t u1cmd, uint16_t u2cmd) +{ + spivdbg("U1 cmd: %04x U2 cmd: %04x\n", u1cmd, u2cmd); + + /* Lock, select, send the U2 16-bit command, the U1 16-bit command, de-select, + * and un-lock. + */ + + pga11x_lock(spi); + SPI_SELECT(spi, SPIDEV_MUX, true); + pga11x_send16(spi, u2cmd); + pga11x_send16(spi, u1cmd); + SPI_SELECT(spi, SPIDEV_MUX, false); + pga11x_unlock(spi); +} +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: pga11x_initialize + * + * Description: + * Initialize the PGA117 amplifier/multiplexer(s). + * + * Input Parameters: + * spi - An SPI "bottom half" device driver instance + * + * Returned Value: + * On success, a non-NULL opaque handle is returned; a NULL is returned + * on any failure. This handle may be used with the other PGA117 interface + * functions to control the multiplexer + * + ****************************************************************************/ + +PGA11X_HANDLE pga11x_initialize(FAR struct spi_dev_s *spi) +{ + spivdbg("Entry\n"); + + /* Configure the SPI us for the device. Do this now only if the PGA11X is + * the only device on the bus. + */ + +#ifdef CONFIG_SPI_OWNBUS + pga11x_configure(spi); +#endif + + /* No other special state is required, just return the SPI driver instance + * as the handle. This gives us a place to extend functionality in the + * future if neccessary. + */ + + return (PGA11X_HANDLE)spi; +} + +/**************************************************************************** + * Name: pga11x_select + * + * Description: + * Select an input channel and gain for all PGA11xs. + * + * If CONFIG_PGA11X_DAISYCHAIN is defined, then pga11x_select() configures + * both chips in the daisy-chain. pga11x_uselect() is provided to support + * configuring the parts in the daisychain independently. + * + * Input Parameters: + * spi - An SPI "bottom half" device driver instance + * settings - New channel and gain settings + * + * Returned Value: + * Zero on sucess; a negated errno value on failure. + * + ****************************************************************************/ + +int pga11x_select(PGA11X_HANDLE handle, + FAR const struct pga11x_settings_s *settings) +{ +#ifndef CONFIG_PGA11X_DAISYCHAIN + FAR struct spi_dev_s *spi = (FAR struct spi_dev_s *)handle; + uint16_t cmd; + + DEBUGASSERT(handle && settings); + spivdbg("channel: %d gain: %d\n", settings->channel, settings->gain); + + /* Format the command */ + + cmd = PGA11X_CMD_WRITE | + ((uint16_t)settings->channel << PGA11X_CHAN_SHIFT) | + ((uint16_t)settings->gain << PGA11X_GAIN_SHIFT); + + /* Send the command */ + + pga11x_write(spi, cmd); + return OK; +#else + FAR struct spi_dev_s *spi = (FAR struct spi_dev_s *)handle; + uint16_t u1cmd; + uint16_t u2cmd; + + DEBUGASSERT(handle && settings); + spivdbg("U1 channel: %d gain: %d\n", settings->u1.channel, settings->u1.gain); + spivdbg("U1 channel: %d gain: %d\n", settings->u1.channel, settings->u1.gain); + + /* Format the commands */ + + u1cmd = PGA11X_CMD_WRITE | + ((uint16_t)settings->u1.channel << PGA11X_CHAN_SHIFT) | + ((uint16_t)settings->u1.gain << PGA11X_GAIN_SHIFT); + + u2cmd = PGA11X_DCCMD_WRITE | + ((uint16_t)settings->u2.channel << PGA11X_CHAN_SHIFT) | + ((uint16_t)settings->u2.gain << PGA11X_GAIN_SHIFT); + + /* Send the command */ + + pga11x_write(spi, u1cmd, u2cmd); + return OK; +#endif +} + +/**************************************************************************** + * Name: pga11x_uselect + * + * Description: + * Select an input channel and gain for one PGA11x. + * + * If CONFIG_PGA11X_DAISYCHAIN is defined, then pga11x_uselect() configures + * one chips in the daisy-chain. + * + * Input Parameters: + * spi - An SPI "bottom half" device driver instance + * pos - Position of the chip in the daisy chain (0 or 1) + * settings - New channel and gain settings + * + * Returned Value: + * Zero on sucess; a negated errno value on failure. + * + ****************************************************************************/ + +#ifdef CONFIG_PGA11X_DAISYCHAIN +int pga11x_uselect(PGA11X_HANDLE handle, int pos, + FAR const struct pga11x_usettings_s *settings) +{ + FAR struct spi_dev_s *spi = (FAR struct spi_dev_s *)handle; + uint16_t u1cmd; + uint16_t u2cmd; + + spivdbg("channel: %d gain: %d\n", settings->channel, settings->gain); + DEBUGASSERT(handle); + + /* Format the commands */ + + if (pos == 0) + { + u1cmd = PGA11X_CMD_WRITE | + ((uint16_t)settings->channel << PGA11X_CHAN_SHIFT) | + ((uint16_t)settings->gain << PGA11X_GAIN_SHIFT); + u2cmd = PGA11X_DCCMD_NOOP; + } + else /* if (pos == 1) */ + { + u1cmd = PGA11X_CMD_NOOP; + u2cmd = PGA11X_DCCMD_WRITE | + ((uint16_t)settings->channel << PGA11X_CHAN_SHIFT) | + ((uint16_t)settings->gain << PGA11X_GAIN_SHIFT); + } + + /* Send the command */ + + pga11x_write(spi, u1cmd, u2cmd); + return OK; +} +#endif + +/**************************************************************************** + * Name: pga11x_read + * + * Description: + * Read from all PGA117 amplifier/multiplexers. + * + * If CONFIG_PGA11X_DAISYCHAIN is defined, then pga11x_read() reads from + * both chips in the daisy-chain. pga11x_uread() is provided to support + * accessing the parts independently. + * + * Input Parameters: + * spi - An SPI "bottom half" device driver instance + * settings - Returned channel and gain settings + * + * Returned Value: + * Zero on sucess; a negated errno value on failure. + * + ****************************************************************************/ + +int pga11x_read(PGA11X_HANDLE handle, FAR struct pga11x_settings_s *settings) +{ +#ifdef CONFIG_PGA11X_DAISYCHAIN + FAR struct spi_dev_s *spi = (FAR struct spi_dev_s *)handle; + uint16_t u1value; + uint16_t u2value; + + spivdbg("Entry\n"); + DEBUGASSERT(handle && settings); + + /* Lock the bus and read the configuration */ + + pga11x_lock(spi); + + /* Select, send the 16-bit command, the 16-bit daisy-chain command, and + * then de-select the part. I do not know if de-selection between word + * transfers is required. However, it is shown in the timing diagrams + * for the part. + */ + + SPI_SELECT(spi, SPIDEV_MUX, true); + pga11x_send16(spi, PGA11X_CMD_READ); + pga11x_send16(spi, PGA11X_DCCMD_READ); + SPI_SELECT(spi, SPIDEV_MUX, false); + + /* Re-select, get the returned values, de-select, and unlock */ + + SPI_SELECT(spi, SPIDEV_MUX, true); + u2value = pga11x_recv16(spi); + u1value = pga11x_recv16(spi); + SPI_SELECT(spi, SPIDEV_MUX, false); + pga11x_unlock(spi); + + /* Decode the returned value */ + + spivdbg("Returning %04x %04x\n", u2value, u1value); + settings->u1.channel = (uint8_t)((u1value & PGA11X_CHAN_MASK) >> PGA11X_CHAN_SHIFT); + settings->u1.gain = (uint8_t)((u1value & PGA11X_GAIN_MASK) >> PGA11X_GAIN_SHIFT); + settings->u2.channel = (uint8_t)((u2value & PGA11X_CHAN_MASK) >> PGA11X_CHAN_SHIFT); + settings->u2.gain = (uint8_t)((u2value & PGA11X_GAIN_MASK) >> PGA11X_GAIN_SHIFT); + return OK; +#else + FAR struct spi_dev_s *spi = (FAR struct spi_dev_s *)handle; + uint16_t value; + + spivdbg("Entry\n"); + DEBUGASSERT(handle); + + /* Lock the bus and read the configuration */ + + pga11x_lock(spi); + + /* Select, send the 16-bit PGA11X_CMD_READ command, and de-select. I do + * not know if de-selection between word transfers is required. However, + * it is shown in the timing diagrams for the part. + */ + + SPI_SELECT(spi, SPIDEV_MUX, true); + pga11x_send16(spi, PGA11X_CMD_READ); + SPI_SELECT(spi, SPIDEV_MUX, false); + + /* Re-select, get the returned value, de-select, and unlock */ + + SPI_SELECT(spi, SPIDEV_MUX, true); + value = pga11x_recv16(spi); + SPI_SELECT(spi, SPIDEV_MUX, false); + pga11x_unlock(spi); + + /* Decode the returned value */ + + spivdbg("Returning: %04x\n", value); + settings->channel = (uint8_t)((value & PGA11X_CHAN_MASK) >> PGA11X_CHAN_SHIFT); + settings->gain = (uint8_t)((value & PGA11X_GAIN_MASK) >> PGA11X_GAIN_SHIFT); + return OK; +#endif +} + +/**************************************************************************** + * Name: pga11x_uread + * + * Description: + * Read from one PGA117 amplifier/multiplexer. + * + * If CONFIG_PGA11X_DAISYCHAIN is defined, then pga11x_read() reads + * the parts independently. + * + * Input Parameters: + * spi - An SPI "bottom half" device driver instance + * pos - Position of the chip in the daisy chain (0 or 1) + * settings - Returned channel and gain settings + * + * Returned Value: + * Zero on sucess; a negated errno value on failure. + * + ****************************************************************************/ + +#ifdef CONFIG_PGA11X_DAISYCHAIN +int pga11x_uread(PGA11X_HANDLE handle, int pos, + FAR struct pga11x_usettings_s *settings) +{ + struct pga11x_settings_s both; + int ret = pga11x_read(handle, &both); + if (ret == OK) + { + if (pos == 0) + { + settings->channel = both.u1.channel; + settings->gain = both.u1.gain; + } + else /* if (pos == 1) */ + { + settings->channel = both.u2.channel; + settings->gain = both.u2.gain; + } + } + + return ret; +} +#endif + +/**************************************************************************** + * Name: pga11x_shutdown + * + * Description: + * Put all PGA11x's in shutdown down mode. + * + * If CONFIG_PGA11X_DAISYCHAIN is defined, then pga11x_shutdown() controls + * both chips in the daisy-chain. pga11x_ushutdown() is provided to + * control the parts independently. + * + * Input Parameters: + * spi - An SPI "bottom half" device driver instance + * + * Returned Value: + * Zero on sucess; a negated errno value on failure. + * + ****************************************************************************/ + +int pga11x_shutdown(PGA11X_HANDLE handle) +{ + FAR struct spi_dev_s *spi = (FAR struct spi_dev_s *)handle; + + spivdbg("Entry\n"); + DEBUGASSERT(handle); + + /* Enter shutdown mode by issuing an SDN_EN command */ + +#ifdef CONFIG_PGA11X_DAISYCHAIN + pga11x_write(spi, PGA11X_CMD_SDN_EN, PGA11X_DCCMD_SDN_EN); +#else + pga11x_write(spi, PGA11X_CMD_SDN_EN); +#endif + return OK; +} + +/**************************************************************************** + * Name: pga11x_ushutdown + * + * Description: + * Put one PGA11x in shutdown down mode. + * + * If CONFIG_PGA11X_DAISYCHAIN is defined, then pga11x_ushutdown() is + * provided to shutdown the parts independently. + * + * Input Parameters: + * spi - An SPI "bottom half" device driver instance + * pos - Position of the chip in the daisy chain (0 or 1) + * + * Returned Value: + * Zero on sucess; a negated errno value on failure. + * + ****************************************************************************/ + +#ifdef CONFIG_PGA11X_DAISYCHAIN +int pga11x_ushutdown(PGA11X_HANDLE handle, int pos) +{ + FAR struct spi_dev_s *spi = (FAR struct spi_dev_s *)handle; + + spivdbg("Entry\n"); + DEBUGASSERT(handle); + + /* Enter shutdown mode by issuing an SDN_EN command */ + + if (pos == 0) + { + pga11x_write(spi, PGA11X_CMD_SDN_EN, PGA11X_DCCMD_NOOP); + } + else + { + pga11x_write(spi, PGA11X_CMD_NOOP, PGA11X_DCCMD_SDN_EN); + } + + return OK; +} +#endif + +/**************************************************************************** + * Name: pga11x_enable + * + * Description: + * Take all PGA11x's out of shutdown down mode. + * + * If CONFIG_PGA11X_DAISYCHAIN is defined, then pga11x_enable() controls + * both chips in the daisy-chain. pga11x_uenable() is provided to + * control the parts independently. + * + * Input Parameters: + * spi - An SPI "bottom half" device driver instance + * + * Returned Value: + * Zero on sucess; a negated errno value on failure. + * + ****************************************************************************/ + +int pga11x_enable(PGA11X_HANDLE handle) +{ + FAR struct spi_dev_s *spi = (FAR struct spi_dev_s *)handle; + + spivdbg("Entry\n"); + DEBUGASSERT(handle); + + /* Lock the bus and send the shutdown disable command. Shutdown mode is + * cleared (returned to the last valid write configuration) by the SDN_DIS + * command or by any valid Write command + */ + +#ifdef CONFIG_PGA11X_DAISYCHAIN + pga11x_write(spi, PGA11X_CMD_SDN_DIS, PGA11X_DCCMD_SDN_DIS); +#else + pga11x_write(spi, PGA11X_CMD_SDN_DIS); +#endif + return OK; +} + +/**************************************************************************** + * Name: pga11x_uenable + * + * Description: + * Take one PGA11x out of shutdown down mode. + * + * If CONFIG_PGA11X_DAISYCHAIN is defined, then pga11x_uenable() is + * provided to enable the parts independently. + * + * Input Parameters: + * spi - An SPI "bottom half" device driver instance + * pos - Position of the chip in the daisy chain (0 or 1) + * + * Returned Value: + * Zero on sucess; a negated errno value on failure. + * + ****************************************************************************/ + +#ifdef CONFIG_PGA11X_DAISYCHAIN +int pga11x_uenable(PGA11X_HANDLE handle, int pos) +{ + FAR struct spi_dev_s *spi = (FAR struct spi_dev_s *)handle; + + spivdbg("Entry\n"); + DEBUGASSERT(handle); + + /* Enter shutdown mode by issuing an SDN_EN command */ + + if (pos == 0) + { + pga11x_write(spi, PGA11X_CMD_SDN_DIS, PGA11X_DCCMD_NOOP); + } + else + { + pga11x_write(spi, PGA11X_CMD_NOOP, PGA11X_DCCMD_SDN_DIS); + } + + return OK; +} +#endif + +#endif /* CONFIG_ADC && CONFIG_ADC_PGA11X */ + -- cgit v1.2.3