// Timer-triggered memory-to-memory DMA transfer demonstrator for STM32F4xx (probably good enough for STM32F2xx too)// Note, that we won't use the memory-to-memory mode here, as that would simply run not waiting for the triggers// To be run in debugger, watching the dst array to being gradually filled, one word each time the trigger reloads// (and perhaps watching a cycle-counter to see how the timer's ARR setting influences the total time)// (C)2014 by wek at efton dot sk// Legalese: do whatever you want with this#include "stm32f4xx.h"#include#define TIMER 1#if (TIMER == 1) #define _DMA_ 2 #define _Stream_ 5 #define _Channel_ 6#elif (TIMER == 2) // this won't work, as DMA1 can't transfer #define _DMA_ 1 #define _Stream_ 1 #define _Channel_ 3#else #error "no such"#endif#define GLUE5_(a, b, c, d, e) a ## b ## c ## d ## e#define GLUE5(a, b, c, d, e) GLUE5_(a, b, c, d, e)#define GLUE4(a, b, c, d) GLUE5_(a, b, c, d, )#define DBGMCU_APBxFZ GLUE4(DBGMCU->APB, _DMA_, FZ, )#define DBGMCU_APB1_FZ_DBG_TIMy_STOP GLUE4(DBGMCU_APB1_FZ_DBG_TIM, TIMER, _STOP, )#define RCC_AHB1ENR_DMAxEN GLUE4(RCC_AHB1ENR_DMA, _DMA_, EN, )#define DMAx GLUE4(DMA, _DMA_, , )#define DMAStream GLUE4(DMA, _DMA_, _Stream, _Stream_)#if (_Stream_ < 4) #define yISR LISR#else #define yISR HISR#endif#define DMA_yISR_TCIFz GLUE4(DMA_, yISR, _TCIF, _Stream_)#define TIMz GLUE4(TIM, TIMER, , )#define APBxENR GLUE4(APB, _DMA_, ENR, )#define RCC_APBxENR_TIMzEN GLUE5(RCC_APB, _DMA_, ENR_TIM, TIMER, EN)#define BUFSIZE 10volatile uint32_t src[BUFSIZE] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};volatile uint32_t dst[BUFSIZE];int main(void) {// DBGMCU->APB2FZ |= DBGMCU_APB1_FZ_DBG_TIM1_STOP; DBGMCU_APBxFZ |= DBGMCU_APB1_FZ_DBG_TIMy_STOP;// TIM1_UP -> DMA2 Stream5 Channel6 RCC->AHB1ENR |= RCC_AHB1ENR_DMAxEN; while (!(RCC->AHB1ENR & RCC_AHB1ENR_DMAxEN)); DMAStream->NDTR = BUFSIZE; // halfwords to transfer DMAStream->M0AR = (uint32_t)src; DMAStream->PAR = (uint32_t)dst; DMAStream->FCR = 0 | ( 0 * DMA_SxFCR_DMDIS ) // direct mode | (DMA_SxFCR_FTH__FULL * DMA_SxFCR_FTH_0 ) // [irrelevant - keep it full] | ( 0 * DMA_SxFCR_FEIE ) // no interrupt ; DMAStream->CR = 0 | (_Channel_ * DMA_SxCR_CHSEL_0 ) // channel select | (DMA_SxCR_xBURST_INCR1 * DMA_SxCR_MBURST_0 ) // memory burst (only in FIFO mode) | (DMA_SxCR_xBURST_INCR1 * DMA_SxCR_PBURST_0 ) // peripheral burst (only in FIFO mode) | (0 * DMA_SxCR_ACK ) // "reserved" (says manual) | (0 * DMA_SxCR_CT ) // current target (only in double-buffer mode) | (0 * DMA_SxCR_DBM ) // double-buffer mode | (DMA_SxCR_PL_PRIORITY_VERY_HIGH * DMA_SxCR_PL_0 ) // priority level | (0 * DMA_SxCR_PINCOS ) // peripheral increment offset size (only if peripheral address increments, FIFO mode and PBURST is 0) | (DMA_SxCR_xSIZE_WORD * DMA_SxCR_MSIZE_0 ) // memory data size; in direct mode forced to the same value as PSIZE | (DMA_SxCR_xSIZE_WORD * DMA_SxCR_PSIZE_0 ) // peripheral data size | (1 * DMA_SxCR_MINC ) // memory address increments | (1 * DMA_SxCR_PINC ) // peripheral address increments | (0 * DMA_SxCR_CIRC ) // circular mode (forced to 1 if double-buffer mode, forced to 0 if flow control is peripheral) | (DMA_SxCR_DIR_M2P * DMA_SxCR_DIR_0 ) // data transfer direction | (0 * DMA_SxCR_PFCTRL ) // peripheral is the flow controller (i.e. who determines end of transfer) - only for SDIO | (0 * DMA_SxCR_TCIE ) // transfer complete interrupt enable | (0 * DMA_SxCR_HTIE ) // half transfer interrupt enable | (0 * DMA_SxCR_TEIE ) // transfer error interrupt enable | (0 * DMA_SxCR_DMEIE ) // direct mode error interrupt enable | (1 * DMA_SxCR_EN ) // stream enable ; ; RCC->APBxENR |= RCC_APBxENR_TIMzEN; TIMz->PSC = 0; // clk is 16MHz, no prescaler TIMz->ARR = 100; // -> the whole 10-beat DMA transfer takes cca 1000 clk TIMz->DIER = TIM_DIER_UDE; /* Update DMA enable */ TIMz->CR1 = TIM_CR1_CEN; /* Counter enable */ while (!(DMAx->yISR & DMA_yISR_TCIFz)); // wait until DMA transfer finishes __NOP(); // place "finished" breakpoint here while(1) { }}