Paparazzi UAS  v7.0_unstable
Paparazzi is a free software Unmanned Aircraft System.
dshot_rpmCapture.c
Go to the documentation of this file.
2 #include <string.h>
3 #if DSHOT_STATISTICS
4 #include <stdutil.h>
5 #endif
6 
7 #if DSHOT_SPEED == 0
8 #error dynamic dshot speed is not yet implemented in DSHOT BIDIR
9 #endif
10 
11 #ifdef STM32H7XX
12 static const float TIM_FREQ_MHZ = (STM32_TIMCLK1 / 1e6d);
13 #else
14 // MUST FIXE : on F4 and F7, dshot bidir is not compatible with 84MHz timers
15 static const float TIM_FREQ_MHZ = (STM32_SYSCLK / 1e6d);
16 #endif
17 static const float bit1t_us = TIM_FREQ_MHZ * 6.67d * 4 / 5;
18 static const float speed_factor = DSHOT_SPEED / 150;
19 
21 static const uint32_t TIM_PRESCALER = 1U;
22 
23 /* gpt based timout is dependant of CPU speed, because
24  the more time you spend in the ISR and context switches,
25  the less you have to wait for telemetry frame completion */
26 #if defined STM32H7XX
27 #define SWTICH_TO_CAPTURE_BASE_TIMOUT 38U
28 #elif defined STM32F7XX
29 #define SWTICH_TO_CAPTURE_BASE_TIMOUT 32U
30 #else
31 #define SWTICH_TO_CAPTURE_BASE_TIMOUT 28U
32 #endif
33 
34 
35 static void startCapture(DshotRpmCapture *drcp);
36 static void stopCapture(DshotRpmCapture *drcp);
37 static void initCache(DshotRpmCapture *drcp);
38 static uint32_t processErpsDmaBuffer(const uint16_t *capture, size_t dmaLen);
39 static void buildDmaConfig(DshotRpmCapture *drcp);
40 static void dmaErrCb(DMADriver *dmad, dmaerrormask_t em);
41 static void gptCb(GPTDriver *gptd);
42 
43 #if DSHOT_STATISTICS
44 static volatile dmaerrormask_t lastErr;
45 static volatile uint32_t dmaErrs = 0;
46 #endif
47 
48 #if !defined __GNUC__ || __GNUC__ < 13
49 #define nullptr NULL
50 #endif
51 
52 
53 
54 static const struct {
55  uint32_t active;
56  uint32_t dier;
57 } activeDier[4] = {
59  TIM_DIER_CC1DE | TIM_DIER_TDE},
61  TIM_DIER_CC1DE | TIM_DIER_CC2DE | TIM_DIER_TDE},
63  TIM_DIER_CC1DE | TIM_DIER_CC2DE | TIM_DIER_CC3DE | TIM_DIER_TDE},
65  TIM_DIER_CC1DE | TIM_DIER_CC2DE | TIM_DIER_CC3DE | TIM_DIER_CC4DE | TIM_DIER_TDE}
66 };
67 
68 static const TimICConfig timicCfgSkel = {
69  .timer = NULL,
70  .capture_cb = NULL,
71  .overflow_cb = NULL,
72  .mode = TIMIC_INPUT_CAPTURE,
73  .active = activeDier[DSHOT_CHANNELS-1].active,
74  .dier = activeDier[DSHOT_CHANNELS-1].dier,
75  .dcr = 0, // direct access, not using DMAR
76  .prescaler = TIM_PRESCALER,
77  .arr = 0xffffffff
78 };
79 
80 /*
81 this driver need additional field in gptDriver structure (in halconf.h):
82 one should add following line in the GPT section of halconf.h :
83 #define GPT_DRIVER_EXT_FIELDS void *user_data;
84 */
85 #ifndef GPT_DRIVER_EXT_FIELDS
86 #error dshot rpmcapture driver involve defining #define GPT_DRIVER_EXT_FIELDS void *user_data; in halconf.h
87 #else
88 // redefine at the needed value to trigger error if defined with another value
89 #define GPT_DRIVER_EXT_FIELDS void *user_data;
90 #endif
91 
92 static const GPTConfig gptCfg = {
93  .frequency = 1000U * 1000U, // 1MHz
94  .callback = &gptCb,
95  .cr2 = 0,
96  .dier = 0
97 };
98 
99 
109  stm32_tim_t *timer)
110 {
111  memset(drcp, 0, sizeof(*drcp));
112  drcp->config = cfg;
113  drcp->icCfg = timicCfgSkel;
114  drcp->icCfg.timer = timer;
115  initCache(drcp);
116 }
117 
125 {
126  stopCapture(drcp);
127 }
128 
129 #if DSHOT_STATISTICS
136 uint32_t dshotRpmGetDmaErr(void) {
137 return dmaErrs;
138 }
139 #endif
140 
141 
150 {
151  startCapture(drcp);
152  static systime_t ts = 0;
153  if (ts == 0)
154  ts = chVTGetSystemTimeX();
155 
156  memset(drcp->config->dma_capture->dma_buf, 0, sizeof(drcp->config->dma_capture->dma_buf));
157 
158  for (size_t i = 0; i < DSHOT_CHANNELS; i++) {
159  dmaStartTransfert(&drcp->dmads[i], &drcp->icd.config->timer->CCR[i],
160  drcp->config->dma_capture->dma_buf[i].buf,
162  }
163 
164  osalSysLock();
165  // dma end callback will resume the thread upon completion of ALL dma transaction
166  // else, the timeout will take care of thread resume
167  static const sysinterval_t timeoutUs = SWTICH_TO_CAPTURE_BASE_TIMOUT +
168  (120U * 150U / DSHOT_SPEED);
169  //palSetLine(LINE_LA_DBG_1);
170  gptStartOneShotI(drcp->config->gptd, timeoutUs);
171  // palClearLine(LINE_LA_DBG_1);
172  chThdSuspendS(&drcp->dmads[0].thread);
173  // palSetLine(LINE_LA_DBG_1);
174  // chSysPolledDelayX(1);
175  // palClearLine(LINE_LA_DBG_1);
176 
177  for (size_t i = 0; i < DSHOT_CHANNELS; i++)
178  dmaStopTransfertI(&drcp->dmads[i]);
179 
180  osalSysUnlock();
181  stopCapture(drcp);
182 
183 #if DSHOT_STATISTICS
184  const rtcnt_t tstart = chSysGetRealtimeCounterX();
185 #endif
186 
187  for (size_t i = 0; i < DSHOT_CHANNELS; i++) {
188 #if __DCACHE_PRESENT
189  if (drcp->config->dcache_memory_in_use == true) {
190  cacheBufferInvalidate(drcp->config->dma_capture->dma_buf[i].buf,
192  }
193 #endif
194  drcp->rpms[i] = processErpsDmaBuffer(drcp->config->dma_capture->dma_buf[i].buf,
196  dmaGetTransactionCounter(&drcp->dmads[i]));
197  }
198 #if DSHOT_STATISTICS
199  const rtcnt_t tstop = chSysGetRealtimeCounterX();
200  drcp->nbDecode += DSHOT_CHANNELS;
201  drcp->accumDecodeTime += (tstop - tstart);
202 #endif
203 
204 #if defined(DFREQ) && (DFREQ < 10) && (DFREQ != 0)
205  DebugTrace("dma out on %s", msg == MSG_OK ? "completion" : "timeout");
206 #endif
207 }
208 
209 #if DSHOT_STATISTICS
217 void dshotRpmTrace(DshotRpmCapture *drcp, uint8_t index)
218 {
219  for (size_t i = 0; i < DSHOT_CHANNELS; i++) {
220  if ((index != 0xff) && (index != i))
221  continue;
222  uint16_t cur = drcp->config->dma_capture->dma_buf[i].buf[0];
223  for (size_t j = 1; j < DSHOT_DMA_DATA_LEN; j++) {
224  const uint16_t dur = drcp->config->dma_capture->dma_buf[i].buf[j] - cur;
225  cur = drcp->config->dma_capture->dma_buf[i].buf[j];
226  chprintf(chp, "[%u] %u, ", i, dur);
227  }
228  DebugTrace("");
229  }
230  DebugTrace("");
231 }
232 #endif
233 
234 
235 /*
236 # _ __ _ _
237 # | '_ \ (_) | |
238 # | |_) | _ __ _ __ __ __ _ | |_ ___
239 # | .__/ | '__| | | \ \ / / / _` | | __| / _ \
240 # | | | | | | \ V / | (_| | \ |_ | __/
241 # |_| |_| |_| \_/ \__,_| \__| \___|
242 */
243 
250 static void buildDmaConfig(DshotRpmCapture *drcp)
251 {
252  static const DMAConfig skel = (DMAConfig) {
253  .stream = 0,
254 #if STM32_DMA_SUPPORTS_DMAMUX
255  .dmamux = 0,
256 #else
257  .channel = 0,
258 #endif
259  .inc_peripheral_addr = false,
260  .inc_memory_addr = true,
261  .op_mode = DMA_ONESHOT,
262  .end_cb = NULL,
263  .error_cb = &dmaErrCb,
264  //.error_cb = NULL,
265 #if STM32_DMA_USE_ASYNC_TIMOUT
266  .timeout = TIME_MS2I(100),
267 #endif
268  .direction = DMA_DIR_P2M,
269  .dma_priority = 1,
270  .irq_priority = 7,
271  .psize = 2,
272  .msize = 2,
273  .pburst = 0,
274  .mburst = 0,
275  .fifo = 4,
276  .periph_inc_size_4 = false,
277  .transfert_end_ctrl_by_periph = false
278  };
279 
280  for (size_t i = 0; i < DSHOT_CHANNELS; i++) {
281  drcp->dmaCfgs[i] = skel;
282  drcp->dmaCfgs[i].stream = drcp->config->dma_streams[i].stream;
283 #if STM32_DMA_SUPPORTS_DMAMUX
284  drcp->dmaCfgs[i].dmamux = drcp->config->dma_streams[i].dmamux;
285 #else
286  drcp->dmaCfgs[i].channel = drcp->config->dma_streams[i].channel;
287 #endif
288  }
289 }
290 
297 static void initCache(DshotRpmCapture *drcp)
298 {
299  const DshotRpmCaptureConfig *cfg = drcp->config;
300  timIcObjectInit(&drcp->icd);
301  timIcStart(&drcp->icd, &drcp->icCfg);
302  cfg->gptd->user_data = drcp; // user data in GPT driver
303 
304  buildDmaConfig(drcp);
305  for (size_t i=0; i < DSHOT_CHANNELS; i++) {
306  dmaObjectInit(&drcp->dmads[i]);
307  dmaStart(&drcp->dmads[i], &drcp->dmaCfgs[i]);
308  }
309 
310  timerDmaCache_cache(&drcp->cache, &drcp->dmads[0], drcp->icCfg.timer);
311  dshotRpmCaptureStop(drcp);
312 }
313 
320 static void startCapture(DshotRpmCapture *drcp)
321 {
322  gptStart(drcp->config->gptd, &gptCfg);
323  timerDmaCache_restore(&drcp->cache, &drcp->dmads[0], drcp->icCfg.timer);
324  timIcStartCapture(&drcp->icd);
325 }
326 
333 static void stopCapture(DshotRpmCapture *drcp)
334 {
335  timIcStopCapture(&drcp->icd);
336  timIcRccDisable(&drcp->icd);
337  gptStop(drcp->config->gptd);
338 }
339 
340 
347 static uint32_t processErpsDmaBuffer(const uint16_t *capture, size_t dmaLen)
348 {
349  static const size_t frameLen = 20U;
350  uint32_t erpsVal = 0;
351  uint_fast8_t bit = 0x0;
352  uint_fast8_t bitIndex = 0;
353  uint_fast16_t prec = capture[0];
354 
355  for (size_t i = 1U; i < dmaLen; i++) {
356  const uint_fast16_t len = capture[i] - prec;
357  prec = capture[i];
358 
359  // GRC encoding garanties that there can be no more than 3 consecutives bits at the same level
360  // made some test to replace division by multiplication + shift without any speed gain
361  const uint_fast8_t nbConsecutives = (len + (ERPS_BIT1_DUTY / 2U)) / ERPS_BIT1_DUTY;
362  if (bit) {
363  switch(nbConsecutives) {
364  case 1U: erpsVal |= (0b001 << (frameLen - bitIndex)); break;
365  case 2U: erpsVal |= (0b011 << (frameLen - bitIndex - 1U)); break;
366  default: erpsVal |= (0b111 << (frameLen - bitIndex - 2U)); break;
367  }
368  }
369  bit = ~bit; // flip bit
370  bitIndex += nbConsecutives;
371  }
372  // there can be several high bits hidden in the trailing high level signal
373  for (size_t j=bitIndex; j <= frameLen; j++)
374  erpsVal |= (1U << (frameLen - j));
375 
376  // alternative implementation for the trailing high level signal
377  // slower on M4
378  /* switch(frameLen - bitIndex) { */
379  /* case 0U: erpsVal |= 0b001; break; */
380  /* case 1U: erpsVal |= 0b011; break; */
381  /* case 2U: erpsVal |= 0b111; break; */
382  /* default: erpsVal |= 0b1111; break; */
383  /* } */
384 
385  // DebugTrace("bit index = %u; erpsVal = 0x%lx", bitIndex, erpsVal);
386  return erpsVal;
387 }
388 
398 {
399 #if DSHOT_STATISTICS
400  lastErr = em;
401  dmaErrs++;
402 #else
403  (void) em;
404 #endif
405 }
406 
414 static void gptCb(GPTDriver *gptd)
415 {
416  chSysLockFromISR();
417  DshotRpmCapture *drcd = (DshotRpmCapture *) gptd->user_data;
418  chThdResumeI(&drcd->dmads[0].thread, MSG_TIMEOUT);
419  chSysUnlockFromISR();
420 }
timeDelta_t timeoutUs
static const uint32_t ERPS_BIT1_DUTY
static uint32_t processErpsDmaBuffer(const uint16_t *capture, size_t dmaLen)
convert DMA buffer full of pulse length into raw ERPS frame
static const uint32_t TIM_PRESCALER
static void buildDmaConfig(DshotRpmCapture *drcp)
build dma configuration structure
static void gptCb(GPTDriver *gptd)
dma timeout ISR :
void dshotRpmCaptureStart(DshotRpmCapture *drcp, const DshotRpmCaptureConfig *cfg, stm32_tim_t *timer)
Configures and activates the DSHOT ERPS CAPTURE driver.
static void dmaErrCb(DMADriver *dmad, dmaerrormask_t em)
dma error ISR :
static const GPTConfig gptCfg
static void startCapture(DshotRpmCapture *drcp)
start the DMA capture
static const struct @5 activeDier[4]
static const float bit1t_us
void dshotRpmCatchErps(DshotRpmCapture *drcp)
capture the DSHOT ERPS frame(s) : one frame for each DSHOT_CHANNELS
static const TimICConfig timicCfgSkel
static const float TIM_FREQ_MHZ
void dshotRpmCaptureStop(DshotRpmCapture *drcp)
stop the the DSHOT ERPS CAPTURE driver.
static void initCache(DshotRpmCapture *drcp)
build dma and timer registers cache
#define SWTICH_TO_CAPTURE_BASE_TIMOUT
static void stopCapture(DshotRpmCapture *drcp)
stop the DMA capture
static const float speed_factor
DshotDmaStreamChan dma_streams[DSHOT_CHANNELS]
: array of DMA stream for each capture channel
TimICDriver icd
: input capture timer driver
DMADriver dmads[DSHOT_CHANNELS]
: dma input capture drivers
#define DSHOT_DMA_EXTRADATA_LEN
TimICConfig icCfg
: input capture timer configuration
const DshotRpmCaptureConfig * config
: pointer to configuration structure
TimerDmaCache cache
: cache for timer and dma configuration
uint32_t rpms[DSHOT_CHANNELS]
: array of rpms
DshotRpmCaptureOneChannelDmaBuffer dma_buf[DSHOT_CHANNELS]
#define STM32_SYSCLK
DshotRpmCaptureDmaBuffer * dma_capture
: pointer to the input capture DMA buffer
uint16_t buf[DSHOT_DMA_DATA_LEN+DSHOT_DMA_EXTRADATA_LEN]
DMAConfig dmaCfgs[DSHOT_CHANNELS]
: dma input capture configuration
#define DSHOT_DMA_DATA_LEN
GPTDriver * gptd
: GPT Driver to manage microseconds timeout
: DSHOT Rpm Capture Driver configuration structure.
#define DSHOT_CHANNELS
#define DSHOT_SPEED
Base freq of DSHOT signal (in kHz) Possible values are: 150, 300, 600.
void chprintf(BaseSequentialStream *lchp, const char *fmt,...)
Definition: printf.c:395
void dmaObjectInit(DMADriver *dmap)
Definition: hal_stm32_dma.c:69
bool dmaStartTransfert(DMADriver *dmap, volatile void *periphp, void *mem0p, const size_t size)
Starts a DMA transaction.
void dmaStopTransfertI(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_P2M
PERIPHERAL to MEMORY
Definition: hal_stm32_dma.h:99
static size_t dmaGetTransactionCounter(DMADriver *dmap)
dmaerrormask_t
Possible DMA failure causes.
Definition: hal_stm32_dma.h:87
uint32_t stream
stream associated with transaction
uint8_t channel
DMA stream configuration structure.
Structure representing a DMA driver.
uint8_t msg[10]
Buffer used for general comunication over SPI (out buffer)
void timIcObjectInit(TimICDriver *timicp)
Initializes an input capture driver.
void timIcStopCapture(TimICDriver *timicp)
stop to capture
void timIcStart(TimICDriver *timicp, const TimICConfig *configp)
start an input capture driver
void timIcStartCapture(TimICDriver *timicp)
start to capture
void timIcRccDisable(const TimICDriver *const timicp)
@ CH1_BOTH_EDGES
@ CH3_BOTH_EDGES
@ CH4_BOTH_EDGES
@ CH2_BOTH_EDGES
@ TIMIC_INPUT_CAPTURE
const TimICConfig * config
Current configuration data.
stm32_tim_t * timer
hardware timer pointer (example : &STM32_TIM1)
TimIC Driver configuration structure.
void timerDmaCache_restore(const TimerDmaCache *tdcp, DMADriver *toDma, stm32_tim_t *toTim)
Definition: timerDmaCache.c:17
void timerDmaCache_cache(TimerDmaCache *tdcp, const DMADriver *fromDma, const stm32_tim_t *fromTim)
Definition: timerDmaCache.c:9
unsigned short uint16_t
Typedef defining 16 bit unsigned short type.
Definition: vl53l1_types.h:88
unsigned int uint32_t
Typedef defining 32 bit unsigned int type.
Definition: vl53l1_types.h:78
unsigned char uint8_t
Typedef defining 8 bit unsigned char type.
Definition: vl53l1_types.h:98