UART | DMA | STM32F103C8T6 - ARM Cortex M3

 For more details on RCC and UART configuration, see previous posts.

Program:

/* STM32 - USART - Asynchronous mode:
   LSB first
   Baud Rate = 9600, SysClk = 72 MHz
   Start bit - 8 data bits - 1 Stop bit
   
   DMA:
   Peripheral Register -> USART1 -> DR
   Memory Register -> data (8 - bit)
*/

#include "stm32f10x.h"
#define baud_9600 0x1D4C              // for SysClk = 72 MHz

void RCC_config(void);
void USART1_IRQHandler(void);

static uint8_t data[8] = {'U','S','A','R','T','D','M','A'};

void USART1_IRQHandler()
{
  // Transmission complete
  if((USART1 -> SR & USART_SR_TC)){
    GPIOC -> BSRR = GPIO_BSRR_BR13;      // PC13 LED - ON
  }
}

void RCC_config() // RCC clock configuration
{
  RCC -> CR |= RCC_CR_HSEON;                               // HSE ON
  RCC -> CFGR |= (RCC_CFGR_PLLSRC | RCC_CFGR_PLLMULL9);    
// Setting PLL - HSE * 9
  RCC -> CFGR |= RCC_CFGR_SW_1;                            // SysClk = PLLCLK
  RCC -> CR |= RCC_CR_PLLON;                                              
  // Turn ON PLL after above PLL configurations, making SysClk = HSE * 9 = 8 * 9 = 72 MHz
RCC -> CFGR |= RCC_CFGR_PPRE1_DIV2;
// APB1 = AHB / 2 = 72 / 2 = 36 MHz (max)
}

int main()
{
  RCC_config();
 
  /* PC13 LED configuration */
  RCC -> APB2ENR |= RCC_APB2ENR_IOPCEN;                  // Enable Port-C Clock
  GPIOC -> CRH |= GPIO_CRH_MODE13_1 | GPIO_CRH_CNF13_0;  
// PortC_Pin 13 as output @2MHZ with open-drain
  GPIOC -> BSRR = GPIO_BSRR_BS13;                       // Initialize PC13 LED - OFF
 
  /* USART1 configuration */
  RCC -> APB2ENR |= RCC_APB2ENR_USART1EN | RCC_APB2ENR_IOPAEN;            
// Enable UART and Port A clock
  GPIOA -> CRH |= (GPIO_CRH_MODE9_1 | GPIO_CRH_CNF9_1 | GPIO_CRH_CNF10_1);
// PA9- CNF: 10, MODE: 10; PA10- CNF: 10, MODE: 00
  GPIOA -> CRH &= ((~GPIO_CRH_CNF9_0) & (~GPIO_CRH_CNF10_0));            
// Clear default reset value
  USART1 -> BRR = baud_9600;                                              
// Baud Rate = 9600
  USART1 -> CR1 |= USART_CR1_UE;                                          
// Enable USART, M = 0 - 8 data bits
  /* USART1 -> CR2 : STOP = 0 -> Indicating 1 stop bit */
  USART1 -> CR1 |= USART_CR1_TCIE;                                        
// Enable Transmission complete interrupt
  USART1 -> CR1 |= USART_CR1_TE;                                          
// Sending a Idle line
  /* This should clear TXE and TC flag, after which interrupt is enabled */
  NVIC_EnableIRQ(USART1_IRQn);                                            
// Enable USART1 global interrupt in NVIC
                                                               
  /* DMA Configuration */
  RCC -> AHBENR |= RCC_AHBENR_DMA1EN;                                      
// Enable DMA 1 clock
USART1 -> CR3 |= USART_CR3_DMAT;                                        
// Enable DMA Transmitter in USART 1
  DMA1_Channel4 -> CCR |= DMA_CCR4_DIR | DMA_CCR1_MINC | DMA_CCR7_PSIZE_1;
// Read from memory and write to peripheral
  /* PINC - disabled, MINC - enabled, PSIZE - 32-bit, MSIZE - 8-bit, PL = Low*/
  DMA1_Channel4 -> CNDTR = 8;                                              
// Counter = 1 for transfer of 1 data
  DMA1_Channel4 -> CPAR = (uint32_t)&USART1 -> DR;                        
// Address of UART data register
  DMA1_Channel4 -> CMAR = (uint32_t)data;                                  
// Address of data from memory- array name is address
 
  DMA1_Channel4 -> CCR |= DMA_CCR4_EN;                                    
// Enable the DMA Channel
   
  while(1)
  {
    // Do nothing
  }
  return 0;
}

DMA:

Direct Memory Access controller can transfer data between peripheral and memory or in between memories, allowing the CPU to be free to do other tasks and increase its efficiency.

Each Peripheral is assigned a specific channel in DMA 1 or 2. The channels can be allotted priority.

We will use DMA to get data from the memory instead of using for loop which would utilize CPU time, and transmit it over the UART.

Steps:

1. In reference manual, we can see that USART 1 is assigned channel 4 in DMA 1



Enable the DMA 1 clock in RCC register and enable DMA in USART 1.

2. Configuration in DMA_CCR register:

- DIR: Direction of Transfer is from memory to peripheral register, in this case.

- CIRC: Circular mode is disabled - we are just just doing one time transfer

- PINC: We don't need to increment peripheral address, because the data from memory need

to be copied to the same register USART1 -> DR

- MINC: We need to increment the memory so as to transfer character by character from array, in our case

- PSIZE: Peripheral size (32 - bit register)

- MSIZE: Memory size (8 - bit data)

- PL: Channel priority level

Let's leave it at low as we are using only 1 channel

3. DMA_CNDTR register - No. of data to transfer

In our program, we are transferring 8 characters

4. DMA_CPAR - Peripheral register address

5. DMA_CMAR - Initial address of data to be transferred from or to memory

6Enable the channel to start the DMA data transfer and then USART starts to transfer the data once data is written in the USART1 -> DR register.

For ADC in STM32, see this post


Comments