#申请原创# 1、串口是设备与设备之间或者MCU和外设之间常用的通信模式,一个好用的串口接收程序对 协议解析,数据接收都非常方便。
很多人都应该用过串口空闲中断+DMA这个模式用于接收不定长的数据这,STM32H5的库函数对这个方式进行封装,直接调用库函数就可以使用空闲中断DMA接收数据了。
为了方便测试,下面的还是使用USART3进行演示,USART3可以和stlink的VCP进行通信。
2、首先是对串口的DMA进行初始化,设置GPDMA1_REQUEST_USART3_RX接收,初始化dma的通道,设置dma 工作在CircularMode模式。最后将DMA和USART3相关联。
- /* USART3 DMA Init */
- /* GPDMA1_REQUEST_USART3_RX Init */
- NodeConfig.NodeType = DMA_GPDMA_LINEAR_NODE;
- NodeConfig.Init.Request = GPDMA1_REQUEST_USART3_RX;
- NodeConfig.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;
- NodeConfig.Init.Direction = DMA_PERIPH_TO_MEMORY;
- NodeConfig.Init.SrcInc = DMA_SINC_FIXED;
- NodeConfig.Init.DestInc = DMA_DINC_INCREMENTED;
- NodeConfig.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_BYTE;
- NodeConfig.Init.DestDataWidth = DMA_DEST_DATAWIDTH_BYTE;
- NodeConfig.Init.SrcBurstLength = 1;
- NodeConfig.Init.DestBurstLength = 1;
- NodeConfig.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT0|DMA_DEST_ALLOCATED_PORT0;
- NodeConfig.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;
- NodeConfig.Init.Mode = DMA_NORMAL;
- NodeConfig.TriggerConfig.TriggerPolarity = DMA_TRIG_POLARITY_MASKED;
- NodeConfig.DataHandlingConfig.DataExchange = DMA_EXCHANGE_NONE;
- NodeConfig.DataHandlingConfig.DataAlignment = DMA_DATA_RIGHTALIGN_ZEROPADDED;
- if (HAL_DMAEx_List_BuildNode(&NodeConfig, &Node_GPDMA1_Channel0) != HAL_OK)
- {
- Error_Handler();
- }
- if (HAL_DMAEx_List_InsertNode(&List_GPDMA1_Channel0, NULL, &Node_GPDMA1_Channel0) != HAL_OK)
- {
- Error_Handler();
- }
- if (HAL_DMAEx_List_SetCircularMode(&List_GPDMA1_Channel0) != HAL_OK)
- {
- Error_Handler();
- }
- handle_GPDMA1_Channel0.Instance = GPDMA1_Channel0;
- handle_GPDMA1_Channel0.InitLinkedList.Priority = DMA_LOW_PRIORITY_LOW_WEIGHT;
- handle_GPDMA1_Channel0.InitLinkedList.LinkStepMode = DMA_LSM_FULL_EXECUTION;
- handle_GPDMA1_Channel0.InitLinkedList.LinkAllocatedPort = DMA_LINK_ALLOCATED_PORT0;
- handle_GPDMA1_Channel0.InitLinkedList.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;
- handle_GPDMA1_Channel0.InitLinkedList.LinkedListMode = DMA_LINKEDLIST_CIRCULAR;
- if (HAL_DMAEx_List_Init(&handle_GPDMA1_Channel0) != HAL_OK)
- {
- Error_Handler();
- }
- if (HAL_DMAEx_List_LinkQ(&handle_GPDMA1_Channel0, &List_GPDMA1_Channel0) != HAL_OK)
- {
- Error_Handler();
- }
- __HAL_LINKDMA(huart, hdmarx, handle_GPDMA1_Channel0);
- if (HAL_DMA_ConfigChannelAttributes(&handle_GPDMA1_Channel0, DMA_CHANNEL_NPRIV) != HAL_OK)
- {
- Error_Handler();
- }
ST的库里面封装了下面这个函数用于启动dma接收 HAL_UARTEx_ReceiveToIdle_DMA(&huart3, aRXBufferUser, RX_BUFFER_SIZE))。这里面的数组是接收数据存储的地方,长度是一次最大的接收长度。在这个函数里面可以看到这个函数使能了串口空闲中断,并设置DMA接收的参数。这个函数非常方便,不需要我们去额外配置空闲中断,直接调用即可。
- HAL_StatusTypeDef HAL_UARTEx_ReceiveToIdle_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
- {
- HAL_StatusTypeDef status;
- /* Check that a Rx process is not already ongoing */
- if (huart->RxState == HAL_UART_STATE_READY)
- {
- if ((pData == NULL) || (Size == 0U))
- {
- return HAL_ERROR;
- }
- /* Set Reception type to reception till IDLE Event*/
- huart->ReceptionType = HAL_UART_RECEPTION_TOIDLE;
- huart->RxEventType = HAL_UART_RXEVENT_TC;
- status = UART_Start_Receive_DMA(huart, pData, Size);
- /* Check Rx process has been successfully started */
- if (status == HAL_OK)
- {
- if (huart->ReceptionType == HAL_UART_RECEPTION_TOIDLE)
- {
- __HAL_UART_CLEAR_FLAG(huart, UART_CLEAR_IDLEF);
- ATOMIC_SET_BIT(huart->Instance->CR1, USART_CR1_IDLEIE);
- }
- else
- {
-
- status = HAL_ERROR;
- }
- }
- return status;
- }
为了验证功能,将接收的数据再次发送到stlink vcp 上
- void UserDataTreatment(UART_HandleTypeDef *huart, uint8_t* pData, uint16_t Size)
- {
-
- uint8_t* pBuff = pData;
- uint8_t i;
- for (i = 0; i < Size; i++)
- {
- while (!(__HAL_UART_GET_FLAG(huart, UART_FLAG_TXE))) {}
- huart->Instance->TDR = *pBuff;
- pBuff++;
- }
- }
还有就是USART3的中断函数了,里面直接对空闲中断做了判断,如果使能了DMA就调用对应的回调函数,读取接收到的数据。
3、测试结果:
测试代码如下,需要注意要把代码解压到STM32Cube_FW_H5_V1.1.0\Projects\NUCLEO-H563ZI\Examples\UART路径下面
UART_ReceptionToIdle_CircularDMA.rar
(637.55 KB, 下载次数: 29)
|