This is an automated email from the ASF dual-hosted git repository.

jerpelea pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git

commit f1e7b143d9400a32041e2495e4b9cfef69067ab5
Author: raiden00pl <[email protected]>
AuthorDate: Wed May 20 10:07:29 2026 +0200

    !drivers: separate pulse count feature from PWM driver
    
    BREAKING CHANGE: separate pulse count feature from PWM driver.
    
    Coupling PWM driver with pulse count feature was bad decision from the 
beginning,
    these are two different things:
    
    - PWM is a modulation scheme: it continuously represents a value by varying 
duty
    cycle, usually at a fixed frequency.
    - Pulse train generation is a finite waveform transaction: generate N 
edges/pulses
    with selected timing, then complete.
    
    This change introduce a new pulse count driver with new API.
    Now user can generate pulse train by providing:
    
    - high pulse length in ns
    - low pulse length in ns
    - pulse count
    
    All architectures supporting pulse count have been adapted in subsequent 
commits.
    Users must migrate their code to use the new driver with new API.
    
    Signed-off-by: raiden00pl <[email protected]>
---
 .../applications/examples/pulsecount/index.rst     |  21 ++
 Documentation/applications/examples/pwm/index.rst  |   8 +-
 .../components/drivers/character/timers/index.rst  |   1 +
 .../drivers/character/timers/pulsecount.rst        |  97 ++++++
 .../components/drivers/character/timers/pwm.rst    |   8 +-
 drivers/timers/CMakeLists.txt                      |   4 +
 drivers/timers/Kconfig                             |  12 +-
 drivers/timers/Make.defs                           |   6 +
 drivers/timers/pulsecount.c                        | 361 +++++++++++++++++++++
 drivers/timers/pwm.c                               | 163 +---------
 include/nuttx/fs/ioctl.h                           |   6 +
 include/nuttx/timers/pulsecount.h                  | 189 +++++++++++
 include/nuttx/timers/pwm.h                         |  71 +---
 13 files changed, 704 insertions(+), 243 deletions(-)

diff --git a/Documentation/applications/examples/pulsecount/index.rst 
b/Documentation/applications/examples/pulsecount/index.rst
new file mode 100644
index 00000000000..b9b34e18d4a
--- /dev/null
+++ b/Documentation/applications/examples/pulsecount/index.rst
@@ -0,0 +1,21 @@
+====================================
+``pulsecount`` Pulse Count Example
+====================================
+
+This example starts a finite pulse train on a pulsecount device and
+waits for completion when the device is opened in blocking mode.
+
+Required configuration:
+
+- ``CONFIG_PULSECOUNT`` – Enables pulsecount support.
+- ``CONFIG_NSH_BUILTIN_APPS`` – Builds the example as an NSH built-in.
+
+Specific configuration options:
+
+- ``CONFIG_EXAMPLES_PULSECOUNT_DEVPATH`` – The default pulsecount device.
+  Default: ``/dev/pulsecount0``.
+- ``CONFIG_EXAMPLES_PULSECOUNT_HIGH_NS`` – The pulse high time. Default:
+  ``5000000`` ns.
+- ``CONFIG_EXAMPLES_PULSECOUNT_LOW_NS`` – The pulse low time. Default:
+  ``5000000`` ns.
+- ``CONFIG_EXAMPLES_PULSECOUNT_COUNT`` – The finite pulse count.
diff --git a/Documentation/applications/examples/pwm/index.rst 
b/Documentation/applications/examples/pwm/index.rst
index 33519d722f1..90c0002929e 100644
--- a/Documentation/applications/examples/pwm/index.rst
+++ b/Documentation/applications/examples/pwm/index.rst
@@ -10,8 +10,6 @@ This test depends on these specific PWM/NSH configurations 
settings (your
 specific PWM settings might require additional settings).
 
 - ``CONFIG_PWM`` – Enables PWM support.
-- ``CONFIG_PWM_PULSECOUNT`` – Enables PWM pulse count support (if the hardware
-  supports it).
 - ``CONFIG_NSH_BUILTIN_APPS`` – Build the PWM test as an NSH built-in function.
 
 Specific configuration options for this example include:
@@ -22,8 +20,4 @@ Specific configuration options for this example include:
 - ``CONFIG_EXAMPLES_PWM_DUTYPCT`` – The initial PWM duty as a percentage. 
Default:
   ``50%``.
 - ``CONFIG_EXAMPLES_PWM_DURATION`` – The initial PWM pulse train duration in
-  seconds. Used only if the current pulse count is zero (pulse count is only
-  supported if ``CONFIG_PWM_PULSECOUNT`` is defined). Default: ``5`` seconds.
-- ``CONFIG_EXAMPLES_PWM_PULSECOUNT`` – The initial PWM pulse count. This 
option is
-  only available if ``CONFIG_PWM_PULSECOUNT`` is non-zero. Default: ``0`` 
(i.e., use
-  the duration, not the count).
+  seconds. Default: ``5`` seconds.
diff --git a/Documentation/components/drivers/character/timers/index.rst 
b/Documentation/components/drivers/character/timers/index.rst
index b615da0cbef..51ce8415fda 100644
--- a/Documentation/components/drivers/character/timers/index.rst
+++ b/Documentation/components/drivers/character/timers/index.rst
@@ -8,6 +8,7 @@ Timers Drivers
   timer.rst
   oneshot.rst
   pwm.rst
