STM32F072从零配置工程-串口DMA实现

话不多说,先贴上主要的外设初始化流程:

int main(void) { /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* Configure the system clock */ SystemClock_Config(); /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_DMA_Init(); MX_USART2_UART_Init(); /* Infinite loop */ while (1) { if(UART2_Length && Tx2_Complete_Flag == 0) { DMA_Tx2_Data(User_UART2_Buffer, UART2_Length); UART2_Length = 0; }} }


分析一下初始化流程:
GPIO的初始化:由于没有使用到GPIO的初始化,因此GPIO的初始化还是以时钟使能为主;
void MX_GPIO_Init(void) { /* GPIO Ports Clock Enable */ __HAL_RCC_GPIOF_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE(); }


DMA初始化:使能了DMA时钟,配置了DMA通道中断的中断优先级并使能了DMA中断函数;
void MX_DMA_Init(void) { /* DMA controller clock enable */ __HAL_RCC_DMA1_CLK_ENABLE(); /* DMA interrupt init */ /* DMA1_Channel4_5_6_7_IRQn interrupt configuration */ HAL_NVIC_SetPriority(DMA1_Channel4_5_6_7_IRQn, 2, 0); HAL_NVIC_EnableIRQ(DMA1_Channel4_5_6_7_IRQn); }


串口UART初始化:也是整个流程的重点配置;
首先是对UART2串口的配置,一如往常的配置;
接下来在HAL_UART_MspInit是对串口引脚和DMA的配置:
使能了UART2和GPIOA的时钟;
配置PA2和PA3为引脚复用;
配置DMA的TX和RX,这里添加了两行代码来设定DMA源地址和目标地址;
链接DMA句柄和UART2的DMA句柄;
设置串口中断函数的中断优先级;
开启UART的IDLE中断并使能串口;
清除串口的TC标志位,防止第一次接收数据产生错误;
分别使能DMARx/Tx的TC中断,并使能其对应的串口DMA中断使能位;
开启DMARx中断,并关闭DMATx中断;
uint8_t UART2_Tx_Buffer[TX_BUF_LEN]; uint8_t UART2_Rx_Buffer[RX_BUF_LEN]; 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* uartHandle) {GPIO_InitTypeDef GPIO_InitStruct = {0}; if(uartHandle->Instance==USART2) { /* USART2 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.Instance->CPAR = (uint32_t)(&(USART2->RDR)); hdma_usart2_rx.Instance->CMAR = (uint32_t)UART2_Rx_Buffer; 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_HIGH; if (HAL_DMA_Init(&hdma_usart2_rx) != HAL_OK) { Error_Handler(); }__HAL_LINKDMA(uartHandle,hdmarx,hdma_usart2_rx); /* USART2_TX Init */ hdma_usart2_tx.Instance = DMA1_Channel4; hdma_usart2_tx.Instance->CPAR = (uint32_t)(&(USART2->TDR)); hdma_usart2_tx.Instance->CMAR = (uint32_t)UART2_Tx_Buffer; 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(uartHandle,hdmatx,hdma_usart2_tx); /* USART2 interrupt Init */ HAL_NVIC_SetPriority(USART2_IRQn, 1, 0); HAL_NVIC_EnableIRQ(USART2_IRQn); /* USER CODE BEGIN USART2_MspInit 1 */__HAL_UART_ENABLE_IT(uartHandle, UART_IT_IDLE); __HAL_UART_ENABLE(uartHandle); __HAL_UART_CLEAR_FLAG(uartHandle, UART_CLEAR_TCF); __HAL_UART_CLEAR_IT(uartHandle, UART_CLEAR_TCF); __HAL_DMA_ENABLE_IT(&hdma_usart2_rx, DMA_IT_TC); SET_BIT(uartHandle->Instance->CR3, USART_CR3_DMAR); __HAL_DMA_DISABLE(&hdma_usart2_tx); __HAL_DMA_ENABLE_IT(&hdma_usart2_tx, DMA_IT_TC); SET_BIT(uartHandle->Instance->CR3, USART_CR3_DMAT); __HAL_DMA_ENABLE(&hdma_usart2_rx); /* USER CODE END USART2_MspInit 1 */ } }


