项目上正好用到了这个功能,特此记录下来备忘,方便后人
简单说要注意的几个点
- 串口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);
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)
{
SCB_InvalidateDCache_by_Addr((uint32_t *)Uart3.Rx_Buf,UART_DMA_BUF_LEN);//要加一句这个,确保数据不会因为Cache的问题乱掉
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;
}
HAL_UART_Receive_DMA(&huart3,Uart3.Rx_Buf,UART_DMA_BUF_LEN);
}
}
}
void USART3_IRQHandler(void)
{
UART_Receive_IDLE(&huart3);//
HAL_UART_IRQHandler(&huart3);
}
主函数里的接收函数如下
进来处理Cache一致性的问题
接着判断接收是否有效,长度是否有效,在进入对于的处理机制里面,处理完以后,清除掉长度值和标志位
uint8_t UART3_DualCPU_DataRX()
{
SCB_InvalidateDCache_by_Addr((uint32_t *)Uart3.Rx_Buf,UART_DMA_BUF_LEN);//要加一句这个,确保数据不会因为Cache的问题乱掉
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的问题,要特别注意
来源:https://blog.csdn.net/tpoem/article/details/102754024