diff --git a/arch/arm/src/efm32/efm32_adc.c b/arch/arm/src/efm32/efm32_adc.c index 102384ff3de..032f92a6556 100644 --- a/arch/arm/src/efm32/efm32_adc.c +++ b/arch/arm/src/efm32/efm32_adc.c @@ -2,7 +2,9 @@ * arch/arm/src/efm32/efm32_adc.c * * Copyright (C) 2014 Bouteville Pierre-Noel. All rights reserved. + * Copyright (C) 2016 Gregory Nutt. All rights reserved. * Authors: Bouteville Pierre-Noel + * Gregory Nutt * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -100,6 +102,7 @@ struct efm32_dev_s { + FAR const struct adc_callback_s *cb; uint8_t irq; /* Interrupt generated by this ADC block */ uint8_t nchannels; /* Number of channels */ uint8_t current; /* Current ADC channel being converted */ @@ -124,6 +127,8 @@ static int adc_interrupt(FAR struct adc_dev_s *dev); /* ADC Driver 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); @@ -148,6 +153,7 @@ static void adc_startconv(FAR struct efm32_dev_s *priv, bool enable); static const struct adc_ops_s g_adcops = { + .ao_bind = adc_bind, .ao_reset = adc_reset, .ao_setup = adc_setup, .ao_shutdown = adc_shutdown, @@ -790,7 +796,7 @@ static void adc_startconv(struct efm32_dev_s *priv, bool enable) #endif /**************************************************************************** - * Name: adc_reset + * Name: adc_hw_reset * * Description: * Deinitializes the ADCx peripheral registers to their default @@ -873,6 +879,25 @@ static void adc_enable(FAR struct efm32_dev_s *priv, bool enable) adc_putreg(priv, EFM32_ADC_CR2_OFFSET, regval); } +/**************************************************************************** + * 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 efm32_dev_s *priv = (FAR struct efm32_dev_s *)dev->ad_priv; + + DEBUGASSERT(priv != NULL); + priv->cb = callback; + return OK; +} + /**************************************************************************** * Name: adc_reset * @@ -1180,14 +1205,21 @@ static int adc_interrupt(FAR struct adc_dev_s *dev) value = adc_getreg(priv, EFM32_ADC_DR_OFFSET); value &= ADC_DR_DATA_MASK; - /* Give the ADC data to the ADC driver. adc_receive accepts 3 parameters: - * - * 1) The first is the ADC device instance for this ADC block. - * 2) The second is the channel number for the data, and - * 3) The third is the converted data for the channel. - */ + /* Verify that the upper-half driver has bound its callback functions */ - adc_receive(dev, priv->chanlist[priv->current], value); + if (priv->cb != NULL) + { + /* Give the ADC data to the ADC driver. The ADC receive method + * accepts 3 parameters: + * + * 1) The first is the ADC device instance for this ADC block. + * 2) The second is the channel number for the data, and + * 3) The third is the converted data for the channel. + */ + + DEBUGASSERT(priv->cb->au_receive != NULL); + priv->cb->au_receive(dev, priv->chanlist[priv->current], value); + } /* Set the channel number of the next channel that will complete conversion */ @@ -1272,7 +1304,8 @@ struct adc_dev_s *efm32_adcinitialize(int intf, const uint8_t *chanlist, int nch /* Configure the selected ADC */ - priv = dev->ad_priv; + priv = dev->ad_priv; + priv->cb = NULL; DEBUGASSERT(nchannels <= ADC_MAX_SAMPLES); priv->nchannels = nchannels; diff --git a/arch/arm/src/lpc17xx/lpc17_adc.c b/arch/arm/src/lpc17xx/lpc17_adc.c index 9d761c6afad..490b25bf800 100644 --- a/arch/arm/src/lpc17xx/lpc17_adc.c +++ b/arch/arm/src/lpc17xx/lpc17_adc.c @@ -2,8 +2,9 @@ * arch/arm/src/lpc17xx/lpc17_adc.c * * Copyright (C) 2011 Li Zhuoyi. All rights reserved. + * Copyright (C) 2016 Gregory Nutt. All rights reserved. * Author: Li Zhuoyi - * History: 0.1 2011-08-05 initial version + * Gregory Nutt * * This file is a part of NuttX: * @@ -50,6 +51,7 @@ #include #include #include +#include #include #include @@ -87,6 +89,7 @@ struct up_dev_s { + FAR const struct adc_callback_s *cb; uint8_t mask; uint32_t sps; int irq; @@ -98,8 +101,12 @@ struct up_dev_s * Private Function Prototypes ****************************************************************************/ +static void adc_receive(FAR struct up_dev_s *priv, uint8_t ch, int32_t data); + /* 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); @@ -113,6 +120,7 @@ static int adc_interrupt(int irq, void *context); static const struct adc_ops_s g_adcops = { + .ao_bind = adc_bind, .ao_reset = adc_reset, .ao_setup = adc_setup, .ao_shutdown = adc_shutdown, @@ -137,6 +145,46 @@ static struct adc_dev_s g_adcdev = * Private Functions ****************************************************************************/ +/**************************************************************************** + * Name: adc_receive + * + * Description: + * Provide received ADC dat to the upper-half driver. + * + ****************************************************************************/ + +static void adc_receive(FAR struct up_dev_s *priv, uint8_t ch, int32_t data) +{ + /* 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, ch, data); + } +} + +/**************************************************************************** + * 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 up_dev_s *priv = (FAR struct up_dev_s *)dev->ad_priv; + + DEBUGASSERT(priv != NULL); + priv->cb = callback; + return OK; +} + /**************************************************************************** * Name: adc_reset * @@ -384,7 +432,7 @@ static int adc_interrupt(int irq, void *context) { value = priv->buf[ch] / priv->count[ch]; value <<= 15; - adc_receive(&g_adcdev, ch, value); + adc_receive(priv, ch, value); priv->buf[ch] = 0; priv->count[ch] = 0; } @@ -409,7 +457,7 @@ static int adc_interrupt(int irq, void *context) { value = priv->buf[ch] / priv->count[ch]; value <<= 15; - adc_receive(&g_adcdev, ch, value); + adc_receive(priv, ch, value); priv->buf[ch] = 0; priv->count[ch] = 0; } @@ -446,7 +494,7 @@ static int adc_interrupt(int irq, void *context) #else /* CONFIG_ADC_WORKER_THREAD */ if ((regVal) & (1 << 31)) { - adc_receive(&g_adcdev, 0, (regVal >> 4) & 0xFFF); + adc_receive(priv, 0, (regVal >> 4) & 0xFFF); } #endif /* CONFIG_ADC_WORKER_THREAD */ @@ -473,7 +521,7 @@ static int adc_interrupt(int irq, void *context) #else /* CONFIG_ADC_WORKER_THREAD */ if ((regVal) & (1 << 31)) { - adc_receive(&g_adcdev, 1, (regVal >> 4) & 0xFFF); + adc_receive(priv, 1, (regVal >> 4) & 0xFFF); } #endif /* CONFIG_ADC_WORKER_THREAD */ @@ -500,7 +548,7 @@ static int adc_interrupt(int irq, void *context) #else /* CONFIG_ADC_WORKER_THREAD */ if ((regVal) & (1 << 31)) { - adc_receive(&g_adcdev, 2, (regVal >> 4) & 0xFFF); + adc_receive(priv, 2, (regVal >> 4) & 0xFFF); } #endif /* CONFIG_ADC_WORKER_THREAD */ @@ -512,7 +560,7 @@ static int adc_interrupt(int irq, void *context) regVal = getreg32(LPC17_ADC_DR3); if ((regVal) & (1 << 31)) { - adc_receive(&g_adcdev, 3, (regVal >> 4) & 0xFFF); + adc_receive(priv, 3, (regVal >> 4) & 0xFFF); } } @@ -521,7 +569,7 @@ static int adc_interrupt(int irq, void *context) regVal = getreg32(LPC17_ADC_DR4); if ((regVal) & (1 << 31)) { - adc_receive(&g_adcdev, 4, (regVal >> 4) & 0xFFF); + adc_receive(priv, 4, (regVal >> 4) & 0xFFF); } } @@ -530,7 +578,7 @@ static int adc_interrupt(int irq, void *context) regVal = getreg32(LPC17_ADC_DR5); if ((regVal) & (1 << 31)) { - adc_receive(&g_adcdev, 5, (regVal >> 4) & 0xFFF); + adc_receive(priv, 5, (regVal >> 4) & 0xFFF); } } @@ -539,7 +587,7 @@ static int adc_interrupt(int irq, void *context) regVal = getreg32(LPC17_ADC_DR6); if ((regVal) & (1 << 31)) { - adc_receive(&g_adcdev, 6, (regVal >> 4) & 0xFFF); + adc_receive(priv, 6, (regVal >> 4) & 0xFFF); } } @@ -548,7 +596,7 @@ static int adc_interrupt(int irq, void *context) regVal = getreg32(LPC17_ADC_DR7); if ((regVal) & (1 << 31)) { - adc_receive(&g_adcdev, 7, (regVal >> 4) & 0xFFF); + adc_receive(priv, 7, (regVal >> 4) & 0xFFF); } } diff --git a/arch/arm/src/lpc43xx/lpc43_adc.c b/arch/arm/src/lpc43xx/lpc43_adc.c index 487c0b5cf37..4286451f11e 100644 --- a/arch/arm/src/lpc43xx/lpc43_adc.c +++ b/arch/arm/src/lpc43xx/lpc43_adc.c @@ -7,8 +7,9 @@ * Ported from from the LPC17 version: * * Copyright(C) 2011 Li Zhuoyi. All rights reserved. + * Copyright(C) 2016 Gregory Nutt. All rights reserved. * Author: Li Zhuoyi - * History: 0.1 2011-08-05 initial version + * Gregory Nutt * * This file is a part of NuttX: * @@ -55,6 +56,7 @@ #include #include #include +#include #include #include @@ -112,6 +114,7 @@ struct up_dev_s { + FAR const struct adc_callback_s *cb; uint8_t mask; uint8_t mask_int; uint32_t freq; @@ -126,6 +129,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); @@ -139,6 +144,7 @@ static int adc_interrupt(int irq, void *context); static const struct adc_ops_s g_adcops = { + .ao_bind = adc_bind, .ao_reset = adc_reset, .ao_setup = adc_setup, .ao_shutdown = adc_shutdown, @@ -166,6 +172,25 @@ static struct adc_dev_s g_adcdev = * 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 up_dev_s *priv = (FAR struct up_dev_s *)dev->ad_priv; + + DEBUGASSERT(priv != NULL); + priv->cb = callback; + return OK; +} + /**************************************************************************** * Name: adc_reset * @@ -454,8 +479,20 @@ static int adc_interrupt(int irq, void *context) { if (priv->mask & (1 << i)) { + int32_t data; + regval = getreg32(LPC43_ADC0_DR(i)); - adc_receive(&g_adcdev, i,(regval&ADC_DR_VVREF_MASK)>>ADC_DR_VVREF_SHIFT); + data = (regval & ADC_DR_VVREF_MASK) >> ADC_DR_VVREF_SHIFT; + + /* 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, i, data); + } } } diff --git a/arch/arm/src/sama5/sam_adc.c b/arch/arm/src/sama5/sam_adc.c index 261434229e6..68565bc502f 100644 --- a/arch/arm/src/sama5/sam_adc.c +++ b/arch/arm/src/sama5/sam_adc.c @@ -57,6 +57,7 @@ #include #include #include +#include #include #include @@ -378,6 +379,7 @@ struct sam_adc_s { + FAR const struct adc_callback_s *cb; sem_t exclsem; /* Supports exclusive access to the ADC interface */ bool initialized; /* The ADC driver is already initialized */ uint32_t frequency; /* ADC clock frequency */ @@ -446,6 +448,8 @@ static int sam_adc_interrupt(int irq, void *context); /* ADC methods */ #ifdef SAMA5_ADC_HAVE_CHANNELS +static int sam_adc_bind(FAR struct adc_dev_s *dev, + FAR const struct adc_callback_s *callback); static void sam_adc_reset(struct adc_dev_s *dev); static int sam_adc_setup(struct adc_dev_s *dev); static void sam_adc_shutdown(struct adc_dev_s *dev); @@ -477,6 +481,7 @@ static void sam_adc_channels(struct sam_adc_s *priv); static const struct adc_ops_s g_adcops = { + .ao_bind = sam_adc_bind, .ao_reset = sam_adc_reset, .ao_setup = sam_adc_setup, .ao_shutdown = sam_adc_shutdown, @@ -664,9 +669,15 @@ static void sam_adc_dmadone(void *arg) chan = (int)((*buffer & ADC_LCDR_CHANB_MASK) >> ADC_LCDR_CHANB_SHIFT); sample = ((*buffer & ADC_LCDR_DATA_MASK) >> ADC_LCDR_DATA_SHIFT); - /* And give the sample data to the ADC upper half */ + /* Verify that the upper-half driver has bound its callback functions */ - (void)adc_receive(priv->dev, chan, sample); + if (priv->cb != NULL) + { + /* Give the sample data to the ADC upper half */ + + DEBUGASSERT(priv->cb->au_receive != NULL); + priv->cb->au_receive(priv->dev, chan, sample); + } } } @@ -858,7 +869,17 @@ static void sam_adc_endconversion(void *arg) /* Read the ADC sample and pass it to the upper half */ regval = sam_adc_getreg(priv, SAM_ADC_CDR(chan)); - (void)adc_receive(priv->dev, chan, regval & ADC_CDR_DATA_MASK); + + /* 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(priv->dev, chan, regval & ADC_CDR_DATA_MASK); + } + pending &= ~bit; } } @@ -954,6 +975,26 @@ static int sam_adc_interrupt(int irq, void *context) /**************************************************************************** * ADC methods ****************************************************************************/ + +/**************************************************************************** + * Name: sam_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 sam_adc_bind(FAR struct adc_dev_s *dev, + FAR const struct adc_callback_s *callback) +{ + struct sam_adc_s *priv = (struct sam_adc_s *)dev->ad_priv; + + DEBUGASSERT(priv != NULL); + priv->cb = callback; + return OK; +} + /**************************************************************************** * Name: sam_adc_reset * @@ -1980,6 +2021,7 @@ struct adc_dev_s *sam_adc_initialize(void) /* Initialize the private ADC device data structure */ sem_init(&priv->exclsem, 0, 1); + priv->cb = NULL; priv->dev = &g_adcdev; #ifdef CONFIG_SAMA5_ADC_DMA diff --git a/arch/arm/src/stm32/stm32_adc.c b/arch/arm/src/stm32/stm32_adc.c index a2d97cf51cd..81205714aa9 100644 --- a/arch/arm/src/stm32/stm32_adc.c +++ b/arch/arm/src/stm32/stm32_adc.c @@ -272,6 +272,7 @@ struct stm32_dev_s { + FAR const struct adc_callback_s *cb; uint8_t irq; /* Interrupt generated by this ADC block */ uint8_t nchannels; /* Number of channels */ uint8_t cchannels; /* Number of configured channels */ @@ -364,6 +365,8 @@ static int adc123_interrupt(int irq, FAR void *context); /* ADC Driver 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); @@ -412,6 +415,7 @@ static void adc_startconv(FAR struct stm32_dev_s *priv, bool enable); static const struct adc_ops_s g_adcops = { + .ao_bind = adc_bind, #if defined(CONFIG_STM32_STM32L15XX) && \ (STM32_CFGR_PLLSRC != 0 || STM32_SYSCLK_SW != RCC_CFGR_SW_HSI) .ao_reset = adc_reset_hsi_disable, @@ -1660,6 +1664,25 @@ static void adc_dmaconvcallback(DMA_HANDLE handle, uint8_t isr, FAR void *arg) } #endif +/**************************************************************************** + * 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 stm32_dev_s *priv = (FAR struct stm32_dev_s *)dev->ad_priv; + + DEBUGASSERT(priv != NULL); + priv->cb = callback; + return OK; +} + /**************************************************************************** * Name: adc_reset * @@ -2702,15 +2725,21 @@ static int adc_interrupt(FAR struct adc_dev_s *dev) data = adc_getreg(priv, STM32_ADC_DR_OFFSET) & ADC_DR_RDATA_MASK; - /* Give the ADC data to the ADC driver. adc_receive() accepts 3 - * parameters: - * - * 1) The first is the ADC device instance for this ADC block. - * 2) The second is the channel number for the data, and - * 3) The third is the converted data for the channel. - */ + /* Verify that the upper-half driver has bound its callback functions */ - adc_receive(dev, priv->chanlist[priv->current], data); + if (priv->cb != NULL) + { + /* Give the ADC data to the ADC driver. The ADC receive() method + * accepts 3 parameters: + * + * 1) The first is the ADC device instance for this ADC block. + * 2) The second is the channel number for the data, and + * 3) The third is the converted data for the channel. + */ + + DEBUGASSERT(priv->cb->au_receive != NULL); + priv->cb->au_receive(dev, priv->chanlist[priv->current], data); + } /* Set the channel number of the next channel that will complete * conversion. @@ -2995,6 +3024,7 @@ struct adc_dev_s *stm32_adcinitialize(int intf, FAR const uint8_t *chanlist, DEBUGASSERT(cchannels <= ADC_MAX_SAMPLES); + priv->cb = NULL; priv->cchannels = cchannels; memcpy(priv->chanlist, chanlist, cchannels); diff --git a/arch/arm/src/tiva/tiva_adc.h b/arch/arm/src/tiva/tiva_adc.h index c6fc9ac2e07..b5ba3045183 100644 --- a/arch/arm/src/tiva/tiva_adc.h +++ b/arch/arm/src/tiva/tiva_adc.h @@ -125,32 +125,32 @@ struct tiva_adc_step_cfg_s { - uint8_t adc; /* Parent peripheral */ - uint8_t sse; /* Parent sample sequencer (SSE) */ - uint8_t step; /* Which step in the sequencer */ - uint8_t shold; /* Sample and hold time */ - uint8_t flags; /* Last step? Interrupt enabled? - * Internal temperature sensor? */ - uint8_t ain; /* Which analog input */ + uint8_t adc; /* Parent peripheral */ + uint8_t sse; /* Parent sample sequencer (SSE) */ + uint8_t step; /* Which step in the sequencer */ + uint8_t shold; /* Sample and hold time */ + uint8_t flags; /* Last step? Interrupt enabled? + * Internal temperature sensor? */ + uint8_t ain; /* Which analog input */ }; /* Sample Sequencer configuration options */ struct tiva_adc_sse_cfg_s { - uint8_t priority; /* Conversion priority, 0-3 no duplicates */ - uint8_t trigger; /* Trigger source */ + uint8_t priority; /* Conversion priority, 0-3 no duplicates */ + uint8_t trigger; /* Trigger source */ }; /* ADC peripheral configuration options */ struct tiva_adc_cfg_s { - uint8_t adc; /* ADC peripheral number */ - bool sse[4]; /* active SSEs in a bitmask */ - struct tiva_adc_sse_cfg_s ssecfg[4]; /* SSE configuration */ - uint8_t steps; /* Size of the stepcfg array */ - struct tiva_adc_step_cfg_s *stepcfg; /* Step configuration array */ + uint8_t adc; /* ADC peripheral number */ + bool sse[4]; /* active SSEs in a bitmask */ + struct tiva_adc_sse_cfg_s ssecfg[4]; /* SSE configuration */ + uint8_t steps; /* Size of the stepcfg array */ + struct tiva_adc_step_cfg_s *stepcfg; /* Step configuration array */ }; /**************************************************************************** diff --git a/arch/arm/src/tiva/tiva_adclow.c b/arch/arm/src/tiva/tiva_adclow.c index 7b2f8e2d9d0..e8a9d53d5c5 100644 --- a/arch/arm/src/tiva/tiva_adclow.c +++ b/arch/arm/src/tiva/tiva_adclow.c @@ -1,8 +1,10 @@ /**************************************************************************** * arch/arm/src/tiva/tiva_adclow.c * + * Copyright (C) 2016 Gregory Nutt. All rights reserved. * Copyright (C) 2015 TRD2 Inc. All rights reserved. * Author: Calvin Maguranis + * Gregory Nutt * * References: * @@ -67,8 +69,8 @@ #include #include #include -#include #include +#include #include @@ -135,6 +137,8 @@ /* Upper level ADC driver ***************************************************/ +static int tiva_adc_bind(FAR struct adc_dev_s *dev, + FAR const struct adc_callback_s *callback); static void tiva_adc_reset(struct adc_dev_s *dev); static int tiva_adc_setup(struct adc_dev_s *dev); static void tiva_adc_shutdown(struct adc_dev_s *dev); @@ -149,6 +153,7 @@ static int tiva_adc_ioctl(struct adc_dev_s *dev, int cmd, unsigned long arg); static const struct adc_ops_s g_adcops = { + .ao_bind = tiva_adc_bind, .ao_reset = tiva_adc_reset, .ao_setup = tiva_adc_setup, .ao_shutdown = tiva_adc_shutdown, @@ -163,6 +168,7 @@ static const struct adc_ops_s g_adcops = struct tiva_adc_s { struct adc_dev_s *dev; + const struct adc_callback_s *cb; bool cfg; /* Configuration state */ bool ena; /* Operation state */ uint8_t devno; /* ADC device number */ @@ -373,6 +379,25 @@ static void tiva_adc_irqinitialize(struct tiva_adc_cfg_s *cfg) #endif } +/**************************************************************************** + * Name: tiva_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 tiva_adc_bind(FAR struct adc_dev_s *dev, + FAR const struct adc_callback_s *callback) +{ + struct tiva_adc_s *priv = (struct tiva_adc_s *)dev->ad_priv; + + DEBUGASSERT(priv != NULL); + priv->cb = callback; + return OK; +} + /**************************************************************************** * Name: tiva_adc_reset * @@ -559,11 +584,20 @@ static int tiva_adc_ioctl(struct adc_dev_s *dev, int cmd, unsigned long arg) fifo_count = tiva_adc_sse_data(priv->devno, sse, buf); - for (i = 0; i < fifo_count; ++i) + /* Verify that the upper-half driver has bound its callback functions */ + + if (priv->cb != NULL) { - (void)adc_receive(dev, - tiva_adc_get_ain(priv->devno, sse, i), - buf[i]); + DEBUGASSERT(priv->cb->au_receive != NULL); + + for (i = 0; i < fifo_count; ++i) + { + /* Perform the data received callback */ + + priv->cb->au_receive(dev, + tiva_adc_get_ain(priv->devno, sse, i), + buf[i]); + } } /* Release our lock on the ADC structure */ @@ -651,18 +685,28 @@ static void tiva_adc_read(void *arg) /* This is a serious error: indicates invalid pointer indirection * and should cause a full system stop. */ + alldbg("PANIC!!! Invalid ADC device number given %d\n", sse->adc); PANIC(); return; } - for (i = 0; i < fifo_count; ++i) + /* Verify that the upper-half driver has bound its callback functions */ + + if (priv->cb != NULL) { - (void)adc_receive(dev, - tiva_adc_get_ain(sse->adc, sse->num, i), - buf[i]); - avdbg("AIN%d=0x%04x\n", - tiva_adc_get_ain(sse->adc, sse->num, i), buf[i]); + DEBUGASSERT(priv->cb->au_receive != NULL); + + for (i = 0; i < fifo_count; ++i) + { + /* Perform the data received callback */ + + priv->cb->au_receive(dev, + tiva_adc_get_ain(sse->adc, sse->num, i), + buf[i]); + avdbg("AIN%d = 0x%04x\n", + tiva_adc_get_ain(sse->adc, sse->num, i), buf[i]); + } } /* Exit, re-enabling ADC interrupts */ @@ -858,6 +902,7 @@ int tiva_adc_initialize(const char *devpath, struct tiva_adc_cfg_s *cfg, /* Now we are initialized */ adc->ena = true; + adc->cb = NULL; #ifdef CONFIG_DEBUG_ANALOG tiva_adc_runtimeobj_vals(); 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..8fbe3921ab0 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); @@ -136,14 +140,15 @@ static int adc_interrupt(int irq, void *context); static const struct adc_ops_s g_adcops = { - .ao_reset = adc_reset, /* ao_reset */ - .ao_setup = adc_setup, /* ao_setup */ + .ao_bind = adc_bind, /* ao_bind */ + .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 */ + .ao_rxint = adc_rxint, /* ao_rxint */ + .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 +195,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 +246,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 +293,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 +336,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 +396,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 +430,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 ************************************************************************************/