+  pulsecount.rst
   watchdog.rst
   rtc.rst
   capture.rst
diff --git a/Documentation/components/drivers/character/timers/pulsecount.rst 
b/Documentation/components/drivers/character/timers/pulsecount.rst
new file mode 100644
index 00000000000..627be45a165
--- /dev/null
+++ b/Documentation/components/drivers/character/timers/pulsecount.rst
@@ -0,0 +1,97 @@
+===================
+Pulsecount Drivers
+===================
+
+The pulsecount driver generates a finite pulse train and reports completion
+after the requested number of pulses has been produced.  It is intended for
+hardware that can generate pulse output with a fixed repetition count, often
+using PWM/timer peripherals internally.
+
+This interface is separate from the PWM driver.  PWM describes a continuous
+periodic output using frequency and duty cycle.  Pulsecount describes a finite
+waveform using explicit high time, low time, and pulse count.
+
+Driver Model
+============
+
+The NuttX pulsecount driver is split into two parts:
+
+#. An upper-half character driver that provides the common application
+   interface.
+#. A lower-half platform driver that programs the hardware timer/PWM
+   peripheral and calls ``pulsecount_expired()`` when the finite pulse train
+   completes.
+
+Files supporting pulsecount can be found in the following locations:
+
+- ``include/nuttx/timers/pulsecount.h`` - public interface and lower-half
+  callbacks.
+- ``drivers/timers/pulsecount.c`` - generic upper-half driver.
+- ``arch/<architecture>/src/<chip>/*pulsecount*.c`` - platform lower-half
+  drivers.
+
+Application Interface
+=====================
+
+Applications use the pulsecount driver through a character device such as
+``/dev/pulsecount0``.  Include the pulsecount header:
+
+.. code-block:: c
+
+   #include <nuttx/timers/pulsecount.h>
+
+The driver is controlled through ``ioctl`` commands:
+
+- ``PULSECOUNTIOC_SETCHARACTERISTICS``
+- ``PULSECOUNTIOC_GETCHARACTERISTICS``
+- ``PULSECOUNTIOC_START``
+- ``PULSECOUNTIOC_STOP``
+
+Pulse Characteristics
+=====================
+
+``PULSECOUNTIOC_SETCHARACTERISTICS`` takes a pointer to
+``struct pulsecount_info_s``:
+
+.. code-block:: c
+
+   struct pulsecount_info_s
+   {
+     uint32_t high_ns;  /* Pulse high time in nanoseconds */
+     uint32_t low_ns;   /* Pulse low time in nanoseconds */
+     uint32_t count;    /* Number of pulses to generate */
+   };
+
+``high_ns`` and ``low_ns`` must both be non-zero.  Their sum is the pulse
+period.  ``count`` is the number of complete high/low pulses to generate.
+
+This API previously followed the PWM-style ``frequency`` plus ``duty``
+model.  It now uses explicit high/low nanosecond timing because that is more
+user-friendly for finite pulse trains: applications can describe the waveform
+directly and do not need to convert timing requirements into fixed-point duty
+cycle values.
+
+The lower-half driver may quantize the requested timings to the nearest values
+that the hardware timer can represent.  Very long periods or very short
+pulses can be rejected if they are outside the timer's clock and counter
+range.
+
+Starting And Stopping
+=====================
+
+After setting the pulse characteristics, start the pulse train with
+``PULSECOUNTIOC_START``.  By default, this call blocks until the requested
+pulse count completes.  Open the device with ``O_NONBLOCK`` to start the
+pulse train and return immediately.
+
+``PULSECOUNTIOC_STOP`` stops pulse generation before the count completes.
+TODO: support cancelling a blocking ``PULSECOUNTIOC_START``.
+
+Example
+=======
+
+The ``apps/examples/pulsecount`` example starts a finite pulse train from
+NSH.  It can be built with ``CONFIG_EXAMPLES_PULSECOUNT`` and configured with
+``CONFIG_EXAMPLES_PULSECOUNT_HIGH_NS``,
+``CONFIG_EXAMPLES_PULSECOUNT_LOW_NS``, and
+``CONFIG_EXAMPLES_PULSECOUNT_COUNT``.
diff --git a/Documentation/components/drivers/character/timers/pwm.rst 
b/Documentation/components/drivers/character/timers/pwm.rst
index 61bb6dca340..59169ba92e4 100644
--- a/Documentation/components/drivers/character/timers/pwm.rst
+++ b/Documentation/components/drivers/character/timers/pwm.rst
@@ -5,8 +5,8 @@ PWM Drivers
 For the purposes of this driver, a PWM device is any device that
 generates periodic output pulses of controlled frequency and pulse
 width. Such a device might be used, for example, to perform
-pulse-width modulated output or frequency/pulse-count modulated
-output (such as might be needed to control a stepper motor).
+pulse-width modulated output. Finite pulse-count output is provided
+through the separate pulsecount driver interface.
 
 The NuttX PWM driver is split into two parts:
 
@@ -209,3 +209,7 @@ wait for an end of cycle.
 
 The ``CONFIG_PWM_DEADTIME`` option brings the possibility to introduce
 dead time values between complementary PWM outputs.
