From 85caee63b43e0f796008e29fd5cb6d4a1593c43f Mon Sep 17 00:00:00 2001 From: Nicholas Chin Date: Tue, 18 Feb 2020 14:19:40 -0600 Subject: [PATCH] drivers/analog: Adds driver for the ADS7828 I2C analog to digital converter --- drivers/analog/Kconfig | 20 ++ drivers/analog/Make.defs | 4 + drivers/analog/ads7828.c | 496 +++++++++++++++++++++++++++++++++ include/nuttx/analog/adc.h | 75 +++-- include/nuttx/analog/ads7828.h | 83 ++++++ include/nuttx/analog/ioctl.h | 6 + 6 files changed, 657 insertions(+), 27 deletions(-) create mode 100644 drivers/analog/ads7828.c create mode 100644 include/nuttx/analog/ads7828.h diff --git a/drivers/analog/Kconfig b/drivers/analog/Kconfig index cc23fef13c..8fe91d718b 100644 --- a/drivers/analog/Kconfig +++ b/drivers/analog/Kconfig @@ -137,6 +137,26 @@ config PGA11X_MULTIPLE endif # if ADC_PGA11X +config ADC_ADS7828 + bool "TI ADS7828 support" + default n + select I2C + ---help--- + Enable driver support for the ADS7828 12-Bit I2C powered ADC. + + This driver supports reading single or multiple ADC conversion result + as well as onfiguring the ADC, via ioctl calls. + +if ADC_ADS7828 + +config ADS7828_FREQUENCY + int "TI ADS7828 I2C frequency" + default 100000 + ---help--- + ADS7828 supports standard, fast, and high-speed I2C modes. + +endif # ADC_ADS7828 + endif # ADC config COMP diff --git a/drivers/analog/Make.defs b/drivers/analog/Make.defs index 872ebec65f..c2494df3d1 100644 --- a/drivers/analog/Make.defs +++ b/drivers/analog/Make.defs @@ -103,6 +103,10 @@ ifeq ($(CONFIG_ADC_ADS125X),y) CSRCS += ads1255.c endif +ifeq ($(CONFIG_ADC_ADS7828),y) + CSRCS += ads7828.c +endif + ifeq ($(CONFIG_ADC_LTC1867L),y) CSRCS += ltc1867l.c endif diff --git a/drivers/analog/ads7828.c b/drivers/analog/ads7828.c new file mode 100644 index 0000000000..621b71f73d --- /dev/null +++ b/drivers/analog/ads7828.c @@ -0,0 +1,496 @@ +/**************************************************************************** + * drivers/analog/ads7828.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#if defined(CONFIG_ADC_ADS7828) + +#define ADS7828_NUM_CHANNELS 8 + +/* All conversions are started by writing a command byte to the ADS7828. + * Below are the bit/ mask definitions for this byte. + */ + +#define ADS7828_CMD_BYTE_MODE (1 << 7) +#define ADS7828_CMD_BYTE_CHANNEL_SHIFT 4 +#define ADS7828_CMD_BYTE_CHANNEL_MASK (7 << ADS7828_CMD_BYTE_CHANNEL_SHIFT) +#define ADS7828_CMD_BYTE_REF (1 << 3) +#define ADS7828_CMD_BYTE_PWR_DOWN (1 << 2) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct ads7828_dev_s +{ + FAR struct i2c_master_s *i2c; + FAR const struct adc_callback_s *cb; + uint8_t addr; + + /* List of channels to read on every convert trigger. + * Bit position corresponds to channel. i.e. bit0 = 1 to read channel 0. + */ + + uint8_t chanstrobed; + + /* Current configuration of the ADC. Encoded in its position in the + * command byte that is sent to the ADS7828 to trigger a conversion + */ + + uint8_t cmdbyte; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* ads7828 helpers */ + +static int ads7828_manage_strobe(FAR struct ads7828_dev_s *priv, + uint8_t channel, bool add_nremove); +static int ads7828_readchannel(FAR struct ads7828_dev_s *priv, + FAR struct adc_msg_s *msg); + +/* ADC methods */ + +static int ads7828_bind(FAR struct adc_dev_s *dev, + FAR const struct adc_callback_s *callback); +static void ads7828_reset(FAR struct adc_dev_s *dev); +static int ads7828_setup(FAR struct adc_dev_s *dev); +static void ads7828_shutdown(FAR struct adc_dev_s *dev); +static void ads7828_rxint(FAR struct adc_dev_s *dev, bool enable); +static int ads7828_ioctl(FAR struct adc_dev_s *dev, int cmd, + unsigned long arg); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct adc_ops_s g_adcops = +{ + .ao_bind = ads7828_bind, /* ao_bind */ + .ao_reset = ads7828_reset, /* ao_reset */ + .ao_setup = ads7828_setup, /* ao_setup */ + .ao_shutdown = ads7828_shutdown, /* ao_shutdown */ + .ao_rxint = ads7828_rxint, /* ao_rxint */ + .ao_ioctl = ads7828_ioctl /* ao_read */ +}; + +static struct ads7828_dev_s g_adcpriv; + +static struct adc_dev_s g_adcdev = +{ + .ad_ops = &g_adcops, + .ad_priv = &g_adcpriv, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: ads7828_manage_strobe + * + * Description: + * Controls which channels are read on ANIOC_TRIGGER. By default all + * channels are read. + * + * Returned Value: + * 0 on success. Negated errno on failure. + * + ****************************************************************************/ + +static int ads7828_manage_strobe(FAR struct ads7828_dev_s *priv, + uint8_t channel, bool add_nremove) +{ + int ret = OK; + + if (priv == NULL || channel >= ADS7828_NUM_CHANNELS) + { + ret = -EINVAL; + } + else + { + uint8_t flag = 1U << channel; + + if (add_nremove) + { + priv->chanstrobed |= flag; + } + else + { + priv->chanstrobed &= ~flag; + } + } + + return ret; +} + +/**************************************************************************** + * Name: ads7828_readchannel + * + * Description: + * Reads a conversion from the ADC. + * + * Input Parameters: + * msg - msg->am_channel should be set to the channel to be read. + * msg->am_data will store the result of the read. + * + * Returned Value: + * 0 on success. Negated errno on failure. + * + * Assumptions/Limitations: + * NOTE: When used in single-ended mode, msg->am_channel will be converted + * to the corresponding channel selection bits in the command byte. + * In differential mode, msg->am_channel is used as the channel + * selection bits. The corresponding "channels" are as follows: + * + * msg->am_channel Analog Source + * 0 +CH0, -CH1 + * 1 +CH2, -CH3 + * 2 +CH4, -CH5 + * 3 +CH6, -CH7 + * 4 +CH1, -CH0 + * 5 +CH3, -CH2 + * 6 +CH5, -CH4 + * 7 +CH7, -CH6 + * + ****************************************************************************/ + +static int ads7828_readchannel(FAR struct ads7828_dev_s *priv, + FAR struct adc_msg_s *msg) +{ + int ret = OK; + if (priv == NULL || msg == NULL) + { + ret = -EINVAL; + } + else + { + struct i2c_msg_s i2cmsg[2]; + uint8_t channel = msg->am_channel; + + /* Convert single-ended channels to write encoding. */ + + if (priv->cmdbyte & ADS7828_CMD_BYTE_MODE) + { + channel = channel / 2; + + if (msg->am_channel % 2 != 0) + { + channel += 4; + } + } + + uint8_t cmdbyte = channel << ADS7828_CMD_BYTE_CHANNEL_SHIFT; + cmdbyte |= priv->cmdbyte; + + i2cmsg[0].frequency = CONFIG_ADS7828_FREQUENCY; + i2cmsg[0].addr = priv->addr; + i2cmsg[0].flags = I2C_M_NOSTOP; + i2cmsg[0].buffer = &cmdbyte; + i2cmsg[0].length = sizeof(cmdbyte); + + i2cmsg[1].frequency = CONFIG_ADS7828_FREQUENCY; + i2cmsg[1].addr = priv->addr; + i2cmsg[1].flags = I2C_M_READ; + + uint16_t buf; + i2cmsg[1].buffer = (uint8_t *)(&buf); + i2cmsg[1].length = sizeof(buf); + ret = I2C_TRANSFER(priv->i2c, i2cmsg, 2); + if (ret < 0) + { + aerr("ADS7828 I2C transfer failed: %d", ret); + } + + msg->am_data = be16toh(buf); + } + + return ret; +} + +/**************************************************************************** + * Name: ads7828_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 ads7828_bind(FAR struct adc_dev_s *dev, + FAR const struct adc_callback_s *callback) +{ + FAR struct ads7828_dev_s *priv = (FAR struct ads7828_dev_s *)dev->ad_priv; + + DEBUGASSERT(priv != NULL); + priv->cb = callback; + return OK; +} + +/**************************************************************************** + * Name: ads7828_reset + * + * Description: + * Reset the ADC device. Called early to initialize the hardware. This + * is called, before ao_setup() and on error conditions. + * + ****************************************************************************/ + +static void ads7828_reset(FAR struct adc_dev_s *dev) +{ + FAR struct ads7828_dev_s *priv = + (FAR struct ads7828_dev_s *)dev->ad_priv; + + priv->cmdbyte = 0; + priv->chanstrobed = 0xffu; +} + +/**************************************************************************** + * Name: ads7828_setup + * + * Description: + * Configure the ADC. This method is called the first time that the ADC + * device is opened. The ADS7828 is quite simple and nothing special is + * needed to be done. + * + ****************************************************************************/ + +static int ads7828_setup(FAR struct adc_dev_s *dev) +{ + return OK; +} + +/**************************************************************************** + * Name: ads7828_shutdown + * + * Description: + * Disable the ADC. This method is called when the ADC device is closed. + * This method should reverse the operation of the setup method, but as + * the ADS7828 is quite simple does not need to do anything. + * + ****************************************************************************/ + +static void ads7828_shutdown(FAR struct adc_dev_s *dev) +{ +} + +/**************************************************************************** + * Name: ads7828_rxint + * + * Description: + * Needed for ADC upper-half compatability but conversion interrupts + * are not supported by the ADC7828. + * + ****************************************************************************/ + +static void ads7828_rxint(FAR struct adc_dev_s *dev, bool enable) +{ +} + +/**************************************************************************** + * Name: ads7828_ioctl + * + * Description: + * All ioctl calls will be routed through this method + * + ****************************************************************************/ + +static int ads7828_ioctl(FAR struct adc_dev_s *dev, int cmd, + unsigned long arg) +{ + FAR struct ads7828_dev_s *priv = (FAR struct ads7828_dev_s *)dev->ad_priv; + int ret = OK; + + switch (cmd) + { + case ANIOC_TRIGGER: + { + struct adc_msg_s msg; + int i; + + for (i = 0; (i < ADS7828_NUM_CHANNELS) && (ret == OK); i++) + { + if ((priv->chanstrobed >> i) & 1u) + { + msg.am_channel = i; + ret = ads7828_readchannel(priv, &msg); + if (ret == OK) + { + priv->cb->au_receive(&g_adcdev, i, msg.am_data); + } + } + } + } + break; + + /* Add a channel to list of channels read on ANIOC_TRIGGER */ + + case ANIOC_ADS7828_ADD_CHAN: + { + ret = ads7828_manage_strobe(priv, (uint8_t)arg, true); + } + break; + + /* Remove a channel from list of channels read on ANIOC_TRIGGER */ + + case ANIOC_ADS7828_REMOVE_CHAN: + { + ret = ads7828_manage_strobe(priv, (uint8_t)arg, false); + } + break; + + /* Read a single channel from the ADC */ + + case ANIOC_ADS7828_READ_CHANNEL: + { + FAR struct adc_msg_s *msg = (FAR struct adc_msg_s *)arg; + ret = ads7828_readchannel(priv, msg); + } + break; + + /* Set the ADC reference voltage */ + + case ANIOC_ADS7828_SET_REF: + { + if (arg == ADS7828_REF_INTERNAL) + { + priv->cmdbyte |= ADS7828_CMD_BYTE_REF; + } + else if (arg == ADS7828_REF_EXTERNAL) + { + priv->cmdbyte &= ~ADS7828_CMD_BYTE_REF; + } + else + { + ret = -EINVAL; + } + } + break; + + /* Set mode to single-ended or differential-mode */ + + case ANIOC_ADS7828_MODE: + { + if (arg == ADS7828_SINGLE_ENDED) + { + priv->cmdbyte |= ADS7828_CMD_BYTE_MODE; + } + else if (arg == ADS7828_DIFFERENTIAL) + { + priv->cmdbyte &= ~ADS7828_CMD_BYTE_MODE; + } + else + { + ret = -EINVAL; + } + } + break; + + /* Set whether to power down ADC between conversions or not */ + + case ANIOC_ADS7828_POWER_SAVE: + { + if ((bool)arg) + { + priv->cmdbyte &= ~ADS7828_CMD_BYTE_PWR_DOWN; + } + else + { + priv->cmdbyte |= ADS7828_CMD_BYTE_PWR_DOWN; + } + } + break; + + /* Command was not recognized */ + + default: + ret = -ENOTTY; + aerr("ADS7828 ERROR: Unrecognized cmd: %d\n", cmd); + break; + } + + return ret; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: ads7828_initialize + * + * Description: + * Initialize the selected adc + * + * Input Parameters: + * i2c - Pointer to a I2C master struct for the bus the ADC resides on. + * addr - I2C address of the ADC + * + * Returned Value: + * Valid ADC device structure reference on success; a NULL on failure + * + ****************************************************************************/ + +FAR struct adc_dev_s *ads7828_initialize(FAR struct i2c_master_s *i2c, + uint8_t addr) +{ + DEBUGASSERT(i2c != NULL); + + /* Driver state data */ + + FAR struct ads7828_dev_s *priv; + priv = (FAR struct ads7828_dev_s *)g_adcdev.ad_priv; + + priv->cb = NULL; + priv->i2c = i2c; + priv->addr = addr; + priv->cmdbyte = 0; + priv->chanstrobed = 0xffu; + + return &g_adcdev; +} + +#endif diff --git a/include/nuttx/analog/adc.h b/include/nuttx/analog/adc.h index 6f4f963217..af083b420c 100644 --- a/include/nuttx/analog/adc.h +++ b/include/nuttx/analog/adc.h @@ -1,4 +1,4 @@ -/************************************************************************************ +/**************************************************************************** * include/nuttx/analog/adc.h * * Copyright (C) 2016-2017 Gregory Nutt. All rights reserved. @@ -38,14 +38,14 @@ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * - ************************************************************************************/ + ****************************************************************************/ #ifndef __INCLUDE_NUTTX_ANALOG_ADC_H #define __INCLUDE_NUTTX_ANALOG_ADC_H -/************************************************************************************ +/**************************************************************************** * Included Files - ************************************************************************************/ + ****************************************************************************/ #include #include @@ -60,12 +60,13 @@ #include #include -/************************************************************************************ +/**************************************************************************** * Pre-processor Definitions - ************************************************************************************/ + ****************************************************************************/ -/* Default configuration settings that may be overridden in the NuttX configuration - * file. The configured size is limited to 255 to fit into a uint8_t. +/* Default configuration settings that may be overridden in the NuttX + * configuration file. The configured size is limited to 255 to fit into a + * uint8_t. */ #if !defined(CONFIG_ADC_FIFOSIZE) @@ -85,9 +86,10 @@ #define ADC_RXINT(dev) ((dev)->ad_ops->ao_rxint((dev))) #define ADC_IOCTL(dev,cmd,arg) ((dev)->ad_ops->ao_ioctl((dev),(cmd),(arg))) -/************************************************************************************ +/**************************************************************************** * Public Types - ************************************************************************************/ + ****************************************************************************/ + /* These are callbacks to notify the upper-half driver of ADC events */ struct adc_dev_s; @@ -97,7 +99,8 @@ struct adc_callback_s * new ADC sample data is available. * * Input Parameters: - * dev - The ADC device structure that was previously registered by adc_register() + * dev - The ADC device structure that was previously registered by + * adc_register() * ch - And ID for the ADC channel number that generated the data * data - The actual converted data from the channel. * @@ -127,9 +130,9 @@ struct adc_fifo_s struct adc_msg_s af_buffer[CONFIG_ADC_FIFOSIZE]; }; -/* This structure defines all of the operations providd by the architecture specific - * logic. All fields must be provided with non-NULL function pointers by the - * caller of adc_register(). +/* This structure defines all of the operations providd by the architecture + * specific logic. All fields must be provided with non-NULL function + * pointers by the caller of adc_register(). */ struct adc_dev_s; @@ -205,26 +208,26 @@ struct adc_dev_s FAR void *ad_priv; /* Used by the arch-specific logic */ }; -/************************************************************************************ +/**************************************************************************** * Public Function Prototypes - ************************************************************************************/ + ****************************************************************************/ #if defined(__cplusplus) extern "C" { #endif -/************************************************************************************ +/**************************************************************************** * "Upper-Half" ADC Driver Interfaces - ************************************************************************************/ + ****************************************************************************/ -/************************************************************************************ +/**************************************************************************** * Name: adc_register * * Description: - * Register a ADC driver. This function binds an instance of a "lower half" ADC - * driver with the "upper half" ADC device and registers that device so that can - * be used by application code. + * Register a ADC driver. This function binds an instance of a "lower half" + * ADC driver with the "upper half" ADC device and registers that device + * so that can be used by application code. * * Input Parameters: * path - The full path to the driver to be registers in the NuttX pseudo- @@ -238,21 +241,21 @@ extern "C" * Returned Value: * Zero on success; a negated errno value on failure. * - ************************************************************************************/ + ****************************************************************************/ int adc_register(FAR const char *path, FAR struct adc_dev_s *dev); -/************************************************************************************ +/**************************************************************************** * Platform-Independent "Lower Half" ADC Driver Interfaces - ************************************************************************************/ + ****************************************************************************/ -/************************************************************************************ +/**************************************************************************** * Name: up_ads1255initialize * * Description: * Initialize the TI ADS 125X lower half driver * - ************************************************************************************/ + ****************************************************************************/ FAR struct adc_dev_s *up_ads1255initialize(FAR struct spi_dev_s *spi, unsigned int devno); @@ -275,6 +278,24 @@ FAR struct adc_dev_s *up_ads1255initialize(FAR struct spi_dev_s *spi, FAR struct adc_dev_s *lmp92001_adc_initialize(FAR struct i2c_master_s *i2c, uint8_t addr); +/**************************************************************************** + * Name: ads7828_initialize + * + * Description: + * Initialize ADC + * + * Input Parameters: + * i2c - Pointer to a valid I2C master struct. + * addr - I2C device address. + * + * Returned Value: + * Valid ADS7828 device structure reference on success; a NULL on failure + * + ****************************************************************************/ + +FAR struct adc_dev_s *ads7828_initialize(FAR struct i2c_master_s *i2c, + uint8_t addr); + #if defined(__cplusplus) } #endif diff --git a/include/nuttx/analog/ads7828.h b/include/nuttx/analog/ads7828.h new file mode 100644 index 0000000000..05f989e78c --- /dev/null +++ b/include/nuttx/analog/ads7828.h @@ -0,0 +1,83 @@ +/**************************************************************************** + * include/nuttx/analog/ads7828.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __INCLUDE_NUTTX_ANALOG_ADS7828_H +#define __INCLUDE_NUTTX_ANALOG_ADS7828_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* IOCTL Commands + * Cmd: ANIOC_ADS7828_SET_REF Arg: enum ads7828_ref_e + * Cmd: ANIOC_ADS7828_MODE Arg: ads7828_mode_e + * Cmd: ANIOC_ADS7828_POWER_SAVE Arg: bool value + * Cmd: ANIOC_ADS7828_ADD_CHAN Arg: uint8_t value + * Cmd: ANIOC_ADS7828_REMOVE_CHAN ARG: uint8_t value + * Cmd: ANIOC_ADS7828_READ_CHANNEL Arg: struct adc_msg_s *channel + */ + +#define ANIOC_ADS7828_SET_REF _ANIOC(AN_ADS7828_FIRST + 0) +#define ANIOC_ADS7828_MODE _ANIOC(AN_ADS7828_FIRST + 1) +#define ANIOC_ADS7828_POWER_SAVE _ANIOC(AN_ADS7828_FIRST + 2) +#define ANIOC_ADS7828_ADD_CHAN _ANIOC(AN_ADS7828_FIRST + 3) +#define ANIOC_ADS7828_REMOVE_CHAN _ANIOC(AN_ADS7828_FIRST + 4) +#define ANIOC_ADS7828_READ_CHANNEL _ANIOC(AN_ADS7828_FIRST + 5) + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +enum ads7828_ref_e +{ + ADS7828_REF_EXTERNAL = 0u, + ADS7828_REF_INTERNAL +}; + +enum ads7828_mode_e +{ + ADS7828_DIFFERENTIAL = 0u, + ADS7828_SINGLE_ENDED +}; + +enum ads7828_diffch_e +{ + ADS7828_DIFF_PCH0_NCH1 = 0u, + ADS7828_DIFF_PCH2_NCH3, + ADS7828_DIFF_PCH4_NCH5, + ADS7828_DIFF_PCH6_NCH7, + ADS7828_DIFF_PCH1_NCH0, + ADS7828_DIFF_PCH3_NCH2, + ADS7828_DIFF_PCH5_NCH4, + ADS7828_DIFF_PCH7_NCH6 +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#endif /* __INCLUDE_NUTTX_ANALOG_ADS7828_H */ diff --git a/include/nuttx/analog/ioctl.h b/include/nuttx/analog/ioctl.h index 1816887770..8098537be3 100644 --- a/include/nuttx/analog/ioctl.h +++ b/include/nuttx/analog/ioctl.h @@ -85,6 +85,12 @@ #define AN_LMP92001_FIRST (AN_FIRST + AN_NCMDS + AN_ADS2142_NCMDS) #define AN_LMP92001_NCMDS 7 +/* See include/nuttx/analog/ads7828.h */ + +#define AN_ADS7828_FIRST (AN_FIRST + AN_NCMDS + AN_ADS2142_NCMDS + \ + AN_LMP92001_NCMDS) +#define AN_ADS7828_NCMDS 6 + /**************************************************************************** * Public Function Prototypes ****************************************************************************/