STM32 F765的串口DMA+IDLE中断的发送接收不定长数据 您所在的位置:网站首页 uart_flag_idle STM32 F765的串口DMA+IDLE中断的发送接收不定长数据

STM32 F765的串口DMA+IDLE中断的发送接收不定长数据

2023-03-16 17:07| 来源: 网络整理| 查看: 265

本文已参与[新人创作礼]活动,一起开启掘金创作之路。

项目上正好用到了这个功能,特此记录下来备忘,方便后人

简单说要注意的几个点

串口DMA的初始化 串口IDLE中断的处理 接收和发送的逻辑控制 F7的D-Cache一致性的问题 串口DMA的初始化

我这里用的是串口3

对应的接收和发送DMA mode用的是Normal,不是Circle

初始化完引脚,时钟,DMA后记得使能对应的串口中断

然后还需要编写一个接收使能的函数,如下

执行这个函数会进入一次IDLE中断,这里需要自行处理这种情况

可以通过标志位的方式屏蔽掉这个不正常的进入,或者清除标志位的方式都可以吧

uint8_t UART_Init(void) { uint8_t res = HAL_OK; MX_DMA_Init(); res = MX_USART1_UART_Init(); if (res == HAL_OK) { UART1_RX_EN(); } res = MX_USART3_UART_Init(); if (res == HAL_OK) { UART3_RX_EN(); } return res; } void UART3_RX_EN(void) { __HAL_UART_ENABLE_IT(&huart3,UART_IT_IDLE); //这里需要补充一句Cache无效化的函数(2020-04-23) HAL_UART_Receive_DMA(&huart3,Uart3.Rx_Buf,UART_DMA_BUF_LEN); } 复制代码 串口IDLE中断的处理方式 & F7的D-Cache一致性的问题&接收和发送的逻辑控制

上述三个问题接下来一并说了

首先我这里定义了一个结构体的方式管理数据缓存和长度,还有有效数据标志,可能还有更机智的方法,待研究,至少目前这样的方法我觉得用的效果就已经很好了

typedef struct { uint8_t Rx_Buf[UART_DMA_BUF_LEN];//可以定义一个宏定义管理缓冲区的长度 uint8_t Idle_Flag;//可以理解为有效数据接收标志 uint16_t Rx_Len;//收到数据的长度 }ST_DMA_UART; 复制代码

如下是我的串口空闲中断的处理

 STM32 F7系列有Cache,这个加快CPU的执行速度,但是在用到涉及DMA的功能的时候就需要考虑它对代码产生的负面影响!!

这个可以具体去搜索STM32 F7 Cache一致性的文章 读一读,这里不过多阐述

我现在的理解就是如果涉及到DMA的操作,最好在函数开始执行之前加上指定内存地址的变量 D Cache无效化的操作

当然这个不是绝对的,但是这个应该是一个调试代码的方向。我加了以后代码的功能正常了,即使调试的时候打断点数据也不会乱掉。。。

这个D Cache还是挺复杂的,值得好好深入研究下

 SCB_InvalidateDCache_by_Addr((uint32_t *)Uart3.Rx_Buf,UART_DMA_BUF_LEN);

这种处理的逻辑应该是比较常见的

简单来说就是进入有效的IDLE中断后

清除中断标志 DMA传输停止 检测到有效长度后,算出接收到的有效数据长度 修改标志位,方便主函数里面的函数处理接收到的数据 再次打开DMA接收 void UART_Receive_IDLE(UART_HandleTypeDef *huart) { uint32_t temp; if((__HAL_UART_GET_FLAG(huart,UART_FLAG_IDLE) != RESET)) { __HAL_UART_CLEAR_IDLEFLAG(huart); HAL_UART_DMAStop(huart); temp = huart->hdmarx->Instance->NDTR; if(huart->Instance == huart3.Instance) { if(UART_DMA_BUF_LEN - temp!=0) { Uart3.Rx_Len = UART_DMA_BUF_LEN - temp; Uart3.Idle_Flag=DMA_RECV; } } } } void USART3_IRQHandler(void) { UART_Receive_IDLE(&huart3);// HAL_UART_IRQHandler(&huart3); } 复制代码

主函数里的接收函数如下

进来处理Cache一致性的问题

接着判断接收是否有效,长度是否有效,在进入对于的处理机制里面,处理完以后,清除掉长度值和标志位

uint8_t UART3_DualCPU_DataRX() { if ((Uart3.Idle_Flag == DMA_RECV) && (Uart3.Rx_Len != 0)) { //执行对应的处理操作 Uart3.Idle_Flag = DMA_NOT_RECV;//自己弄个宏定义反应接收的情况 Uart3.Rx_Len = 0; } } 复制代码

然后发送函数

进来处理Cache一致性的问题

接着填你要发的数据

然后调用底层发送

底层发送函数一定要填你要发送的长度 我这里的调试时如果不写那一句设定长度的话,数据的长度会不对

void UART3_DualCPU_DataTX() { SCB_InvalidateDCache_by_Addr((uint32_t *)uart3_buf,UART3_BUF_LEN);//要加一句这个,确保数据不会因为Cache的问题乱掉 //处理发送数据的代码 UART3_DMA_TX(uart3_buf, UART3_BUF_LEN); } void UART3_DMA_TX(uint8_t * str,uint16_t len) { __HAL_DMA_SET_COUNTER(&hdma_usart3_tx,len);//这里要设置长度!!! HAL_UART_Transmit_DMA(&huart3, str, len); } 复制代码

总的来说,这个F7的串口DMA基本上就是这么弄的了

效果很棒,基本上都是发送函数一运行完,接收方就收到了,然后再处理发回来,马上我就能收到,速度很快了

我这个是用在两个CPU对向发送数据上的,觉得很好了

之前也找了很多很多的代码看过

F7的代码实现和F4的还是有很大不同的,特别是Cache的问题,要特别注意

 

2020-4-23更新

在操作DMA之前,无论发送还是接收,都需要加一句****SCB_InvalidateDCache_by_Addr或者SCB_CleanInvalidateDCache_by_Addr,这个具体加哪一个要试一试,我这里加SCB_CleanInvalidateDCache_by_Addr,因为我的串口DMA是单次调用的,不是循环模式,所以重新开启收功能之前都需要调用一遍失能Cache函数,然后收发使用到的缓存都需要32字节对齐,这个是SCB_CleanInvalidateDCache_by_Addr或者SCB_CleanInvalidateDCache_by_Addr要求的,估计以前用了这个没起作用,应该也和这个有关,没有32字节对齐。

 

 

 

补充一些几个关键函数的调用路径和简单的内部实现描述

HAL_UART_Transmit_DMA

1. 全局状态 gState = HAL_UART_STATE_BUSY_TX

2. 调用HAL_DMA_Start_IT

3. DMA状态hdma->State = HAL_DMA_STATE_BUSY;

4. 开启外设DMA __HAL_DMA_ENABLE(hdma);

5. 执行 SET_BIT(huart->Instance->CR3, USART_CR3_DMAT);  数据就被发出去了

UART_EndTransmit_IT

1. 全局状态 huart->gState = HAL_UART_STATE_READY;

2. HAL_UART_TxCpltCallback(huart);

UART_EndRxTransfer(huart);  1. 接收状态恢复 huart->RxState = HAL_UART_STATE_READY; 2. 解除中断跳转 huart->RxISR = NULL;

 

HAL_UART_Receive_DMA

1. 判断if (huart->RxState == HAL_UART_STATE_READY),不是READY的话之前退出

2. 接收状态 huart->RxState = HAL_UART_STATE_BUSY_RX;

3. 调用HAL_DMA_Start_IT

4. DMA状态hdma->State = HAL_DMA_STATE_BUSY;

5. 开启外设DMA __HAL_DMA_ENABLE(hdma);

 

HAL_UART_DMAStop 1. 判断是不是有发送请求 如果有,停止发送 2. 判断是不是有接收请求 如果有,停止接收 3. 终止DMA传输 HAL_DMA_Abort  调用   __HAL_DMA_DISABLE(hdma);关闭外设DMA

 

更新20-07-19

在调用DMA发送之前一定要做一个判断,判断上一次发送有没有完成,以免出现发送间隔过快,前一次发送没有完成后一次马上又开始导致HAL库函数出现HAL_BUSY的返回值,具体做法可以在调用前判断发送完成标志,或者在HAL库串口发送完成回调函数里面做一个标志位,完成即置位,在发送前判断改标志必须为1才可以进行发送,否则不进行

开发者涨薪指南

48位大咖的思考法则、工作方式、逻辑体系



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有