31 #include <stdnoreturn.h>
34 #include "mcu_periph/sys_time_arch.h"
49 #ifndef DSHOT_TELEMETRY_BAUD
50 #define DSHOT_TELEMETRY_BAUD 115200U
53 #ifndef DSHOT_BIDIR_EXTENTED_TELEMETRY
54 #define DSHOT_BIDIR_EXTENTED_TELEMETRY FALSE
59 #ifndef DSHOT_TELEMETRY_TIMEOUT_MS
60 #define DSHOT_TELEMETRY_TIMEOUT_MS 3
66 #define PWM_FREQ (STM32_TIMCLK1 / 1000U)
71 #define PWM_FREQ (STM32_SYSCLK / 2000U)
81 #define TICKS_PER_PERIOD 1000
85 #ifndef DSHOT_BIT0_DUTY_RATIO
86 #define DSHOT_BIT0_DUTY_RATIO 373U
90 # define DSHOT_FREQ (DSHOT_SPEED*1000)
91 # define DSHOT_BIT0_DUTY (DSHOT_PWM_PERIOD * DSHOT_BIT0_DUTY_RATIO / 1000U)
92 # define DSHOT_BIT1_DUTY (DSHOT_BIT0_DUTY*2)
94 # define DSHOT_FREQ (driver->config->speed_khz * 1000U)
95 # define DSHOT_BIT0_DUTY (driver->bit0Duty)
96 # define DSHOT_BIT1_DUTY (driver->bit1Duty)
98 #define TICK_FREQ (PWM_FREQ * TICKS_PER_PERIOD)
99 #define DSHOT_PWM_PERIOD (TICK_FREQ/DSHOT_FREQ)
102 #define DCR_DBL ((DSHOT_CHANNELS-1) << 8)
104 #define DCR_DBA(pwmd) (((uint32_t *) (&pwmd->tim->CCR) - ((uint32_t *) pwmd->tim)))
106 #define DSHOT_MAX_VALUE ((1U<<11U)-1U)
108 #define ARRAY_LEN(a) (sizeof(a)/sizeof(a[0]))
109 #define Min(x,y) (x < y ? x : y)
133 #if DSHOT_BIDIR_EXTENTED_TELEMETRY
156 chDbgAssert(
config->dma_buf != NULL,
".dma_buf must reference valid DshotDmaBuffer object");
162 memset((
void *)
config->dma_buf, 0,
sizeof(*(
config->dma_buf)));
165 static const SerialConfig tlmcfg = {
168 .cr2 = USART_CR2_STOP1_BITS,
172 static const uint32_t pwmPolarity =
DSHOT_BIDIR ? PWM_OUTPUT_ACTIVE_LOW : PWM_OUTPUT_ACTIVE_HIGH;
178 #if STM32_DMA_SUPPORTS_DMAMUX
181 .channel =
config->dma_channel,
184 .irq_priority = CORTEX_MAX_KERNEL_PRIORITY + 1,
186 .psize = timerWidthInBytes,
187 .msize = timerWidthInBytes,
189 .dcache_memory_in_use =
config->dcache_memory_in_use,
191 .inc_peripheral_addr =
false,
192 .inc_memory_addr =
true,
206 {.mode = pwmPolarity,
215 .cr2 = STM32_TIM_CR2_CCDS,
216 .dier = STM32_TIM_DIER_UDE
232 chDbgAssert(dmaOk ==
true,
"dshot dma start error");
236 chThdCreateStatic(driver->waDshotTlmRec,
sizeof(driver->waDshotTlmRec), NORMALPRIO,
278 chDbgAssert(dmaOk ==
true,
"dshot dma start error");
305 chDbgAssert(
false,
"dshotSetThrottle throttle error");
317 chDbgAssert(
false,
"dshotSetThrottle index error");
358 systime_t now = chVTGetSystemTimeX();
368 chDbgAssert(
false,
"dshotSetThrottle index error");
372 chThdSleepUntilWindowed(now, now + TIME_US2I(500));
408 const tprio_t currentPrio = chThdSetPriority(HIGHPRIO);
416 chMBPostTimeout(&driver->
mb, index, TIME_IMMEDIATE);
428 processBidirErpm(driver);
429 dshotRestart(driver);
430 chThdSetPriority(currentPrio);
489 #if DSHOT_BIDIR && DSHOT_BIDIR_EXTENTED_TELEMETRY
510 tlm->
ts = chVTGetSystemTimeX();
530 #if DSHOT_BIDIR_EXTENTED_TELEMETRY
534 updateTelemetryFromBidirEdt(&driver->erps, tlm);
560 #if DSHOT_BIDIR_EXTENTED_TELEMETRY
564 updateTelemetryFromBidirEdt(&driver->erps, tlm);
591 uint16_t csum = (dp->throttle << 1) | dp->telemetryRequest;
592 for (
int i = 0; i < 3; i++) {
597 dp->crc = ~(dp->crc);
604 .telemetryRequest = (tlmRequest ? 1 : 0),
614 dp->throttle = throttle;
615 dp->telemetryRequest = 0;
620 dp->telemetryRequest = tlmRequest ? 1 : 0;
638 if (timerWidth == 2) {
641 #if DSHOT_AT_LEAST_ONE_32B_TIMER
644 chSysHalt(
"use of 32 bit timer implies to define DSHOT_AT_LEAST_ONE_32B_TIMER to TRUE");
658 for (
int i = 0; i < 8; i++) {
659 crc_u = (crc_u & 0x80) ? 0x7 ^ (crc_u << 1) : (crc_u << 1);
668 for (
int i = 0; i < BufLen; i++) {
686 __attribute__((
const))
695 #if STM32_PWM_USE_TIM5
719 chRegSetThreadName(
"dshotTlmRec");
721 chMBFetchTimeout(&driver->
mb, &escIdx, TIME_INFINITE);
729 while (sdGetTimeout(driver->
config->
tlm_sd, TIME_IMMEDIATE) >= 0) {};
736 tlm.
frame.voltage = __builtin_bswap16(tlm.
frame.voltage);
737 tlm.
frame.current = __builtin_bswap16(tlm.
frame.current);
738 tlm.
frame.consumption = __builtin_bswap16(tlm.
frame.consumption);
739 tlm.
frame.rpm = __builtin_bswap16(tlm.
frame.rpm);
740 tlm.
ts = chVTGetSystemTimeX();
const DshotErps * DshotErpsSetFromFrame(DshotErps *derpsp, uint32_t frame)
initialise from GCR encoded frame
uint32_t DshotErpsGetRpm(const DshotErps *derpsp)
calculate and return rpm
uint32_t DshotErpsGetEperiod(const DshotErps *derpsp)
return eperiod from mantisse and exponent
bool DshotErpsCheckCrc4(const DshotErps *derpsp)
check packed validity
static uint16_t DshotErpsEdtStatus(const DshotErps *derpsp)
return status value
static uint8_t DshotErpsEdtTempCentigrade(const DshotErps *derpsp)
return temperature for a temperature telemetry frame
static uint16_t DshotErpsEdtStress(const DshotErps *derpsp)
return stress value
static EdtType DshotErpsEdtType(const DshotErps *derpsp)
return type of a telemetry frame
static uint16_t DshotErpsEdtCentiVolts(const DshotErps *derpsp)
return voltage for a voltage telemetry frame
static uint16_t DshotErpsEdtCurrentAmp(const DshotErps *derpsp)
return current intensity for a current telemetry frame
static bool DshotErpsIsEdt(const DshotErps *derpsp)
return true if current frame is a telemetry frame
ERPS complete frame, raw and decoded.
void dshotRpmCaptureStart(DshotRpmCapture *drcp, const DshotRpmCaptureConfig *cfg, stm32_tim_t *timer)
Configures and activates the DSHOT ERPS CAPTURE driver.
void dshotRpmCatchErps(DshotRpmCapture *drcp)
capture the DSHOT ERPS frame(s) : one frame for each DSHOT_CHANNELS
static uint32_t dshotRpmGetFrame(const DshotRpmCapture *drcp, uint8_t index)
return last collected erps frame
static void buildDshotDmaBuffer(DSHOTDriver *driver)
void dshotStart(DSHOTDriver *driver, const DSHOTConfig *config)
Configures and activates the DSHOT peripheral.
void dshotSetThrottle(DSHOTDriver *driver, const uint8_t index, const uint16_t throttle)
prepare throttle order for specified ESC
static size_t getTimerWidth(const PWMDriver *pwmp)
#define TICKS_PER_PERIOD
Ticks per period that let use any timer: does not care if linked to PCLK1 or PCLK2 tick_per_period wi...
uint32_t dshotGetTelemetryFrameCount(const DSHOTDriver *driver)
return number of telemetry succesfull frame since dshotStart
static uint8_t calculateCrc8(const uint8_t *Buf, const uint8_t BufLen)
DshotTelemetry dshotGetTelemetry(DSHOTDriver *driver, uint32_t index)
return last received telemetry data
static void setDshotPacketThrottle(DshotPacket *const dp, const uint16_t throttle)
void dshotSendThrottles(DSHOTDriver *driver, const uint16_t throttles[DSHOT_CHANNELS])
send throttle packed order to all of the ESCs
static void setCrc4(DshotPacket *dp)
uint32_t dshotGetCrcErrorCount(const DSHOTDriver *driver)
return number of telemetry crc error since dshotStart
void dshotStop(DSHOTDriver *driver)
stop the DSHOT driver and free the related resources : pwm driver and dma driver.
#define DSHOT_TELEMETRY_TIMEOUT_MS
Telemetry timeout in ms.
#define DSHOT_BIT0_DUTY_RATIO
void dshotSendFrame(DSHOTDriver *driver)
send throttle order
static noreturn void dshotTlmRec(void *arg)
static void setDshotPacketTlm(DshotPacket *const dp, const bool tlmRequest)
#define DSHOT_TELEMETRY_BAUD
Baudrate of the serial link used for telemetry data Can depend on the ESC, but only 115k have been us...
static DshotPacket makeDshotPacket(const uint16_t throttle, const bool tlmRequest)
void dshotSendSpecialCommand(DSHOTDriver *driver, const uint8_t index, const dshot_special_commands_t specmd)
send special order to one of the ESC (BHELIX, KISS, ...)
static uint8_t updateCrc8(uint8_t crc, uint8_t crc_seed)
DSHOT driver based on ChibiOS.
mutex_t tlmMtx[DSHOT_CHANNELS]
#define DSHOT_PRE_FRAME_SILENT_SYNC_BITS
SerialDriver * tlm_sd
if non null : dshot telemetry serial driver
#define DSHOT_BIT_WIDTHS
DMA buffer size and number of channels.
PWMDriver * pwmp
PWM driver that feed up to 4 dshot lines.
#define DSHOT_DMA_BUFFER_SIZE
uint16_t widths16[DSHOT_DMA_BUFFER_SIZE][DSHOT_CHANNELS]
#define DSHOT_CHANNEL_FIRST_INDEX
DshotTelemetry dt[DSHOT_CHANNELS]
DshotTelemetryFrame frame
#define DSHOT_ALL_MOTORS
special value for index : send order to all channels
#define DSHOT_BIDIR_TLM_EDT
DshotDmaBuffer * dma_buf
dshot dma buffer, should be defined in a non Dcached region
dshot_special_commands_t
DSHOT special commands (0-47) for KISS and BLHELI ESC.
@ DSHOT_CMD_SILENT_MODE_ON_OFF
@ DSHOT_CMD_SAVE_SETTINGS
@ DSHOT_CMD_AUDIO_STREAM_MODE_ON_OFF
@ DSHOT_CMD_SETTINGS_REQUEST
@ DSHOT_CMD_BIDIR_EDT_MODE_OFF
@ DSHOT_CMD_SPIN_DIRECTION_1
@ DSHOT_CMD_SPIN_DIRECTION_2
@ DSHOT_CMD_BIDIR_EDT_MODE_ON
#define DSHOT_BIDIR_ERR_CRC
special values returned by dshotGetRpm function
DshotPacket dp[DSHOT_CHANNELS]
DSHOT Driver configuration structure.
telemetry packed as sent by some KISS ESC
void dmaObjectInit(DMADriver *dmap)
void dmaStop(DMADriver *dmap)
Deactivates the DMA peripheral.
void dmaStopTransfert(DMADriver *dmap)
Stops an ongoing transaction.
bool dmaStart(DMADriver *dmap, const DMAConfig *cfg)
Configures and activates the DMA peripheral.
@ DMA_ONESHOT
One transert then stop
@ DMA_DIR_M2P
MEMORY to PERIPHERAL
uint32_t stream
stream associated with transaction
static msg_t dmaTransfert(DMADriver *dmap, volatile void *periphp, void *mem0p, const size_t size)
volatile dmastate_t state
Driver state.
DMA stream configuration structure.
#define STM32_PWM_USE_TIM2
static const struct usb_config_descriptor config
uint32_t crc_errors
number of crc errors
DshotPackets dshotMotors
object managing dma control frame for outgoing command
mailbox_t mb
mailbox for dshot telemetry thread
DMADriver dmap
DMA driver associated with pwm timer.
PWMConfig pwm_conf
PWM config associated with pwm timer.
const DSHOTConfig * config
DMA config associated with pwm timer.
msg_t _mbBuf[1]
mailbox buffer for dshot telemetry thread
uint32_t tlm_frame_nb
number of sucessful telemetry frame received
DMAConfig dma_conf
DMA config associated with pwm timer.
unsigned short uint16_t
Typedef defining 16 bit unsigned short type.
unsigned int uint32_t
Typedef defining 32 bit unsigned int type.
unsigned char uint8_t
Typedef defining 8 bit unsigned char type.