采用HAL库来实现串口DMA,与单纯的串口UART配置不同,串口DMA在MspInit中添加了对DMA的配置;
配置的过程是从Instance、Direction、PeriphInc/ MemInc、PeriphDataAlignment/ MemDataAlignment、Mode、Priority这几个方面来配置;
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; huart2.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE; huart2.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT; if (HAL_UART_Init(&huart2) != HAL_OK) { Error_Handler(); } }void HAL_UART_MspInit(UART_HandleTypeDef* huart) { GPIO_InitTypeDef GPIO_InitStruct = {0}; if(huart->Instance==USART2) { /* Peripheral clock enable */ __HAL_RCC_USART2_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); /**USART2 GPIO Configuration PA2------> USART2_TX PA3------> USART2_RX */ GPIO_InitStruct.Pin = GPIO_PIN_2|GPIO_PIN_3; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; GPIO_InitStruct.Alternate = GPIO_AF1_USART2; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); /* USART2 DMA Init */ /* USART2_RX Init */ hdma_usart2_rx.Instance = DMA1_Channel5; 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_VERY_HIGH; if (HAL_DMA_Init(&hdma_usart2_rx) != HAL_OK) { Error_Handler(); }__HAL_LINKDMA(huart,hdmarx,hdma_usart2_rx); /* USART2_TX Init */ hdma_usart2_tx.Instance = DMA1_Channel4; 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_VERY_HIGH; if (HAL_DMA_Init(&hdma_usart2_tx) != HAL_OK) { Error_Handler(); }__HAL_LINKDMA(huart,hdmatx,hdma_usart2_tx); /* USART2 interrupt Init */ HAL_NVIC_SetPriority(USART2_IRQn, 1, 0); HAL_NVIC_EnableIRQ(USART2_IRQn); } }
参考主函数main:
#define SENDBUFF_SIZE100 uint8_t aRxBuffer; uint8_t aTxBuffer[SENDBUFF_SIZE]; int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_DMA_Init(); MX_USART2_UART_Init(); for(uint8_t i=0; i) { aTxBuffer[i] = 'Y'; }HAL_UART_Receive_IT(&huart3,&aRxBuffer,1); HAL_UART_Transmit_DMA(&huart3,aTxBuffer, SENDBUFF_SIZE); while (1) {} }
其中HAL_UART_Receive_IT()的功能还是一样,开启相关中断(PE、EIE、RXNE);
HAL_UART_Transmit_DMA实现了三个主要的功能:
使用HAL_DMA_Start_IT配置DMA传输的源地址和目标地址、以及传输长度;
使用__HAL_UART_CLEAR_FLAG清除ICR中的TC标志,主要是为了防止第一次接收失败;
使用SET_BIT开启串口的DMA传输使能位;
HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size) { if(huart->RxState == HAL_UART_STATE_READY) { /* Enable the UART Error Interrupt: (Frame error, noise error, overrun error) */ SET_BIT(huart->Instance->CR3, USART_CR3_EIE); /* Enable the UART Parity Error and Data Register not empty Interrupts */ SET_BIT(huart->Instance->CR1, USART_CR1_PEIE | USART_CR1_RXNEIE); return HAL_OK; } }HAL_StatusTypeDef HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size) { /* Check that a Tx process is not already ongoing */ if(huart->gState == HAL_UART_STATE_READY) { /* Enable the UART transmit DMA channel */ HAL_DMA_Start_IT(huart->hdmatx, (uint32_t)huart->pTxBuffPtr, (uint32_t)&huart->Instance->TDR, Size); /* Clear the TC flag in the ICR register */ __HAL_UART_CLEAR_FLAG(huart, UART_CLEAR_TCF); /* Enable the DMA transfer for transmit request by setting the DMAT bit in the UART CR3 register */ SET_BIT(huart->Instance->CR3, USART_CR3_DMAT); return HAL_OK; } }
关于串口和DMA中断的实现:
其中HAL_UART_IRQHandler函数主要是实现判断RXNE中断发生与否,并调用回调函数HAL_UART_RxCpltCallback()来实现具体的函数功能;
/** * @brief This function handles USART2 global interrupt / USART2 wake-up interrupt through EXTI line 26. */ void USART2_IRQHandler(void) { HAL_UART_IRQHandler(&huart2); }/** * @brief This function handles DMA1 channel 4, 5, 6 and 7 interrupts. */ void DMA1_Channel4_5_6_7_IRQHandler(void) { HAL_DMA_IRQHandler(&hdma_usart2_tx); HAL_DMA_IRQHandler(&hdma_usart2_rx); }
其中回调函数的实现同上一章:
HAL_UART_Transmit实现将数据传输出去;
HAL_UART_Receive_IT实现接收数据的同时开启相关中断(PE、EIE和RXNE);
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { HAL_UART_Transmit(&huart3, &aRxBuffer, 1, 0); HAL_UART_Receive_IT(&huart3, &aRxBuffer, 1); }HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout) { if(huart->gState == HAL_UART_STATE_READY) { huart->TxXferSize = Size; huart->TxXferCount = Size; while(huart->TxXferCount > 0) { huart->TxXferCount--; if ((huart->Init.WordLength == UART_WORDLENGTH_9B) && (huart->Init.Parity == UART_PARITY_NONE)) { tmp = (uint16_t*) pData; huart->Instance->TDR = (*tmp & (uint16_t)0x01FFU); pData += 2; } else { huart->Instance->TDR = (*pData++ & (uint8_t)0xFFU); } } /* At end of Tx process, restore huart->gState to Ready */ huart->gState = HAL_UART_STATE_READY; return HAL_OK; } }HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size) { /* Check that a Rx process is not already ongoing */ if(huart->RxState == HAL_UART_STATE_READY) { /* Enable the UART Error Interrupt: (Frame error, noise error, overrun error) */ SET_BIT(huart->Instance->CR3, USART_CR3_EIE); /* Enable the UART Parity Error and Data Register not empty Interrupts */ SET_BIT(huart->Instance->CR1, USART_CR1_PEIE | USART_CR1_RXNEIE); return HAL_OK; } }
这里看一下DMA中断中的传输函数处理:
当传输完成后,进入DMA中断函数,判断相应的DMA通道TC标志位是否置位和DMA的TC使能标志位是否置位;
接下来清除DMA的TC标志位,同时若定义了回调函数则进入回调函数XferCpltCallback()中;
void HAL_DMA_IRQHandler(DMA_HandleTypeDef *hdma) { /* Transfer Complete Interrupt management ***********************************/ else if ((RESET != (flag_it & (DMA_FLAG_TC1 << hdma->ChannelIndex))) && (RESET != (source_it & DMA_IT_TC))) { if((hdma->Instance->CCR & DMA_CCR_CIRC) == 0U) { /* Disable the transfer complete& transfer error interrupts */ /* if the DMA mode is not CIRCULAR */ hdma->Instance->CCR &= ~(DMA_IT_TC | DMA_IT_TE); /* Change the DMA state */ hdma->State = HAL_DMA_STATE_READY; }/* Clear the transfer complete flag */ hdma->DmaBaseAddress->IFCR = DMA_FLAG_TC1 << hdma->ChannelIndex; if(hdma->XferCpltCallback != NULL) { /* Transfer complete callback */ hdma->XferCpltCallback(hdma); } } }
【STM32F072从零配置工程-基于HAL库的串口UART_DMA配置】转载于:https://www.cnblogs.com/lpfdezh/p/11213146.html
推荐阅读
- c语言|一文搞懂栈(stack)、堆(heap)、单片机裸机内存管理malloc
- 嵌入式-外设|DDR3基础详解
- ffmpeg|从零开始仿写一个抖音App——基于FFmpeg的极简视频播放器
- #|Multimedia
- USB转串口|USB转四串口芯片CH9344
- python|Python的DEBUG LOG
- ARM的C语言程序优化
- linux|【原创】ARM LINUX 外部RTC实时时钟驱动移植(RX8025)
- linux进程通信(一)--共享内存+信号量,代码示例
- C|堆和栈