• 102查看
  • 0回复

[芯片硬件] 基于STM32H7 UART 空闲事件及DMA传输示例

[复制链接]

该用户从未签到

发表于 27-4-2024 09:48:32 | 显示全部楼层 |阅读模式

汽车零部件采购、销售通信录       填写你的培训需求,我们帮你找      招募汽车专业培训老师


基于STM32H7 UART 空闲事件及DMA传输示例w1.jpg

有人使用STM32H7芯片的UART,想利用DMA实现不定长的数据接收,实现过程似乎不太顺利。另外,刚好最近有人使用H7芯片做UART收发,当开启D-Cache未做MPU配置,使用指令对D-Cache做相应失效处理,发现通信结果异常。我这里简单演示下实现过程,分使用和不使用D-Cache两种做法,以供参考。

这里使用UART4做发送,UART5做接收,都使用DMA,均工作在Normal模式。

基于STM32H7 UART 空闲事件及DMA传输示例w2.jpg

基于STM32H7 UART 空闲事件及DMA传输示例w3.jpg

在主循环里每隔一定时间让UART4发送一串数据出去,一共3串依次发送。每次发送的数据长度不一样,但不会超过20个字符。

基于STM32H7 UART 空闲事件及DMA传输示例w4.jpg

让UART5基于DMA做不定长的数据接收,将接收长度定义在20。同时开启UART5的空闲中断。在UART5的空闲中断里再次开启下一轮的DMA接收准备。

基于STM32H7 UART 空闲事件及DMA传输示例w5.jpg

关键API函数,UART4的发送函数和UART5用到的接收函数如下所示:

基于STM32H7 UART 空闲事件及DMA传输示例w6.jpg

顺便提下,在H7系列的HAL库里定义了好几个UART接收类型,使用时适当注意下。这里用的是下图划红线的关注IDLE事件的接收类型。

基于STM32H7 UART 空闲事件及DMA传输示例w7.jpg

我在IDLE事件的中断回调函数里就做了一件事,为下次UART5的DMA接收做准备。至于接收到的数据的后续处理就没做进一步操作了。

基于STM32H7 UART 空闲事件及DMA传输示例w8.jpg

这里是对不定长数据进行接收,使用IDLE事件的中断比使用DMA接收完成中断更方便些。

不开启D-Cache时的主要测试代码如下:
#define BUFFER_SIZE    20ALIGN_32BYTES(uint8_t RxData[BUFFER_SIZE];)
ALIGN_32BYTES(char TxData1[]="a5a5lfjaf888";)
ALIGN_32BYTES(char TxData2[]="1234$%^&555";)
ALIGN_32BYTES(char TxData3[]="aa8$%^&*33333";)
uint8_t Tempcnt;
int main(void){/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* Enable I-Cache---------------------------------------------------------*/   SCB_EnableICache();
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */  HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */  SystemClock_Config();
/* USER CODE BEGIN SysInit */  /* USER CODE END SysInit */
/* Initialize all configured peripherals */  MX_GPIO_Init();  MX_DMA_Init();  MX_UART4_Init();  MX_UART5_Init();/* USER CODE BEGIN 2 */
  HAL_Delay(5);
  __HAL_UART_CLEAR_FLAG(&huart5, UART_CLEAR_IDLEF);          HAL_UARTEx_ReceiveToIdle_DMA(&huart5, RxData,  BUFFER_SIZE);
/* USER CODE END 2 */
/* Infinite loop *//* USER CODE BEGIN WHILE */while (1)  {/* USER CODE END WHILE */
    /* USER CODE BEGIN 3 */
    HAL_Delay (500);  //延时长点方便观察调试窗口的数据变化    for (uint8_t i=0;i<BUFFER_SIZE;i++)    {     RxData=0;     //清理接收缓冲,准备接收新数据    }
    Tempcnt++;
if(Tempcnt==1)
    {        huart4.gState  = HAL_UART_STATE_READY;      HAL_UART_Transmit_DMA(&huart4, (uint8_t *)TxData1,  (uint16_t) strlen(TxData1));
    }

else if(Tempcnt==2)
    {      huart4.gState  = HAL_UART_STATE_READY;     HAL_UART_Transmit_DMA(&huart4, (uint8_t *)TxData2,  (uint16_t) strlen(TxData2));    }

else   if(Tempcnt==3)
    {     huart4.gState  = HAL_UART_STATE_READY;    HAL_UART_Transmit_DMA(&huart4, (uint8_t *)TxData3,  (uint16_t) strlen(TxData3));
    Tempcnt=0;    }
  }/* USER CODE END 3 */}

