You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by ac...@apache.org on 2022/07/07 15:45:33 UTC
[incubator-nuttx] branch master updated: Added ADC to RP2040
This is an automated email from the ASF dual-hosted git repository.
acassis pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git
The following commit(s) were added to refs/heads/master by this push:
new aa6ec6518c Added ADC to RP2040
aa6ec6518c is described below
commit aa6ec6518c218c71f9e205f415b76317dbc75ad3
Author: curuvar <58...@users.noreply.github.com>
AuthorDate: Thu Jul 7 07:10:58 2022 -0400
Added ADC to RP2040
---
.gitignore | 4 +-
arch/arm/src/rp2040/Make.defs | 4 +
arch/arm/src/rp2040/hardware/rp2040_adc.h | 120 ++++
arch/arm/src/rp2040/rp2040_adc.c | 652 +++++++++++++++++++++
.../src/rp2040/{rp2040_ws2812.h => rp2040_adc.h} | 81 +--
arch/arm/src/rp2040/rp2040_ws2812.h | 2 +-
boards/Kconfig | 3 +
.../arm/rp2040/adafruit-feather-rp2040/README.txt | 1 +
.../adafruit-feather-rp2040/src/rp2040_bringup.c | 45 ++
boards/arm/rp2040/adafruit-kb2040/README.txt | 1 +
.../rp2040/adafruit-kb2040/src/rp2040_bringup.c | 45 ++
boards/arm/rp2040/common/Kconfig | 54 ++
boards/arm/rp2040/pimoroni-tiny2040/README.txt | 1 +
.../rp2040/pimoroni-tiny2040/src/rp2040_bringup.c | 47 ++
boards/arm/rp2040/raspberrypi-pico/README.txt | 1 +
.../rp2040/raspberrypi-pico/src/rp2040_bringup.c | 47 ++
drivers/analog/adc.c | 8 +-
17 files changed, 1076 insertions(+), 40 deletions(-)
diff --git a/.gitignore b/.gitignore
index 658e3c09b5..f08b12a7f3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -26,10 +26,12 @@
*.sym
*.wsp
*.ddc
+*.dds
*~
.depend
/.config
-/.config.old
+/.config.*
+/.config-*
/.config\ *
/.cproject
/.gdbinit
diff --git a/arch/arm/src/rp2040/Make.defs b/arch/arm/src/rp2040/Make.defs
index 7bd096d8c6..6f7e129df4 100644
--- a/arch/arm/src/rp2040/Make.defs
+++ b/arch/arm/src/rp2040/Make.defs
@@ -70,6 +70,10 @@ ifeq ($(CONFIG_WS2812),y)
CHIP_CSRCS += rp2040_ws2812.c
endif
+ifeq ($(CONFIG_ADC),y)
+CHIP_CSRCS += rp2040_adc.c
+endif
+
ifeq ($(CONFIG_RP2040_FLASH_BOOT),y)
ifneq ($(PICO_SDK_PATH),)
include chip/boot2/Make.defs
diff --git a/arch/arm/src/rp2040/hardware/rp2040_adc.h b/arch/arm/src/rp2040/hardware/rp2040_adc.h
new file mode 100644
index 0000000000..96225128b2
--- /dev/null
+++ b/arch/arm/src/rp2040/hardware/rp2040_adc.h
@@ -0,0 +1,120 @@
+/****************************************************************************
+ * arch/arm/src/rp2040/hardware/rp2040_adc.h
+ *
+ * Generated from rp2040.svd originally provided by
+ * Raspberry Pi (Trading) Ltd.
+ *
+ * Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd.
+ *
+ * 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 of the copyright holder 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 HOLDER 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.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_RP2040_HARDWARE_RP2040_ADC_H
+#define __ARCH_ARM_SRC_RP2040_HARDWARE_RP2040_ADC_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include "hardware/rp2040_memorymap.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Register offsets *********************************************************/
+
+#define RP2040_ADC_CS_OFFSET 0x000000 /* ADC Control and Status register */
+#define RP2040_ADC_RESULT_OFFSET 0x000004 /* ADC Results register */
+#define RP2040_ADC_FCS_OFFSET 0x000008 /* ADC FIFO Control and Status register */
+#define RP2040_ADC_FIFO_OFFSET 0x00000c /* ADC Result FIFO */
+#define RP2040_ADC_DIV_OFFSET 0x000010 /* ADC Clock Divider register */
+#define RP2040_ADC_INTR_OFFSET 0x000014 /* ADC Raw Interrupts register */
+#define RP2040_ADC_INTE_OFFSET 0x000018 /* ADC Interrupt Enable register */
+#define RP2040_ADC_INTF_OFFSET 0x00001c /* ADC Interrupt Force register */
+#define RP2040_ADC_INTS_OFFSET 0x000020 /* ADC Interrupt Status register */
+
+/* Register definitions *****************************************************/
+
+#define RP2040_ADC_CS (RP2040_ADC_BASE + RP2040_ADC_CS_OFFSET)
+#define RP2040_ADC_RESULT (RP2040_ADC_BASE + RP2040_ADC_RESULT_OFFSET)
+#define RP2040_ADC_FCS (RP2040_ADC_BASE + RP2040_ADC_FCS_OFFSET)
+#define RP2040_ADC_FIFO (RP2040_ADC_BASE + RP2040_ADC_FIFO_OFFSET)
+#define RP2040_ADC_DIV (RP2040_ADC_BASE + RP2040_ADC_DIV_OFFSET)
+#define RP2040_ADC_INTR (RP2040_ADC_BASE + RP2040_ADC_INTR_OFFSET)
+#define RP2040_ADC_INTE (RP2040_ADC_BASE + RP2040_ADC_INTE_OFFSET)
+#define RP2040_ADC_INTF (RP2040_ADC_BASE + RP2040_ADC_INTF_OFFSET)
+#define RP2040_ADC_INTS (RP2040_ADC_BASE + RP2040_ADC_INTS_OFFSET)
+
+/* Register bit definitions *************************************************/
+
+#define RP2040_ADC_CS_RROBIN_SHIFT (16)
+#define RP2040_ADC_CS_RROBIN_MASK (0x001fl << RPC2040_ADC_CS_RROBIN_SHIFT)
+#define RP2040_ADC_CS_AINSEL_SHIFT (12)
+#define RP2040_ADC_CS_AINSEL_MASK (0x0007l << RPC2040_ADC_CS_AINSEL_SHIFT)
+#define RP2040_ADC_CS_ERR_STICKY (1 << 10)
+#define RP2040_ADC_CS_ERR (1 << 9)
+#define RP2040_ADC_CS_READY (1 << 8)
+#define RP2040_ADC_CS_START_MANY (1 << 3)
+#define RP2040_ADC_CS_START_ONCE (1 << 2)
+#define RP2040_ADC_CS_TS_ENA (1 << 1)
+#define RP2040_ADC_CS_EN (1 << 0)
+
+#define RP2040_ADC_RESULT_VAL_MASK (0x00000fffl)
+
+#define RP2040_ADC_FCS_THRESH_SHIFT (24)
+#define RP2040_ADC_FCS_THRESH_MASK (0x000fl << RP2040_ADC_FCS_THRESH_SHIFT)
+#define RP2040_ADC_FCS_LEVEL_SHIFT (16)
+#define RP2040_ADC_FCS_LEVEL_MASK (0x000fl << RP2040_ADC_FCS_LEVEL_SHIFT)
+#define RP2040_ADC_FCS_OVER (1 << 11)
+#define RP2040_ADC_FCS_UNDER (1 << 10)
+#define RP2040_ADC_FCS_FULL (1 << 9)
+#define RP2040_ADC_FCS_EMPTY (1 << 8)
+#define RP2040_ADC_FCS_DREQ_EN (1 << 3)
+#define RP2040_ADC_FCS_ERR (1 << 2)
+#define RP2040_ADC_FCS_SHIFT (1 << 1)
+#define RP2040_ADC_FCS_EN (1 << 0)
+
+#define RP2040_ADC_FIFO_ERR (1 << 15)
+#define RP2040_ADC_FIFO_VAL_MASK (0x0fffl)
+
+#define RP2040_ADC_DIV_INT_SHIFT (8)
+#define RP2040_ADC_DIV_INT_MASK (0x0fffl << RP2040_ADC_DIV_INT_SHIFT)
+#define RP2040_ADC_DIV_FRAC_SHIFT (0)
+#define RP2040_ADC_DIV_FRAC_MASK (0x00ffl << RP2040_ADC_DIV_FRAC_MASK)
+
+#define RP2040_ADC_INTR_FIFO (1 << 0) /* Raw interrupt status */
+
+#define RP2040_ADC_INTE_FIFO (1 << 0) /* Set to pass interrupts */
+
+#define RP2040_ADC_INTF_FIFO (1 << 0) /* Write 1 to force the interrupt. */
+
+#define RP2040_ADC_INTS_FIFO (1 << 0) /* Masked interrupt status */
+
+#endif /* __ARCH_ARM_SRC_RP2040_HARDWARE_RP2040_ADC_H */
diff --git a/arch/arm/src/rp2040/rp2040_adc.c b/arch/arm/src/rp2040/rp2040_adc.c
new file mode 100644
index 0000000000..cdbb54599a
--- /dev/null
+++ b/arch/arm/src/rp2040/rp2040_adc.c
@@ -0,0 +1,652 @@
+/****************************************************************************
+ * arch/arm/src/rp2040/rp2040_adc.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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Notes:
+ * The ADC driver upper-half returns signed values of up to 32-bits to
+ * the user and expects the high-order bits in any result to be
+ * significant. So, while the RP2040 hardware returns an unsigned value
+ * in the low-order 12 bits of the result register, we shift this
+ * value to the high-order bits.
+ *
+ * The result is that to convert a 32-bit value returned from the ADC
+ * driver, you should use V = ADC_AVDD * value / (2^31) where ADC_AVDD
+ * is the analogue reference voltage supplied to the RP2040 chip. If
+ * 8 or 16 bit values are returned the divisor would be (2^15) or (2^7)
+ * respectively.
+ *
+ * Also, if the conversion error bit was set for a particular sample,
+ * the return value will be negated. Any negative return value should
+ * be treated as erroneous.
+ *
+ * -------------
+ *
+ * This lower-half supports multiple drivers (/dev/adc0, /dav/dca1, etc.)
+ * that each may read data from any of the ADC ports. The driver reads
+ * whichever ADC ports are needed by ANY of ther drivers in strict
+ * round-robin fashion, passing the converted values to the drivers that
+ * needed it. Data is only passed if the driver is open.
+ *
+ * --------------
+ *
+ * This code reads the ADC ports at full speed. At the time this comment
+ * was written, the upper-half will throw away any converted values it
+ * receives when the buffer is full; therefor, if the data is not read
+ * for a while, the returned values may be stale when finally read. You
+ * can use the ANIOC_RESET_FIFO ioctl call to flush this stale data.
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <rp2040_adc.h>
+
+#include <stdlib.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "arm_internal.h"
+
+#include <rp2040_gpio.h>
+
+#include <hardware/rp2040_adc.h>
+
+#include <nuttx/analog/adc.h>
+#include <nuttx/kmalloc.h>
+
+#ifdef CONFIG_RP2040_ADC
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define ADC_CHANNEL_COUNT 5
+#define ADC_TEMP_CHANNEL 4
+
+/* Get the private data pointer from a device pointer */
+
+#define PRIV(x) ((FAR struct private_s *)(x)->ad_priv)
+
+/****************************************************************************
+ * Private Type Definitions
+ ****************************************************************************/
+
+struct private_s
+{
+ FAR const struct adc_callback_s *callback; /* ADC Callback Structure */
+ FAR FAR struct adc_dev_s *next_device;
+ FAR FAR struct adc_dev_s *prior_device;
+ bool has_channel[ADC_CHANNEL_COUNT];
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static int interrupt_handler(int irq,
+ FAR void *context,
+ FAR void *arg);
+
+static void get_next_channel(void);
+static void add_device(FAR struct adc_dev_s *dev);
+static void remove_device(FAR struct adc_dev_s *dev);
+
+/* ADC methods */
+
+static int my_bind(FAR struct adc_dev_s *dev,
+ FAR const struct adc_callback_s *callback);
+
+static void my_reset(FAR struct adc_dev_s *dev);
+static int my_setup(FAR struct adc_dev_s *dev);
+static void my_shutdown(FAR struct adc_dev_s *dev);
+static void my_rxint(FAR struct adc_dev_s *dev, bool enable);
+static int my_ioctl(FAR struct adc_dev_s *dev, int cmd, unsigned long arg);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct adc_ops_s g_adcops =
+{
+ .ao_bind = my_bind, /* Called first during initialization. */
+ .ao_reset = my_reset, /* Called second during intialization. */
+ .ao_setup = my_setup, /* Called during first open. */
+ .ao_shutdown = my_shutdown, /* Called during last close. */
+ .ao_rxint = my_rxint, /* Called to enable/disable interrupts. */
+ .ao_ioctl = my_ioctl, /* Called for custom ioctls. */
+};
+
+static const int8_t g_gpio_map[ADC_CHANNEL_COUNT] =
+{
+ 26, 27, 28, 29, -1
+};
+
+static FAR struct adc_dev_s *g_first_device = NULL;
+
+static uint8_t g_current_channel = 0xf0; /* too big */
+static uint8_t g_active_count = 0;
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: interrupt_handler
+ *
+ * Description:
+ * ADC interrupt handler. Note that this one handler is shared between
+ * all ADC devices.
+ *
+ * Note: This is called from inside an interrupt service routine.
+ ****************************************************************************/
+
+static int interrupt_handler(int irq, void *context, void *arg)
+{
+ FAR struct adc_dev_s *a_device;
+ FAR const struct adc_callback_s *a_callback;
+ int32_t value;
+ bool error_bit_set;
+
+ if (g_active_count == 0)
+ {
+ /* Last device has been removed -- turn off ADC */
+
+ putreg32(0, RP2040_ADC_CS);
+
+ putreg32(0, RP2040_ADC_INTE);
+
+ up_disable_irq(RP2040_ADC_IRQ_FIFO);
+
+ irq_detach(RP2040_ADC_IRQ_FIFO);
+
+ /* Flush the FIFO */
+
+ while (getreg32(RP2040_ADC_FCS) & RP2040_ADC_FCS_LEVEL_MASK)
+ {
+ getreg32(RP2040_ADC_FIFO);
+ }
+
+ return OK;
+ }
+
+ /* Fetch the data from the FIFO register */
+
+ value = getreg32(RP2040_ADC_FIFO);
+ error_bit_set = (value & RP2040_ADC_FIFO_ERR) != 0;
+
+ /* Shift value to top of signed 32-bit word for upper-halfs benefit. */
+
+ value <<= 19;
+
+ if (error_bit_set)
+ {
+ value = -value;
+ }
+
+ for (a_device = g_first_device;
+ a_device != NULL;
+ a_device = PRIV(a_device)->next_device)
+ {
+ a_callback = PRIV(a_device)->callback;
+
+ if (a_callback != NULL && a_callback->au_receive != NULL)
+ {
+ if (PRIV(a_device)->has_channel[g_current_channel])
+ {
+ if (a_callback->au_receive(a_device,
+ g_current_channel,
+ value) != OK)
+ {
+ /* ### TODO ### Upper half buffer overflow */
+ }
+ }
+ }
+ }
+
+ /* Start next channel read */
+
+ get_next_channel();
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: get_next_channel
+ *
+ * Description:
+ * Update g_current_channel to point to next channel in use and start
+ * the conversion.
+ *
+ * Note: This is called from inside a critical section.
+ ****************************************************************************/
+
+static void get_next_channel(void)
+{
+ FAR struct adc_dev_s *a_device;
+ uint8_t next = g_current_channel + 1;
+ uint32_t value;
+
+ while (true)
+ {
+ if (next >= ADC_CHANNEL_COUNT)
+ {
+ next = 0;
+ }
+
+ for (a_device = g_first_device;
+ a_device != NULL;
+ a_device = PRIV(a_device)->next_device)
+ {
+ if (PRIV(a_device)->has_channel[next])
+ {
+ g_current_channel = next;
+
+ while (getreg32(RP2040_ADC_FCS)
+ & RP2040_ADC_FCS_LEVEL_MASK)
+ {
+ getreg32(RP2040_ADC_FIFO);
+ }
+
+ /* Enable Interrupt on ADC Completion */
+
+ putreg32(RP2040_ADC_INTE_FIFO, RP2040_ADC_INTE);
+
+ /* Configure CS to read one value from current channel */
+
+ value = (g_current_channel << RP2040_ADC_CS_AINSEL_SHIFT)
+ | RP2040_ADC_CS_EN;
+
+ if (g_current_channel == ADC_TEMP_CHANNEL)
+ {
+ value |= RP2040_ADC_CS_TS_ENA;
+ }
+
+ putreg32(value, RP2040_ADC_CS);
+
+ while ((getreg32(RP2040_ADC_CS)
+ & RP2040_ADC_CS_READY) == 0)
+ {
+ /* Wait for ready to go high. The rp2040 docs
+ * say this is only a few clock cycles so we'll
+ * just busy wait.
+ */
+ }
+
+ /* Start the conversion */
+
+ value += RP2040_ADC_CS_START_ONCE;
+
+ putreg32(value, RP2040_ADC_CS);
+
+ return;
+ }
+ }
+
+ /* if we've looped all the way we're in trouble */
+
+ ASSERT(next != g_current_channel);
+
+ /* try another */
+
+ next += 1;
+ }
+
+ return;
+}
+
+/****************************************************************************
+ * Name: add_device
+ *
+ * Description:
+ * This function is called to link the device int the device list.
+ * It also makes sure ADC reads are taking place
+ *
+ * Note: This is called from inside a critical section.
+ ****************************************************************************/
+
+static void add_device(FAR struct adc_dev_s *dev)
+{
+ uint32_t value;
+
+ g_active_count += 1;
+
+ if (g_first_device != NULL)
+ {
+ PRIV(g_first_device)->prior_device = dev;
+ }
+
+ PRIV(dev)->next_device = g_first_device;
+ PRIV(dev)->prior_device = NULL;
+
+ g_first_device = dev;
+
+ if (PRIV(g_first_device)->next_device == NULL)
+ {
+ /* We just added first device */
+
+ /* Make sure ADC interrupts are disabled */
+
+ putreg32(0, RP2040_ADC_INTE);
+
+ /* Configure FCS to use FIFO and interrupt on first value */
+
+ value = (1 << RP2040_ADC_FCS_THRESH_SHIFT)
+ | RP2040_ADC_FCS_OVER
+ | RP2040_ADC_FCS_UNDER
+ | RP2040_ADC_FCS_ERR
+ | RP2040_ADC_FCS_EN;
+
+ putreg32(value, RP2040_ADC_FCS);
+
+ /* Set up for interrupts */
+
+ irq_attach(RP2040_ADC_IRQ_FIFO, interrupt_handler, NULL);
+
+ up_enable_irq(RP2040_ADC_IRQ_FIFO);
+
+ /* Start conversions on first required channel. */
+
+ get_next_channel();
+
+ ainfo("new cur %d\n", g_current_channel);
+ }
+}
+
+/****************************************************************************
+ * Name: remove_device
+ *
+ * Description:
+ * This function is called to unlink the device from the device list.
+ *
+ * Note: This is called from inside a critical section.
+ ****************************************************************************/
+
+void remove_device(FAR struct adc_dev_s *dev)
+{
+ FAR struct adc_dev_s *a_device;
+
+ if (dev == g_first_device)
+ {
+ /* Special handling for first device */
+
+ g_first_device = PRIV(g_first_device)->next_device;
+
+ if (g_first_device != NULL)
+ {
+ PRIV(g_first_device)->prior_device = NULL;
+ }
+
+ g_active_count -= 1;
+ }
+ else
+ {
+ /* Make sure dev is on the change, and unlink if it is. */
+
+ for (a_device = g_first_device;
+ a_device != NULL;
+ a_device = PRIV(a_device)->next_device)
+ {
+ if (a_device == dev)
+ {
+ PRIV(PRIV(dev)->prior_device)->next_device =
+ PRIV(dev)->next_device;
+
+ PRIV(PRIV(dev)->next_device)->prior_device =
+ PRIV(dev)->prior_device;
+
+ g_active_count -= 1;
+
+ break;
+ }
+ }
+ }
+}
+
+/****************************************************************************
+ * Name: my_bind
+ *
+ * Description:
+ * This function is called when a driver is registered. It give us a
+ * chance to bind the upper-half callbacks to our private data structure so
+ * they can be accessed later.
+ *
+ ****************************************************************************/
+
+static int my_bind(struct adc_dev_s *dev,
+ const struct adc_callback_s *callback)
+{
+ DEBUGASSERT(PRIV(dev) != NULL);
+
+ ainfo("entered\n");
+
+ PRIV(dev)->callback = callback;
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: my_reset
+ *
+ * Description:
+ * This is called by the upper-half as part of the driver registration
+ * process. The upper half documentation also claims that it may
+ * be called as part of an error condition.
+ *
+ * Set the pin for the ADC's to standard GPIO input with no pulls.
+ *
+ ****************************************************************************/
+
+static void my_reset(struct adc_dev_s *dev)
+{
+ int a_gpio;
+
+ ainfo("entered\n");
+
+ for (int i = 0; i < ADC_CHANNEL_COUNT; ++i)
+ {
+ a_gpio = g_gpio_map[i];
+
+ if (a_gpio >= 0)
+ {
+ rp2040_gpio_setdir(a_gpio, false);
+ rp2040_gpio_set_function(a_gpio, RP2040_GPIO_FUNC_NULL);
+ rp2040_gpio_set_pulls(a_gpio, false, false);
+ }
+ }
+}
+
+/****************************************************************************
+ * Name: my_setup
+ *
+ * Description:
+ * This is called when a particular ADC driver is first opened.
+ *
+ * We don't do anything here.
+ *
+ * Note: This is called from inside a critical section.
+ ****************************************************************************/
+
+static int my_setup(struct adc_dev_s *dev)
+{
+ int ret;
+
+ ainfo("entered: 0x%08lX\n", dev);
+
+ /* Note: We check g_active_count here so we can return an error
+ * in the, probably impossible, case we have too many.
+ */
+
+ if (g_active_count >= 200)
+ {
+ aerr("Too many active devices.");
+ ret = -EBUSY;
+ }
+ else
+ {
+ ret = OK;
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: my_shutdown
+ *
+ * Description:
+ * This is called to shutdown an ADC device. It unlinks the
+ * device from out local chain and turns off ADC interrupts if no
+ * more devices are active.
+ *
+ * Note: This is called from inside a critical section.
+ ****************************************************************************/
+
+static void my_shutdown(FAR struct adc_dev_s *dev)
+{
+ ainfo("entered: 0x%08lX\n", dev);
+
+ /* Remove adc_dev_s structure from the list */
+
+ remove_device(dev);
+}
+
+/****************************************************************************
+ * Name: my_rxint
+ *
+ * Description:
+ * Call to enable or disable ADC RX interrupts
+ *
+ * Note: This is called from inside a critical section.
+ ****************************************************************************/
+
+static void my_rxint(struct adc_dev_s *dev, bool enable)
+{
+ if (enable)
+ {
+ ainfo("entered: enable: 0x%08lX\n", dev);
+
+ add_device(dev);
+ }
+ else
+ {
+ ainfo("entered: disable: 0x%08lX\n", dev);
+
+ remove_device(dev);
+ }
+}
+
+/****************************************************************************
+ * Name: my_ioctl
+ *
+ * Description:
+ * All ioctl calls will be routed through this method
+ *
+ ****************************************************************************/
+
+static int my_ioctl(struct adc_dev_s *dev,
+ int cmd,
+ unsigned long arg)
+{
+ /* No ioctl commands supported */
+
+ ainfo("entered\n");
+
+ return -ENOTTY;
+}
+
+/****************************************************************************
+ * Public Function
+ ****************************************************************************/
+
+#ifdef CONFIG_ADC
+
+/****************************************************************************
+ * Name: my_setup
+ *
+ * Description:
+ * Initialize and register the ADC driver.
+ *
+ * Input Parameters:
+ * path - Path to the ws2812 device (e.g. "/dev/adc0")
+ * read_adc0 - This device reads ADC0
+ * read_adc1 - This device reads ADC1
+ * read_adc2 - This device reads ADC3
+ * read_adc3 - This device reads ADC4
+ * read_temp - This device reads the chip temperature.
+ *
+ * Returned Value:
+ * An opaque pointer that can be passed to rp2040_adc_release on
+ * success or NULL (with errno set) on failure
+ ****************************************************************************/
+
+int rp2040_adc_setup(FAR const char *path,
+ bool read_adc0,
+ bool read_adc1,
+ bool read_adc2,
+ bool read_adc3,
+ bool read_temp)
+{
+ FAR struct adc_dev_s *dev;
+ FAR struct private_s *priv;
+ int ret;
+
+ ainfo("entered\n");
+
+ if (!read_adc0 && !read_adc1 && !read_adc2 && !read_adc3 && !read_temp)
+ {
+ aerr("No ADC inputs selected.\n");
+ return -EINVAL;
+ }
+
+ dev = kmm_zalloc(sizeof(struct adc_dev_s));
+
+ if (dev == NULL)
+ {
+ aerr("Failed to allocate adc_dev_s.\n");
+ return -ENOMEM;
+ }
+
+ priv = kmm_zalloc(sizeof(struct private_s));
+
+ if (priv == NULL)
+ {
+ aerr("Failed to allocate private_s.\n");
+ kmm_free(dev);
+ return -ENOMEM;
+ }
+
+ priv->has_channel[0] = read_adc0;
+ priv->has_channel[1] = read_adc1;
+ priv->has_channel[2] = read_adc2;
+ priv->has_channel[3] = read_adc3;
+ priv->has_channel[4] = read_temp;
+
+ dev->ad_ops = &g_adcops;
+ dev->ad_priv = priv;
+
+ ret = adc_register(path, dev);
+
+ return ret;
+}
+
+#endif /* if CONFIG_ADC */
+#endif /* if CONFIG_RP2040_ADC */
\ No newline at end of file
diff --git a/arch/arm/src/rp2040/rp2040_ws2812.h b/arch/arm/src/rp2040/rp2040_adc.h
similarity index 52%
copy from arch/arm/src/rp2040/rp2040_ws2812.h
copy to arch/arm/src/rp2040/rp2040_adc.h
index 533eb0dc18..10aaf82e28 100644
--- a/arch/arm/src/rp2040/rp2040_ws2812.h
+++ b/arch/arm/src/rp2040/rp2040_adc.h
@@ -1,5 +1,5 @@
/****************************************************************************
- * arch/arm/src/rp2040/rp2040_ws2812.h
+ * arch/arm/src/rp2040/rp2040_adc.h
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
@@ -18,8 +18,27 @@
*
****************************************************************************/
-#ifndef __ARCH_ARM_SRC_RP2040_RP2040_WS2812_H
-#define __ARCH_ARM_SRC_RP2040_RP2040_WS2812_H
+/****************************************************************************
+ * Note:
+ * The ADC driver upper-half returns signed values of up to 32-bits to
+ * the user and expects the high-order bits in any result to be
+ * significant. So, while the RP2040 hardware returns an unsigned value
+ * in the low-order 12 bits of the result register, we shift this
+ * value to the high-order bits.
+ *
+ * The result is that to convert a 32-bit value returned from the ADC
+ * driver, you should use V = ADC_AVDD * value / (2^31) where ADC_AVDD
+ * is the analogue reference voltage supplied to the RP2040 chip. If
+ * 8 or 16 bit values are returned the divisor would be (2^15) or (2^7)
+ * respectively.
+ *
+ * Also, if the conversion error bit was set for a particular sample,
+ * the return value will be negated. Any negative return value should
+ * be treated as erroneous.
+ ****************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_RP2040_RP2040_ADC_H
+#define __ARCH_ARM_SRC_RP2040_RP2040_ADC_H
/****************************************************************************
* Included Files
@@ -38,53 +57,46 @@ extern "C"
#define EXTERN extern
#endif
-#ifdef CONFIG_WS2812
+#ifdef CONFIG_RP2040_ADC
/****************************************************************************
* Public Function Prototypes
****************************************************************************/
-/****************************************************************************
- * Name: rp2040_ws2812_setup
- *
- * Description:
- * Initialize and register the ws2812 driver.
- *
- * Input Parameters:
- * Path to the ws2812 device (e.g. "/dev/leds0")
- * Port number for the ws2812 chain
- * The number of pixels in the chain
- * Whether ws2812s have white LEDs
- *
- * Returned Value:
- * An opaque pointer that can be passed to rp2040_ws2812_teardown on
- * success or NULL (with errno set) on failure
- ****************************************************************************/
-
-FAR void * rp2040_ws2812_setup(FAR const char *path,
- int port,
- uint16_t pixel_count,
- bool has_white);
+#ifdef CONFIG_ADC
/****************************************************************************
- * Name: rp2040_ws2812_release
+ * Name: rp2040_adc_setup
*
* Description:
- * This function releases the internal memory structures created when
- * a driver is opened. It will fail with an error -EBUSY the driver
- * is open when it is called.
+ * Initialize and register the ADC driver.
*
* Input Parameters:
- * driver - Opaque pointer returned by rp2040_ws2812_setup.
+ * path - Path to the ws2812 device (e.g. "/dev/adc0")
+ * read_adc0 - This device reads ADC0
+ * read_adc1 - This device reads ADC1
+ * read_adc2 - This device reads ADC3
+ * read_adc3 - This device reads ADC4
+ * read_temp - This device reads the chip temperature.
*
* Returned Value:
* OK on success or an ERROR on failure
- *
****************************************************************************/
-int rp2040_ws2812_release(FAR void * driver);
+int rp2040_adc_setup(FAR const char *path,
+ bool read_adc0,
+ bool read_adc1,
+ bool read_adc2,
+ bool read_adc3,
+ bool read_temp);
-#endif
+#else /* CONFIG_ADC */
+
+/* ### TODO ### Add programmatic access function. */
+
+#endif /* CONFIG_ADC */
+
+#endif /* CONFIG_RP2040_ADC */
#undef EXTERN
#if defined(__cplusplus)
@@ -92,4 +104,5 @@ int rp2040_ws2812_release(FAR void * driver);
#endif
#endif /* __ASSEMBLY__ */
-#endif /* __ARCH_ARM_SRC_RP2040_RP2040_I2C_H */
+
+#endif /* __ARCH_ARM_SRC_RP2040_RP2040_ADC_H */
diff --git a/arch/arm/src/rp2040/rp2040_ws2812.h b/arch/arm/src/rp2040/rp2040_ws2812.h
index 533eb0dc18..3f91cfc9fe 100644
--- a/arch/arm/src/rp2040/rp2040_ws2812.h
+++ b/arch/arm/src/rp2040/rp2040_ws2812.h
@@ -92,4 +92,4 @@ int rp2040_ws2812_release(FAR void * driver);
#endif
#endif /* __ASSEMBLY__ */
-#endif /* __ARCH_ARM_SRC_RP2040_RP2040_I2C_H */
+#endif /* __ARCH_ARM_SRC_RP2040_RP2040_WS2812_H */
diff --git a/boards/Kconfig b/boards/Kconfig
index c71cb1b3a1..0cb6e5fed4 100644
--- a/boards/Kconfig
+++ b/boards/Kconfig
@@ -3510,6 +3510,9 @@ endif
if ARCH_CHIP_STM32
source "boards/arm/stm32/common/Kconfig"
endif
+if ARCH_CHIP_RP2040
+source "boards/arm/rp2040/common/Kconfig"
+endif
endif
config BOARD_CRASHDUMP
diff --git a/boards/arm/rp2040/adafruit-feather-rp2040/README.txt b/boards/arm/rp2040/adafruit-feather-rp2040/README.txt
index 0ff8384310..c289a627f2 100644
--- a/boards/arm/rp2040/adafruit-feather-rp2040/README.txt
+++ b/boards/arm/rp2040/adafruit-feather-rp2040/README.txt
@@ -14,6 +14,7 @@ Currently only the following devices are supported.
- SPI
- DMAC
- PWM
+ - ADC
- USB device
- MSC, CDC/ACM serial and these composite device are supported.
- CDC/ACM serial device can be used for the console.
diff --git a/boards/arm/rp2040/adafruit-feather-rp2040/src/rp2040_bringup.c b/boards/arm/rp2040/adafruit-feather-rp2040/src/rp2040_bringup.c
index f564f478fd..c0637c8131 100644
--- a/boards/arm/rp2040/adafruit-feather-rp2040/src/rp2040_bringup.c
+++ b/boards/arm/rp2040/adafruit-feather-rp2040/src/rp2040_bringup.c
@@ -74,6 +74,10 @@
#define HAS_WHITE false
#endif /* CONFIG_WS2812_HAS_WHITE */
+#if defined(CONFIG_ADC) && defined(CONFIG_RP2040_ADC)
+#include "rp2040_adc.h"
+#endif
+
/****************************************************************************
* Public Functions
****************************************************************************/
@@ -462,5 +466,46 @@ int rp2040_bringup(void)
HAS_WHITE);
#endif
+#if defined(CONFIG_ADC) && defined(CONFIG_RP2040_ADC)
+
+# ifdef CONFIG_RPC2040_ADC_CHANNEL0
+# define ADC_0 true
+# else
+# define ADC_0 false
+# endif
+
+# ifdef CONFIG_RPC2040_ADC_CHANNEL1
+# define ADC_1 true
+# else
+# define ADC_1 false
+# endif
+
+# ifdef CONFIG_RPC2040_ADC_CHANNEL2
+# define ADC_2 true
+# else
+# define ADC_2 false
+# endif
+
+# ifdef CONFIG_RPC2040_ADC_CHANNEL3
+# define ADC_3 true
+# else
+# define ADC_3 false
+# endif
+
+# ifdef CONFIG_RPC2040_ADC_TEMPERATURE
+# define ADC_TEMP true
+# else
+# define ADC_TEMP false
+# endif
+
+ ret = rp2040_adc_setup("/dev/adc0", ADC_0, ADC_1, ADC_2, ADC_3, ADC_TEMP);
+ if (ret != OK)
+ {
+ syslog(LOG_ERR, "Failed to initialize ADC Driver: %d\n", ret);
+ return ret;
+ }
+
+#endif /* defined(CONFIG_ADC) && defined(CONFIG_RP2040_ADC) */
+
return ret;
}
diff --git a/boards/arm/rp2040/adafruit-kb2040/README.txt b/boards/arm/rp2040/adafruit-kb2040/README.txt
index 3bce577003..7451e2cfd3 100644
--- a/boards/arm/rp2040/adafruit-kb2040/README.txt
+++ b/boards/arm/rp2040/adafruit-kb2040/README.txt
@@ -13,6 +13,7 @@ Currently only the following devices are supported.
- SPI
- DMAC
- PWM
+ - ADC
- USB device
- MSC, CDC/ACM serial and these composite device are supported.
- CDC/ACM serial device can be used for the console.
diff --git a/boards/arm/rp2040/adafruit-kb2040/src/rp2040_bringup.c b/boards/arm/rp2040/adafruit-kb2040/src/rp2040_bringup.c
index 2ea4c442ff..c1a071ccfe 100644
--- a/boards/arm/rp2040/adafruit-kb2040/src/rp2040_bringup.c
+++ b/boards/arm/rp2040/adafruit-kb2040/src/rp2040_bringup.c
@@ -74,6 +74,10 @@
#define HAS_WHITE false
#endif /* CONFIG_WS2812_HAS_WHITE */
+#if defined(CONFIG_ADC) && defined(CONFIG_RP2040_ADC)
+#include "rp2040_adc.h"
+#endif
+
/****************************************************************************
* Public Functions
****************************************************************************/
@@ -462,5 +466,46 @@ int rp2040_bringup(void)
HAS_WHITE);
#endif
+#if defined(CONFIG_ADC) && defined(CONFIG_RP2040_ADC)
+
+# ifdef CONFIG_RPC2040_ADC_CHANNEL0
+# define ADC_0 true
+# else
+# define ADC_0 false
+# endif
+
+# ifdef CONFIG_RPC2040_ADC_CHANNEL1
+# define ADC_1 true
+# else
+# define ADC_1 false
+# endif
+
+# ifdef CONFIG_RPC2040_ADC_CHANNEL2
+# define ADC_2 true
+# else
+# define ADC_2 false
+# endif
+
+# ifdef CONFIG_RPC2040_ADC_CHANNEL3
+# define ADC_3 true
+# else
+# define ADC_3 false
+# endif
+
+# ifdef CONFIG_RPC2040_ADC_TEMPERATURE
+# define ADC_TEMP true
+# else
+# define ADC_TEMP false
+# endif
+
+ ret = rp2040_adc_setup("/dev/adc0", ADC_0, ADC_1, ADC_2, ADC_3, ADC_TEMP);
+ if (ret != OK)
+ {
+ syslog(LOG_ERR, "Failed to initialize ADC Driver: %d\n", ret);
+ return ret;
+ }
+
+#endif /* defined(CONFIG_ADC) && defined(CONFIG_RP2040_ADC) */
+
return ret;
}
diff --git a/boards/arm/rp2040/common/Kconfig b/boards/arm/rp2040/common/Kconfig
new file mode 100644
index 0000000000..266bebabdd
--- /dev/null
+++ b/boards/arm/rp2040/common/Kconfig
@@ -0,0 +1,54 @@
+#
+# For a description of the syntax of this configuration file,
+# see the file kconfig-language.txt in the NuttX tools repository.
+#
+
+if ARCH_BOARD_RASPBERRYPI_PICO
+
+config RP2040_ADC
+ bool "Enable ADC Support"
+ default n
+ ---help---
+ If y, the RP2040 ADC code will be built. For ADC device driver
+ support you must also select "Analog-to-Digital Conversion"
+ under the "Device Driver/Analog Device Support" menu.
+
+ If the ADC device driver is not built, basic functions
+ to programatically access the ADC ports will be added.
+
+if RP2040_ADC && ADC
+
+ config RPC2040_ADC_CHANNEL0
+ bool "Read ADC channel 0"
+ default n
+ ---help---
+ If y, then ADC0 will be read.
+
+ config RPC2040_ADC_CHANNEL1
+ bool "Read ADC channel 1"
+ default n
+ ---help---
+ If y, then ADC1 will be read.
+
+ config RPC2040_ADC_CHANNEL2
+ bool "Read ADC channel 2"
+ default n
+ ---help---
+ If y, then ADC2 will be read.
+
+ config RPC2040_ADC_CHANNEL3
+ bool "Read ADC channel 3"
+ default n
+ ---help---
+ If y, then ADC3 will be read.
+
+ config RPC2040_ADC_TEMPERATURE
+ bool "Read ADC chip temperature channel"
+ default n
+ ---help---
+ If y, then the ADC chip temperature
+ will be read.
+
+ endif
+
+endif
diff --git a/boards/arm/rp2040/pimoroni-tiny2040/README.txt b/boards/arm/rp2040/pimoroni-tiny2040/README.txt
index c742f8c037..12af598c3d 100644
--- a/boards/arm/rp2040/pimoroni-tiny2040/README.txt
+++ b/boards/arm/rp2040/pimoroni-tiny2040/README.txt
@@ -17,6 +17,7 @@ Currently only the following devices are supported.
- SPI
- DMAC
- PWM
+ - ADC
- USB device
- MSC, CDC/ACM serial and these composite device are supported.
- CDC/ACM serial device can be used for the console.
diff --git a/boards/arm/rp2040/pimoroni-tiny2040/src/rp2040_bringup.c b/boards/arm/rp2040/pimoroni-tiny2040/src/rp2040_bringup.c
index cfeb9cc61e..8082bbf121 100644
--- a/boards/arm/rp2040/pimoroni-tiny2040/src/rp2040_bringup.c
+++ b/boards/arm/rp2040/pimoroni-tiny2040/src/rp2040_bringup.c
@@ -38,6 +38,10 @@
#include "rp2040_pwmdev.h"
#endif
+#if defined(CONFIG_ADC) && defined(CONFIG_RP2040_ADC)
+#include "rp2040_adc.h"
+#endif
+
#if defined(CONFIG_RP2040_BOARD_HAS_WS2812) && defined(CONFIG_WS2812)
#include "rp2040_ws2812.h"
#endif
@@ -408,6 +412,49 @@ int rp2040_bringup(void)
# endif
#endif
+ /* Initialize ADC */
+
+#if defined(CONFIG_ADC) && defined(CONFIG_RP2040_ADC)
+
+# ifdef CONFIG_RPC2040_ADC_CHANNEL0
+# define ADC_0 true
+# else
+# define ADC_0 false
+# endif
+
+# ifdef CONFIG_RPC2040_ADC_CHANNEL1
+# define ADC_1 true
+# else
+# define ADC_1 false
+# endif
+
+# ifdef CONFIG_RPC2040_ADC_CHANNEL2
+# define ADC_2 true
+# else
+# define ADC_2 false
+# endif
+
+# ifdef CONFIG_RPC2040_ADC_CHANNEL3
+# define ADC_3 true
+# else
+# define ADC_3 false
+# endif
+
+# ifdef CONFIG_RPC2040_ADC_TEMPERATURE
+# define ADC_TEMP true
+# else
+# define ADC_TEMP false
+# endif
+
+ ret = rp2040_adc_setup("/dev/adc0", ADC_0, ADC_1, ADC_2, ADC_3, ADC_TEMP);
+ if (ret != OK)
+ {
+ syslog(LOG_ERR, "Failed to initialize ADC Driver: %d\n", ret);
+ return ret;
+ }
+
+#endif /* defined(CONFIG_ADC) && defined(CONFIG_RP2040_ADC) */
+
/* Initialize board neo-pixel */
#if defined(CONFIG_RP2040_BOARD_HAS_WS2812) && defined(CONFIG_WS2812)
diff --git a/boards/arm/rp2040/raspberrypi-pico/README.txt b/boards/arm/rp2040/raspberrypi-pico/README.txt
index ed799eadd3..4bd892f45c 100644
--- a/boards/arm/rp2040/raspberrypi-pico/README.txt
+++ b/boards/arm/rp2040/raspberrypi-pico/README.txt
@@ -14,6 +14,7 @@ Currently only the following devices are supported.
- SPI
- DMAC
- PWM
+ - ADC
- USB device
- MSC, CDC/ACM serial and these composite device are supported.
- CDC/ACM serial device can be used for the console.
diff --git a/boards/arm/rp2040/raspberrypi-pico/src/rp2040_bringup.c b/boards/arm/rp2040/raspberrypi-pico/src/rp2040_bringup.c
index 23face61e8..b5b19e7e1b 100644
--- a/boards/arm/rp2040/raspberrypi-pico/src/rp2040_bringup.c
+++ b/boards/arm/rp2040/raspberrypi-pico/src/rp2040_bringup.c
@@ -64,6 +64,10 @@
#include "rp2040_pwmdev.h"
#endif
+#if defined(CONFIG_ADC) && defined(CONFIG_RP2040_ADC)
+#include "rp2040_adc.h"
+#endif
+
#if defined(CONFIG_RP2040_BOARD_HAS_WS2812) && defined(CONFIG_WS2812)
#include "rp2040_ws2812.h"
#endif
@@ -487,6 +491,49 @@ int rp2040_bringup(void)
}
#endif
+ /* Initialize ADC */
+
+#if defined(CONFIG_ADC) && defined(CONFIG_RP2040_ADC)
+
+# ifdef CONFIG_RPC2040_ADC_CHANNEL0
+# define ADC_0 true
+# else
+# define ADC_0 false
+# endif
+
+# ifdef CONFIG_RPC2040_ADC_CHANNEL1
+# define ADC_1 true
+# else
+# define ADC_1 false
+# endif
+
+# ifdef CONFIG_RPC2040_ADC_CHANNEL2
+# define ADC_2 true
+# else
+# define ADC_2 false
+# endif
+
+# ifdef CONFIG_RPC2040_ADC_CHANNEL3
+# define ADC_3 true
+# else
+# define ADC_3 false
+# endif
+
+# ifdef CONFIG_RPC2040_ADC_TEMPERATURE
+# define ADC_TEMP true
+# else
+# define ADC_TEMP false
+# endif
+
+ ret = rp2040_adc_setup("/dev/adc0", ADC_0, ADC_1, ADC_2, ADC_3, ADC_TEMP);
+ if (ret != OK)
+ {
+ syslog(LOG_ERR, "Failed to initialize ADC Driver: %d\n", ret);
+ return ret;
+ }
+
+#endif /* defined(CONFIG_ADC) && defined(CONFIG_RP2040_ADC) */
+
/* Initialize board neo-pixel */
#if defined(CONFIG_RP2040_BOARD_HAS_WS2812) && defined(CONFIG_WS2812)
diff --git a/drivers/analog/adc.c b/drivers/analog/adc.c
index a27866d1a5..02b2f37d69 100644
--- a/drivers/analog/adc.c
+++ b/drivers/analog/adc.c
@@ -148,14 +148,14 @@ static int adc_open(FAR struct file *filep)
/* Finally, Enable the ADC RX interrupt */
dev->ad_ops->ao_rxint(dev, true);
-
- /* Save the new open count on success */
-
- dev->ad_ocount = tmp;
}
leave_critical_section(flags);
}
+
+ /* Save the new open count on success */
+
+ dev->ad_ocount = tmp;
}
nxsem_post(&dev->ad_closesem);