UART (USART) is a hardware communication protocol that facilitates asynchronous serial communication between devices. It's widely used due to its simplicity and minimal hardware requirements. The article highlights two primary scenarios:
Source: microcontrollerslab.com
On the software side, the article suggests using serial terminal programs like RealTerm to monitor and send data over UART. These tools are invaluable for debugging and real-time communication analysis.
This function sets up UART2 with a baud rate of 9600. It calculates the USARTDIV value based on the peripheral clock (f_apb1) and configures the necessary registers to enable UART transmission and reception. Such low-level configurations are typical in embedded systems programming, offering precise control over hardware peripherals.
#define BAUD 9600
void UART2_Init(){
float usartdiv=f_apb1/(16.0*BAUD);
uint16_t mantissa=floor(usartdiv);
uint8_t uart_frac=round(16*(usartdiv-mantissa));
RCC->APB1ENR|=RCC_APB1ENR_USART2EN;
GPIOA->CRL&=~(0b11<<GPIO_CRL_CNF2_Pos);
GPIOA->CRL|=(0b11<<GPIO_CRL_MODE2_Pos)|(0b10<<GPIO_CRL_CNF2_Pos);
USART2->CR1|=USART_CR1_UE_Msk|USART_CR1_RE;
USART2->CR1&=~USART_CR1_M;
USART2->CR2&=~USART_CR2_STOP;
USART2->BRR=((uint8_t)mantissa<<4)|(uint8_t)uart_frac;
USART2->CR1|=USART_CR1_TE;
}
If you want to use DMA for UART communication, you need to initialize DMA channel for receiving, and transmitting.
From the table below you can see that UART2 RX is assigned to Channel 6 and UART2 TX to Channel 7. Therefore, you must enable and initialize those two channels.
void DMA_UART2_RX_Init(void){
RCC->AHBENR|=RCC_AHBENR_DMA1EN_Msk;
DMA1_Channel6->CPAR=&(USART3->DR);
DMA1_Channel6->CMAR=&UART_RX_Data[0];
DMA1_Channel6->CCR|=0b11<<DMA_CCR_PL_Pos; //Priority very high
DMA1_Channel6->CCR&=~(0b11<<DMA_CCR_MSIZE_Pos); //8bit size
DMA1_Channel6->CCR&=~(0b11<<DMA_CCR_PSIZE_Pos); //8bit size
DMA1_Channel6->CCR&=~DMA_CCR_DIR; //Read from peripheral
DMA1_Channel6->CCR|=DMA_CCR_MINC;
DMA1_Channel6->CCR&=~DMA_CCR_PINC;
DMA1_Channel6->CNDTR=UART2_RX_CNT;
DMA1_Channel6->CCR|=DMA_CCR_TCIE; //Enable Transfer complete interrupt
NVIC_SetPriority (DMA1_Channel6_IRQn, 1); // Set Priority
NVIC_EnableIRQ (DMA1_Channel6_IRQn); // Enable Interrupt
}
void DMA_UART2_TX_Init(void){
RCC->AHBENR|=RCC_AHBENR_DMA1EN_Msk;
DMA1_Channel7->CPAR=&(USART3->DR);
DMA1_Channel7->CMAR=&UART_TX_Data[0];
DMA1_Channel7->CCR|=0b11<<DMA_CCR_PL_Pos; //Priority very high
DMA1_Channel7->CCR&=~(0b11<<DMA_CCR_MSIZE_Pos); //8bit size
DMA1_Channel7->CCR&=~(0b11<<DMA_CCR_PSIZE_Pos); //8bit size
DMA1_Channel7->CCR|=DMA_CCR_DIR; //Send to peripheral
DMA1_Channel7->CCR|=DMA_CCR_MINC;
DMA1_Channel7->CCR&=~DMA_CCR_PINC;
DMA1_Channel7->CNDTR=UART2_TX_CNT;
DMA1_Channel7->CCR|=DMA_CCR_TCIE; //Enable Transfer complete interrupt
NVIC_SetPriority (DMA1_Channel7_IRQn, 1); // Set Priority
NVIC_EnableIRQ (DMA1_Channel7_IRQn); // Enable Interrupt
}
To send data via DMA you only need to fill the DMA buffer (UART_TX_Data[]) and start DMA transfer:
void UART_DMA_TX(void){
DMA1_Channel7->CCR&=~DMA_CCR_EN; //Disable DMA channel, if not already
DMA1_Channel7->CNDTR=UART2_TX_CNT; //Set number of transactions
DMA1_Channel7->CCR|=DMA_CCR_EN; //Enable DMA channel
}
To read data via DMA you only need to start DMA transfer and read the UART_RX_Data[] array:
void UART_DMA_RX(void){
DMA1_Channel6->CCR&=~DMA_CCR_EN; //Disable DMA channel, if not already
DMA1_Channel6->CNDTR=UART2_TX_CNT; //Set number of transactions
DMA1_Channel6->CCR|=DMA_CCR_EN; //Enable DMA channel
}
void DMA1_Channel7_IRQHandler(void){
//Do stuff
}
void DMA1_Channel6_IRQHandler(void){
//Do stuff
}
Copyright 2024-2025, Mario Matovina
Send me an e-mail