Paparazzi UAS v7.0_unstable
Paparazzi is a free software Unmanned Aircraft System.
Loading...
Searching...
No Matches
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
12static 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
15static const float TIM_FREQ_MHZ = (STM32_SYSCLK / 1e6d);
16#endif
17static const float bit1t_us = TIM_FREQ_MHZ * 6.67d * 4 / 5;
18static const float speed_factor = DSHOT_SPEED / 150;
19
21static 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
36static void stopCapture(DshotRpmCapture *drcp);
37static void initCache(DshotRpmCapture *drcp);
41static void gptCb(GPTDriver *gptd);
42
43#if DSHOT_STATISTICS
44static volatile dmaerrormask_t lastErr;
45static volatile uint32_t dmaErrs = 0;
46#endif
47
48#if !defined __GNUC__ || __GNUC__ < 13
49#define nullptr NULL
50#endif
51
52
53
54static const struct {
55 uint32_t active;
56 uint32_t dier;
57} activeDier[4] = {
66};
67
68static 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/*
81this driver need additional field in gptDriver structure (in halconf.h):
82one 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
92static 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;
116}
117
128
129#if DSHOT_STATISTICS
137return dmaErrs;
138}
139#endif
140
141
150{
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
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
182
183#if DSHOT_STATISTICS
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
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
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
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
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
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);
312}
313
321{
322 gptStart(drcp->config->gptd, &gptCfg);
323 timerDmaCache_restore(&drcp->cache, &drcp->dmads[0], drcp->icCfg.timer);
324 timIcStartCapture(&drcp->icd);
325}
326
334{
335 timIcStopCapture(&drcp->icd);
336 timIcRccDisable(&drcp->icd);
337 gptStop(drcp->config->gptd);
338}
339
340
348{
349 static const size_t frameLen = 20U;
350 uint32_t erpsVal = 0;
351 uint_fast8_t bit = 0x0;
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
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
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
414static void gptCb(GPTDriver *gptd)
415{
417 DshotRpmCapture *drcd = (DshotRpmCapture *) gptd->user_data;
418 chThdResumeI(&drcd->dmads[0].thread, MSG_TIMEOUT);
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
#define DSHOT_DMA_EXTRADATA_LEN
#define STM32_SYSCLK
#define DSHOT_DMA_DATA_LEN
: 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)
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
static size_t dmaGetTransactionCounter(DMADriver *dmap)
dmaerrormask_t
Possible DMA failure causes.
uint32_t stream
stream associated with transaction
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
stm32_tim_t * timer
hardware timer pointer (example : &STM32_TIM1)
TimIC Driver configuration structure.
uint16_t foo
Definition main_demo5.c:58
void timerDmaCache_restore(const TimerDmaCache *tdcp, DMADriver *toDma, stm32_tim_t *toTim)
void timerDmaCache_cache(TimerDmaCache *tdcp, const DMADriver *fromDma, const stm32_tim_t *fromTim)
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.