+
+Finite pulse trains are not configured through ``pwm_info_s``. Use the
+:doc:`pulsecount <pulsecount>` driver for hardware that supports a fixed pulse
+count.
diff --git a/drivers/timers/CMakeLists.txt b/drivers/timers/CMakeLists.txt
index 1107f1139af..da67a9cabd1 100644
--- a/drivers/timers/CMakeLists.txt
+++ b/drivers/timers/CMakeLists.txt
@@ -86,6 +86,10 @@ if(CONFIG_PWM)
   list(APPEND SRCS pwm.c)
 endif()
 
+if(CONFIG_PULSECOUNT)
+  list(APPEND SRCS pulsecount.c)
+endif()
+
 if(CONFIG_DSHOT)
   list(APPEND SRCS dshot.c)
 endif()
diff --git a/drivers/timers/Kconfig b/drivers/timers/Kconfig
index b34721657af..143446ceb8e 100644
--- a/drivers/timers/Kconfig
+++ b/drivers/timers/Kconfig
@@ -5,7 +5,7 @@
 
 menu "Timer Driver Support"
 
-config ARCH_HAVE_PWM_PULSECOUNT
+config ARCH_HAVE_PULSECOUNT
        bool
        default n
 
@@ -24,18 +24,18 @@ config PWM
                This selection enables building of the "upper-half" PWM driver.
                See include/nuttx/timers/pwm.h for further PWM driver 
information.
 
-if PWM
-
-config PWM_PULSECOUNT
-       bool "PWM Pulse Count Support"
+config PULSECOUNT
+       bool "Pulse Count Support"
        default n
-       depends on ARCH_HAVE_PWM_PULSECOUNT
+       depends on ARCH_HAVE_PULSECOUNT
        ---help---
                Some hardware will support generation of a fixed number of 
pulses.
                This might be used, for example to support a stepper motor.  If 
the
                hardware will support a fixed pulse count, then this 
configuration
                should be set to enable the capability.
 
+if PWM
+
 config PWM_OVERWRITE
        bool "PWM Overwrite Support"
        default n
diff --git a/drivers/timers/Make.defs b/drivers/timers/Make.defs
index 6e671ebdbd1..51debd2db42 100644
--- a/drivers/timers/Make.defs
+++ b/drivers/timers/Make.defs
@@ -118,6 +118,12 @@ ifeq ($(CONFIG_PWM),y)
   TMRVPATH = :timers
 endif
 
+ifeq ($(CONFIG_PULSECOUNT),y)
+  CSRCS += pulsecount.c
+  TMRDEPPATH = --dep-path timers
+  TMRVPATH = :timers
+endif
+
 ifeq ($(CONFIG_DSHOT),y)
   CSRCS += dshot.c
   TMRDEPPATH = --dep-path timers
