From 37efbe6accb82bad51d55ebe4ae90429993c421d Mon Sep 17 00:00:00 2001 From: dongjiuzhu1 Date: Thu, 23 Oct 2025 10:37:19 +0800 Subject: [PATCH 1/3] drivers/capture: add signal notification support for edge capture events This commit adds comprehensive signal notification support to the capture driver, enabling applications to be asynchronously notified when capture edge events occur on any channel, rather than having to poll for events. Background: The capture driver is used to measure frequency and duty cycle of external signals by detecting edges (rising/falling). Previously, applications could only retrieve captured values through periodic polling via CAPIOC_GETDUTY and CAPIOC_GETFREQ ioctl commands, which is inefficient for event-driven applications. Problem: Without event notification, applications must: - Continuously poll the capture device to check for new events - Waste CPU cycles in polling loops - Experience higher latency in responding to capture events - Cannot efficiently handle multiple capture channels simultaneously Solution: This commit adds a signal-based notification mechanism that allows applications to register callbacks for specific capture channels and edge types (rising, falling, or both). When the hardware detects the configured edge, a signal is sent to the registered task. Signed-off-by: dongjiuzhu1 --- drivers/timers/Kconfig | 12 ++++ drivers/timers/capture.c | 124 +++++++++++++++++++++++++++++++-- include/nuttx/timers/capture.h | 58 ++++++++++++++- 3 files changed, 189 insertions(+), 5 deletions(-) diff --git a/drivers/timers/Kconfig b/drivers/timers/Kconfig index b96322734c952..47bb8ab05698c 100644 --- a/drivers/timers/Kconfig +++ b/drivers/timers/Kconfig @@ -92,6 +92,18 @@ config CAPTURE This selection enables building of the "upper-half" Capture driver. See include/nuttx/timers/capture.h for further Capture driver information. +if CAPTURE + +config CAPTURE_NOTIFY + bool "Capture Notification Support" + default n + ---help--- + Some hardware will support notification when a capture event + occurs. If the hardware will support notification, then this + configuration should be set to enable the capability. + +endif # CAPTURE + config TIMER bool "Timer Support" default n diff --git a/drivers/timers/capture.c b/drivers/timers/capture.c index f39a338852746..e9746a9a43739 100644 --- a/drivers/timers/capture.c +++ b/drivers/timers/capture.c @@ -57,14 +57,32 @@ * Private Types ****************************************************************************/ +#ifdef CONFIG_CAPTURE_NOTIFY +struct cap_signal_s +{ + pid_t pid; /* The pid of the registering task */ + struct cap_notify_s notify; /* The notification callback info */ +#ifdef CONFIG_SIG_EVTHREAD + struct sigwork_s work; /* The signal work structure */ +#endif +}; +#endif + /* This structure describes the state of the upper half driver */ struct cap_upperhalf_s { - uint8_t crefs; /* The number of times the device has been opened */ - uint8_t nchan; /* The number of channels, only invalid for multi channels */ - mutex_t lock; /* Supports mutual exclusion */ - FAR struct cap_lowerhalf_s **lower; /* lower-half state */ + uint8_t crefs; /* The number of times the device has been opened */ + uint8_t nchan; /* The number of channels, only invalid for multi channels */ + mutex_t lock; /* Supports mutual exclusion */ + FAR struct cap_lowerhalf_s **lower; /* lower-half state */ +#ifdef CONFIG_CAPTURE_NOTIFY + /* The array of signal structures, the length of the array is nchan, + * save signal info for each channel. + */ + + struct cap_signal_s signals[1]; +#endif }; /**************************************************************************** @@ -97,6 +115,41 @@ static const struct file_operations g_capops = * Private Functions ****************************************************************************/ +/**************************************************************************** + * Name: cap_notify_cb + * + * Description: + * Capture edge interrupt notification callback + * + ****************************************************************************/ + +#ifdef CONFIG_CAPTURE_NOTIFY +static void cap_notify_cb(FAR struct cap_lowerhalf_s *lower, FAR void *priv) +{ + FAR struct cap_upperhalf_s *upper = priv; + uint8_t i; + + DEBUGASSERT(upper != NULL); + + for (i = 0; i < upper->nchan; i++) + { + if (lower == upper->lower[i]) + { + FAR struct cap_signal_s *signal = &upper->signals[i]; + +# ifdef CONFIG_SIG_EVTHREAD + nxsig_notification(signal->pid, &signal->notify.event, + SI_QUEUE, &signal->work); +# else + nxsig_notification(signal->pid, &signal->notify.event, + SI_QUEUE, NULL); +# endif + break; + } + } +} +#endif + /**************************************************************************** * Name: cap_open * @@ -384,6 +437,69 @@ static int cap_ioctl(FAR struct file *filep, int cmd, unsigned long arg) } break; + /* CAPIOC_REGISTER - Register to receive a signal whenever there is + * an interrupt received on an input capture pin. This feature, + * of course, depends upon interrupt capture support from the platform. + * Argument: The event of signal to be generated when the interrupt + * occurs. + * + * the argument is a pointer to a struct cap_notify_s, that contains + * the channel number, edge type and signal event info. + */ + +#ifdef CONFIG_CAPTURE_NOTIFY + case CAPIOC_REGISTER: + { + FAR struct cap_signal_s *tmp; + FAR struct cap_notify_s *new; + pid_t pid; + + new = (FAR struct cap_notify_s *)(uintptr_t)arg; + if (!new || (new->chan >= upper->nchan && new->chan < 0)) + { + ret = -EINVAL; + break; + } + + pid = nxsched_getpid(); + tmp = &upper->signals[new->chan]; + if (tmp->pid != 0 && tmp->pid != pid) + { + ret = -EBUSY; + break; + } + + tmp->pid = pid; + memcpy(&tmp->notify, new, sizeof(*new)); + DEBUGASSERT(lower[new->chan]->ops->bind != NULL); + ret = lower[new->chan]->ops->bind(lower[new->chan], new->type, + cap_notify_cb, upper); + } + break; + + /* CAPIOC_UNREGISTER - Stop receiving signals for capture interrupts. + * Argument: The channel number + */ + + case CAPIOC_UNREGISTER: + { +# ifdef CONFIG_SIG_EVTHREAD + FAR struct sigwork_s *work; +# endif + int chan = (int)arg; + + upper->signals[chan].pid = 0; +# ifdef CONFIG_SIG_EVTHREAD + work = &upper->signals[chan].work; + nxsig_cancel_notification(work); +# endif + + DEBUGASSERT(lower[chan]->ops->unbind != NULL); + ret = lower[chan]->ops->unbind(lower[chan]); + } + break; +#endif + /* Any unrecognized IOCTL commands might be platform-specific ioctl * commands */ diff --git a/include/nuttx/timers/capture.h b/include/nuttx/timers/capture.h index 2b72b22d26a6f..aa22edfee3f1d 100644 --- a/include/nuttx/timers/capture.h +++ b/include/nuttx/timers/capture.h @@ -29,6 +29,7 @@ #include #include +#include /**************************************************************************** * Pre-processor Definitions @@ -115,6 +116,22 @@ #define CAPIOC_ADD_WP _CAPIOC(9) +/* Command: CAPIOC_REGISTER + * Description: Register capture event notification. + * Arguments: A reference to struct cap_notify_s. + * Return: OK on success; ERROR on failure. + */ + +#define CAPIOC_REGISTER _CAPIOC(10) + +/* Command: CAPIOC_UNREGISTER + * Description: Unregister capture event notification. + * Arguments: Int value for capture channel. + * Return: OK on success; ERROR on failure. + */ + +#define CAPIOC_UNREGISTER _CAPIOC(11) + /**************************************************************************** * Public Types ****************************************************************************/ @@ -126,11 +143,38 @@ struct cap_all_s uint8_t duty; }; +struct cap_lowerhalf_s; +#ifdef CONFIG_CAPTURE_NOTIFY + +/* Capture type */ + +enum cap_type_e +{ + CAP_TYPE_RISING, + CAP_TYPE_FALLING, + CAP_TYPE_BOTH, +}; + +/* Capture notify information, used to cmd:CAPIOC_REGISTER */ + +struct cap_notify_s +{ + struct sigevent event; /* The signal to be sent */ + int chan; /* Capture channel */ + enum cap_type_e type; /* Capture edge type */ + FAR void *ptr; /* User data pointer */ +}; + +/* Capture edge interrupt notification callback */ + +typedef CODE void (*capture_notify_t)(FAR struct cap_lowerhalf_s *lower, + FAR void *priv); +#endif + /* This structure provides the "lower-half" driver operations available to * the "upper-half" driver. */ -struct cap_lowerhalf_s; struct cap_ops_s { /* Required methods *******************************************************/ @@ -162,6 +206,18 @@ struct cap_ops_s CODE int (*ioctl)(FAR struct cap_lowerhalf_s *dev, int cmd, unsigned long arg); + +#ifdef CONFIG_CAPTURE_NOTIFY + /* Bind the capture edge interrupt notification callback */ + + CODE int (*bind)(FAR struct cap_lowerhalf_s *lower, + enum cap_type_e type, capture_notify_t cb, + FAR void *priv); + + /* Un-Bind the capture edge interrupt notification callback */ + + CODE int (*unbind)(FAR struct cap_lowerhalf_s *lower); +#endif }; /* This structure provides the publicly visible representation of the From 7f50b6fd117a149259118fa81a9d0e3f3ab61a15 Mon Sep 17 00:00:00 2001 From: dongjiuzhu1 Date: Fri, 24 Oct 2025 11:45:56 +0800 Subject: [PATCH 2/3] drivers/capture: add fake capture driver for testing and development This commit introduces a software-based fake capture driver that simulates a 10Hz square wave with 50% duty cycle, enabling development and testing of capture-related applications without requiring real hardware. Background: The capture driver subsystem requires hardware support (timers with input capture functionality) to measure external signal frequency and duty cycle. During development, testing, or on platforms without capture hardware, it's difficult to develop and test applications that use the capture API. Problem: Without a fake/simulator driver: - Developers cannot test capture applications without specific hardware - Continuous Integration (CI) systems cannot run capture-related tests - Applications cannot be developed on platforms lacking capture hardware - Testing edge notification features requires complex hardware setup - Difficult to reproduce specific timing scenarios for debugging Solution: This commit provides a software-simulated capture driver that generates predictable capture events using a watchdog timer. The fake driver produces a square wave signal at 10Hz frequency with 50% duty cycle, allowing applications to test the full capture API without hardware. Signed-off-by: dongjiuzhu1 --- drivers/drivers_initialize.c | 5 + drivers/timers/CMakeLists.txt | 4 + drivers/timers/Kconfig | 7 + drivers/timers/Make.defs | 5 + drivers/timers/fake_capture.c | 352 +++++++++++++++++++++++++++++++++ include/nuttx/timers/capture.h | 23 +++ 6 files changed, 396 insertions(+) create mode 100644 drivers/timers/fake_capture.c diff --git a/drivers/drivers_initialize.c b/drivers/drivers_initialize.c index fec2a9857580c..0494768dbac0a 100644 --- a/drivers/drivers_initialize.c +++ b/drivers/drivers_initialize.c @@ -51,6 +51,7 @@ #include #include #include +#include #include #include #include @@ -298,5 +299,9 @@ void drivers_initialize(void) ptp_clock_dummy_initialize(0); #endif +#ifdef CONFIG_FAKE_CAPTURE + fake_capture_initialize(2); +#endif + drivers_trace_end(); } diff --git a/drivers/timers/CMakeLists.txt b/drivers/timers/CMakeLists.txt index 0f1370b71bedd..bde7acf51444f 100644 --- a/drivers/timers/CMakeLists.txt +++ b/drivers/timers/CMakeLists.txt @@ -84,6 +84,10 @@ endif() if(CONFIG_CAPTURE) list(APPEND SRCS capture.c) + + if(CONFIG_FAKE_CAPTURE) + list(APPEND SRCS fake_capture.c) + endif() endif() if(CONFIG_GOLDFISH_TIMER) diff --git a/drivers/timers/Kconfig b/drivers/timers/Kconfig index 47bb8ab05698c..7a822250c6bfa 100644 --- a/drivers/timers/Kconfig +++ b/drivers/timers/Kconfig @@ -102,6 +102,13 @@ config CAPTURE_NOTIFY occurs. If the hardware will support notification, then this configuration should be set to enable the capability. +config FAKE_CAPTURE + bool "Fake Capture Driver" + default n + depends on CAPTURE_NSIGNALS > 0 + ---help--- + Enables a fake capture driver for testing purposes. + endif # CAPTURE config TIMER diff --git a/drivers/timers/Make.defs b/drivers/timers/Make.defs index b9ab14e0fbdaf..03e8247813ed1 100644 --- a/drivers/timers/Make.defs +++ b/drivers/timers/Make.defs @@ -114,6 +114,11 @@ endif ifeq ($(CONFIG_CAPTURE),y) CSRCS += capture.c + + ifeq ($(CONFIG_FAKE_CAPTURE),y) + CSRCS += fake_capture.c + endif + TMRDEPPATH = --dep-path timers TMRVPATH = :timers endif diff --git a/drivers/timers/fake_capture.c b/drivers/timers/fake_capture.c new file mode 100644 index 0000000000000..ffa9cf1ffc9bd --- /dev/null +++ b/drivers/timers/fake_capture.c @@ -0,0 +1,352 @@ +/**************************************************************************** + * drivers/timers/fake_capture.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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define CAPTURE_DUTY_DEFAULT 50 +#define CAPTURE_FREQ_DEFAULT 10 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct fake_capture_s +{ + struct cap_lowerhalf_s lower; /* Lower half capture driver */ + spinlock_t lock; /* For mutually exclusive access */ + struct wdog_s wdog; /* For timing capture events */ + bool high; /* Current edge state */ + capture_notify_t cb; /* Capture event callback function pointer */ + FAR void *priv; /* Pointer to private data */ + enum cap_type_e type; /* Edge type for the capture channel */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int fake_capture_start(FAR struct cap_lowerhalf_s *lower); +static int fake_capture_stop(FAR struct cap_lowerhalf_s *lower); +static int fake_capture_getduty(FAR struct cap_lowerhalf_s *lower, + FAR uint8_t *duty); +static int fake_capture_getfreq(FAR struct cap_lowerhalf_s *lower, + FAR uint32_t *freq); +static int fake_capture_getedges(FAR struct cap_lowerhalf_s *lower, + FAR uint32_t *edges); +static int fake_capture_bind(FAR struct cap_lowerhalf_s *lower, + enum cap_type_e type, capture_notify_t cb, + FAR void *priv); +static int fake_capture_unbind(FAR struct cap_lowerhalf_s *lower); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct cap_ops_s g_fake_cap_ops = +{ + .start = fake_capture_start, + .stop = fake_capture_stop, + .getduty = fake_capture_getduty, + .getfreq = fake_capture_getfreq, + .getedges = fake_capture_getedges, + .bind = fake_capture_bind, + .unbind = fake_capture_unbind, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void fake_capture_isr(wdparm_t arg) +{ + FAR struct fake_capture_s *capture = (FAR struct fake_capture_s *)arg; + irqstate_t flags; + bool last; + + flags = spin_lock_irqsave(&capture->lock); + + last = capture->high; + capture->high = !capture->high; + + if (capture->cb != NULL && + (capture->type == CAP_TYPE_BOTH || + (capture->type == CAP_TYPE_RISING && !last) || + (capture->type == CAP_TYPE_FALLING && last))) + { + capture->cb(&capture->lower, capture->priv); + } + + spin_unlock_irqrestore(&capture->lock, flags); + + wd_start_next(&capture->wdog, MSEC2TICK(1000 / CAPTURE_FREQ_DEFAULT), + fake_capture_isr, (wdparm_t)capture); +} + +/**************************************************************************** + * Name: fake_capture_start + * + * Description: + * This function is a requirement of the upper-half driver. When called, + * enables the capture channel, interruption routine, sets the positive + * edge to trigger this interrupt and resets the frequency and duty + * values. The positive edge is always the first expected. + * + * Input Parameters: + * lower - Pointer to the capture channel lower-half data structure. + * + * Returned Value: + * Returns OK on success. + * + ****************************************************************************/ + +static int fake_capture_start(FAR struct cap_lowerhalf_s *lower) +{ + FAR struct fake_capture_s *capture = (FAR struct fake_capture_s *)lower; + + wd_start(&capture->wdog, MSEC2TICK(1000 / CAPTURE_FREQ_DEFAULT), + fake_capture_isr, (wdparm_t)capture); + return OK; +} + +/**************************************************************************** + * Name: fake_capture_stop + * + * Description: + * This function is a requirement of the upper-half driver. When called, + * disables the capture channel and the interrupt routine associated. + * + * Input Parameters: + * lower - Pointer to the capture channel lower-half data structure. + * + * Returned Value: + * Returns OK on success. + * + ****************************************************************************/ + +static int fake_capture_stop(FAR struct cap_lowerhalf_s *lower) +{ + FAR struct fake_capture_s *capture = (FAR struct fake_capture_s *)lower; + + wd_cancel(&capture->wdog); + return OK; +} + +/**************************************************************************** + * Name: fake_capture_getduty + * + * Description: + * This function is a requirement of the upper-half driver. Returns + * the last calculated duty cycle value. + * + * Input Parameters: + * lower - Pointer to the capture channel lower-half data structure. + * duty - uint8_t pointer where the duty cycle value is written. + * + * Returned Value: + * Returns OK on success. + * + ****************************************************************************/ + +static int fake_capture_getduty(FAR struct cap_lowerhalf_s *lower, + FAR uint8_t *duty) +{ + *duty = CAPTURE_DUTY_DEFAULT; + + return OK; +} + +/**************************************************************************** + * Name: fake_capture_getfreq + * + * Description: + * This function is a requirement of the upper-half driver. Returns + * the last calculated frequency value. + * + * Input Parameters: + * lower - Pointer to the capture channel lower-half data structure. + * duty - uint8_t pointer where the frequency value is written. + * + * Returned Value: + * Returns OK on success. + * + ****************************************************************************/ + +static int fake_capture_getfreq(FAR struct cap_lowerhalf_s *lower, + FAR uint32_t *freq) +{ + *freq = CAPTURE_FREQ_DEFAULT; + + return OK; +} + +/**************************************************************************** + * Name: fake_capture_getedges + * + * Description: + * This function is a requirement of the upper-half driver. Returns + * the current edge type configured for the capture channel. + * + * Input Parameters: + * lower - Pointer to the capture channel lower-half data structure. + * type - Pointer to the edge type variable to be updated. + * + * Returned Value: + * Returns OK on success. + * + ****************************************************************************/ + +static int fake_capture_getedges(FAR struct cap_lowerhalf_s *lower, + FAR uint32_t *edges) +{ + FAR struct fake_capture_s *capture = (FAR struct fake_capture_s *)lower; + irqstate_t flags = spin_lock_irqsave(&capture->lock); + + *edges = capture->high; + + spin_unlock_irqrestore(&capture->lock, flags); + return OK; +} + +/**************************************************************************** + * Name: fake_capture_bind + * + * Description: + * This function is used to bind a upper-half provided callback that will + * be invoked upon capture edge events. + * + * Input Parameters: + * lower - Pointer to the capture channel lower-half data structure. + * cb - The callback function pointer. + * priv - The private argument to be passed to the callback function. + * edge - The edge type that will trigger the callback. + * + * Returned Value: + * Returns OK on success. + * + ****************************************************************************/ + +static int fake_capture_bind(FAR struct cap_lowerhalf_s *lower, + enum cap_type_e type, capture_notify_t cb, + FAR void *priv) +{ + FAR struct fake_capture_s *capture = (FAR struct fake_capture_s *)lower; + irqstate_t flags = spin_lock_irqsave(&capture->lock); + + capture->cb = cb; + capture->priv = priv; + capture->type = type; + + spin_unlock_irqrestore(&capture->lock, flags); + return 0; +} + +/**************************************************************************** + * Name: fake_capture_unbind + * + * Description: + * This function is used to un-bind a previously bound upper-half provided + * callback that was to be invoked upon capture edge events. + * + * Input Parameters: + * lower - Pointer to the capture channel lower-half data structure. + * + * Returned Value: + * Returns OK on success. + * + ****************************************************************************/ + +static int fake_capture_unbind(FAR struct cap_lowerhalf_s *lower) +{ + FAR struct fake_capture_s *capture = (FAR struct fake_capture_s *)lower; + irqstate_t flags = spin_lock_irqsave(&capture->lock); + + capture->cb = NULL; + capture->priv = NULL; + + spin_unlock_irqrestore(&capture->lock, flags); + return 0; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: fake_capture_initialize + * + * Description: + * This function is called by board-specific logic to initialize + * fake capture. + * + * Input Parameters: + * channel - The capture channel number to initialize. + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. The following + * possible error values may be returned (most are returned by + * register_driver()): + * + ****************************************************************************/ + +int fake_capture_initialize(int channels) +{ + char path[32]; + int ret = 0; + int i; + + for (i = 0; i < channels; i++) + { + FAR struct fake_capture_s *priv; + + priv = kmm_zalloc(sizeof(*priv)); + DEBUGASSERT(priv); + + spin_lock_init(&priv->lock); + priv->lower.ops = &g_fake_cap_ops; + snprintf(path, sizeof(path), "/dev/fake_capture%d", i); + ret = cap_register(path, &priv->lower); + if (ret < 0) + { + cperr("Failed to register capture:%d\n", ret); + kmm_free(priv); + break; + } + } + + return ret; +} diff --git a/include/nuttx/timers/capture.h b/include/nuttx/timers/capture.h index aa22edfee3f1d..1e658d75ba4a0 100644 --- a/include/nuttx/timers/capture.h +++ b/include/nuttx/timers/capture.h @@ -31,6 +31,8 @@ #include #include +#include + /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ @@ -274,6 +276,27 @@ int cap_register(FAR const char *devpath, int cap_register_multiple(FAR const char *devpath, FAR struct cap_lowerhalf_s **lower, int n); +/**************************************************************************** + * Name: fake_capture_initialize + * + * Description: + * This function is called by board-specific logic to initialize + * fake capture. + * + * Input Parameters: + * channel - The capture channel number to initialize. + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. The following + * possible error values may be returned (most are returned by + * register_driver()): + * + ****************************************************************************/ + +#ifdef CONFIG_FAKE_CAPTURE +int fake_capture_initialize(int channels); +#endif + #undef EXTERN #ifdef __cplusplus } From 468f90104adc29d2521cc90e8f0c6dcd428e147d Mon Sep 17 00:00:00 2001 From: dongjiuzhu1 Date: Tue, 30 Dec 2025 23:27:15 +0800 Subject: [PATCH 3/3] Documentation: update capture.rst with notification and fake driver These documentation updates correspond to commits: - drivers/capture: add signal notification support for edge capture events - drivers/capture: add fake capture driver for testing and development Signed-off-by: dongjiuzhu1 --- .../drivers/character/timers/capture.rst | 203 +++++++++++++++++- 1 file changed, 201 insertions(+), 2 deletions(-) diff --git a/Documentation/components/drivers/character/timers/capture.rst b/Documentation/components/drivers/character/timers/capture.rst index 03b492146f18e..046f1b049116a 100644 --- a/Documentation/components/drivers/character/timers/capture.rst +++ b/Documentation/components/drivers/character/timers/capture.rst @@ -72,17 +72,58 @@ Supported ``ioctl`` Commands **Argument:** ``int`` (value to watch for). +.. c:macro:: CAPIOC_REGISTER + + Register for capture edge event notifications. This allows applications + to receive asynchronous signal notifications when capture edge events + occur, instead of polling for events. + + **Argument:** ``struct cap_notify_s *`` (pointer to notification structure). + + The ``struct cap_notify_s`` contains: + + * ``event`` - The signal event configuration (``struct sigevent``) + * ``chan`` - Capture channel number + * ``type`` - Edge type (``CAP_TYPE_RISING``, ``CAP_TYPE_FALLING``, or ``CAP_TYPE_BOTH``) + * ``ptr`` - User data pointer + + **Returns:** + + * ``OK`` on success + * ``-EINVAL`` for invalid channel + * ``-EBUSY`` if channel already registered by another task + +.. c:macro:: CAPIOC_UNREGISTER + + Unregister capture edge event notifications. + + **Argument:** ``int`` (channel number). + + **Returns:** ``OK`` on success. + Configuration ------------- To enable the capture driver, enable the following configuration options: -* ``CONFIG_CAPTURE`` -* ``CONFIG_STM32H7_TIM4_CAP`` (for STM32H7 Timer 4) +* ``CONFIG_CAPTURE`` - Enable the capture driver framework +* ``CONFIG_CAPTURE_NOTIFY`` - Enable signal notification support for edge events +* ``CONFIG_FAKE_CAPTURE`` - Enable fake capture driver for testing (generates 10Hz signal with 50% duty cycle) +* ``CONFIG_STM32H7_TIM4_CAP`` (for STM32H7 Timer 4, platform-specific) The ``CONFIG_CAPTURE`` option enables the lower-half driver and registers the ``/dev/capture`` device. +The ``CONFIG_CAPTURE_NOTIFY`` option enables the signal notification feature, +allowing applications to receive asynchronous notifications when capture +edge events occur. This requires hardware support for edge interrupts and +depends on ``CONFIG_CAPTURE``. + +The ``CONFIG_FAKE_CAPTURE`` option enables a software-based fake capture +driver that simulates a 10Hz square wave with 50% duty cycle. This is +useful for development and testing without requiring actual hardware. +It depends on ``CONFIG_CAPTURE`` and ``CONFIG_CAPTURE_NSIGNALS > 0``. + Without it, capture is still possible manually by including the appropriate header (e.g., ``arch/arm/src/stm32h7/stm32_capture.h``) and performing a manual initialization. @@ -125,6 +166,129 @@ frequency: return 0; } +Signal Notification Example +---------------------------- + +Here is an example using signal notifications for event-driven capture +(requires ``CONFIG_CAPTURE_NOTIFY``): + +.. code-block:: c + + #include + #include + #include + #include + #include + + static volatile int edge_count = 0; + + static void capture_handler(int signo, siginfo_t *info, void *context) + { + edge_count++; + } + + int main(int argc, char *argv[]) + { + int fd; + struct cap_notify_s notify; + struct sigaction sa; + uint32_t frequency; + uint8_t duty; + + /* Set up signal handler */ + sa.sa_sigaction = capture_handler; + sa.sa_flags = SA_SIGINFO; + sigemptyset(&sa.sa_mask); + sigaction(SIGUSR1, &sa, NULL); + + /* Open capture device */ + fd = open("/dev/capture0", O_RDONLY); + if (fd < 0) + { + perror("Failed to open capture device"); + return 1; + } + + /* Configure notification for both edges on channel 0 */ + notify.chan = 0; + notify.type = CAP_TYPE_BOTH; + notify.event.sigev_notify = SIGEV_SIGNAL; + notify.event.sigev_signo = SIGUSR1; + notify.event.sigev_value.sival_ptr = NULL; + + if (ioctl(fd, CAPIOC_REGISTER, (unsigned long)¬ify) < 0) + { + perror("Failed to register notification"); + close(fd); + return 1; + } + + printf("Waiting for capture events...\n"); + + /* Wait for some events */ + sleep(2); + + /* Get frequency and duty cycle */ + ioctl(fd, CAPIOC_FREQUENCE, (unsigned long)&frequency); + ioctl(fd, CAPIOC_DUTYCYCLE, (unsigned long)&duty); + + printf("Captured %d edges\n", edge_count); + printf("Frequency: %u Hz, Duty: %u%%\n", frequency, duty); + + /* Unregister notification */ + ioctl(fd, CAPIOC_UNREGISTER, 0); + + close(fd); + return 0; + } + +Fake Capture Testing Example +----------------------------- + +The fake capture driver can be used for testing without hardware +(requires ``CONFIG_FAKE_CAPTURE``): + +.. code-block:: c + + #include + #include + #include + #include + + int main(int argc, char *argv[]) + { + int fd; + uint32_t frequency; + uint8_t duty; + + /* Open fake capture device */ + fd = open("/dev/fake_capture0", O_RDONLY); + if (fd < 0) + { + perror("Failed to open fake capture device"); + return 1; + } + + /* Start capture */ + ioctl(fd, CAPIOC_START, 0); + + /* Wait for capture to stabilize */ + sleep(1); + + /* Read values (should be 10Hz, 50% duty) */ + ioctl(fd, CAPIOC_FREQUENCE, (unsigned long)&frequency); + ioctl(fd, CAPIOC_DUTYCYCLE, (unsigned long)&duty); + + printf("Fake Capture - Frequency: %u Hz, Duty: %u%%\n", + frequency, duty); + + /* Stop capture */ + ioctl(fd, CAPIOC_STOP, 0); + + close(fd); + return 0; + } + Notes ----- @@ -136,3 +300,38 @@ Notes * **Important:** In debug builds of NuttX, calling an unsupported ``ioctl`` command will trigger a ``DEBUGASSERT`` in the driver, which will halt or crash the system. + +Signal Notification Features +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When ``CONFIG_CAPTURE_NOTIFY`` is enabled: + +* Applications can register for asynchronous edge event notifications +* Supports per-channel registration with independent configurations +* Edge types supported: rising edge (``CAP_TYPE_RISING``), falling edge + (``CAP_TYPE_FALLING``), or both (``CAP_TYPE_BOTH``) +* Only one task can register per channel at a time +* Signal notifications use standard POSIX ``sigevent`` mechanism +* Lower-half drivers must implement ``bind()`` and ``unbind()`` operations +* Ideal for event-driven applications like tachometers, encoders, and + frequency counters + +Fake Capture Driver +~~~~~~~~~~~~~~~~~~~ + +The fake capture driver (``CONFIG_FAKE_CAPTURE``) provides: + +* Software simulation of capture events using watchdog timers +* Fixed 10Hz frequency with 50% duty cycle +* Edge toggles every 50ms (rising and falling) +* Supports all standard capture operations including notifications +* Available at ``/dev/fake_capture0``, ``/dev/fake_capture1``, etc. +* Useful for development, testing, and CI/CD without hardware +* Platform-independent implementation +* Automatically initialized at boot (2 channels by default) + +Limitations: + +* Fixed timing parameters (not configurable at runtime) +* Software timing accuracy (not hardware-precise) +* Suitable for functional testing, not timing precision validation