Paparazzi UAS  v7.0_unstable
Paparazzi is a free software Unmanned Aircraft System.
hal_stm32_dma.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2019 Alexandre Bustico, Gautier Hattenberger
3  *
4  * This file is part of paparazzi.
5  *
6  * paparazzi is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2, or (at your option)
9  * any later version.
10  *
11  * paparazzi is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with paparazzi; see the file COPYING. If not, see
18  * <http://www.gnu.org/licenses/>.
19  */
20 
29 #include "hal_stm32_dma.h"
30 #include "string.h"
31 
32 /*
33 TODO :
34 
35 ° split lld and hardware independant code : hal_stm32_dma et hal_lld_stm32_dma
36 
37 ° port to H7,L4+ : bdma, dmav3, mdma+dmamux
38 
39 ° allow fifo burst when STM32_DMA_USE_ASYNC_TIMOUT is true by forcing a flush of the fifo : could be
40  done disabling stream : should flush fifo and trig full code ISR. full code ISR should re-enable stream
41  after a timout.
42 
43 */
44 
45 
46 #if (! STM32_DMA_ADVANCED) && STM32_DMA_USE_DOUBLE_BUFFER
47 #error "STM32_DMA_USE_DOUBLE_BUFFER only available on DMAv2"
48 #endif
49 
50 #if (STM32_DMA_USE_ASYNC_TIMOUT) && STM32_DMA_USE_DOUBLE_BUFFER
51 #error "STM32_DMA_USE_DOUBLE_BUFFER only not yet compatible with STM32_DMA_USE_ASYNC_TIMOUT"
52 #endif
53 
54 
61 static void dma_lld_serve_interrupt(DMADriver *dmap, uint32_t flags);
62 
63 #if STM32_DMA_ADVANCED
64 static inline uint32_t getFCR_FS(const DMADriver *dmap) {
65  return (dmap->dmastream->stream->FCR & DMA_SxFCR_FS_Msk);
66 }
67 #endif
68 
70 {
71  osalDbgCheck(dmap != NULL);
72 
73  dmap->state = DMA_STOP;
74  dmap->config = NULL;
75  dmap->mem0p = NULL;
76 #if STM32_DMA_USE_WAIT == TRUE
77  dmap->thread = NULL;
78 #endif
79 #if STM32_DMA_USE_MUTUAL_EXCLUSION == TRUE
80  osalMutexObjectInit(&dmap->mutex);
81 #endif
82 #if defined( STM32_DMA_DRIVER_EXT_INIT_HOOK)
83  STM32_DMA_DRIVER_EXT_INIT_HOOK(dmap);
84 #endif
85 #if CH_DBG_SYSTEM_STATE_CHECK == TRUE
86  dmap->nbTransferError = dmap->nbDirectModeError = dmap->nbFifoError = 0U;
87  dmap->lastError = 0U;
88 #endif
89 #if STM32_DMA_USE_ASYNC_TIMOUT
90  chVTObjectInit(&dmap->vt);
91 #endif
92 }
93 
94 
105 bool dmaStart(DMADriver *dmap, const DMAConfig *cfg)
106 {
107  osalDbgCheck((dmap != NULL) && (cfg != NULL));
108  #if STM32_DMA_USE_DOUBLE_BUFFER
109  osalDbgAssert((cfg->op_mode != DMA_CONTINUOUS_DOUBLE_BUFFER) || (!STM32_DMA_USE_ASYNC_TIMOUT),
110  "STM32_DMA_USE_ASYNC_TIMOUT not yet implemented in DMA_CONTINUOUS_DOUBLE_BUFFER mode");
111 
112  osalDbgAssert((cfg->op_mode != DMA_CONTINUOUS_DOUBLE_BUFFER) || (cfg->next_cb != NULL),
113  "DMA_CONTINUOUS_DOUBLE_BUFFER mode implies next_cb not NULL");
114 #endif
115  osalSysLock();
116  osalDbgAssert((dmap->state == DMA_STOP) || (dmap->state == DMA_READY),
117  "invalid state");
118  dmap->config = cfg;
119  const bool statusOk = dma_lld_start(dmap, true);
120  dmap->state = DMA_READY;
121  #if STM32_DMA_USE_DOUBLE_BUFFER
122  dmap->next_cb_errors = 0U;
123 #endif
124  osalSysUnlock();
125  return statusOk;
126 }
127 
138 bool dmaReloadConf(DMADriver *dmap, const DMAConfig *cfg)
139 {
140  osalDbgCheck((dmap != NULL) && (cfg != NULL));
141 #if STM32_DMA_USE_DOUBLE_BUFFER
142  osalDbgAssert((cfg->op_mode != DMA_CONTINUOUS_DOUBLE_BUFFER) || (!STM32_DMA_USE_ASYNC_TIMOUT),
143  "STM32_DMA_USE_ASYNC_TIMOUT not yet implemented in DMA_CONTINUOUS_DOUBLE_BUFFER mode");
144 
145  osalDbgAssert((cfg->op_mode != DMA_CONTINUOUS_DOUBLE_BUFFER) || (cfg->next_cb != NULL),
146  "DMA_CONTINUOUS_DOUBLE_BUFFER mode implies next_cb not NULL");
147 #endif
148  osalSysLock();
149  osalDbgAssert(dmap->state == DMA_READY, "invalid state");
150  dmap->config = cfg;
151  const bool statusOk = dma_lld_start(dmap, false);
152 #if STM32_DMA_USE_DOUBLE_BUFFER
153  dmap->next_cb_errors = 0U;
154 #endif
155  osalSysUnlock();
156  return statusOk;
157 }
158 
159 
167 void dmaStop(DMADriver *dmap)
168 {
169  osalDbgCheck(dmap != NULL);
170 
171  osalDbgAssert((dmap->state == DMA_STOP) || (dmap->state == DMA_READY),
172  "invalid state");
173 
174  dma_lld_stop(dmap);
175 
176  osalSysLock();
177  dmap->config = NULL;
178  dmap->state = DMA_STOP;
179  dmap->mem0p = NULL;
180  osalSysUnlock();
181 }
182 
183 
201 bool dmaStartTransfert(DMADriver *dmap, volatile void *periphp, void * mem0p, const size_t size)
202 {
203  osalSysLock();
204  const bool statusOk = dmaStartTransfertI(dmap, periphp, mem0p, size);
205  osalSysUnlock();
206  return statusOk;
207 }
208 
226 bool dmaStartTransfertI(DMADriver *dmap, volatile void *periphp, void * mem0p, const size_t size)
227 {
228  osalDbgCheckClassI();
229 
230  #if STM32_DMA_USE_DOUBLE_BUFFER
231  if (dmap->config->op_mode == DMA_CONTINUOUS_DOUBLE_BUFFER) {
232  osalDbgAssert(mem0p == NULL,
233  "in double buffer mode memory pointer is dynamically completed by next_cb callback");
234  mem0p = dmap->config->next_cb(dmap, size);
235  }
236 #endif
237 
238 #if (CH_DBG_ENABLE_ASSERTS != FALSE)
239  if (size != dmap->size) {
240  osalDbgCheck((dmap != NULL) && (mem0p != NULL) && (periphp != NULL) &&
241  (size > 0U) && ((size == 1U) ||
243  (((size & 1U) == 0U)))));
244 
245  const DMAConfig *cfg = dmap->config;
246  osalDbgAssert((dmap->state == DMA_READY) ||
247  (dmap->state == DMA_COMPLETE) ||
248  (dmap->state == DMA_ERROR),
249  "not ready");
250  /* if (cfg->pburst) */
251  /* osalDbgAssert((uint32_t) periphp % (cfg->pburst * cfg->psize) == 0, "peripheral address not aligned"); */
252  /* else */
253  osalDbgAssert((uint32_t) periphp % cfg->psize == 0, "peripheral address not aligned");
254 
255  /* if (cfg->mburst) */
256  /* osalDbgAssert((uint32_t) mem0p % (cfg->mburst * cfg->msize) == 0, "memory address not aligned"); */
257  /* else */
258  osalDbgAssert((uint32_t) mem0p % cfg->msize == 0, "memory address not aligned");
259 
260  /*
261  In the circular mode, it is mandatory to respect the following rule in case of a burst mode
262  configured for memory:
263  DMA_SxNDTR = Multiple of ((Mburst beat) × (Msize)/(Psize)), where:
264  – (Mburst beat) = 4, 8 or 16 (depending on the MBURST bits in the DMA_SxCR
265  register)
266  – ((Msize)/(Psize)) = 1, 2, 4, 1/2 or 1/4 (Msize and Psize represent the MSIZE and
267  PSIZE bits in the DMA_SxCR register. They are byte dependent)
268  – DMA_SxNDTR = Number of data items to transfer on the AHB peripheral port
269 
270  NDTR must also be a multiple of the Peripheral burst size multiplied by the peripheral data
271  size, otherwise this could result in a bad DMA behavior.
272 
273  */
274 # if STM32_DMA_ADVANCED
275  if (cfg->mburst) {
276  osalDbgAssert((size % (cfg->mburst * cfg->msize / cfg->psize)) == 0,
277  "mburst alignment rule not respected");
278  osalDbgAssert((size % (cfg->mburst * cfg->msize)) == 0,
279  "mburst alignment rule not respected");
280  osalDbgAssert((((uint32_t) mem0p) % cfg->mburst) == 0,
281  "memory address alignment rule not respected");
282  }
283  if (cfg->pburst) {
284  osalDbgAssert((size % (cfg->pburst * cfg->psize)) == 0,
285  "pburst alignment rule not respected");
286  osalDbgAssert((((uint32_t) periphp) % cfg->pburst) == 0,
287  "peripheral address alignment rule not respected");
288  }
289  if (cfg->periph_inc_size_4) {
290  osalDbgAssert(cfg->inc_peripheral_addr,
291  "periph_inc_size_4 implies enabling inc_peripheral_addr");
292  osalDbgAssert(cfg->fifo,
293  "periph_inc_size_4 implies enabling fifo");
294  }
295 
296 # endif // STM32_DMA_ADVANCED
297  }
298 #endif // CH_DBG_ENABLE_ASSERTS != FALSE
299  dmap->state = DMA_ACTIVE;
300 
301 #if STM32_DMA_USE_ASYNC_TIMOUT
302  dmap->currPtr = mem0p;
303  if (dmap->config->timeout != TIME_INFINITE) {
304  chVTSetI(&dmap->vt, dmap->config->timeout,
305  &dma_lld_serve_timeout_interrupt, (void *) dmap);
306  }
307 #endif
308 
309  return dma_lld_start_transfert(dmap, periphp, mem0p, size);
310 }
311 
327 #ifndef DMA_request_TypeDef
328 void dmaGetRegisters(DMADriver *dmap, volatile void *periphp, void *mem0p,
329  const size_t size,
330  DMA_Stream_TypeDef *registers)
331 {
332 #if STM32_DMA_USE_DOUBLE_BUFFER
333  if (dmap->config->op_mode == DMA_CONTINUOUS_DOUBLE_BUFFER) {
334  osalDbgAssert(mem0p == NULL,
335  "in double buffer mode memory pointer is dynamically completed by next_cb callback");
336  mem0p = dmap->config->next_cb(dmap, size);
337  }
338 #endif
339 
340 #if (CH_DBG_ENABLE_ASSERTS != FALSE)
341  osalDbgCheck((dmap != NULL) && (mem0p != NULL) && (periphp != NULL) &&
342  (size > 0U) && ((size == 1U) ||
344  (((size & 1U) == 0U)))));
345 
346  const DMAConfig *cfg = dmap->config;
347  osalDbgAssert(dmap->state == DMA_READY, "not ready");
348  osalDbgAssert((uint32_t) periphp % cfg->psize == 0,
349  "peripheral address not aligned");
350 
351  osalDbgAssert((uint32_t) mem0p % cfg->msize == 0,
352  "memory address not aligned");
353 
354 # if STM32_DMA_ADVANCED
355  if (cfg->mburst) {
356  osalDbgAssert((size % (cfg->mburst * cfg->msize / cfg->psize)) == 0,
357  "mburst alignment rule not respected");
358  osalDbgAssert((size % (cfg->mburst * cfg->msize)) == 0,
359  "mburst alignment rule not respected");
360  osalDbgAssert((((uint32_t) mem0p) % cfg->mburst) == 0,
361  "memory address alignment rule not respected");
362  }
363  if (cfg->pburst) {
364  osalDbgAssert((size % (cfg->pburst * cfg->psize)) == 0,
365  "pburst alignment rule not respected");
366  osalDbgAssert((((uint32_t) periphp) % cfg->pburst) == 0,
367  "peripheral address alignment rule not respected");
368  }
369  if (cfg->periph_inc_size_4) {
370  osalDbgAssert(cfg->inc_peripheral_addr,
371  "periph_inc_size_4 implies enabling inc_peripheral_addr");
372  osalDbgAssert(cfg->fifo,
373  "periph_inc_size_4 implies enabling fifo");
374  }
375 
376 # endif // STM32_DMA_ADVANCED
377 #endif // CH_DBG_ENABLE_ASSERTS != FALSE
378 
379  dma_lld_get_registers(dmap, periphp, mem0p, size, registers);
380 }
381 #endif
382 
394 {
395 
396  osalDbgCheck(dmap != NULL);
397 
398  osalSysLock();
399  osalDbgAssert((dmap->state == DMA_READY) || (dmap->state == DMA_ACTIVE),
400  "invalid state");
401  if (dmap->state != DMA_READY) {
403  dmap->state = DMA_READY;
404  _dma_reset_s(dmap);
405  }
406  osalSysUnlock();
407 }
408 
409 
410 
422 {
423  osalDbgCheckClassI();
424  osalDbgCheck(dmap != NULL);
425  osalDbgAssert((dmap->state == DMA_READY) ||
426  (dmap->state == DMA_ACTIVE) ||
427  (dmap->state == DMA_COMPLETE),
428  "invalid state");
429 
430 
431  if (dmap->state != DMA_READY) {
433  dmap->state = DMA_READY;
434  _dma_reset_i(dmap);
435  }
436 }
437 
439 {
440  for(uint8_t i = 0; i < 16; i++) {
441  if (dmap->dmastream == STM32_DMA_STREAM(i))
442  return i;
443  }
444  return 0xff;
445 }
446 
447 #if (STM32_DMA_USE_WAIT == TRUE) || defined(__DOXYGEN__)
448 
472 msg_t dmaTransfertTimeout(DMADriver *dmap, volatile void *periphp, void *mem0p, const size_t size,
473  sysinterval_t timeout)
474 {
475  msg_t msg;
476 
477  osalSysLock();
478  osalDbgAssert(dmap->thread == NULL, "already waiting");
479  osalDbgAssert(dmap->config->op_mode == DMA_ONESHOT, "blocking API is incompatible with circular modes");
480  dmaStartTransfertI(dmap, periphp, mem0p, size);
481  msg = osalThreadSuspendTimeoutS(&dmap->thread, timeout);
482  if (msg != MSG_OK) {
483  dmaStopTransfertI(dmap);
484  }
485  osalSysUnlock();
486  return msg;
487 }
488 #endif
489 
490 #if (STM32_DMA_USE_MUTUAL_EXCLUSION == TRUE) || defined(__DOXYGEN__)
503 {
504 
505  osalDbgCheck(dmap != NULL);
506 
507  osalMutexLock(&dmap->mutex);
508 }
509 
520 {
521 
522  osalDbgCheck(dmap != NULL);
523 
524  osalMutexUnlock(&dmap->mutex);
525 }
526 #endif /* DMA_USE_MUTUAL_EXCLUSION == TRUE */
527 
528 
529 
530 /*
531 # _ _ _
532 # | | | | | |
533 # | | ___ __ __ | | ___ __ __ ___ | |
534 # | | / _ \ \ \ /\ / / | | / _ \ \ \ / / / _ \ | |
535 # | |____ | (_) | \ V V / | |____ | __/ \ V / | __/ | |
536 # |______| \___/ \_/\_/ |______| \___| \_/ \___| |_|
537 # _____ _
538 # | __ \ (_)
539 # | | | | _ __ _ __ __ ___ _ __
540 # | | | | | '__| | | \ \ / / / _ \ | '__|
541 # | |__| | | | | | \ V / | __/ | |
542 # |_____/ |_| |_| \_/ \___| |_|
543 */
544 
552 bool dma_lld_start(DMADriver *dmap, bool allocate_stream)
553 {
554  uint32_t psize_msk, msize_msk;
555 
556  const DMAConfig *cfg = dmap->config;
557  osalDbgAssert(PORT_IRQ_IS_VALID_KERNEL_PRIORITY(cfg->irq_priority),
558  "illegal IRQ priority");
559  switch (cfg->psize) {
560  case 1 : psize_msk = STM32_DMA_CR_PSIZE_BYTE; break;
561  case 2 : psize_msk = STM32_DMA_CR_PSIZE_HWORD; break;
562  case 4 : psize_msk = STM32_DMA_CR_PSIZE_WORD; break;
563  default: osalSysHalt("psize should be 1 or 2 or 4");
564  return false;
565  }
566  switch (cfg->msize) {
567  case 1 : msize_msk = STM32_DMA_CR_MSIZE_BYTE; break;
568  case 2 : msize_msk = STM32_DMA_CR_MSIZE_HWORD; break;
569  case 4 : msize_msk = STM32_DMA_CR_MSIZE_WORD; break;
570  default: osalDbgAssert(false, "msize should be 1 or 2 or 4");
571  return false;
572  }
573 
574  uint32_t dir_msk = 0UL;
575  switch (cfg->direction) {
576  case DMA_DIR_P2M: dir_msk = STM32_DMA_CR_DIR_P2M; break;
577  case DMA_DIR_M2P: dir_msk = STM32_DMA_CR_DIR_M2P; break;
578  case DMA_DIR_M2M: dir_msk = STM32_DMA_CR_DIR_M2M; break;
579  default: osalDbgAssert(false, "direction not set or incorrect");
580  }
581 
582  uint32_t isr_flags = cfg->op_mode == DMA_ONESHOT ? STM32_DMA_CR_TCIE : 0UL;
583 
584  // in M2M mode, half buffer transfert ISR is disabled, but
585  // full buffer transfert complete ISR is enabled
586  if (cfg->end_cb) {
587  isr_flags |= STM32_DMA_CR_TCIE;
588  if ((cfg->direction != DMA_DIR_M2M) &&
590  isr_flags |= STM32_DMA_CR_HTIE;
591  }
592  }
593 
594  if (cfg->error_cb) {
595  isr_flags |= STM32_DMA_CR_DMEIE | STM32_DMA_CR_TCIE;
596  }
597 
598 #if CH_KERNEL_MAJOR < 6
599  dmap->dmastream = STM32_DMA_STREAM(cfg->stream);
600 #endif
601 
602  // portable way (V1, V2) to retreive controler number
603 #if STM32_DMA_SUPPORTS_DMAMUX == 0
604 #if STM32_DMA_ADVANCED
605  dmap->controller = 1 + (cfg->stream / STM32_DMA_STREAM_ID(2, 0));
606 #else
607  dmap->controller = 1 + (cfg->stream / STM32_DMA_STREAM_ID(2, 1));
608 #endif
609 #endif
610 
611  dmap->dmamode = STM32_DMA_CR_PL(cfg->dma_priority) |
612  dir_msk | psize_msk | msize_msk | isr_flags |
613  (cfg->op_mode == DMA_CONTINUOUS_HALF_BUFFER ? STM32_DMA_CR_CIRC : 0UL) |
614 #if STM32_DMA_USE_DOUBLE_BUFFER
615  (cfg->op_mode == DMA_CONTINUOUS_DOUBLE_BUFFER ? STM32_DMA_CR_DBM : 0UL) |
616 #endif
617  (cfg->inc_peripheral_addr ? STM32_DMA_CR_PINC : 0UL) |
618  (cfg->inc_memory_addr ? STM32_DMA_CR_MINC : 0UL)
619 
620 #if STM32_DMA_SUPPORTS_CSELR
621  | STM32_DMA_CR_CHSEL(cfg->request)
622 #elif STM32_DMA_ADVANCED
623 #if STM32_DMA_SUPPORTS_DMAMUX == 0
624  | STM32_DMA_CR_CHSEL(cfg->channel)
625 #endif
626  | (cfg->periph_inc_size_4 ? STM32_DMA_CR_PINCOS : 0UL) |
627  (cfg->transfert_end_ctrl_by_periph? STM32_DMA_CR_PFCTRL : 0UL)
628 # endif
629  ;
630 
631 
632 #if STM32_DMA_ADVANCED
633  uint32_t pburst_msk, mburst_msk, fifo_msk; // STM32_DMA_CR_PBURST_INCRx, STM32_DMA_CR_MBURST_INCRx
634  switch (cfg->pburst) {
635  case 0 : pburst_msk = 0UL; break;
636  case 4 : pburst_msk = STM32_DMA_CR_PBURST_INCR4; break;
637  case 8 : pburst_msk = STM32_DMA_CR_PBURST_INCR8; break;
638  case 16 : pburst_msk = STM32_DMA_CR_PBURST_INCR16; break;
639  default: osalDbgAssert(false, "pburst size should be 0 or 4 or 8 or 16");
640  return false;
641  }
642  switch (cfg->mburst) {
643  case 0 : mburst_msk = 0UL; break;
644  case 4 : mburst_msk = STM32_DMA_CR_MBURST_INCR4; break;
645  case 8 : mburst_msk = STM32_DMA_CR_MBURST_INCR8; break;
646  case 16 : mburst_msk = STM32_DMA_CR_MBURST_INCR16; break;
647  default: osalDbgAssert(false, "mburst size should be 0 or 4 or 8 or 16");
648  return false;
649  }
650  switch (cfg->fifo) {
651  case 0 : fifo_msk = 0UL; break;
652  case 1 : fifo_msk = STM32_DMA_FCR_FTH_1Q; break;
653  case 2 : fifo_msk = STM32_DMA_FCR_FTH_HALF; break;
654  case 3 : fifo_msk = STM32_DMA_FCR_FTH_3Q; break;
655  case 4 : fifo_msk = STM32_DMA_FCR_FTH_FULL; break;
656  default: osalDbgAssert(false, "fifo threshold should be 1(/4) or 2(/4) or 3(/4) or 4(/4)");
657  return false;
658  }
659 
660 
661 # if (CH_DBG_ENABLE_ASSERTS != FALSE)
662 # if STM32_DMA_USE_ASYNC_TIMOUT
663  osalDbgAssert(dmap->config->timeout != 0,
664  "timeout cannot be 0 if STM32_DMA_USE_ASYNC_TIMOUT is enabled");
665  osalDbgAssert(!((dmap->config->timeout != TIME_INFINITE) && (dmap->config->fifo != 0)),
666  "timeout should be dynamically disabled (dmap->config->timeout = TIME_INFINITE) "
667  "if STM32_DMA_USE_ASYNC_TIMOUT is enabled and fifo is enabled (fifo != 0)");
668 
669 # endif
670 
671 
672  // lot of combination of parameters are forbiden, and some conditions must be meet
673  if (!cfg->mburst != !cfg->pburst) {
674  osalDbgAssert(false, "pburst and mburst should be enabled or disabled together");
675  return false;
676  }
677 
678  // from RM0090, Table 48. FIFO threshold configurations
679  if (cfg->fifo && cfg->mburst) {
680  const size_t fifo_level = cfg->fifo * 4U;
681  osalDbgAssert(fifo_level % (cfg->mburst * cfg->msize) == 0, "threshold combination forbidden");
682  }
683  // if (cfg->fifo) {
684  // switch (cfg->msize) {
685  // case 1: // msize 1
686  // switch (cfg->mburst) {
687  // case 4 : // msize 1 mburst 4
688  // switch (cfg->fifo) {
689  // case 1: break; // msize 1 mburst 4 fifo 1/4
690  // case 2: break; // msize 1 mburst 4 fifo 2/4
691  // case 3: break; // msize 1 mburst 4 fifo 3/4
692  // case 4: break; // msize 1 mburst 4 fifo 4/4
693  // }
694  // break;
695  // case 8 : // msize 1 mburst 8
696  // switch (cfg->fifo) {
697  // case 1: goto forbiddenCombination; // msize 1 mburst 8 fifo 1/4
698  // case 2: break; // msize 1 mburst 8 fifo 2/4
699  // case 3: goto forbiddenCombination; // msize 1 mburst 8 fifo 3/4
700  // case 4: break; // msize 1 mburst 8 fifo 4/4
701  // }
702  // break;
703  // case 16 : // msize 1 mburst 16
704  // switch (cfg->fifo) {
705  // case 1: goto forbiddenCombination; // msize 1 mburst 16 fifo 1/4
706  // case 2: goto forbiddenCombination; // msize 1 mburst 16 fifo 2/4
707  // case 3: goto forbiddenCombination; // msize 1 mburst 16 fifo 3/4
708  // case 4: break; // msize 1 mburst 16 fifo 4/4
709  // }
710  // break;
711  // }
712  // break;
713  // case 2: // msize 2
714  // switch (cfg->mburst) {
715  // case 4 : // msize 2 mburst 4
716  // switch (cfg->fifo) {
717  // case 1: goto forbiddenCombination; // msize 2 mburst 4 fifo 1/4
718  // case 2: break; // msize 2 mburst 4 fifo 2/4
719  // case 3: goto forbiddenCombination; // msize 2 mburst 4 fifo 3/4
720  // case 4: break; // msize 2 mburst 4 fifo 4/4
721  // }
722  // break;
723  // case 8 :
724  // switch (cfg->fifo) {
725  // case 1: goto forbiddenCombination; // msize 2 mburst 8 fifo 1/4
726  // case 2: goto forbiddenCombination; // msize 2 mburst 8 fifo 2/4
727  // case 3: goto forbiddenCombination; // msize 2 mburst 8 fifo 3/4
728  // case 4: break; // msize 2 mburst 8 fifo 4/4
729  // }
730  // break;
731  // case 16 :
732  // switch (cfg->fifo) {
733  // case 1: goto forbiddenCombination; // msize 2 mburst 16 fifo 1/4
734  // case 2: goto forbiddenCombination; // msize 2 mburst 16 fifo 2/4
735  // case 3: goto forbiddenCombination; // msize 2 mburst 16 fifo 3/4
736  // case 4: goto forbiddenCombination; // msize 2 mburst 16 fifo 4/4
737  // }
738  // }
739  // break;
740  // case 4:
741  // switch (cfg->mburst) {
742  // case 4 :
743  // switch (cfg->fifo) {
744  // case 1: goto forbiddenCombination; // msize 4 mburst 4 fifo 1/4
745  // case 2: goto forbiddenCombination; // msize 4 mburst 4 fifo 2/4
746  // case 3: goto forbiddenCombination; // msize 4 mburst 4 fifo 3/4
747  // case 4: break; // msize 4 mburst 4 fifo 4/4
748  // }
749  // break;
750  // case 8 :
751  // switch (cfg->fifo) {
752  // case 1: goto forbiddenCombination; // msize 4 mburst 8 fifo 1/4
753  // case 2: goto forbiddenCombination; // msize 4 mburst 8 fifo 2/4
754  // case 3: goto forbiddenCombination; // msize 4 mburst 8 fifo 3/4
755  // case 4: goto forbiddenCombination; // msize 4 mburst 8 fifo 4/4
756  // }
757  // break;
758  // case 16 :
759  // switch (cfg->fifo) {
760  // case 1: goto forbiddenCombination; // msize 4 mburst 16 fifo 1/4
761  // case 2: goto forbiddenCombination; // msize 4 mburst 16 fifo 2/4
762  // case 3: goto forbiddenCombination; // msize 4 mburst 16 fifo 3/4
763  // case 4: goto forbiddenCombination; // msize 4 mburst 16 fifo 4/4
764  // }
765  // }
766  // }
767  // }
768 # endif
769 
770  dmap->dmamode |= (pburst_msk | mburst_msk);
771 
772 # if (CH_DBG_ENABLE_ASSERTS != FALSE)
773 
774 
775  /*
776  When burst transfers are requested on the peripheral AHB port and the FIFO is used
777  (DMDIS = 1 in the DMA_SxCR register), it is mandatory to respect the following rule to
778  avoid permanent underrun or overrun conditions, depending on the DMA stream direction:
779  If (PBURST × PSIZE) = FIFO_SIZE (4 words), FIFO_Threshold = 3/4 is forbidden
780  */
781  if ( ((cfg->pburst * cfg->psize) == STM32_DMA_FIFO_SIZE) && (cfg->fifo == 3)) {
782  goto forbiddenCombination;
783  }
784 
785  /*
786  When memory-to-memory mode is used, the Circular and direct modes are not allowed.
787  Only the DMA2 controller is able to perform memory-to-memory transfers.
788  */
789 #if STM32_DMA_SUPPORTS_DMAMUX == 0
790  if (cfg->direction == DMA_DIR_M2M) {
791  osalDbgAssert(dmap->controller == 2, "M2M not available on DMA1");
792  }
793 #endif
794 
795 # endif
796 #endif
797 
798 #if STM32_DMA_ADVANCED
799  if (cfg->fifo) {
800  dmap->fifomode = STM32_DMA_FCR_DMDIS | STM32_DMA_FCR_FEIE | fifo_msk;
801  } else {
802  osalDbgAssert(cfg->psize == cfg->msize,
803  "msize == psize is mandatory when fifo is disabled");
804  dmap->fifomode = 0U;
805  }
806 #endif
807 
808  if (allocate_stream == true) {
809 #if CH_KERNEL_MAJOR < 6
810  const bool error = dmaStreamAllocate( dmap->dmastream,
811  cfg->irq_priority,
812  (stm32_dmaisr_t) &dma_lld_serve_interrupt,
813  (void *) dmap );
814 #else
815  dmap->dmastream = dmaStreamAllocI(dmap->config->stream,
816  cfg->irq_priority,
817  (stm32_dmaisr_t) &dma_lld_serve_interrupt,
818  (void *) dmap );
819  const bool error = dmap->dmastream == NULL;
820 #endif
821  if (error) {
822  osalDbgAssert(false, "stream already allocated");
823  return false;
824  }
825  }
826  return true;
827 
828 #if (CH_DBG_ENABLE_ASSERTS != FALSE)
829 #if STM32_DMA_ADVANCED
830 forbiddenCombination:
831  chSysHalt("forbidden combination of msize, mburst, fifo, see FIFO threshold "
832  "configuration in reference manuel");
833  return false;
834 # endif
835 #endif
836 }
837 
838 static inline size_t getCrossCacheBoundaryAwareSize(const void *memp,
839  const size_t dsize)
840 {
841  // L1 cache on F7 and H7 is organised of line of 32 bytes
842  // returned size is not 32 bytes aligned by a mask operation
843  // because cache management does internal mask and this operation
844  // would be useless
845 
846 #if defined CACHE_LINE_SIZE && CACHE_LINE_SIZE != 0
847  const uint32_t endp = ((uint32_t) memp % CACHE_LINE_SIZE +
848  dsize % CACHE_LINE_SIZE );
849  return endp < CACHE_LINE_SIZE ? dsize + CACHE_LINE_SIZE :
850  dsize + CACHE_LINE_SIZE *2U;
851 #else
852  (void) memp;
853  return dsize;
854 #endif
855 }
856 
864 #ifndef DMA_request_TypeDef
865 void dma_lld_get_registers(DMADriver *dmap, volatile void *periphp,
866  void *mem0p, const size_t size,
867  DMA_Stream_TypeDef *registers)
868 {
869  dmaStreamSetPeripheral(dmap->dmastream, periphp);
870 #if STM32_DMA_SUPPORTS_DMAMUX
871  dmaSetRequestSource(dmap->dmastream, dmap->config->dmamux);
872 #endif
873 
874  dmaStreamSetMemory0(dmap->dmastream, mem0p);
875 #if STM32_DMA_USE_DOUBLE_BUFFER
876  if (dmap->config->op_mode == DMA_CONTINUOUS_DOUBLE_BUFFER) {
877  dmaStreamSetMemory1(dmap->dmastream, dmap->config->next_cb(dmap, size));
878  }
879 #endif
880  dmaStreamSetTransactionSize(dmap->dmastream, size);
881  dmaStreamSetMode(dmap->dmastream, dmap->dmamode);
882 #if STM32_DMA_ADVANCED
883  dmaStreamSetFIFO(dmap->dmastream, dmap->fifomode);
884 #endif
885 
886  memcpy(registers, dmap->dmastream->stream, sizeof(DMA_Stream_TypeDef));
887  registers->CR |= STM32_DMA_CR_EN;
888 }
889 #endif
897 bool dma_lld_start_transfert(DMADriver *dmap, volatile void *periphp, void *mem0p, const size_t size)
898 {
899 #if __DCACHE_PRESENT
900  if (dmap->config->activate_dcache_sync &&
901  (dmap->config->direction != DMA_DIR_P2M)) {
902  const size_t cacheSize = getCrossCacheBoundaryAwareSize(mem0p,
903  size * dmap->config->msize);
904  cacheBufferFlush(mem0p, cacheSize);
905  }
906 #endif
907  dmap->mem0p = mem0p;
908 #if __DCACHE_PRESENT
909  dmap->periphp = periphp;
910 #endif
911  dmap->size = size;
912  dmaStreamSetPeripheral(dmap->dmastream, periphp);
913 #if STM32_DMA_SUPPORTS_DMAMUX
914  dmaSetRequestSource(dmap->dmastream, dmap->config->dmamux);
915 #endif
916 
917  dmaStreamSetMemory0(dmap->dmastream, mem0p);
918 #if STM32_DMA_USE_DOUBLE_BUFFER
919  if (dmap->config->op_mode == DMA_CONTINUOUS_DOUBLE_BUFFER) {
920  dmaStreamSetMemory1(dmap->dmastream, dmap->config->next_cb(dmap, size));
921  }
922 #endif
923  dmaStreamSetTransactionSize(dmap->dmastream, size);
924  dmaStreamSetMode(dmap->dmastream, dmap->dmamode);
925 #if STM32_DMA_ADVANCED
926  dmaStreamSetFIFO(dmap->dmastream, dmap->fifomode);
927 #endif
928  dmaStreamEnable(dmap->dmastream);
929 
930  return true;
931 }
932 
941 {
942  dmaStreamDisable(dmap->dmastream);
943 }
944 
953 {
954 #if CH_KERNEL_MAJOR < 6
955  dmaStreamRelease(dmap->dmastream);
956 #else
957  dmaStreamFree(dmap->dmastream);
958 #endif
959 }
960 
961 
962 /*===========================================================================*/
963 /* Driver local functions. */
964 /*===========================================================================*/
965 
972 static void dma_lld_serve_interrupt(DMADriver *dmap, uint32_t flags)
973 {
974 
975  /* DMA errors handling.*/
976 #if CH_DBG_SYSTEM_STATE_CHECK && STM32_DMA_ADVANCED
977  const uint32_t feif_msk = dmap->config->fifo != 0U ? STM32_DMA_ISR_FEIF : 0U;
978 #else
979  const uint32_t feif_msk = 0U;
980 #endif
981  //const uint32_t feif_msk = STM32_DMA_ISR_FEIF;
982  if ((flags & (STM32_DMA_ISR_TEIF | STM32_DMA_ISR_DMEIF | feif_msk)) != 0U) {
983  /* DMA, this could help only if the DMA tries to access an unmapped
984  address space or violates alignment rules.*/
985  dmaerrormask_t err =
986  ( (flags & STM32_DMA_ISR_TEIF) ? DMA_ERR_TRANSFER_ERROR : 0UL) |
987  ( (flags & STM32_DMA_ISR_DMEIF) ? DMA_ERR_DIRECTMODE_ERROR : 0UL);
988 
989  if (dmap->config->fifo != 0U) {
990 #if STM32_DMA_ADVANCED
991  dmaerrormask_t fserr =
992  ( (flags & feif_msk) ? DMA_ERR_FIFO_ERROR : 0UL) |
993  ( getFCR_FS(dmap) == (0b100 << DMA_SxFCR_FS_Pos) ? DMA_ERR_FIFO_EMPTY: 0UL) |
994  ( getFCR_FS(dmap) == (0b101 << DMA_SxFCR_FS_Pos) ? DMA_ERR_FIFO_FULL: 0UL) ;
995  err |= fserr;
996 #endif
997  }
998 
999 
1000  _dma_isr_error_code(dmap, err);
1001  } else {
1002  /* It is possible that the transaction has already be reset by the
1003  DMA error handler, in this case this interrupt is spurious.*/
1004  if (dmap->state == DMA_ACTIVE) {
1005 #if __DCACHE_PRESENT
1006  if (dmap->config->activate_dcache_sync)
1007  switch (dmap->config->direction) {
1008  case DMA_DIR_M2P : break;
1009  case DMA_DIR_P2M : if (dmap->mem0p >= (void *) 0x20000000) {
1010  const size_t cacheSize =
1012  dmap->config->msize);
1013  cacheBufferInvalidate(dmap->mem0p, cacheSize);
1014  }
1015  break;
1016 
1017  case DMA_DIR_M2M : if (dmap->periphp >= (void *) 0x20000000) {
1018  const size_t cacheSize =
1019  getCrossCacheBoundaryAwareSize((void *) dmap->periphp,
1020  dmap->size * dmap->config->psize);
1021  cacheBufferInvalidate(dmap->periphp, cacheSize);
1022  }
1023  break;
1024  }
1025 #endif
1026 
1027  if ((flags & STM32_DMA_ISR_TCIF) != 0) {
1028  /* Transfer complete processing.*/
1029  _dma_isr_full_code(dmap);
1030  } else if ((flags & STM32_DMA_ISR_HTIF) != 0) {
1031  /* Half transfer processing.*/
1032  _dma_isr_half_code(dmap);
1033  }
1034  }
1035  }
1036 }
1037 
1038 #if STM32_DMA_USE_ASYNC_TIMOUT
1044 void dma_lld_serve_timeout_interrupt(void *arg)
1045 {
1046  DMADriver *dmap = (DMADriver *) arg;
1047  if (dmap->config->op_mode != DMA_ONESHOT) {
1048  chSysLockFromISR();
1049  chVTSetI(&dmap->vt, dmap->config->timeout,
1050  &dma_lld_serve_timeout_interrupt, (void *) dmap);
1051  chSysUnlockFromISR();
1052  }
1053  async_timout_enabled_call_end_cb(dmap, FROM_TIMOUT_CODE);
1054 }
1055 #endif
1056 
1057 #if STM32_DMA_USE_DOUBLE_BUFFER
1070 void* dma_lld_set_next_double_buffer(DMADriver *dmap, void *nextBuffer)
1071 {
1072  void *lastBuffer;
1073 
1074  if (dmaStreamGetCurrentTarget(dmap->dmastream)) {
1075  lastBuffer = (void *) dmap->dmastream->stream->M0AR;
1076  dmap->dmastream->stream->M0AR = (uint32_t) nextBuffer;
1077  } else {
1078  lastBuffer = (void *) dmap->dmastream->stream->M1AR;
1079  dmap->dmastream->stream->M1AR = (uint32_t) nextBuffer;
1080  }
1081  return lastBuffer;
1082 }
1083 #endif
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.
bool dma_lld_start(DMADriver *dmap, bool allocate_stream)
Configures and activates the DMA peripheral.
void dmaStop(DMADriver *dmap)
Deactivates the DMA peripheral.
msg_t dmaTransfertTimeout(DMADriver *dmap, volatile void *periphp, void *mem0p, const size_t size, sysinterval_t timeout)
Performs a DMA transaction.
void dmaGetRegisters(DMADriver *dmap, volatile void *periphp, void *mem0p, const size_t size, DMA_Stream_TypeDef *registers)
copy the dma register to memory.
bool dmaStartTransfertI(DMADriver *dmap, volatile void *periphp, void *mem0p, const size_t size)
Starts a DMA transaction.
void dma_lld_stop(DMADriver *dmap)
Deactivates the DMA peripheral.
void dma_lld_get_registers(DMADriver *dmap, volatile void *periphp, void *mem0p, const size_t size, DMA_Stream_TypeDef *registers)
Copy the register of a ready stream.
static size_t getCrossCacheBoundaryAwareSize(const void *memp, const size_t dsize)
void dmaStopTransfert(DMADriver *dmap)
Stops an ongoing transaction.
void dmaAcquireBus(DMADriver *dmap)
Gains exclusive access to the DMA peripheral.
void dmaReleaseBus(DMADriver *dmap)
Releases exclusive access to the DMA peripheral.
void dma_lld_stop_transfert(DMADriver *dmap)
Stops an ongoing transaction.
void dmaStopTransfertI(DMADriver *dmap)
Stops an ongoing transaction.
bool dma_lld_start_transfert(DMADriver *dmap, volatile void *periphp, void *mem0p, const size_t size)
Starts a DMA transaction.
bool dmaStart(DMADriver *dmap, const DMAConfig *cfg)
Configures and activates the DMA peripheral.
bool dmaReloadConf(DMADriver *dmap, const DMAConfig *cfg)
Configures and activates the DMA peripheral.
static void dma_lld_serve_interrupt(DMADriver *dmap, uint32_t flags)
DMA ISR service routine.
uint8_t dmaGetStreamIndex(DMADriver *dmap)
STM32 DMA subsystem driver header.
uint8_t msize
DMA memory data granurality in bytes (1,2,4)
@ DMA_CONTINUOUS_HALF_BUFFER
Continuous mode to/from the same buffer.
@ DMA_ONESHOT
One transert then stop
@ DMA_DIR_M2P
MEMORY to PERIPHERAL
@ DMA_DIR_M2M
MEMORY to MEMORY
@ DMA_DIR_P2M
PERIPHERAL to MEMORY
Definition: hal_stm32_dma.h:99
size_t size
hold size of current transaction
bool inc_peripheral_addr
Enable increment of peripheral address after each transfert.
#define _dma_reset_i(dmap)
Resumes a thread waiting for a dma transfert completion.
#define _dma_reset_s(dmap)
Resumes a thread waiting for a dma transfert completion.
uint8_t irq_priority
DMA IRQ priority (2 .
uint8_t dma_priority
DMA priority (1 .
const DMAConfig * config
Current configuration data.
static void _dma_isr_error_code(DMADriver *dmap, dmaerrormask_t err)
Common ISR code, error event.
dmaerrormask_t
Possible DMA failure causes.
Definition: hal_stm32_dma.h:87
@ DMA_ERR_FIFO_EMPTY
DMA FIFO underrun.
Definition: hal_stm32_dma.h:92
@ DMA_ERR_FIFO_FULL
DMA FIFO overrun.
Definition: hal_stm32_dma.h:91
@ DMA_ERR_TRANSFER_ERROR
DMA transfer failure.
Definition: hal_stm32_dma.h:88
@ DMA_ERR_DIRECTMODE_ERROR
DMA Direct Mode failure.
Definition: hal_stm32_dma.h:89
@ DMA_ERR_FIFO_ERROR
DMA FIFO error.
Definition: hal_stm32_dma.h:90
void * mem0p
memory address
dmaerrorcallback_t error_cb
Error callback or NULL.
uint32_t stream
stream associated with transaction
void * dma_lld_set_next_double_buffer(DMADriver *dmap, void *nextBuffer)
dmaopmode_t op_mode
one shot, or circular half buffer, or circular double buffers
uint8_t controller
controller associated with stream
static void _dma_isr_full_code(DMADriver *dmap)
Common ISR code, full buffer event.
uint32_t dmamode
hold DMA CR register for the stream
@ DMA_STOP
Stopped.
Definition: hal_stm32_dma.h:75
@ DMA_ACTIVE
Transfering.
Definition: hal_stm32_dma.h:77
@ DMA_ERROR
Transfert error.
Definition: hal_stm32_dma.h:79
@ DMA_READY
Ready.
Definition: hal_stm32_dma.h:76
@ DMA_COMPLETE
Transfert complete.
Definition: hal_stm32_dma.h:78
uint8_t channel
dmacallback_t end_cb
Callback function associated to the stream or NULL.
static void _dma_isr_half_code(DMADriver *dmap)
Common ISR code, half buffer event.
dmadirection_t direction
DMA transaction direction.
const stm32_dma_stream_t * dmastream
DMA stream associated with peripheral or memory.
volatile dmastate_t state
Driver state.
uint8_t psize
DMA peripheral data granurality in bytes (1,2,4)
bool inc_memory_addr
Enable increment of memory address after each transfert.
DMA stream configuration structure.
Structure representing a DMA driver.
uint8_t msg[10]
Buffer used for general comunication over SPI (out buffer)
static float timeout
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