diff --git a/drivers/timers/pulsecount.c b/drivers/timers/pulsecount.c
new file mode 100644
index 00000000000..d2c1677efc7
--- /dev/null
+++ b/drivers/timers/pulsecount.c
@@ -0,0 +1,361 @@
+/****************************************************************************
+ * drivers/timers/pulsecount.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <errno.h>
+#include <nuttx/debug.h>
+
+#include <nuttx/kmalloc.h>
+#include <nuttx/mutex.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/fs/fs.h>
+#include <nuttx/timers/pulsecount.h>
+
+#ifdef CONFIG_PULSECOUNT
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct pulsecount_upperhalf_s
+{
+  uint8_t crefs;                    /* The number of times the device has
+                                      * been opened */
+  volatile bool started;            /* True: pulse output is active */
+  volatile bool waiting;            /* True: a thread is waiting for expiry */
+  mutex_t lock;                     /* Supports mutual exclusion */
+  sem_t waitsem;                    /* Waits for finite pulse completion */
+  struct pulsecount_info_s info;    /* Pulse output characteristics */
+
+  /* Lower half state */
+
+  FAR struct pulsecount_lowerhalf_s *dev;
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int pulsecount_open(FAR struct file *filep);
+static int pulsecount_close(FAR struct file *filep);
+static ssize_t pulsecount_read(FAR struct file *filep,
+                               FAR char *buffer, size_t buflen);
+static ssize_t pulsecount_write(FAR struct file *filep,
+                                FAR const char *buffer, size_t buflen);
+static int pulsecount_start(FAR struct pulsecount_upperhalf_s *upper,
+                            unsigned int oflags);
+static int pulsecount_ioctl(FAR struct file *filep, int cmd,
+                            unsigned long arg);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct file_operations g_pulsecount_fops =
+{
+  pulsecount_open,   /* open */
+  pulsecount_close,  /* close */
+  pulsecount_read,   /* read */
+  pulsecount_write,  /* write */
+  NULL,              /* seek */
+  pulsecount_ioctl,  /* ioctl */
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static void pulsecount_dump(FAR const char *msg,
+                            FAR const struct pulsecount_info_s *info,
+                            bool started)
+{
+  _info("%s: high: %" PRIu32 " ns low: %" PRIu32
+          " ns count: %" PRIu32 " started: %d\n",
+          msg, info->high_ns, info->low_ns, info->count, started);
+}
+
+static int pulsecount_open(FAR struct file *filep)
+{
+  FAR struct inode *inode = filep->f_inode;
+  FAR struct pulsecount_upperhalf_s *upper = inode->i_private;
+  FAR struct pulsecount_lowerhalf_s *lower;
+  uint8_t tmp;
+  int ret;
+
+  ret = nxmutex_lock(&upper->lock);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  tmp = upper->crefs + 1;
+  if (tmp == 0)
+    {
+      ret = -EMFILE;
+      goto errout_with_lock;
+    }
+
+  if (tmp == 1)
+    {
+      lower = upper->dev;
+      DEBUGASSERT(lower->ops->setup != NULL);
+
+      ret = lower->ops->setup(lower);
+      if (ret < 0)
+        {
+          goto errout_with_lock;
+        }
+    }
+
+  upper->crefs = tmp;
+  ret = OK;
+
+errout_with_lock:
+  nxmutex_unlock(&upper->lock);
+  return ret;
+}
+
+static int pulsecount_close(FAR struct file *filep)
+{
+  FAR struct inode *inode = filep->f_inode;
+  FAR struct pulsecount_upperhalf_s *upper = inode->i_private;
+  FAR struct pulsecount_lowerhalf_s *lower;
+  int ret;
+
+  ret = nxmutex_lock(&upper->lock);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  if (upper->crefs > 1)
+    {
+      upper->crefs--;
+    }
+  else
+    {
+      lower = upper->dev;
+      upper->crefs = 0;
+
+      DEBUGASSERT(lower->ops->shutdown != NULL);
+      lower->ops->shutdown(lower);
+    }
+
+  nxmutex_unlock(&upper->lock);
+  return OK;
+}
+
+static ssize_t pulsecount_read(FAR struct file *filep,
+                               FAR char *buffer, size_t buflen)
+{
+  return 0;
+}
+
+static ssize_t pulsecount_write(FAR struct file *filep,
+                                FAR const char *buffer, size_t buflen)
+{
+  return 0;
+}
+
+static int pulsecount_start(FAR struct pulsecount_upperhalf_s *upper,
+                            unsigned int oflags)
+{
+  FAR struct pulsecount_lowerhalf_s *lower;
+  int ret = OK;
+
+  DEBUGASSERT(upper != NULL);
+  lower = upper->dev;
+  DEBUGASSERT(lower != NULL && lower->ops->start != NULL);
+
+  if (upper->info.count == 0)
+    {
+      return -EINVAL;
+    }
+
+  if (!upper->started)
+    {
+      upper->waiting = (oflags & O_NONBLOCK) == 0;
+      upper->started = true;
+
+      ret = lower->ops->start(lower, &upper->info, upper);
+      if (ret == OK)
+        {
+          while (upper->waiting)
+            {
+              ret = nxsem_wait_uninterruptible(&upper->waitsem);
+              if (ret < 0)
+                {
+                  upper->started = false;
+                  upper->waiting = false;
+                }
+            }
+        }
+      else
+        {
+          upper->started = false;
+          upper->waiting = false;
+        }
+    }
+
+  return ret;
+}
+
+static int pulsecount_ioctl(FAR struct file *filep, int cmd,
+                            unsigned long arg)
+{
+  FAR struct inode *inode = filep->f_inode;
+  FAR struct pulsecount_upperhalf_s *upper = inode->i_private;
+  FAR struct pulsecount_lowerhalf_s *lower = upper->dev;
+  int ret;
+
+  ret = nxmutex_lock(&upper->lock);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  switch (cmd)
+    {
+      case PULSECOUNTIOC_SETCHARACTERISTICS:
+        {
+          FAR const struct pulsecount_info_s *info =
+            (FAR const struct pulsecount_info_s *)((uintptr_t)arg);
+          DEBUGASSERT(info != NULL);
+
+          if (info->high_ns == 0 || info->low_ns == 0 ||
+              pulsecount_frequency(info) == 0 || info->count == 0)
+            {
+              ret = -EINVAL;
+              break;
+            }
+
+          pulsecount_dump("PULSECOUNTIOC_SETCHARACTERISTICS", info,
+                          upper->started);
+          memcpy(&upper->info, info, sizeof(struct pulsecount_info_s));
+
+          if (upper->started)
+            {
+              ret = lower->ops->start(lower, &upper->info, upper);
+            }
+        }
+        break;
+
+      case PULSECOUNTIOC_GETCHARACTERISTICS:
+        {
+          FAR struct pulsecount_info_s *info =
+            (FAR struct pulsecount_info_s *)((uintptr_t)arg);
+          DEBUGASSERT(info != NULL);
+
+          memcpy(info, &upper->info, sizeof(struct pulsecount_info_s));
+          pulsecount_dump("PULSECOUNTIOC_GETCHARACTERISTICS", info,
+                          upper->started);
+        }
+        break;
+
+      case PULSECOUNTIOC_START:
+        {
+          pulsecount_dump("PULSECOUNTIOC_START", &upper->info,
+                          upper->started);
+          ret = pulsecount_start(upper, filep->f_oflags);
+        }
+        break;
+
+      case PULSECOUNTIOC_STOP:
+        {
+          if (upper->started)
+            {
+              ret = lower->ops->stop(lower);
+              upper->started = false;
+              upper->waiting = false;
+            }
+        }
+        break;
+
+      default:
+        {
+          DEBUGASSERT(lower->ops->ioctl != NULL);
+          ret = lower->ops->ioctl(lower, cmd, arg);
+        }
+        break;
+    }
+
+  nxmutex_unlock(&upper->lock);
+  return ret;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+int pulsecount_register(FAR const char *path,
+                        FAR struct pulsecount_lowerhalf_s *dev)
+{
+  FAR struct pulsecount_upperhalf_s *upper;
+
+  upper = kmm_zalloc(sizeof(struct pulsecount_upperhalf_s));
+  if (upper == NULL)
+    {
+      _err("ERROR: Allocation failed\n");
+      return -ENOMEM;
+    }
+
+  nxmutex_init(&upper->lock);
+  nxsem_init(&upper->waitsem, 0, 0);
+
+  upper->dev = dev;
+
+  _info("Registering %s\n", path);
+  return register_driver(path, &g_pulsecount_fops, 0666, upper);
+}
+
+void pulsecount_expired(FAR void *handle)
+{
+  FAR struct pulsecount_upperhalf_s *upper =
+    (FAR struct pulsecount_upperhalf_s *)handle;
+
+  if (upper->started)
+    {
+      if (upper->waiting)
+        {
+          upper->waiting = false;
+          nxsem_post(&upper->waitsem);
+        }
+
+      upper->started = false;
+    }
+}
+
+#endif /* CONFIG_PULSECOUNT */
diff --git a/drivers/timers/pwm.c b/drivers/timers/pwm.c
index cd583de509e..8714abbc745 100644
--- a/drivers/timers/pwm.c
+++ b/drivers/timers/pwm.c
@@ -41,12 +41,9 @@
 #include <nuttx/arch.h>
 #include <nuttx/kmalloc.h>
 #include <nuttx/mutex.h>
-#include <nuttx/semaphore.h>
 #include <nuttx/fs/fs.h>
 #include <nuttx/timers/pwm.h>
 
-#include <nuttx/irq.h>
-
 #ifdef CONFIG_PWM
 
 /****************************************************************************
@@ -61,15 +58,7 @@ struct pwm_upperhalf_s
                                      * been opened */
   volatile bool     started;        /* True: pulsed output is being
                                      * generated */
-#ifdef CONFIG_PWM_PULSECOUNT
-  volatile bool     waiting;        /* True: Caller is waiting for the pulse
-                                     * count to expire */
-#endif
   mutex_t           lock;           /* Supports mutual exclusion */
-#ifdef CONFIG_PWM_PULSECOUNT
-  sem_t             waitsem;        /* Used to wait for the pulse count to
-                                     * expire */
-#endif
   struct pwm_info_s info;           /* Pulsed output characteristics */
   FAR struct pwm_lowerhalf_s *dev;  /* lower-half state */
 };
@@ -126,10 +115,6 @@ static void pwm_dump(FAR const char *msg, FAR const struct 
pwm_info_s *info,
               info->channels[i].channel, info->channels[i].duty);
     }
 
