!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:
raiden00pl
2026-05-25 14:33:11 +02:00
committed by Alin Jerpelea
parent 08a2de27cf
commit f1e7b143d9
13 changed files with 705 additions and 244 deletions
+6
View File
@@ -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
View 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 */
+5 -66
View File
@@ -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
****************************************************************************/