DMA
Configuration
Every data channel needs a DMA channel. RX, TX are independent, so they needs to use 2 DMA channel. UART_RX -> DMA1_channel16; UART_TX -> DMA1_channel17
The developerdefines two functions in the main
- DMA_init():
i. enable DMA1 clock
ii. set channels' priorities and enable interrupt.
static void MX_DMA_Init(void)
{
/* DMA controller clock enable */
__HAL_RCC_DMA1_CLK_ENABLE();
/* DMA interrupt init */
/* DMA1_Channel6_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Channel6_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Channel6_IRQn);
/* DMA1_Channel7_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Channel7_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Channel7_IRQn);
}
-
UART_Init():
i. set the huart2.Init (UART_InitTypeDef) such as baudrate, #bits, parity
ii. invoke HAL_UART_Init().
/* USART2 init function */
static void MX_USART2_UART_Init(void)
{
huart2.Instance = USART2;
huart2.Init.BaudRate = 115200;
huart2.Init.WordLength = UART_WORDLENGTH_8B;
huart2.Init.StopBits = UART_STOPBITS_1;
huart2.Init.Parity = UART_PARITY_NONE;
huart2.Init.Mode = UART_MODE_TX_RX;
huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart2.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart2) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
}
Inside HAL_xxx_Init:
- Call the weak function HAL_UART_MspInit(); // developer should override this function
- Using *.Init to set *.Instance's registers
The developer should override HAL_xxx_MspInit():
* Define in the stm32f1xx_hal_msp.c file
- 1. check if the instance is the desire instance. Because all UART1, UART2 and UART3 are going to use this function.
if(huart->Instance == USART2){
- Enable corresponding GPIO clock, and peripheral clock
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_USART2_CLK_ENABLE();
- Configure GPIO
GPIO_InitStruct.Pin = GPIO_PIN_2;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; // alternative function推免
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_3;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
- Configure DMA channels
/* USART2 DMA Init */
/* USART2_RX Init */
hdma_usart2_rx.Instance = DMA1_Channel6;
hdma_usart2_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_usart2_rx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_usart2_rx.Init.MemInc = DMA_MINC_ENABLE;
hdma_usart2_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_usart2_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_usart2_rx.Init.Mode = DMA_NORMAL;
hdma_usart2_rx.Init.Priority = DMA_PRIORITY_LOW;
if (HAL_DMA_Init(&hdma_usart2_rx) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
__HAL_LINKDMA(huart,hdmarx,hdma_usart2_rx);
// bind the huart's RX DMA channel to this DMA
// bind DMA parent to the huart
/* USART2_TX Init */
hdma_usart2_tx.Instance = DMA1_Channel7;
hdma_usart2_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
hdma_usart2_tx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_usart2_tx.Init.MemInc = DMA_MINC_ENABLE;
hdma_usart2_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_usart2_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_usart2_tx.Init.Mode = DMA_NORMAL;
hdma_usart2_tx.Init.Priority = DMA_PRIORITY_LOW;
if (HAL_DMA_Init(&hdma_usart2_tx) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
__HAL_LINKDMA(huart,hdmatx,hdma_usart2_tx);
- Enable UART interrupt
/* USART2 interrupt Init */
HAL_NVIC_SetPriority(USART2_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(USART2_IRQn);
Tranfer data in DMA mode
-
HAL_UART_Transmit_DMA()
A non-blocking function. Return HAL_OK or HAL_BUSY
// two successive call to the function, the second may be ignored.
HAL_UART_Transmit_DMA(&huart1, data, 5 );
HAL_UART_Transmit_DMA(&huart1, data2, 6); // the second would be ignore if the first is not completed.
// !!!!design a queue that send the following in the uart complete callback.
//This function bind the pre-defined handler to the DMA TX channel, and start DMA transmit
-
HAL_UART_Receive_DMA()
A non-blocking function. Return HAL_OK or HAL_BUSY
// The DMA will block in the background if no data is coming.
-
user defined callback functions
void HAL_UART_TxCpltCallback(UART_HandleTypeDef * huart);
void HAL_UART_RxCpltCallback(UART_HandleTypeDef * huart);
void HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef * huart);
void HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef * huart);
void HAL_UART_ErrorCallback(UART_HandleTypeDef * huart);
//....
Interrupt relations
Three type of interrupts are involved in this app.
Code in the stm32f1xx_it. This wrapper provides an argument to the core handler via software C "extern" keyword.
-
void DMA1_Channel6_IRQHandler(void)
{
HAL_DMA_IRQHandler(&hdma_usart2_rx); // defined in the "stm32f1xx_hal_dma.c"
}
-
void DMA1_Channel7_IRQHandler(void)
{
HAL_DMA_IRQHandler(&hdma_usart2_tx);
}
-
void USART2_IRQHandler(void)
{
HAL_UART_IRQHandler(&huart2);
}
DMA interrupt [HAL_DMA_IRQHandler]
Three source can trigger DMA interrupt, half complete, full complete and error. Inside the HAL_DMA_IRQHandler, it polling the three conditions and invoke the
callback method defined in the DMA_HandlerTypeDef
The callback functions are defined by the HAL uart library in the stm32f1xx_hal_uart.c. They will bind to the DMA handler when calling to transmit/receive data.
-
UART_DMATransmitCplt(DMA_HandleTypeDef *hdma)
//This function is defined in the stm32f1xx_hal_uart.c
//It invokes after the DMA transmit completes, therefore, both rx/tx will invoke this function
//During normal mode, it enable the uart transmit complete interrupt. Becuase the transmit is complete, so the transmit complete function would be invoked immediately.
//The UART complete handler will just clear the the busy state of this uart.
static void UART_DMATransmitCplt(DMA_HandleTypeDef *hdma)
{
UART_HandleTypeDef* huart = ( UART_HandleTypeDef* )((DMA_HandleTypeDef* )hdma)->Parent;
/* DMA Normal mode*/
if((hdma->Instance->CCR & DMA_CCR_CIRC) == 0U)
{
huart->TxXferCount = 0U;
/* Disable the DMA transfer for transmit request by setting the DMAT bit
in the UART CR3 register */
CLEAR_BIT(huart->Instance->CR3, USART_CR3_DMAT);
/* Enable the UART Transmit Complete Interrupt */
SET_BIT(huart->Instance->CR1, USART_CR1_TCIE);
}
/* DMA Circular mode */
else
{
HAL_UART_TxCpltCallback(huart);
}
}
static void UART_DMAReceiveCplt(DMA_HandleTypeDef *hdma)
{
UART_HandleTypeDef* huart = ( UART_HandleTypeDef* )((DMA_HandleTypeDef* )hdma)->Parent;
/* DMA Normal mode*/
if((hdma->Instance->CCR & DMA_CCR_CIRC) == 0U)
{
huart->RxXferCount = 0U;
/* Disable RXNE, PE and ERR (Frame error, noise error, overrun error) interrupts */
CLEAR_BIT(huart->Instance->CR1, USART_CR1_PEIE);
CLEAR_BIT(huart->Instance->CR3, USART_CR3_EIE);
/* Disable the DMA transfer for the receiver request by setting the DMAR bit
in the UART CR3 register */
CLEAR_BIT(huart->Instance->CR3, USART_CR3_DMAR);
/* At end of Rx process, restore huart->RxState to Ready */
huart->RxState = HAL_UART_STATE_READY;
}
HAL_UART_RxCpltCallback(huart);
}
-
When transmitting data, the transmit function will bind the tx DMA channel's interrupt to the following handlers.
UART_DMATxHalfCplt(DMA_HandleTypeDef *hdma)
//ask the developer's action via the weak function HAL_UART_TxHalfCpltCallback
static void UART_DMATxHalfCplt(DMA_HandleTypeDef *hdma)
{
UART_HandleTypeDef* huart = (UART_HandleTypeDef*)((DMA_HandleTypeDef*)hdma)->Parent;
HAL_UART_TxHalfCpltCallback(huart); // By default, it does nothing
}
static void UART_DMARxHalfCplt(DMA_HandleTypeDef *hdma)
{
UART_HandleTypeDef* huart = (UART_HandleTypeDef*)((DMA_HandleTypeDef*)hdma)->Parent;
HAL_UART_RxHalfCpltCallback(huart);
}
-
UART_DMAError(DMA_HandleTypeDef *hdma)
UART Interrupt[HAL_UART_IRQHandler]
UART interrupt handler defined in the stm32_hal_uart.c. It pulls the source that cause the interrupt.
void HAL_UART_IRQHandler(UART_HandleTypeDef *huart)
{
uint32_t isrflags = READ_REG(huart->Instance->SR);
uint32_t cr1its = READ_REG(huart->Instance->CR1);
uint32_t cr3its = READ_REG(huart->Instance->CR3);
uint32_t errorflags = 0x00U;
uint32_t dmarequest = 0x00U;
/* If no error occurs */
errorflags = (isrflags & (uint32_t)(USART_SR_PE | USART_SR_FE | USART_SR_ORE | USART_SR_NE));
if(errorflags == RESET)
{
/* UART in mode Receiver -------------------------------------------------*/
if(((isrflags & USART_SR_RXNE) != RESET) && ((cr1its & USART_CR1_RXNEIE) != RESET))
{
UART_Receive_IT(huart);
return;
}
}
//////// not entire code are shown here. ///////
/* UART in mode Transmitter ------------------------------------------------*/
if(((isrflags & USART_SR_TXE) != RESET) && ((cr1its & USART_CR1_TXEIE) != RESET))
{
UART_Transmit_IT(huart);
return;
}
/* UART in mode Transmitter end --------------------------------------------*/
if(((isrflags & USART_SR_TC) != RESET) && ((cr1its & USART_CR1_TCIE) != RESET))
{
UART_EndTransmit_IT(huart);
return;
}
}