【前言】
STM32H7S78拥有3个USART4个UART以及一个LPUART共8个串口外设。
这8个外设均支持DMA收发。
在stm32H7中,相比stm32F1\F4他的串口外设有一些不同,不同点主要在于以下几个方面:
1、拥有USART_TDR以及USART_RDR,两个寄存器,将收发分另存放,不同以前的只有一个USART_DR这一个数据寄存器。
2、支持TX—RX翻转功能,在设计串口GPIO中,可以实时配置翻转IO,实现实时配置。
3、使用GPDMA来设置DMA功能,使得配置稍有复杂。
本篇主要展示使用串口的DMA接收,并实现不定长的数据接收。
【STM32CubeMX配置】
1、配置GPDMA通道,选择Channel 0,然后配置Request Configuration为UART4_RX,使能Destination Data Setting中的Destination Address Increment After Transfer,Channel Configuration for Linked List为Circular,即为循环接收。
2、打开UART的中断以及DMA中断:
【用户代码实现】
1、接收数组的创建,创建三个数据用于存放接收缓存,以及三个变量
uint8_t aRXBufferUser[UART_RX_BUFFER_SIZE] __attribute__((section("noncacheable_buffer"))); /** * @brief Data buffers used to manage received data in interrupt routine */ uint8_t aRXBufferA[UART_RX_BUFFER_SIZE] __attribute__((section("noncacheable_buffer"))); uint8_t aRXBufferB[UART_RX_BUFFER_SIZE] __attribute__((section("noncacheable_buffer"))); __IO uint32_t uwNbReceivedChars; uint8_t *pBufferReadyForUser; uint8_t *pBufferReadyForReception;
2、基中由于GPDMA需要使用__attribute__((section("noncacheable_buffer"))) 来指定内存存放在的位置,在分段配置文件中添加指定的内存位置:
RW_NONCACHEABLEBUFFER 0x24072000-0x400 0x400 { *(.noncacheable_buffer) }
3、同时也需要指定DMA Node缓存地址指向指定地址来分配内存。
因为STM32H7的GPDMA是不能直接访问ITCM、DTCM两个内存的,在用户手册表有上说明:
4、编写中断回调函数:
/** * @brief User implementation of the Reception Event Callback * (Rx event notification called after use of advanced reception service). * @param huart UART handle * @param Size Number of data available in application reception buffer (indicates a position in * reception buffer until which, data are available) * @retval None */ void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size) { static uint8_t old_pos = 0; uint8_t *ptemp; uint8_t i; /* Check if number of received data in recpetion buffer has changed */ if (Size != old_pos) { /* Check if position of index in reception buffer has simply be increased of if end of buffer has been reached */ if (Size > old_pos) { /* Current position is higher than previous one */ uwNbReceivedChars = Size - old_pos; /* Copy received data in "User" buffer for evacuation */ for (i = 0; i < uwNbReceivedChars; i++) { pBufferReadyForUser[i] = aRXBufferUser[old_pos + i]; } } else { /* Current position is lower than previous one : end of buffer has been reached */ /* First copy data from current position till end of buffer */ uwNbReceivedChars = UART_RX_BUFFER_SIZE - old_pos; /* Copy received data in "User" buffer for evacuation */ for (i = 0; i < uwNbReceivedChars; i++) { pBufferReadyForUser[i] = aRXBufferUser[old_pos + i]; } /* Check and continue with beginning of buffer */ if (Size > 0) { for (i = 0; i < Size; i++) { pBufferReadyForUser[uwNbReceivedChars + i] = aRXBufferUser[i]; } uwNbReceivedChars += Size; } } /* Process received data that has been extracted from Rx User buffer */ UserDataTreatment(huart, pBufferReadyForUser, uwNbReceivedChars); /* Swap buffers for next bytes to be processed */ ptemp = pBufferReadyForUser; pBufferReadyForUser = pBufferReadyForReception; pBufferReadyForReception = ptemp; } /* Update old_pos as new reference of position in User Rx buffer that indicates position to which data have been processed */ old_pos = Size; }
中断回调函数中,接收到数据后,回首根据返回接收到的字符长度来进行数据分装,将接收到的buff放入另一个buff中。同时使用UserDataTreatment(huart, pBufferReadyForUser, uwNbReceivedChars);这个函数将接收到的数据从串口返回。
5、发送函数为:
void UserDataTreatment(UART_HandleTypeDef *huart, uint8_t* pData, uint16_t Size) { /* * This function might be called in any of the following interrupt contexts : * - DMA TC and HT events * - UART IDLE line event * * pData and Size defines the buffer where received data have been copied, in order to be processed. * During this processing of already received data, reception is still ongoing. * */ uint8_t* pBuff = pData; uint8_t i; /* Implementation of loopback is on purpose implemented in direct register access, in order to be able to echo received characters as fast as they are received. Wait for TC flag to be raised at end of transmit is then removed, only TXE is checked */ for (i = 0; i < Size; i++) { while (!(__HAL_UART_GET_FLAG(huart, UART_FLAG_TXE))) {} huart->Instance->TDR = *pBuff; pBuff++; } }
【实验效果】
使用串口向开发板发送任意长度的数据能够正确的返回。
【总结】
此次串口不定长DMA接收,主要用到的是串口中断回调函数的编写,DMA的配置。在实际工程中可以高效的使用串口来进行数据交互。