有一段代码很有意思:
这段代码将uartHandle.hdmarx与hdma_usart2_rx的句柄链接在一起,意味着两者可以在以后的配置和使用中是对等的,用哪个进行配置都可以;
__HAL_LINKDMA(uartHandle,hdmarx,hdma_usart2_rx);


重点:
由于HAL库中并没有包含对IDLE中断的回调处理函数,因此可以自己通过修改HAL库函数来实现IDLE回调函数;
首先在HAL_UART_IRQHandler中添加对IDLE回调函数的判断;
void HAL_UART_IRQHandler(UART_HandleTypeDef *huart) { uint32_t isrflags= READ_REG(huart->Instance->ISR); uint32_t cr1its= READ_REG(huart->Instance->CR1); uint32_t cr3its; uint32_t errorflags; /* If no error occurs */ errorflags = (isrflags & (uint32_t)(USART_ISR_PE | USART_ISR_FE | USART_ISR_ORE | USART_ISR_NE)); if (errorflags == RESET) { /* UART in mode Receiver ---------------------------------------------------*/ if(((isrflags & USART_ISR_RXNE) != RESET) && ((cr1its & USART_CR1_RXNEIE) != RESET)) { UART_Receive_IT(huart); return; } }/* If some errors occur */ cr3its = READ_REG(huart->Instance->CR3); if((errorflags != RESET) && (((cr3its & USART_CR3_EIE) != RESET) || ((cr1its & (USART_CR1_RXNEIE | USART_CR1_PEIE)) != RESET)) ) { /* UART parity error interrupt occurred -------------------------------------*/ if(((isrflags & USART_ISR_PE) != RESET) && ((cr1its & USART_CR1_PEIE) != RESET)) { __HAL_UART_CLEAR_IT(huart, UART_CLEAR_PEF); huart->ErrorCode |= HAL_UART_ERROR_PE; }/* UART frame error interrupt occurred --------------------------------------*/ if(((isrflags & USART_ISR_FE) != RESET) && ((cr3its & USART_CR3_EIE) != RESET)) { __HAL_UART_CLEAR_IT(huart, UART_CLEAR_FEF); huart->ErrorCode |= HAL_UART_ERROR_FE; }/* UART noise error interrupt occurred --------------------------------------*/ if(((isrflags & USART_ISR_NE) != RESET) && ((cr3its & USART_CR3_EIE) != RESET)) { __HAL_UART_CLEAR_IT(huart, UART_CLEAR_NEF); huart->ErrorCode |= HAL_UART_ERROR_NE; }/* UART Over-Run interrupt occurred -----------------------------------------*/ if(((isrflags & USART_ISR_ORE) != RESET) && (((cr1its & USART_CR1_RXNEIE) != RESET) || ((cr3its & USART_CR3_EIE) != RESET))) { __HAL_UART_CLEAR_IT(huart, UART_CLEAR_OREF); huart->ErrorCode |= HAL_UART_ERROR_ORE; }/* Call UART Error Call back function if need be --------------------------*/ if(huart->ErrorCode != HAL_UART_ERROR_NONE) { /* UART in mode Receiver ---------------------------------------------------*/ if(((isrflags & USART_ISR_RXNE) != RESET) && ((cr1its & USART_CR1_RXNEIE) != RESET)) { UART_Receive_IT(huart); }/* If Overrun error occurs, or if any error occurs in DMA mode reception, consider error as blocking */ if (((huart->ErrorCode & HAL_UART_ERROR_ORE) != RESET) || (HAL_IS_BIT_SET(huart->Instance->CR3, USART_CR3_DMAR))) { /* Blocking error : transfer is aborted Set the UART state ready to be able to start again the process, Disable Rx Interrupts, and disable Rx DMA request, if ongoing */ UART_EndRxTransfer(huart); /* Disable the UART DMA Rx request if enabled */ if (HAL_IS_BIT_SET(huart->Instance->CR3, USART_CR3_DMAR)) { CLEAR_BIT(huart->Instance->CR3, USART_CR3_DMAR); /* Abort the UART DMA Rx channel */ if(huart->hdmarx != NULL) { /* Set the UART DMA Abort callback : will lead to call HAL_UART_ErrorCallback() at end of DMA abort procedure */ huart->hdmarx->XferAbortCallback = UART_DMAAbortOnError; /* Abort DMA RX */ if(HAL_DMA_Abort_IT(huart->hdmarx) != HAL_OK) { /* Call Directly huart->hdmarx->XferAbortCallback function in case of error */ huart->hdmarx->XferAbortCallback(huart->hdmarx); } } else { /* Call user error callback */ HAL_UART_ErrorCallback(huart); } } else { /* Call user error callback */ HAL_UART_ErrorCallback(huart); } } else { /* Non Blocking error : transfer could go on. Error is notified to user through user error callback */ HAL_UART_ErrorCallback(huart); huart->ErrorCode = HAL_UART_ERROR_NONE; } } return; } /* End if some error occurs */#if !defined(STM32F030x6) && !defined(STM32F030x8)&& !defined(STM32F070xB)&& !defined(STM32F070x6)&& !defined(STM32F030xC) /* UART wakeup from Stop mode interrupt occurred ---------------------------*/ if(((isrflags & USART_ISR_WUF) != RESET) && ((cr3its & USART_CR3_WUFIE) != RESET)) { __HAL_UART_CLEAR_IT(huart, UART_CLEAR_WUF); /* Set the UART state ready to be able to start again the process */ huart->gState= HAL_UART_STATE_READY; huart->RxState = HAL_UART_STATE_READY; HAL_UARTEx_WakeupCallback(huart); return; } #endif /* !defined(STM32F030x6) && !defined(STM32F030x8)&& !defined(STM32F070xB)&& !defined(STM32F070x6)&& !defined(STM32F030xC) *//* UART in mode Transmitter ------------------------------------------------*/ if(((isrflags & USART_ISR_TXE) != RESET) && ((cr1its & USART_CR1_TXEIE) != RESET)) { UART_Transmit_IT(huart); return; }if(((isrflags & USART_ISR_IDLE) != RESET) && ((cr1its & USART_CR1_IDLEIE) != RESET)) { HAL_UART_IdleCpltCallback(huart); return; } /* UART in mode Transmitter (transmission end) -----------------------------*/ if(((isrflags & USART_ISR_TC) != RESET) && ((cr1its & USART_CR1_TCIE) != RESET)) { UART_EndTransmit_IT(huart); return; }}


接下来在stm32f0xx_hal_uart.c的库代码中添加IDLE的回调函数HAL_UART_IdleCpltCallback()的定义;
__weak void HAL_UART_IdleCpltCallback(UART_HandleTypeDef *huart) { /* Prevent unused argument(s) compilation warning */ UNUSED(huart); /* NOTE: This function Should not be modified, when the callback is needed, the HAL_UART_TxCpltCallback could be implemented in the user file */ }


最后别忘了在对应的stm32f0xx_hal_uart.h中添加对HAL_UART_IdleCpltCallback()的声明;
void HAL_UART_IdleCpltCallback(UART_HandleTypeDef *huart);


接下来看如何使用这个添加的IDLE回调函数:
在串口中断函数USART2_IRQHandler中调用HAL_UART_IRQHandler(&huart2); 以实现在IDLE中断标志位置位后调用HAL_UART_IdleCpltCallback回调函数;
然后串口传输完成中断TC的判断直接在串口中断函数中实现,而不引用相应的TC回调函数;
void HAL_UART_IdleCpltCallback(UART_HandleTypeDef *huart) { uint16_t len = 0; if((__HAL_UART_GET_FLAG(&huart2,UART_FLAG_IDLE) != RESET)) { __HAL_UART_CLEAR_IDLEFLAG(huart); __HAL_DMA_DISABLE(huart->hdmarx); len = RX_BUF_LEN - huart2.hdmarx->Instance->CNDTR; memcpy(User_UART2_Buffer, UART2_Rx_Buffer, len); huart2.hdmarx->Instance->CNDTR = RX_BUF_LEN; __HAL_DMA_ENABLE(huart->hdmarx); UART2_Length = len; }else { UART2_Length = 0; } }/** * @brief This function handles USART2 global interrupt / USART2 wake-up interrupt through EXTI line 26. */ void USART2_IRQHandler(void) { /* USER CODE BEGIN USART2_IRQn 0 */ UART_ErrorFlag_Clear(&huart2); /* USER CODE END USART2_IRQn 0 */ HAL_UART_IRQHandler(&huart2); /* USER CODE BEGIN USART2_IRQn 1 */if(__HAL_UART_GET_IT(&huart2, UART_IT_TC) != RESET) { __HAL_UART_CLEAR_IT(&huart2, UART_IT_TC); __HAL_UART_DISABLE_IT(&huart2, UART_IT_TC); Tx2_Complete_Flag = 0; } /* USER CODE END USART2_IRQn 1 */ }


这里大致讲解一下DMA传输的流程:
当F072接收到一串数据后,进入串口中断函数,清除IDLE中断标志位,关闭串口DMA接收rx,获取接收到的数据len,将接收到的数据UART2_Rx_Buffer拷贝到缓冲User_UART2_Buffer中,重新设置串口DMA接收长度,开启串口DMA接收,将接收到的数据长度赋值给UART2_Length,如果没有接收到数据,就将UART2_Length赋值为0;
接下来在main中判断,若接收到数据UART2_Length不为0,同时Tx2_Complete_Flag为0表示此时串口Tx传输空闲,就执行传输命令;
while (1) { if(UART2_Length && Tx2_Complete_Flag == 0) { DMA_Tx2_Data(User_UART2_Buffer, UART2_Length); UART2_Length = 0; } }


将接收到的数据User_UART2_Buffer和传送给UART2_Tx_Buffer,同时置位Tx2_Complete_Flag标志表示此时开始传输,同时重新设置串口DMATX的传输数据长度,然后开启串口DMA的传输;
void DMA_Tx2_Data(uint8_t *data, uint16_t size) { while(Tx2_Complete_Flag); Tx2_Complete_Flag = 1; memcpy(UART2_Tx_Buffer, data, size); huart2.hdmatx->Instance->CNDTR = TX_BUF_LEN; __HAL_DMA_ENABLE(&hdma_usart2_tx); }


当DMA传输完成后,触发DMA的TC中断标志进入DMA中断函数,清除DMA通道的TC中断标志位,关闭DMATX通道,置位Tx2_Complete_Flag标志表示传输开始,同时开启串口TC中断;
void DMA1_Channel4_5_6_7_IRQHandler(void) { if(__HAL_DMA_GET_FLAG(&hdma_usart2_tx, DMA_FLAG_TC4) != RESET) { __HAL_DMA_CLEAR_FLAG(&hdma_usart2_tx, DMA_FLAG_TC4); __HAL_DMA_DISABLE(&hdma_usart2_tx); Tx2_Complete_Flag = 1; __HAL_UART_ENABLE_IT(&huart2, UART_IT_TC); } }


当串口UART传输完成后,接下来在串口中断函数中检测到串口TC传输完成标志置位,清除TC标志,同时关闭串口TC中断,置位Tx2_Complete_Flag标志为0,表示此时串口接收空闲;

【STM32F072从零配置工程-串口DMA实现】转载于:https://www.cnblogs.com/lpfdezh/p/11213174.html

    推荐阅读