mirror of
https://github.com/apache/nuttx.git
synced 2026-08-01 20:28:58 +00:00
!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 <raiden00@railab.me>
This commit is contained in:
parent
08a2de27cf
commit
f1e7b143d9
13 changed files with 705 additions and 244 deletions
21
Documentation/applications/examples/pulsecount/index.rst
Normal file
21
Documentation/applications/examples/pulsecount/index.rst
Normal file
|
|
@ -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.
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ Timers Drivers
|
|||
timer.rst
|
||||
oneshot.rst
|
||||
pwm.rst
|
||||
pulsecount.rst
|
||||
watchdog.rst
|
||||
rtc.rst
|
||||
capture.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``.
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
361
drivers/timers/pulsecount.c
Normal file
361
drivers/timers/pulsecount.c
Normal file
|
|
@ -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 */
|
||||
|
|
@ -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,80 +280,13 @@ 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;
|
||||
DEBUGASSERT(lower != NULL && lower->ops->start != NULL);
|
||||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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
|
||||
****************************************************************************/
|
||||
|
|
|
|||
189
include/nuttx/timers/pulsecount.h
Normal file
189
include/nuttx/timers/pulsecount.h
Normal file
|
|
@ -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 */
|
||||
|
|
@ -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
|
||||
****************************************************************************/
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue