diff --git a/drivers/analog/adc.c b/drivers/analog/adc.c index ff6031ee68a..7c20c112b93 100644 --- a/drivers/analog/adc.c +++ b/drivers/analog/adc.c @@ -1,9 +1,10 @@ /**************************************************************************** * drivers/analog/adc.c * + * Copyright (C) 2008-2009, 2016 Gregory Nutt. All rights reserved. * Copyright (C) 2011 Li Zhuoyi. All rights reserved. * Author: Li Zhuoyi - * History: 0.1 2011-08-04 initial version + * Gregory Nutt * * Derived from drivers/can.c * @@ -70,12 +71,14 @@ static int adc_close(FAR struct file *filep); static ssize_t adc_read(FAR struct file *fielp, FAR char *buffer, size_t buflen); static int adc_ioctl(FAR struct file *filep, int cmd, unsigned long arg); +static int adc_receive(FAR struct adc_dev_s *dev, uint8_t ch, + int32_t data); /**************************************************************************** * Private Data ****************************************************************************/ -static const struct file_operations adc_fops = +static const struct file_operations g_adc_fops = { adc_open, /* open */ adc_close, /* close */ @@ -88,6 +91,11 @@ static const struct file_operations adc_fops = #endif }; +static const struct adc_callback_s g_adc_callback = +{ + adc_receive /* au_receive */ +}; + /**************************************************************************** * Private Functions ****************************************************************************/ @@ -361,15 +369,11 @@ static int adc_ioctl(FAR struct file *filep, int cmd, unsigned long arg) return ret; } -/**************************************************************************** - * Public Functions - ****************************************************************************/ - /**************************************************************************** * Name: adc_receive ****************************************************************************/ -int adc_receive(FAR struct adc_dev_s *dev, uint8_t ch, int32_t data) +static 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; @@ -408,12 +412,30 @@ int adc_receive(FAR struct adc_dev_s *dev, uint8_t ch, int32_t data) return err; } +/**************************************************************************** + * Public Functions + ****************************************************************************/ + /**************************************************************************** * Name: adc_register ****************************************************************************/ int adc_register(FAR const char *path, FAR struct adc_dev_s *dev) { + int ret; + + DEBUGASSERT(path != NULL && dev != NULL); + + /* Bind the upper-half callbacks to the lower half ADC driver */ + + DEBUGASSERT(dev->ad_ops != NULL && dev->ad_ops->ao_bind != NULL); + ret = dev->ad_ops->ao_bind(dev, &g_adc_callback); + if (ret < 0) + { + adbg("ERROR: Failed to bind callbacks: %d\n", ret) + return ret; + } + /* Initialize the ADC device structure */ dev->ad_ocount = 0; @@ -421,7 +443,19 @@ int adc_register(FAR const char *path, FAR struct adc_dev_s *dev) sem_init(&dev->ad_recv.af_sem, 0, 0); sem_init(&dev->ad_closesem, 0, 1); + /* Reset the ADC hardware */ + + DEBUGASSERT(dev->ad_ops->ao_reset != NULL); dev->ad_ops->ao_reset(dev); - return register_driver(path, &adc_fops, 0444, dev); + /* Register the ADC character driver */ + + ret = register_driver(path, &g_adc_fops, 0444, dev); + if (ret < 0) + { + sem_destroy(&dev->ad_recv.af_sem); + sem_destroy(&dev->ad_closesem); + } + + return ret; } diff --git a/drivers/analog/ads1255.c b/drivers/analog/ads1255.c index 432b7dd7660..3b69e09a0e1 100644 --- a/drivers/analog/ads1255.c +++ b/drivers/analog/ads1255.c @@ -1,10 +1,10 @@ /************************************************************************************ * arch/drivers/analog/ads1255.c * + * Copyright (C) 2010, 2016 Gregory Nutt. All rights reserved. * 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) + * Gregory Nutt * * This file is a part of NuttX: * @@ -47,6 +47,7 @@ #include #include #include +#include #include #include @@ -105,8 +106,9 @@ * ad_private Types ****************************************************************************/ -struct up_dev_s +struct ads1255_dev_s { + FAR const struct adc_callback_s *cb; uint8_t channel; uint32_t sps; uint8_t pga; @@ -123,6 +125,8 @@ struct up_dev_s /* ADC methods */ +static int adc_bind(FAR struct adc_dev_s *dev, + FAR const struct adc_callback_s *callback); 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); @@ -143,7 +147,7 @@ static const struct adc_ops_s g_adcops = .ao_ioctl = adc_ioctl /* ao_read */ }; -static struct up_dev_s g_adcpriv = +static struct ads1255_dev_s g_adcpriv = { .mux = (const uint8_t []) { @@ -190,16 +194,44 @@ static uint8_t getspsreg(uint16_t sps) } /**************************************************************************** - * ad_private Functions + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: adc_bind + * + * Description: + * Bind the upper-half driver callbacks to the lower-half implementation. This + * must be called early in order to receive ADC event notifications. + * + ****************************************************************************/ + +static int adc_bind(FAR struct adc_dev_s *dev, + FAR const struct adc_callback_s *callback) +{ + FAR struct ads1255_dev_s *priv = (FAR struct ads1255_dev_s *)dev->ad_priv; + + DEBUGASSERT(priv != NULL); + priv->cb = callback; + return OK; +} + +/**************************************************************************** + * Name: adc_reset + * + * Description: + * Reset the ADC device. Called early to initialize the hardware. This + * is called, before ao_setup() and on error conditions. + * ****************************************************************************/ -/* Reset the ADC device. Called early to initialize the hardware. This - * is called, before ao_setup() and on error conditions. - */ static void adc_reset(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; + FAR struct ads1255_dev_s *priv = (FAR struct ads1255_dev_s *)dev->ad_priv; + FAR struct spi_dev_s *spi; + + DEBUGASSERT(priv != NULL && priv->spi != NULL); + spi = priv->spi; SPI_SETMODE(spi, SPIDEV_MODE1); SPI_SETBITS(spi, 8); @@ -213,18 +245,27 @@ static void adc_reset(FAR struct adc_dev_s *dev) 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. - */ +/**************************************************************************** + * Name: adc_setup + * + * Description: + * Configure the ADC. This method is called the first time that the ADC + * device is opened. This will occur when the port is first opened. + * This setup includes configuring and attaching ADC interrupts. Interrupts + * are all disabled upon return. + * + ****************************************************************************/ -static int adc_setup(FAR struct adc_dev_s *dev) +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); + FAR struct ads1255_dev_s *priv = (FAR struct ads1255_dev_s *)dev->ad_priv; + FAR struct spi_dev_s *spi; + int ret; + DEBUGASSERT(priv != NULL && priv->spi != NULL); + spi = priv->spi; + + ret = irq_attach(priv->irq, adc_interrupt); if (ret == OK) { SPI_SELECT(spi, priv->devno, true); @@ -251,22 +292,39 @@ static int adc_setup(FAR struct adc_dev_s *dev) return ret; } -/* Disable the ADC. This method is called when the ADC device is closed. - * This method reverses the operation the setup method. - */ +/**************************************************************************** + * Name: adc_shutdown + * + * Description: + * Disable the ADC. This method is called when the ADC device is closed. + * This method reverses the operation the setup method. + * + ****************************************************************************/ static void adc_shutdown(FAR struct adc_dev_s *dev) { - FAR struct up_dev_s *priv = (FAR struct up_dev_s *)dev->ad_priv; + FAR struct ads1255_dev_s *priv = (FAR struct ads1255_dev_s *)dev->ad_priv; + + DEBUGASSERT(priv != NULL); + up_disable_irq(priv->irq); irq_detach(priv->irq); } -/* Call to enable or disable RX interrupts */ +/**************************************************************************** + * Name: adc_rxint + * + * Description: + * 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; + FAR struct ads1255_dev_s *priv = (FAR struct ads1255_dev_s *)dev->ad_priv; + + DEBUGASSERT(priv != NULL); + if (enable) { up_enable_irq(priv->irq); @@ -277,21 +335,42 @@ static void adc_rxint(FAR struct adc_dev_s *dev, bool enable) } } -/* All ioctl calls will be routed through this method */ +/**************************************************************************** + * Name: adc_ioctl + * + * Description: + * All ioctl calls will be routed through this method + * + ****************************************************************************/ -static int adc_ioctl(FAR struct adc_dev_s *dev, int cmd, unsigned long arg) +static int adc_ioctl(FAR struct adc_dev_s *dev, int cmd, unsigned long arg) { dbg("Fix me:Not Implemented\n"); return 0; } +/**************************************************************************** + * Name: adc_interrupt + * + * Description: + * ADC interrupt handler + * + ****************************************************************************/ + static int adc_interrupt(int irq, void *context) { - FAR struct up_dev_s *priv = (FAR struct up_dev_s *)g_adcdev.ad_priv; - FAR struct spi_dev_s *spi = priv->spi; + FAR struct ads1255_dev_s *priv = (FAR struct ads1255_dev_s *)g_adcdev.ad_priv; + FAR struct spi_dev_s *spi; unsigned char buf[4]; unsigned char ch; + DEBUGASSERT(priv != NULL && priv->spi != NULL); + spi = priv->spi; + + /* REVISIT: Cannot perform SPI operations from an interrupt handler! + * Need to use the high priority work queue. + */ + SPI_SELECT(spi, priv->devno, true); SPI_SEND(spi, ADS125X_RDATA); up_udelay(10); @@ -316,7 +395,16 @@ static int adc_interrupt(int irq, void *context) SPI_SEND(spi, ADS125X_WAKEUP); SPI_SELECT(spi, priv->devno, false); - adc_receive(&g_adcdev, priv->channel, *(int32_t *)buf); + /* Verify that the upper-half driver has bound its callback functions */ + + if (priv->cb != NULL) + { + /* Perform the data received callback */ + + DEBUGASSERT(priv->cb->au_receive != NULL); + priv->cb->au_receive(&g_adcdev, priv->channel, *(int32_t *)buf); + } + return OK; } @@ -341,10 +429,13 @@ static int adc_interrupt(int irq, void *context) 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; + FAR struct ads1255_dev_s *priv = (FAR struct ads1255_dev_s *)g_adcdev.ad_priv; + + DEBUGASSERT(spi != NULL); /* Driver state data */ + priv->cb = NULL; priv->spi = spi; priv->devno = devno; return &g_adcdev; diff --git a/include/nuttx/analog/adc.h b/include/nuttx/analog/adc.h index 04db97626cf..aae75649f14 100644 --- a/include/nuttx/analog/adc.h +++ b/include/nuttx/analog/adc.h @@ -75,6 +75,27 @@ /************************************************************************************ * Public Types ************************************************************************************/ +/* These are callbacks to notify the upper-half driver of ADC events */ + +struct adc_dev_s; +struct adc_callback_s +{ + /* This method is called from the lower half, platform-specific ADC logic when + * new ADC sample data is available. + * + * Input Parameters: + * 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. + * + * Returned Value: + * Zero on success; a negated errno value on failure. + */ + + CODE int (*au_receive)(FAR struct adc_dev_s *dev, uint8_t ch, int32_t data); +}; + +/* This describes on ADC message */ struct adc_msg_s { @@ -82,6 +103,8 @@ struct adc_msg_s int32_t am_data; /* ADC convert result (4 bytes) */ } packed_struct; +/* This describes a FIFO of ADC messages */ + struct adc_fifo_s { sem_t af_sem; /* Counting semaphore */ @@ -99,6 +122,13 @@ struct adc_fifo_s struct adc_dev_s; struct adc_ops_s { + /* Bind the upper-half driver callbacks to the lower-half implementation. This + * must be called early in order to receive ADC event notifications. + */ + + CODE int (*ao_bind)(FAR struct adc_dev_s *dev, + FAR const struct adc_callback_s *callback); + /* Reset the ADC device. Called early to initialize the hardware. This * is called, before ao_setup() and on error conditions. */ @@ -188,25 +218,6 @@ extern "C" int adc_register(FAR const char *path, FAR struct adc_dev_s *dev); -/************************************************************************************ - * Name: adc_receive - * - * Description: - * This function is called from the lower half, platform-specific ADC logic when - * new ADC sample data is available. - * - * Input Parameters: - * 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 actualy converted data from the channel. - * - * Returned Value: - * Zero on success; a negated errno value on failure. - * - ************************************************************************************/ - -int adc_receive(FAR struct adc_dev_s *dev, uint8_t ch, int32_t data); - /************************************************************************************ * Platform-Independent "Lower Half" ADC Driver Interfaces ************************************************************************************/