-#ifdef CONFIG_PWM_PULSECOUNT
-  pwminfo(" count: %" PRIx32 "\n", info->channels[0].count);
-#endif
-
   pwminfo(" started: %d\n", started);
 }
 
@@ -295,79 +280,12 @@ static ssize_t pwm_write(FAR struct file *filep, FAR 
const char *buffer,
  *
  ****************************************************************************/
 
-#ifdef CONFIG_PWM_PULSECOUNT
 static int pwm_start(FAR struct pwm_upperhalf_s *upper, unsigned int oflags)
 {
   FAR struct pwm_lowerhalf_s *lower;
-  irqstate_t flags;
   int ret = OK;
 
-  DEBUGASSERT(upper != NULL);
-  lower = upper->dev;
-  DEBUGASSERT(lower != NULL && lower->ops->start != NULL);
-
-  /* Verify that the PWM is not already running */
-
-  if (!upper->started)
-    {
-      /* Indicate that if will be waiting for the pulse count to complete.
-       * Note that we will only wait if a non-zero pulse count is specified
-       * and if the PWM driver was opened in normal, blocking mode.  Also
-       * assume for now that the pulse train will be successfully started.
-       *
-       * We do these things before starting the PWM to avoid race conditions.
-       */
-
-      upper->waiting = (upper->info.channels[0].count > 0) &&
-                       ((oflags & O_NONBLOCK) == 0);
-      upper->started = true;
-
-      /* Invoke the bottom half method to start the pulse train */
-
-      ret = lower->ops->start(lower, &upper->info, upper);
-
-      /* A return value of zero means that the pulse train was started
-       * successfully.
-       */
-
-      if (ret == OK)
-        {
-          /* Should we wait for the pulse output to complete?  Loop in
-           * in case the wakeup form nxsem_wait() is a false alarm.
-           */
-
-          while (upper->waiting)
-            {
-              /* Wait until we are awakened by pwm_expired().  When
-               * pwm_expired is called, it will post the waitsem and
-               * clear the waiting flag.
-               */
-
-              ret = nxsem_wait_uninterruptible(&upper->waitsem);
-              if (ret < 0)
-                {
-                  upper->started = false;
-                  upper->waiting = false;
-                }
-            }
-        }
-      else
-        {
-          /* Looks like we won't be waiting after all */
-
-          pwminfo("start failed: %d\n", ret);
-          upper->started = false;
-          upper->waiting = false;
-        }
-    }
-
-  return ret;
-}
-#else
-static int pwm_start(FAR struct pwm_upperhalf_s *upper, unsigned int oflags)
-{
-  FAR struct pwm_lowerhalf_s *lower;
-  int ret = OK;
+  UNUSED(oflags);
 
   DEBUGASSERT(upper != NULL);
   lower = upper->dev;
@@ -395,7 +313,6 @@ static int pwm_start(FAR struct pwm_upperhalf_s *upper, 
unsigned int oflags)
 
   return ret;
 }
-#endif
 
 /****************************************************************************
  * Name: pwm_ioctl
@@ -453,11 +370,7 @@ static int pwm_ioctl(FAR struct file *filep, int cmd, 
unsigned long arg)
 
           if (upper->started)
             {
-#ifdef CONFIG_PWM_PULSECOUNT
-              ret = lower->ops->start(lower, &upper->info, upper);
-#else
               ret = lower->ops->start(lower, &upper->info);
-#endif
             }
         }
         break;
@@ -513,12 +426,6 @@ static int pwm_ioctl(FAR struct file *filep, int cmd, 
unsigned long arg)
             {
               ret = lower->ops->stop(lower);
               upper->started = false;
-#ifdef CONFIG_PWM_PULSECOUNT
-              if (upper->waiting)
-                {
-                  upper->waiting = false;
-                }
-#endif
             }
         }
         break;
@@ -588,9 +495,6 @@ int pwm_register(FAR const char *path, FAR struct 
pwm_lowerhalf_s *dev)
    */
 
   nxmutex_init(&upper->lock);
-#ifdef CONFIG_PWM_PULSECOUNT
-  nxsem_init(&upper->waitsem, 0, 0);
-#endif
 
   upper->dev = dev;
 
@@ -600,69 +504,4 @@ int pwm_register(FAR const char *path, FAR struct 
pwm_lowerhalf_s *dev)
   return register_driver(path, &g_pwmops, 0666, upper);
 }
 