现在看看验证结果。下面是3次不同接收结果截图,3次发送的数据个数分别是12个、11个和13个。

基于STM32H7 UART 空闲事件及DMA传输示例w9.jpg

基于STM32H7 UART 空闲事件及DMA传输示例w10.jpg

基于STM32H7 UART 空闲事件及DMA传输示例w11.jpg

上面是没有启用D-Cache的代码,若启用D-Cache而又不想配置MPU,就得在适当地方添加对Cache的失效操作或清除操作。调整后的代码如下:

int main(void){/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* Enable I-Cache---------------------------------------------------------*/  SCB_EnableICache();
/* Enable D-Cache---------------------------------------------------------*/  SCB_EnableDCache();  //*****
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */  HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */  SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */  MX_GPIO_Init();  MX_DMA_Init();  MX_UART4_Init();  MX_UART5_Init();/* USER CODE BEGIN 2 */
  HAL_Delay(5);   
   __HAL_UART_CLEAR_FLAG(&huart5, UART_CLEAR_IDLEF);         HAL_UARTEx_ReceiveToIdle_DMA(&huart5, RxData,  BUFFER_SIZE);
        /* USER CODE END 2 */
/* Infinite loop *//* USER CODE BEGIN WHILE */while (1)  {/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */

SCB_InvalidateDCache_by_Addr(RxData,BUFFER_SIZE);//***
    HAL_Delay (500);  //延时长点方便观察调试窗口的数据变化

for (uint8_t i=0;i<BUFFER_SIZE;i++)    {     RxData=0;     //清理接收缓冲,准备接收新数据    }
SCB_CleanDCache_by_Addr ((void *)RxData,BUFFER_SIZE);//***
    Tempcnt++;
if(Tempcnt==1)
    {        huart4.gState  = HAL_UART_STATE_READY;      HAL_UART_Transmit_DMA(&huart4, (uint8_t *)TxData1,  (uint16_t) strlen(TxData1));
    }

else if(Tempcnt==2)
    {      huart4.gState  = HAL_UART_STATE_READY;     HAL_UART_Transmit_DMA(&huart4, (uint8_t *)TxData2,  (uint16_t) strlen(TxData2));    }

else   if(Tempcnt==3)
    {     huart4.gState  = HAL_UART_STATE_READY;    HAL_UART_Transmit_DMA(&huart4, (uint8_t *)TxData3,  (uint16_t) strlen(TxData3));
    Tempcnt=0;    }
  }/* USER CODE END 3 */}
上面代码中除了新增使能D-Cache那句代码外,还添加了2处标有三颗*的代码。分别是SCB_InvalidateDCache_by_Addr()和SCB_CleanDCache_by_Addr(),前一句将基于RxData而开辟的D-Cache行失效,让CPU去内存读取数据。因为此时内存数据可能已经被DMA改写而更新了。后一句就是将前面CPU循环操作对RxData数组清零后的数据写回到内存,并清空相应Cache行。其它代码跟前面不使用D-Cache一样,包括空闲中断回调函数的处理。上面测试代码可供参考验证。

好,今天的话题就分享到这里。下次再聊。


快速发帖

您需要登录后才可以回帖 登录 | 注册

本版积分规则

QQ|手机版|小黑屋|Archiver|汽车工程师之家 ( 渝ICP备18012993号-1 )

GMT+8, 9-5-2024 05:17 , Processed in 0.212980 second(s), 30 queries .

Powered by Discuz! X3.5

© 2001-2013 Comsenz Inc.