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 + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#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 +#include +#include + +#include + +/**************************************************************************** + * 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 */