-/****************************************************************************
- * Name: pwm_expired
- *
- * Description:
- *   If CONFIG_PWM_PULSECOUNT is defined and the pulse count was configured
- *   to a non-zero value, then the "upper half" driver will wait for the
- *   pulse count to expire.  The sequence of expected events is as follows:
- *
- *   1. The upper half driver calls the start method, providing the lower
- *      half driver with the pulse train characteristics.  If a fixed
- *      number of pulses is required, the 'count' value will be nonzero.
- *   2. The lower half driver's start() method must verify that it can
- *      support the request pulse train (frequency, duty, AND pulse count).
- *      If it cannot, it should return an error.  If the pulse count is
- *      non-zero, it should set up the hardware for that number of pulses
- *      and return success.  NOTE:  That is CONFIG_PWM_PULSECOUNT is
- *      defined, the start() method receives an additional parameter
- *      that must be used in this callback.
- *   3. When the start() method returns success, the upper half driver
- *      will "sleep" until the pwm_expired method is called.
- *   4. When the lower half detects that the pulse count has expired
- *      (probably through an interrupt), it must call the pwm_expired
- *      interface using the handle that was previously passed to the
- *      start() method
- *
- * Input Parameters:
- *   handle - This is the handle that was provided to the lower-half
- *     start() method.
- *
- * Returned Value:
- *   None
- *
- * Assumptions:
- *   This function may be called from an interrupt handler.
- *
- ****************************************************************************/
-
-#ifdef CONFIG_PWM_PULSECOUNT
-void pwm_expired(FAR void *handle)
-{
-  FAR struct pwm_upperhalf_s *upper = (FAR struct pwm_upperhalf_s *)handle;
-
-  pwminfo("started: %d waiting: %d\n", upper->started, upper->waiting);
-
-  /* Make sure that the PWM is started */
-
-  if (upper->started)
-    {
-      /* Is there a thread waiting for the pulse train to complete? */
-
-      if (upper->waiting)
-        {
-          /* Yes.. clear the waiting flag and awakened the waiting thread */
-
-          upper->waiting = false;
-          nxsem_post(&upper->waitsem);
-        }
-
-      /* The PWM is now stopped */
-
-      upper->started = false;
-    }
-}
-#endif
-
 #endif /* CONFIG_PWM */
diff --git a/include/nuttx/fs/ioctl.h b/include/nuttx/fs/ioctl.h
index 6a520f24a41..f6ec263ff3d 100644
--- a/include/nuttx/fs/ioctl.h
+++ b/include/nuttx/fs/ioctl.h
@@ -114,6 +114,7 @@
 #define _EEPIOCBASE     (0x4600) /* EEPROM driver ioctl commands */
 #define _PTPBASE        (0x4700) /* PTP ioctl commands */
 #define _DSHOTIOCBASE   (0x4800) /* Dshot device ioctl commands */
+#define _PULSECOUNTBASE (0x4900) /* Pulse count driver ioctl commands */
 #define _WLIOCBASE      (0x8b00) /* Wireless modules ioctl network commands */
 
 /* boardctl() commands share the same number space */
@@ -815,6 +816,11 @@
 #define _DSHOTIOCVALID(c)   (_IOC_TYPE(c)==_DSHOTIOCBASE)
 #define _DSHOTIOC(nr)       _IOC(_DSHOTIOCBASE,nr)
 
+/* Pulse count driver ioctl definitions *************************************/
+
+#define _PULSECOUNTIOCVALID(c) (_IOC_TYPE(c)==_PULSECOUNTBASE)
+#define _PULSECOUNTIOC(nr)     _IOC(_PULSECOUNTBASE,nr)
+
 /****************************************************************************
  * Public Type Definitions
  ****************************************************************************/
