You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by xi...@apache.org on 2026/04/30 04:24:55 UTC
(nuttx) 01/04: drivers/timers: Add common include files, definitions and upper-half for DSHOT
This is an automated email from the ASF dual-hosted git repository.
xiaoxiang781216 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit b810cbe4d36e26f34f60a9c07ee6796c8b491010
Author: Jukka Laitinen <ju...@tii.ae>
AuthorDate: Wed Mar 18 11:48:50 2026 +0200
drivers/timers: Add common include files, definitions and upper-half for DSHOT
DShot is a packet based serial protocol, used for controlling motor controllers
(ESCs), typically for drones.
This adds an upper-half driver for dshot protocol, along with common
configuration options and definitions definitions.
Signed-off-by: Jukka Laitinen <ju...@tii.ae>
---
drivers/timers/CMakeLists.txt | 4 +
drivers/timers/Kconfig | 18 ++
drivers/timers/Make.defs | 6 +
drivers/timers/dshot.c | 638 ++++++++++++++++++++++++++++++++++++++++++
include/nuttx/fs/ioctl.h | 8 +
include/nuttx/timers/dshot.h | 312 +++++++++++++++++++++
6 files changed, 986 insertions(+)
diff --git a/drivers/timers/CMakeLists.txt b/drivers/timers/CMakeLists.txt
index d77de9b3ca4..1107f1139af 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_DSHOT)
+ list(APPEND SRCS dshot.c)
+endif()
+
if(CONFIG_CAPTURE)
list(APPEND SRCS capture.c)
diff --git a/drivers/timers/Kconfig b/drivers/timers/Kconfig
index 14d3c2df34b..c98101103eb 100644
--- a/drivers/timers/Kconfig
+++ b/drivers/timers/Kconfig
@@ -112,6 +112,24 @@ config FAKE_CAPTURE
endif # CAPTURE
+config DSHOT
+ bool "DShot Driver Support"
+ default n
+ ---help---
+ This selection enables the DShot driver for ESC control.
+
+if DSHOT
+
+config DSHOT_NCHANNELS
+ int "Maximum DShot channels in API structs"
+ default 8
+ range 1 16
+ ---help---
+ Specifies the number of output channels per timer. Each timer
+ may support fewer output channels than this value.
+
+endif # DSHOT
+
config TIMER
bool "Timer Support"
default n
diff --git a/drivers/timers/Make.defs b/drivers/timers/Make.defs
index 0cdbf677ab9..6e671ebdbd1 100644
--- a/drivers/timers/Make.defs
+++ b/drivers/timers/Make.defs
@@ -118,6 +118,12 @@ ifeq ($(CONFIG_PWM),y)
TMRVPATH = :timers
endif
+ifeq ($(CONFIG_DSHOT),y)
+ CSRCS += dshot.c
+ TMRDEPPATH = --dep-path timers
+ TMRVPATH = :timers
+endif
+
ifeq ($(CONFIG_CAPTURE),y)
CSRCS += capture.c
diff --git a/drivers/timers/dshot.c b/drivers/timers/dshot.c
new file mode 100644
index 00000000000..37f4c875339
--- /dev/null
+++ b/drivers/timers/dshot.c
@@ -0,0 +1,638 @@
+/****************************************************************************
+ * drivers/timers/dshot.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 <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/fs/fs.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/mutex.h>
+#include <nuttx/timers/dshot.h>
+
+#ifdef CONFIG_DSHOT
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define DSHOT_GCR_MASK 0x000fffffu
+#define DSHOT_ERPMSCALE_NUM 600000u
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct dshot_upperhalf_s
+{
+ uint8_t crefs;
+ mutex_t lock;
+ FAR struct dshot_lowerhalf_s *dev;
+ struct dshot_ch_telemetry_s telemetry[DSHOT_NCHANNELS];
+ bool bidir;
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int dshot_open(FAR struct file *filep);
+static int dshot_close(FAR struct file *filep);
+static ssize_t dshot_read(FAR struct file *filep, FAR char *buffer,
+ size_t buflen);
+static ssize_t dshot_write(FAR struct file *filep, FAR const char *buffer,
+ size_t buflen);
+static int dshot_ioctl(FAR struct file *filep, int cmd, unsigned long arg);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct file_operations g_dshotops =
+{
+ dshot_open, /* open */
+ dshot_close, /* close */
+ dshot_read, /* read */
+ dshot_write, /* write */
+ NULL, /* seek */
+ dshot_ioctl, /* ioctl */
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: dshot_crc
+ *
+ * Description:
+ * Calculate the 4-bit CRC for a DShot packet.
+ *
+ * Input Parameters:
+ * packet - The DShot packet to calculate CRC for.
+ * bidir - Boolean indicating if bidirectional DShot is enabled.
+ *
+ * Returned Value:
+ * The calculated 4-bit CRC value.
+ *
+ ****************************************************************************/
+
+static uint16_t dshot_crc(uint16_t packet, bool bidir)
+{
+ uint16_t crc;
+
+ crc = packet >> 4;
+ crc = crc ^ (crc >> 4) ^ (crc >> 8);
+
+ if (bidir)
+ {
+ crc = ~crc;
+ }
+
+ return crc & 0xf;
+}
+
+/****************************************************************************
+ * Name: dshot_build_packet
+ *
+ * Description:
+ * Build a complete DShot packet from throttle, telemetry, and bidir
+ * values.
+ *
+ * Input Parameters:
+ * throttle - Throttle value (0-2047).
+ * telemetry - Boolean indicating if telemetry request is enabled.
+ * bidir - Boolean indicating if bidirectional DShot is enabled.
+ *
+ * Returned Value:
+ * The constructed 16-bit DShot packet.
+ *
+ ****************************************************************************/
+
+static uint16_t dshot_build_packet(uint16_t throttle, bool telemetry,
+ bool bidir)
+{
+ uint16_t packet = 0;
+ packet |= throttle << 5;
+ packet |= (telemetry ? 1 << 4 : 0);
+ packet |= dshot_crc(packet, bidir);
+
+ return packet;
+}
+
+/****************************************************************************
+ * Name: dshot_parse_raw_packet
+ *
+ * Description:
+ * Parse raw pattern from the line into a 16-bit packet and verify CRC.
+ * Steps:
+ * 1. Extract GCR20 value from the raw value
+ * 2. Extract the 16-bit packet from the GCR20
+ * 3. Check the packet CRC
+ *
+ * Input Parameters:
+ * raw - The raw encoded packet value to parse.
+ *
+ * Returned Value:
+ * On success, returns the 12-bit payload as a positive integer.
+ * On error, returns a negative error code.
+ *
+ ****************************************************************************/
+
+static int dshot_parse_raw_packet(uint32_t raw)
+{
+ static const int8_t gcr_decode[32] =
+ {
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 9, 10, 11, -1, 13, 14, 15,
+ -1, -1, 2, 3, -1, 5, 6, 7,
+ -1, 0, 8, 1, -1, 4, 12, -1
+ };
+
+ uint32_t gcr20 = (raw ^ (raw >> 1)) & DSHOT_GCR_MASK;
+ uint16_t packet = 0;
+ int i;
+
+ for (i = 0; i < 4; i++)
+ {
+ int16_t nibble = gcr_decode[gcr20 & 0x1f];
+
+ if (nibble < 0)
+ {
+ return -EINVAL;
+ }
+
+ packet |= nibble << (4 * i);
+ gcr20 >>= 5;
+ }
+
+ if (dshot_crc(packet, true) != (packet & 0xf))
+ {
+ return -EINVAL;
+ }
+
+ return packet >> 4;
+}
+
+/****************************************************************************
+ * Name: dshot_copy_telemetry
+ *
+ * Description:
+ * Copy telemetry data for selected channels from upper-half to output.
+ *
+ * Input Parameters:
+ * upper - Pointer to the upper-half driver state.
+ * out - Pointer to output telemetry array.
+ * ch_mask - Bitmask indicating which channels to copy.
+ *
+ * Returned Value:
+ * None.
+ *
+ ****************************************************************************/
+
+static void dshot_copy_telemetry(FAR struct dshot_upperhalf_s *upper,
+ FAR struct dshot_ch_telemetry_s *out,
+ uint16_t ch_mask)
+{
+ int i;
+
+ for (i = 0; i < DSHOT_NCHANNELS; i++)
+ {
+ if ((ch_mask & (1u << i)) != 0)
+ {
+ out[i] = upper->telemetry[i];
+ }
+ }
+}
+
+/****************************************************************************
+ * Name: dshot_update_telemetry
+ *
+ * Description:
+ * Update telemetry data from lower-half driver for selected channels.
+ * Parses raw telemetry data and extracts EDT or eRPM information.
+ *
+ * Input Parameters:
+ * upper - Pointer to the upper-half driver state.
+ * ch_mask - Bitmask indicating which channels to update.
+ *
+ * Returned Value:
+ * OK on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+static int dshot_update_telemetry(FAR struct dshot_upperhalf_s *upper,
+ uint16_t ch_mask)
+{
+ FAR struct dshot_lowerhalf_s *lower = upper->dev;
+ struct dshot_raw_telemetry_s raw[DSHOT_NCHANNELS];
+ int i;
+ int ret;
+
+ memset(raw, 0, sizeof(raw));
+
+ DEBUGASSERT(lower->ops->get_raw_telemetry != NULL);
+ ret = lower->ops->get_raw_telemetry(lower, raw, ch_mask);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ for (i = 0; i < DSHOT_NCHANNELS; i++)
+ {
+ if ((ch_mask & (1u << i)) != 0)
+ {
+ int value = dshot_parse_raw_packet(raw[i].raw);
+
+ if (value > 0)
+ {
+ uint16_t telem = (uint16_t)value & 0x0fff;
+ uint8_t exponent = telem >> 9;
+
+ if ((exponent & 1) == 0)
+ {
+ upper->telemetry[i].edt_type = (uint8_t)(telem >> 8);
+ upper->telemetry[i].edt_value = (uint8_t)(telem & 0xff);
+ upper->telemetry[i].timestamp = raw[i].timestamp;
+ }
+ else
+ {
+ uint32_t mantissa = telem & 0x1ff;
+ uint32_t period = mantissa << exponent;
+ if (period != 0)
+ {
+ uint32_t rpm = DSHOT_ERPMSCALE_NUM / period;
+ upper->telemetry[i].erpm =
+ (uint16_t)((rpm > 0xffff) ? 0xffff : rpm);
+ upper->telemetry[i].timestamp = raw[i].timestamp;
+ }
+ }
+ }
+ }
+ }
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: dshot_open
+ *
+ * Description:
+ * This function is called whenever the DShot device is opened.
+ *
+ * Input Parameters:
+ * filep - A pointer to the file structure instance.
+ *
+ * Returned Value:
+ * OK on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+static int dshot_open(FAR struct file *filep)
+{
+ FAR struct inode *inode = filep->f_inode;
+ FAR struct dshot_upperhalf_s *upper = inode->i_private;
+ uint8_t tmp;
+ int ret;
+
+ ret = nxmutex_lock(&upper->lock);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ tmp = upper->crefs + 1;
+ if (tmp == 0)
+ {
+ nxmutex_unlock(&upper->lock);
+ return -EMFILE;
+ }
+
+ if (tmp == 1)
+ {
+ DEBUGASSERT(upper->dev->ops->setup != NULL);
+ ret = upper->dev->ops->setup(upper->dev);
+ if (ret < 0)
+ {
+ nxmutex_unlock(&upper->lock);
+ return ret;
+ }
+ }
+
+ upper->crefs = tmp;
+
+ nxmutex_unlock(&upper->lock);
+ return OK;
+}
+
+/****************************************************************************
+ * Name: dshot_close
+ *
+ * Description:
+ * This function is called when the DShot device is closed.
+ *
+ * Input Parameters:
+ * filep - A pointer to the file structure instance.
+ *
+ * Returned Value:
+ * OK on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+static int dshot_close(FAR struct file *filep)
+{
+ FAR struct inode *inode = filep->f_inode;
+ FAR struct dshot_upperhalf_s *upper = inode->i_private;
+ int ret;
+
+ ret = nxmutex_lock(&upper->lock);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ if (upper->crefs > 1)
+ {
+ upper->crefs--;
+ }
+ else
+ {
+ upper->crefs = 0;
+ DEBUGASSERT(upper->dev->ops->shutdown != NULL);
+ upper->dev->ops->shutdown(upper->dev);
+ }
+
+ nxmutex_unlock(&upper->lock);
+ return OK;
+}
+
+/****************************************************************************
+ * Name: dshot_read
+ *
+ * Description:
+ * A dummy read method. This is provided only to satisfy the VFS layer.
+ *
+ * Input Parameters:
+ * filep - A pointer to the file structure instance.
+ * buffer - The user-provided buffer into which data will be returned.
+ * buflen - The size of the buffer in bytes.
+ *
+ * Returned Value:
+ * Always returns 0 (end-of-file).
+ *
+ ****************************************************************************/
+
+static ssize_t dshot_read(FAR struct file *filep, FAR char *buffer,
+ size_t buflen)
+{
+ return 0;
+}
+
+/****************************************************************************
+ * Name: dshot_write
+ *
+ * Description:
+ * A dummy write method. This is provided only to satisfy the VFS layer.
+ *
+ * Input Parameters:
+ * filep - A pointer to the file structure instance.
+ * buffer - The user-provided buffer from which data will be written.
+ * buflen - The number of bytes to be written.
+ *
+ * Returned Value:
+ * Always returns -EPERM (operation not permitted).
+ *
+ ****************************************************************************/
+
+static ssize_t dshot_write(FAR struct file *filep, FAR const char *buffer,
+ size_t buflen)
+{
+ return -EPERM;
+}
+
+/****************************************************************************
+ * Name: dshot_ioctl
+ *
+ * Description:
+ * The standard ioctl method. This is used to perform DShot-specific
+ * operations including setting throttle, configuring channels, and
+ * retrieving telemetry data.
+ *
+ * Input Parameters:
+ * filep - A pointer to the file structure instance.
+ * cmd - The ioctl command code.
+ * arg - The argument provided with the ioctl command.
+ *
+ * Returned Value:
+ * OK on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+static int dshot_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
+{
+ FAR struct inode *inode = filep->f_inode;
+ FAR struct dshot_upperhalf_s *upper = inode->i_private;
+ FAR struct dshot_lowerhalf_s *lower = upper->dev;
+ int ret;
+
+ ret = nxmutex_lock(&upper->lock);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ switch (cmd)
+ {
+ case DSHOTIOC_CONFIGURE:
+ {
+ FAR struct dshot_config_s *cfg =
+ (FAR struct dshot_config_s *)((uintptr_t)arg);
+
+ if (cfg == NULL)
+ {
+ ret = -EINVAL;
+ break;
+ }
+
+ DEBUGASSERT(lower->ops->configure);
+ ret = lower->ops->configure(lower, cfg);
+ upper->bidir = cfg->bidir;
+ }
+ break;
+
+ case DSHOTIOC_SET_THROTTLE:
+ {
+ FAR struct dshot_throttle_s *req =
+ (FAR struct dshot_throttle_s *)((uintptr_t)arg);
+ uint16_t packets[DSHOT_NCHANNELS];
+ int i;
+
+ if (req == NULL)
+ {
+ ret = -EINVAL;
+ break;
+ }
+
+ /* Retrieve the latest telemetry */
+
+ if (req->telemetry_req != 0)
+ {
+ ret = dshot_update_telemetry(upper, req->telemetry_req);
+ if (ret < 0)
+ {
+ break;
+ }
+
+ dshot_copy_telemetry(upper, req->ch_telemetry,
+ req->telemetry_req);
+ }
+
+ /* Build DShot packets for each channel */
+
+ for (i = 0; i < DSHOT_NCHANNELS; i++)
+ {
+ if ((req->ch_mask & (1u << i)) != 0)
+ {
+ bool telemetry = (req->telemetry_req & (1u << i)) != 0;
+ packets[i] =
+ dshot_build_packet(req->throttle[i], telemetry,
+ upper->bidir);
+ }
+ }
+
+ /* Send the packets */
+
+ DEBUGASSERT(lower->ops->send_command);
+ ret = lower->ops->send_command(lower, packets, req->ch_mask);
+ }
+ break;
+
+ case DSHOTIOC_GET_TELEMETRY:
+ {
+ FAR struct dshot_telemetry_s *tlm =
+ (FAR struct dshot_telemetry_s *)((uintptr_t)arg);
+
+ if (tlm == NULL)
+ {
+ ret = -EINVAL;
+ break;
+ }
+
+ ret = dshot_update_telemetry(upper, tlm->ch_mask);
+ if (ret < 0)
+ {
+ break;
+ }
+
+ dshot_copy_telemetry(upper, tlm->ch_telemetry, tlm->ch_mask);
+ ret = OK;
+ }
+ break;
+
+ default:
+ {
+ if (lower->ops->ioctl != NULL)
+ {
+ ret = lower->ops->ioctl(lower, cmd, arg);
+ }
+ else
+ {
+ ret = -ENOTTY;
+ }
+ }
+ break;
+ }
+
+ nxmutex_unlock(&upper->lock);
+ return ret;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: dshot_register
+ *
+ * Description:
+ * This function binds an instance of a "lower half" DShot driver with the
+ * "upper half" DShot device and registers that device so that it can be
+ * used by application code.
+ *
+ * When this function is called, the "lower half" driver should be in the
+ * reset state (as if the shutdown() method had already been called).
+ *
+ * Input Parameters:
+ * path - The full path to the driver to be registered in the NuttX pseudo-
+ * filesystem. The recommended convention is to name all DShot drivers
+ * as "/dev/dshot0", "/dev/dshot1", etc., where the driver path differs
+ * only in the "minor" number at the end of the device name.
+ * dev - A pointer to an instance of lower half DShot driver. This
+ * instance is bound to the DShot driver and must persist as long as the
+ * driver persists.
+ *
+ * Returned Value:
+ * Zero on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+int dshot_register(FAR const char *path, FAR struct dshot_lowerhalf_s *dev)
+{
+ FAR struct dshot_upperhalf_s *upper;
+ int ret;
+
+ if (dev == NULL || dev->ops == NULL)
+ {
+ return -EINVAL;
+ }
+
+ upper = kmm_zalloc(sizeof(struct dshot_upperhalf_s));
+ if (upper == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ nxmutex_init(&upper->lock);
+ upper->dev = dev;
+
+ ret = register_driver(path, &g_dshotops, 0666, upper);
+ if (ret < 0)
+ {
+ nxmutex_destroy(&upper->lock);
+ kmm_free(upper);
+ return ret;
+ }
+
+ return OK;
+}
+
+#endif /* CONFIG_DSHOT */
diff --git a/include/nuttx/fs/ioctl.h b/include/nuttx/fs/ioctl.h
index a231b827dd0..6a520f24a41 100644
--- a/include/nuttx/fs/ioctl.h
+++ b/include/nuttx/fs/ioctl.h
@@ -113,6 +113,7 @@
#define _1WIREBASE (0x4500) /* 1WIRE ioctl commands */
#define _EEPIOCBASE (0x4600) /* EEPROM driver ioctl commands */
#define _PTPBASE (0x4700) /* PTP ioctl commands */
+#define _DSHOTIOCBASE (0x4800) /* Dshot device ioctl commands */
#define _WLIOCBASE (0x8b00) /* Wireless modules ioctl network commands */
/* boardctl() commands share the same number space */
@@ -807,6 +808,13 @@
#define _PTPIOCVALID(c) (_IOC_TYPE(c)==_PTPBASE)
#define _PTPIOC(nr) _IOC(_PTPBASE,nr)
+/* DSHOT ioctl definitions (see nuttx/timers/dshot.h) ***********************/
+
+/* see nuttx/include/timers/dshot.h */
+
+#define _DSHOTIOCVALID(c) (_IOC_TYPE(c)==_DSHOTIOCBASE)
+#define _DSHOTIOC(nr) _IOC(_DSHOTIOCBASE,nr)
+
/****************************************************************************
* Public Type Definitions
****************************************************************************/
diff --git a/include/nuttx/timers/dshot.h b/include/nuttx/timers/dshot.h
new file mode 100644
index 00000000000..78f0c65ab59
--- /dev/null
+++ b/include/nuttx/timers/dshot.h
@@ -0,0 +1,312 @@
+/****************************************************************************
+ * include/nuttx/timers/dshot.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_DSHOT_H
+#define __INCLUDE_NUTTX_TIMERS_DSHOT_H
+
+/* Common definitions for DShot drivers */
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/compiler.h>
+#include <nuttx/fs/ioctl.h>
+
+#include <time.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#ifdef CONFIG_DSHOT_NCHANNELS
+# if CONFIG_DSHOT_NCHANNELS > 16
+# error "max 16 channels supported"
+# endif
+# define DSHOT_NCHANNELS CONFIG_DSHOT_NCHANNELS
+#else
+# define DSHOT_NCHANNELS 1
+#endif
+
+/* IOCTL Commands ***********************************************************/
+
+/* Configure the DShot instance; speed, bidirectional mode, active channels */
+
+#define DSHOTIOC_CONFIGURE _DSHOTIOC(0x01)
+
+/* Write throttle data or special command for one or more channels */
+
+#define DSHOTIOC_SET_THROTTLE _DSHOTIOC(0x02)
+
+/* Retrieve telemetry from the last successful bidirectional RX */
+
+#define DSHOTIOC_GET_TELEMETRY _DSHOTIOC(0x03)
+
+/* DSHOT Speeds *************************************************************/
+
+#define DSHOT_SPEED_150 150000u
+#define DSHOT_SPEED_300 300000u
+#define DSHOT_SPEED_600 600000u
+#define DSHOT_SPEED_1200 1200000u
+#define DSHOT_SPEED_2400 2400000u
+#define DSHOT_SPEED_3600 3600000u
+
+/* DSHOT special commands (throttle values 0-47)*****************************/
+
+#define DSHOT_CMD_MOTOR_STOP 0
+#define DSHOT_CMD_BEEP1 1
+#define DSHOT_CMD_BEEP2 2
+#define DSHOT_CMD_BEEP3 3
+#define DSHOT_CMD_BEEP4 4
+#define DSHOT_CMD_BEEP5 5
+#define DSHOT_CMD_ESC_INFO 6
+#define DSHOT_CMD_SPIN_DIRECTION_1 7
+#define DSHOT_CMD_SPIN_DIRECTION_2 8
+#define DSHOT_CMD_3D_MODE_OFF 9
+#define DSHOT_CMD_3D_MODE_ON 10
+#define DSHOT_CMD_SETTINGS_REQUEST 11
+#define DSHOT_CMD_SAVE_SETTINGS 12
+#define DSHOT_CMD_EXTENDED_TELEMETRY_ENABLE 13
+#define DSHOT_CMD_EXTENDED_TELEMETRY_DISABLE 14
+
+/* 15 - 19 Unassigned */
+
+#define DSHOT_CMD_SPIN_DIRECTION_NORMAL 20
+#define DSHOT_CMD_SPIN_DIRECTION_REVERSED 21
+#define DSHOT_CMD_LED0_ON 22
+#define DSHOT_CMD_LED1_ON 23
+#define DSHOT_CMD_LED2_ON 24
+#define DSHOT_CMD_LED3_ON 25
+#define DSHOT_CMD_LED0_OFF 26
+#define DSHOT_CMD_LED1_OFF 27
+#define DSHOT_CMD_LED2_OFF 28
+#define DSHOT_CMD_LED3_OFF 29
+
+#define DSHOT_CMD_AUDIO_STREAM_MODE 30
+#define DSHOT_CMD_SILENT_MODE 31
+
+#define DSHOT_CMD_SIGNAL_LINE_TELEMETRY_DISABLE 32
+#define DSHOT_CMD_SIGNAL_LINE_TELEMETRY_ENABLE 33
+#define DSHOT_CMD_SIGNAL_LINE_CONTINUOUS_ERPM_TELEMETRY 34
+#define DSHOT_CMD_SIGNAL_LINE_CONTINUOUS_ERPM_PERIOD_TELEMETRY 35
+
+/* 36 - 41 Unassigned */
+
+#define DSHOT_CMD_SIGNAL_LINE_TEMPERATURE_TELEMETRY 42
+#define DSHOT_CMD_SIGNAL_LINE_VOLTAGE_TELEMETRY 43
+#define DSHOT_CMD_SIGNAL_LINE_CURRENT_TELEMETRY 44
+#define DSHOT_CMD_SIGNAL_LINE_CONSUMPTION_TELEMETRY 45
+#define DSHOT_CMD_SIGNAL_LINE_ERPM_TELEMETRY 46
+#define DSHOT_CMD_SIGNAL_LINE_ERPM_PERIOD_TELEMETRY 47
+
+/* Extended telemetry types *************************************************/
+
+#define DSHOT_EDT_TYPE_TEMP 0x02 /* Temperature in C */
+#define DSHOT_EDT_TYPE_V 0x04 /* Voltage: 0.25V per step */
+#define DSHOT_EDT_TYPE_A 0x06 /* Current in Amp */
+#define DSHOT_EDT_TYPE_DBG1 0x08 /* Debug value 1 */
+#define DSHOT_EDT_TYPE_DBG2 0x0A /* Debug value 2 */
+#define DSHOT_EDT_TYPE_DBG3 0x0C /* Debug value 3 */
+#define DSHOT_EDT_TYPE_STATE 0x0E /* State/Event */
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/* Configuration argument for DSHOTIOC_CONFIGURE
+ *
+ * freq - Bit rate in Hz: DSHOT_SPEED_150/300/600/1200/2400/3600
+ * telem_freq - Telemetry frequency. Use 1.25 * freq for standard speed
+ * (e.g., 750kHz for DShot600). Some ESCs may require
+ * different values (e.g., 1.15 * freq for T-motor F55A).
+ * active_mask - Bitmask of active channels (bit0=ch0, bit1=ch1, etc.)
+ * bidir - Enable bidirectional DShot (inverted command data,
+ * response on same line).
+ */
+
+struct dshot_config_s
+{
+ uint32_t freq;
+ uint32_t telem_freq;
+ uint16_t active_mask;
+ bool bidir;
+};
+
+/* Telemetry data for a single DShot channel. Contains eRPM, extended
+ * telemetry type/value, and timestamp of the last valid response.
+ *
+ * erpm - eRPM value from telemetry response.
+ * edt_type - Extended telemetry type (if supported).
+ * edt_value - Extended telemetry value (if supported).
+ * timestamp - Timestamp of the last valid response.
+ */
+
+struct dshot_ch_telemetry_s
+{
+ uint16_t erpm;
+ uint8_t edt_type;
+ uint8_t edt_value;
+ struct timespec timestamp;
+};
+
+/* Telemetry result for DSHOTIOC_GET_TELEMETRY ioctl command.
+ * Contains bitmask of channels to read and telemetry data for each.
+ *
+ * ch_mask - Bitmask of logical channels to read telemetry from.
+ * ch_telemetry - Telemetry data array for each channel.
+ */
+
+struct dshot_telemetry_s
+{
+ uint16_t ch_mask;
+ struct dshot_ch_telemetry_s ch_telemetry[DSHOT_NCHANNELS];
+};
+
+/* Per-channel throttle value for DSHOTIOC_SET_THROTTLE ioctl command.
+ * Specifies throttle values, channel mask, and optional telemetry
+ * request for efficient batching of operations.
+ *
+ * throttle - Throttle value array (0 = disarm, 48-2047 = armed range,
+ * or special command values).
+ * ch_mask - Bitmask of logical channels to set.
+ * telemetry_req - Bitmask: bit N requests telemetry from channel N.
+ * ch_telemetry - Telemetry data from previous request. Allows reading
+ * previous telemetry response without separate ioctl.
+ */
+
+struct dshot_throttle_s
+{
+ uint16_t throttle[DSHOT_NCHANNELS];
+ uint16_t ch_mask;
+ uint16_t telemetry_req;
+ struct dshot_ch_telemetry_s ch_telemetry[DSHOT_NCHANNELS];
+};
+
+/* Raw telemetry frame captured from the DShot signal line.
+ * Contains the raw GCR-encoded packet and capture timestamp.
+ *
+ * raw - Raw 20-bit GCR encoded packet data.
+ * timestamp - Capture timestamp of the raw packet.
+ */
+
+struct dshot_raw_telemetry_s
+{
+ uint32_t raw;
+ struct timespec timestamp;
+};
+
+/* Lower-half driver operations table. Provides callback functions for
+ * the upper-half driver to control DShot hardware. All methods except
+ * ioctl are mandatory.
+ *
+ * setup - Called when driver is opened. Configure and initialize
+ * device for use.
+ * shutdown - Called when driver is closed. Stop output and free
+ * hardware resources.
+ * configure - Configure speed, active channels, and bidirectional
+ * mode according to dshot_config_s.
+ * send_command - Send DShot command packets to specified channels.
+ * get_raw_telemetry - Fetch raw telemetry packets from specified channels.
+ * ioctl - Optional platform-specific ioctl handler.
+ */
+
+struct dshot_lowerhalf_s;
+struct dshot_ops_s
+{
+ CODE int (*setup)(FAR struct dshot_lowerhalf_s *dev);
+
+ CODE int (*shutdown)(FAR struct dshot_lowerhalf_s *dev);
+
+ CODE int (*configure)(FAR struct dshot_lowerhalf_s *dev,
+ FAR const struct dshot_config_s *cfg);
+
+ CODE int (*send_command)(FAR struct dshot_lowerhalf_s *dev,
+ FAR const uint16_t *packets,
+ uint16_t ch_mask);
+
+ CODE int (*get_raw_telemetry)(FAR struct dshot_lowerhalf_s *dev,
+ FAR struct dshot_raw_telemetry_s *raw,
+ uint16_t ch_mask);
+
+ CODE int (*ioctl)(FAR struct dshot_lowerhalf_s *dev,
+ int cmd, unsigned long arg);
+};
+
+/* Public representation of lower-half state. */
+
+struct dshot_lowerhalf_s
+{
+ FAR const struct dshot_ops_s *ops;
+};
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+#ifdef __cplusplus
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+/****************************************************************************
+ * "Upper-Half" DShot Driver Interfaces
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: dshot_register
+ *
+ * Description:
+ * This function binds an instance of a "lower half" timer driver with the
+ * "upper half" DSHOT device and registers that device so that can be used
+ * by application code.
+ *
+ * When this function is called, the "lower half" driver should be in the
+ * reset state (as if the shutdown() method had already been called).
+ *
+ * Input Parameters:
+ * path - The full path to the driver to be registered in the NuttX pseudo-
+ * filesystem.
+ * dev - A pointer to an instance of lower half DShot driver. This instance
+ * is bound to the DShot driver and must persist as long as the driver
+ * persists.
+ *
+ * Returned Value:
+ * Zero on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+int dshot_register(FAR const char *path, FAR struct dshot_lowerhalf_s *dev);
+
+#undef EXTERN
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __INCLUDE_NUTTX_TIMERS_DSHOT_H */