/* p13_1.c Send a string to USART2 by DMA * * USART2 transmit is on DMA1 Stream6 Channel 4. * See Reference manual Table 28 RM0390 July 2017. * * USART2 Tx signal is connected to pin PA0. To see the output of USART2 * on a PC, you need to use a USB-serial module. Connect the Rx pin of * the module to the PA0 pin of the STM32F446 Nucleo board. Make sure * the USB-serial module you use has a 3.3V interface. * * By default, the system clock is running at 16 MHz. * The UART is configured for 9600 Baud. * PA0 - USART2 TX (AF8) * * This program was tested with Keil uVision v5.24a with DFP v2.11.0 */ #include "stm32F4xx.h" void USART2_init(void); void DMA1_init(void); void DMA1_Stream6_setup(unsigned int src, unsigned int dst, int len); int done = 1; int main (void) { char alphabets[] = "abcdefghijklmnopqrstuvwxyz"; char message[80]; int i; int size = sizeof(alphabets); USART2_init(); DMA1_init(); while (1) { /* prepare the message for transfer */ for (i = 0; i < size; i++) message[i] = alphabets[i]; /* send the message out by USART2 using DMA */ while (done == 0) {} /* wait until DMA data transfer is done */ done = 0; /* clear done flag */ DMA1_Stream6_setup((unsigned int)message, (unsigned int)&USART2->DR, size); } } /* Initialize UART pins, Baudrate The USART2 is configured to send output to pin PA2 at 9600 Baud. */ void USART2_init (void) { RCC->AHB1ENR |= 1; /* enable GPIOA clock */ RCC->APB1ENR |= 0x20000; /* enable USART2 clock */ /* Configure PA2 for USART2_TX */ GPIOA->AFR[0] &= ~0x0F00; GPIOA->AFR[0] |= 0x0700; /* alt7 for USART2 */ GPIOA->MODER &= ~0x0030; GPIOA->MODER |= 0x0020; /* enable alternate function for PA2 */ USART2->BRR = 0x0683; /* 9600 baud @ 16 MHz */ USART2->CR1 = 0x0008; /* enable Tx, 8-bit data */ USART2->CR2 = 0x0000; /* 1 stop bit */ USART2->CR3 = 0x0000; /* no flow control */ USART2->CR1 |= 0x2000; /* enable USART2 */ USART2->SR = ~0x40; /* clear TC flag */ USART2->CR1 |= 0x0040; /* enable transmit complete interrupt */ NVIC_EnableIRQ(USART2_IRQn); /* USART2 interrupt enable at NVIC */ } /* Initialize DMA1 controller * DMA1 controller's clock is enabled and also the DMA interrupt is * enabled in NVIC. */ void DMA1_init(void) { RCC->AHB1ENR |= 0x00200000; /* DMA controller clock enable */ DMA1->HIFCR = 0x003F0000; /* clear all interrupt flags of Stream 6 */ NVIC_EnableIRQ(DMA1_Stream6_IRQn); /* DMA interrupt enable at NVIC */ } /* Set up a DMA transfer for USART2 * The USART2 is connected to DMA1 Stream 6. This function sets up the * peripheral register address, memory address, number of transfers, * data size, transfer direction, and DMA interrupts are enabled. * At the end, the DMA controller is enabled and the USART2 transmit * DMA is enabled. */ void DMA1_Stream6_setup(unsigned int src, unsigned int dst, int len) { DMA1_Stream6->CR &= ~1; /* disable DMA1 Stream 6 */ while (DMA1_Stream6->CR & 1) {} /* wait until DMA1 Stream 6 is disabled */ DMA1->HIFCR = 0x003F0000; /* clear all interrupt flags of Stream 6 */ DMA1_Stream6->PAR = dst; DMA1_Stream6->M0AR = src; DMA1_Stream6->NDTR = len; DMA1_Stream6->CR = 0x08000000; /* USART2_TX on DMA1 Stream6 Channel 4 */ DMA1_Stream6->CR |= 0x00000440; /* data size byte, mem incr, mem-to-peripheral */ DMA1_Stream6->CR |= 0x16; /* enable interrupts DMA_IT_TC | DMA_IT_TE | DMA_IT_DME */ DMA1_Stream6->FCR = 0; /* direct mode, no FIFO */ DMA1_Stream6->CR |= 1; /* enable DMA1 Stream 6 */ USART2->SR &= ~0x0040; /* clear UART transmit complete interrupt flag */ USART2->CR3 |= 0x80; /* enable USART2 transmitter DMA */ } /* DMA1 Stream6 interrupt handler This function handles the interrupts from DMA1 controller Stream6. The error interrupts have a placeholder for error handling code. If the interrupt is from DMA data transfer complete, the DMA controller is disabled, the interrupt flags are cleared. */ void DMA1_Stream6_IRQHandler(void) { if (DMA1->HISR & 0x000C0000) /* if an error occurred */ while(1) {} /* substitute this by error handling */ DMA1->HIFCR = 0x003F0000; /* clear all interrupt flags of Stream 6 */ DMA1_Stream6->CR &= ~0x10; /* disable DMA1 Stream 6 TCIE */ } /* USART2 interrupt handler * USART2 transmit complete interrupt is used to set the done flag to signal * the other part of the program that the data transfer is done. */ void USART2_IRQHandler(void) { USART2->SR &= ~0x0040; /* clear transmit complete interrupt flag */ done = 1; /* set the done flag */