diff --git a/include/nuttx/timers/pulsecount.h 
b/include/nuttx/timers/pulsecount.h
new file mode 100644
index 00000000000..a6d0ec3c4aa
--- /dev/null
+++ b/include/nuttx/timers/pulsecount.h
@@ -0,0 +1,189 @@
+/****************************************************************************
+ * include/nuttx/timers/pulsecount.h
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __INCLUDE_NUTTX_TIMERS_PULSECOUNT_H
+#define __INCLUDE_NUTTX_TIMERS_PULSECOUNT_H
+
+/* A pulsecount device generates a finite pulse train with controlled high
+ * time, low time, and pulse count.  The driver is split into two parts:
+ *
+ * 1. An "upper half", generic character driver that provides the common
+ *    pulsecount interface to application code.
+ * 2. A "lower half", platform-specific driver that implements the timer
+ *    programming needed to generate the pulse train.
+ */
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/compiler.h>
+#include <nuttx/clock.h>
+
+#include <stdint.h>
+#include <fixedmath.h>
+
+#include <nuttx/fs/ioctl.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* IOCTL Commands ***********************************************************/
+
+/* PULSECOUNTIOC_SETCHARACTERISTICS - Set the pulse train characteristics.
+ *
+ *   ioctl argument: A read-only reference to struct pulsecount_info_s.
+ *
+ * PULSECOUNTIOC_GETCHARACTERISTICS - Get the currently selected pulse train
+ *   characteristics.
+ *
+ *   ioctl argument: A writable reference to struct pulsecount_info_s.
+ *
+ * PULSECOUNTIOC_START - Start the configured pulse train.  The
+ *   PULSECOUNTIOC_SETCHARACTERISTICS command must have previously been sent.
+ *   By default this command blocks until the configured pulse count
+ *   completes.  That blocking behavior can be overridden by opening the
+ *   device with O_NONBLOCK.
+ *
+ *   ioctl argument: None.
+ *
+ * PULSECOUNTIOC_STOP - Stop pulse generation and return immediately.
+ *
+ *   TODO: Support cancelling a blocking PULSECOUNTIOC_START.
+ *
+ *   ioctl argument: None.
+ */
+
+#define PULSECOUNTIOC_SETCHARACTERISTICS _PULSECOUNTIOC(1)
+#define PULSECOUNTIOC_GETCHARACTERISTICS _PULSECOUNTIOC(2)
+#define PULSECOUNTIOC_START              _PULSECOUNTIOC(3)
+#define PULSECOUNTIOC_STOP               _PULSECOUNTIOC(4)
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+struct pulsecount_info_s
+{
+  uint32_t high_ns;        /* Pulse high time in nanoseconds */
+  uint32_t low_ns;         /* Pulse low time in nanoseconds */
+  uint32_t count;          /* Number of pulses to generate */
+};
+
+/****************************************************************************
+ * Public Inline Functions
+ ****************************************************************************/
+
+static inline uint64_t
+pulsecount_period_ns(FAR const struct pulsecount_info_s *info)
+{
+  return (uint64_t)info->high_ns + info->low_ns;
+}
+
+static inline uint32_t
+pulsecount_frequency(FAR const struct pulsecount_info_s *info)
+{
+  return NSEC_PER_SEC / pulsecount_period_ns(info);
+}
+
+static inline ub16_t
+pulsecount_duty(FAR const struct pulsecount_info_s *info)
+{
+  return (ub16_t)(((uint64_t)info->high_ns << 16) /
+                  pulsecount_period_ns(info));
+}
+
+struct pulsecount_lowerhalf_s;
+struct pulsecount_ops_s
+{
+  /* This method is called when the driver is opened.  The lower half driver
+   * should configure and initialize the device so that it is ready for use.
+   * It should not generate pulses until the start method is called.
+   */
+
+  CODE int (*setup)(FAR struct pulsecount_lowerhalf_s *dev);
+
+  /* This method is called when the driver is closed.  The lower half driver
+   * should stop pulse output, free resources, disable the timer, and put the
+   * system into the lowest possible power usage state.
+   */
+
+  CODE int (*shutdown)(FAR struct pulsecount_lowerhalf_s *dev);
+
+  /* Configure the timer and start the finite pulse train.  The lower half
+   * must call pulsecount_expired() with the provided handle after the
+   * programmed pulse count completes.
+   */
+
+  CODE int (*start)(FAR struct pulsecount_lowerhalf_s *dev,
+                    FAR const struct pulsecount_info_s *info,
+                    FAR void *handle);
+
+  /* Stop the pulse train and reset the timer resources. */
+
+  CODE int (*stop)(FAR struct pulsecount_lowerhalf_s *dev);
+
+  /* Lower-half logic may support platform-specific ioctl commands. */
+
+  CODE int (*ioctl)(FAR struct pulsecount_lowerhalf_s *dev,
+                    int cmd, unsigned long arg);
+};
+
+struct pulsecount_lowerhalf_s
+{
+  /* The first field of this state structure must be a pointer to the
+   * pulsecount callback structure.
+   */
+
+  FAR const struct pulsecount_ops_s *ops;
+};
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+#ifdef __cplusplus
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+int pulsecount_register(FAR const char *path,
+                        FAR struct pulsecount_lowerhalf_s *dev);
+
+/* This callback is called by the lower half after a finite pulse train has
+ * completed.  The handle must be the value passed to the lower half start()
+ * method.
+ */
+
+void pulsecount_expired(FAR void *handle);
+
+#undef EXTERN
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __INCLUDE_NUTTX_TIMERS_PULSECOUNT_H */
diff --git a/include/nuttx/timers/pwm.h b/include/nuttx/timers/pwm.h
index d0c2a2f9efa..86c8f80c60c 100644
--- a/include/nuttx/timers/pwm.h
+++ b/include/nuttx/timers/pwm.h
@@ -24,10 +24,9 @@
 #define __INCLUDE_NUTTX_TIMERS_PWM_H
 
 /* For the purposes of this driver, a PWM device is any device that generates
- * periodic output pulses s of controlled frequency and pulse width.  Such a
- * device might be used, for example, to perform pulse-width modulated output
- * or frequency/pulse-count modulated output (such as might be needed to
- * control a stepper motor).
+ * periodic output pulses of controlled frequency and pulse width. Such a
+ * device might be used, for example, to perform pulse-width modulated
+ * output.
  *
  * The PWM driver is split into two parts:
  *
@@ -59,10 +58,6 @@
 /* Configuration ************************************************************/
 
 /* CONFIG_PWM - Enables because PWM driver support
- * CONFIG_PWM_PULSECOUNT - Some hardware will support generation of a fixed
- *   number of pulses.  This might be used, for example to support a stepper
- *   motor.  If the hardware will support a fixed pulse count, then this
- *   configuration should be set to enable the capability.
  * CONFIG_DEBUG_PWM_INFO - This will generate output that can be use to
  *   debug the PWM driver.
  */
