Paparazzi UAS v7.0_unstable
Paparazzi is a free software Unmanned Aircraft System.
Loading...
Searching...
No Matches
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/*
33TODO :
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
61static void dma_lld_serve_interrupt(DMADriver *dmap, uint32_t flags);
62
63#if STM32_DMA_ADVANCED
64static 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)
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
105bool dmaStart(DMADriver *dmap, const DMAConfig *cfg)
106{
107 osalDbgCheck((dmap != NULL) && (cfg != NULL));
108 #if STM32_DMA_USE_DOUBLE_BUFFER
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
125 return statusOk;
126}
127
138bool dmaReloadConf(DMADriver *dmap, const DMAConfig *cfg)
139{
140 osalDbgCheck((dmap != NULL) && (cfg != NULL));
141#if STM32_DMA_USE_DOUBLE_BUFFER
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
156 return statusOk;
157}
158
159
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;
181}
182
183
201bool dmaStartTransfert(DMADriver *dmap, volatile void *periphp, void * mem0p, const size_t size)
202{
203 osalSysLock();
204 const bool statusOk = dmaStartTransfertI(dmap, periphp, mem0p, size);
206 return statusOk;
207}
208
226bool dmaStartTransfertI(DMADriver *dmap, volatile void *periphp, void * mem0p, const size_t size)
227{
229
230 #if STM32_DMA_USE_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) {
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
328void dmaGetRegisters(DMADriver *dmap, volatile void *periphp, void *mem0p,
329 const size_t size,
331{
332#if STM32_DMA_USE_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) {
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 }
407}
408
409
410
422{
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
472msg_t dmaTransfertTimeout(DMADriver *dmap, volatile void *periphp, void *mem0p, const size_t size,
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 }
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
553{
555
556 const DMAConfig *cfg = dmap->config;
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
575 switch (cfg->direction) {
579 default: osalDbgAssert(false, "direction not set or incorrect");
580 }
581
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) {
588 if ((cfg->direction != DMA_DIR_M2M) &&
591 }
592 }
593
594 if (cfg->error_cb) {
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) |
616#endif
619
621 | STM32_DMA_CR_CHSEL(cfg->request)
622#elif STM32_DMA_ADVANCED
623#if STM32_DMA_SUPPORTS_DMAMUX == 0
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)) {
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) {
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,
813 (void *) dmap );
814#else
815 dmap->dmastream = dmaStreamAllocI(dmap->config->stream,
816 cfg->irq_priority,
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
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
838static 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
851#else
852 (void) memp;
853 return dsize;
854#endif
855}
856
864#ifndef DMA_request_TypeDef
865void dma_lld_get_registers(DMADriver *dmap, volatile void *periphp,
866 void *mem0p, const size_t size,
868{
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
877 dmaStreamSetMemory1(dmap->dmastream, dmap->config->next_cb(dmap, size));
878 }
879#endif
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));
888}
889#endif
897bool 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);
905 }
906#endif
907 dmap->mem0p = mem0p;
908#if __DCACHE_PRESENT
909 dmap->periphp = periphp;
910#endif
911 dmap->size = size;
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
920 dmaStreamSetMemory1(dmap->dmastream, dmap->config->next_cb(dmap, size));
921 }
922#endif
924 dmaStreamSetMode(dmap->dmastream, dmap->dmamode);
925#if STM32_DMA_ADVANCED
926 dmaStreamSetFIFO(dmap->dmastream, dmap->fifomode);
927#endif
929
930 return true;
931}
932
944
953{
954#if CH_KERNEL_MAJOR < 6
956#else
958#endif
959}
960
961
962/*===========================================================================*/
963/* Driver local functions. */
964/*===========================================================================*/
965
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 =
988
989 if (dmap->config->fifo != 0U) {
990#if STM32_DMA_ADVANCED
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);
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
1044void dma_lld_serve_timeout_interrupt(void *arg)
1045{
1046 DMADriver *dmap = (DMADriver *) arg;
1047 if (dmap->config->op_mode != DMA_ONESHOT) {
1049 chVTSetI(&dmap->vt, dmap->config->timeout,
1050 &dma_lld_serve_timeout_interrupt, (void *) dmap);
1052 }
1054}
1055#endif
1056
1057#if STM32_DMA_USE_DOUBLE_BUFFER
1071{
1072 void *lastBuffer;
1073
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)
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
size_t size
hold size of current transaction
void * dma_lld_set_next_double_buffer(DMADriver *dmap, void *nextBuffer)
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.
@ DMA_ERR_FIFO_EMPTY
DMA FIFO underrun.
@ DMA_ERR_FIFO_FULL
DMA FIFO overrun.
@ DMA_ERR_TRANSFER_ERROR
DMA transfer failure.
@ DMA_ERR_DIRECTMODE_ERROR
DMA Direct Mode failure.
@ DMA_ERR_FIFO_ERROR
DMA FIFO error.
void * mem0p
memory address
dmaerrorcallback_t error_cb
Error callback or NULL.
uint32_t stream
stream associated with transaction
dmaopmode_t op_mode
one shot, or circular half buffer, or circular double buffers
#define STM32_DMA_SUPPORTS_CSELR
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.
@ DMA_ACTIVE
Transfering.
@ DMA_ERROR
Transfert error.
@ DMA_READY
Ready.
@ DMA_COMPLETE
Transfert complete.
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)
uint16_t foo
Definition main_demo5.c:58
static float timeout
unsigned int uint32_t
Typedef defining 32 bit unsigned int type.
unsigned char uint8_t
Typedef defining 8 bit unsigned char type.