@@ -98,11 +93,7 @@
  *  characteristics of the pulsed output.
  *
  * PWMIOC_START - Start the pulsed output.  The PWMIOC_SETCHARACTERISTICS
- *  command must have previously been sent. If CONFIG_PWM_PULSECOUNT is
- *  defined and the pulse count was configured to a non-zero value, then
- *  this ioctl call will, by default, block until the programmed pulse count
- *  completes.  That default blocking behavior can be overridden by using
- *  the O_NONBLOCK flag when the PWM driver is opened.
+ *  command must have previously been sent.
  *
  *  ioctl argument:  None
  *
@@ -174,11 +165,6 @@ struct pwm_chan_s
   uint8_t cpol;
   uint8_t dcpol;
   int8_t channel;
-
-#ifdef CONFIG_PWM_PULSECOUNT
-  uint32_t           count;     /* The number of pulse to generate.  0 means to
-                                 * generate an indefinite number of pulses */
-#endif
 };
 
 /* This structure describes the characteristics of the pulsed output */
@@ -219,17 +205,11 @@ struct pwm_ops_s
 
   /* (Re-)initialize the timer resources and start the pulsed output. The
    * start method should return an error if it cannot start the timer with
-   * the given parameter (frequency, duty, or optionally pulse count)
+   * the given parameter (frequency or duty)
    */
 
-#ifdef CONFIG_PWM_PULSECOUNT
-  CODE int (*start)(FAR struct pwm_lowerhalf_s *dev,
-                    FAR const struct pwm_info_s *info,
-                    FAR void *handle);
-#else
   CODE int (*start)(FAR struct pwm_lowerhalf_s *dev,
                     FAR const struct pwm_info_s *info);
-#endif
 
   /* Stop the pulsed output and reset the timer resources */
 
@@ -313,47 +293,6 @@ extern "C"
 
 int pwm_register(FAR const char *path, FAR struct pwm_lowerhalf_s *dev);
 
-/****************************************************************************
- * Name: pwm_expired
- *
- * Description:
- *   If CONFIG_PWM_PULSECOUNT is defined and the pulse count was configured
- *   to a non-zero value, then the "upper half" driver will wait for the
- *   pulse count to expire.  The sequence of expected events is as follows:
- *
- *   1. The upper half driver calls the start method, providing the lower
- *      half driver with the pulse train characteristics.  If a fixed
- *      number of pulses is required, the 'count' value will be nonzero.
- *   2. The lower half driver's start() method must verify that it can
- *      support the request pulse train (frequency, duty, AND pulse count).
- *      If it cannot, it should return an error.  If the pulse count is
- *      non-zero, it should set up the hardware for that number of pulses
- *      and return success.  NOTE:  That is CONFIG_PWM_PULSECOUNT is
- *      defined, the start() method receives an additional parameter
- *      that must be used in this callback.
- *   3. When the start() method returns success, the upper half driver
- *      will "sleep" until the pwm_expired method is called.
- *   4. When the lower half detects that the pulse count has expired
- *      (probably through an interrupt), it must call the pwm_expired
- *      interface using the handle that was previously passed to the
- *      start() method
- *
- * Input Parameters:
- *   handle - This is the handle that was provided to the lower-half
- *     start() method.
- *
- * Returned Value:
- *   None
- *
- * Assumptions:
- *   This function may be called from an interrupt handler.
- *
- ****************************************************************************/
-
-#ifdef CONFIG_PWM_PULSECOUNT
-void pwm_expired(FAR void *handle);
-#endif
-
 /****************************************************************************
  * Platform-Independent "Lower-Half" PWM Driver Interfaces
  ****************************************************************************/


Reply via email to