STM32H743串口DMA发送数据不正确问题

项目场景:

stm32H743使用串口MDA发送数据,减轻MCU负担

问题描述

通过stm32cubeMX配置串口3,使用DMA发送数据,生成工程后DMA发送数据没问题,但是发送的数据全部为0x00,并不是我发的数据,通过仿真查看发现直到DMA发送函数内部数组的数据都是正确的,但是发出来的数据就变成了0x00,经过各种查找,网上搜索终于发现问题。
参考文章:https://blog.youkuaiyun.com/winnerycy/article/details/119544125
https://blog.youkuaiyun.com/qq_35862573/article/details/104193991
1、没修改前串口3DMA发送的数据如下图:
异常数据
2、解决bug后正确的数据如下图:
正确的数据

原因分析:

1、stm32h743的DMA比较特别,他有几个内存区,默认的内存区与DMA不相连,所以定义buf的时候要定义到与DMA相连的地址上,否则DMA将传输错误的数据。
在这里插入图片描述在这里插入图片描述
在这里插入图片描述

解决方案:

一、使用keil建立的工程解决如下:
1、修改.sct文件
在这里插入图片描述在这里插入图片描述
2、在程序中添加宏和修改变量定义
在这里插入图片描述
在里插入图片描述
3、就这样解决了问题,花了很长时间各种debug,一开始以为是程序问题,因为一下子用了四个串口,有时候屏蔽一些串口数据就正常,有时候又不正常,以为是串口相互影响,后面发现并不是,是因为我删减程序时重新编译了程序,不指定串口buf存储的地址就是随机的,有时候会是DMA可以传输的地址,有时候不是,所以各种怀疑,直到网上搜索到相应参考。

更新问题解决方法:
后面使用还发现有问题,前面的分析是错的,罪魁祸首是开启了CPU DCache,关掉CPU DCache就可以正常使用DMA了,地址的RAM2 DMA也没有问题的。至于CPU DCache开启要怎么才能使用DMA,后续再研究了。

<think> 我们参考上述引用,特别是引用[3]和[4]中提到了使用STM32H743串口DMA和空闲中断来实现接收和发送定长度数据。下面将结合HAL库进行说明。 实现思路: 1. 发送:使用DMA发送,可以避免CPU等待,提高效率。 2. 接收:使用DMA接收定长数据,结合空闲中断(IDLE)来判断一帧数据接收完成。 步骤: 一、CubeMX配置(以USART2为例,引用[3]): 1. 使能USART2,选择异步模式。 2. 使能USART2全局中断(NVIC Settings中使能USART2中断)。 3. 配置DMA: - 添加USART2_RX的DMA流(注意通道选择,根据数据手册选择对应的DMA和流),配置为循环模式(CIRCULAR),外设到内存,数据宽度为字节。 - 添加USART2_TX的DMA流,配置为正常模式(NORMAL),内存到外设,数据宽度为字节。 4. 使能串口空闲中断(在USART2的高级配置中,勾选空闲中断)。 二、代码实现: 1. 在main函数中启动接收(启动DMA接收,并开启空闲中断): ```c // 启动串口DMA接收,设置接收缓冲区和长度 HAL_UART_Receive_DMA(&huart2, rx_buffer, BUFFER_SIZE); // 使能空闲中断 __HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE); ``` 2. 在串口中断服务函数中处理空闲中断: ```c void USART2_IRQHandler(void) { // 处理空闲中断 if (__HAL_UART_GET_FLAG(&huart2, UART_FLAG_IDLE) != RESET) { __HAL_UART_CLEAR_IDLEFLAG(&huart2); // 清除空闲中断标志 // 计算接收到的数据长度 // 公式:总长度 - DMA剩余未传输的数据量 uint16_t rx_len = BUFFER_SIZE - __HAL_DMA_GET_COUNTER(&hdma_usart2_rx); // 在这里处理接收到的数据,rx_buffer中前rx_len个字节是有效数据 // 重新启动DMA接收(因为DMA在循环模式下需要重新启动,但如果是非循环模式,则需要重新配置) // 如果是循环模式,则需要重新启动,但是需要处理缓存和长度 // 注意:由于是循环模式,所以当数据很长超过BUFFER_SIZE时,可能会被覆盖,因此需要及时处理 // 如果是非循环模式,则需要重新启动(但通常接收定长数据使用循环模式,这样可以避免频繁重启) // HAL_UART_Receive_DMA(&huart2, rx_buffer, BUFFER_SIZE); } // 其他中断处理... HAL_UART_IRQHandler(&huart2); } ``` 3. 发送数据:使用DMA发送 ```c HAL_UART_Transmit_DMA(&huart2, tx_buffer, tx_len); ``` 三、注意事项(引用[5]中提到的问题): 1. 对于STM32H7系列,由于有缓存(Cache),在DMA传输中需要注意数据一致性。当DMA接收数据后,CPU读取之前,可能需要无效数据缓存(Invalidate DCache),以确保读取的是内存中最新的数据(而是缓存中的旧数据)。 在空闲中断处理函数中,读取接收数据之前,可以调用: ```c SCB_InvalidateDCache_by_Addr((uint32_t *)rx_buffer, BUFFER_SIZE); ``` 或者,可以在配置DMA时,将接收缓冲区设置为非缓存(Non-Cacheable)区域。 2. 如果只接收一次数据,需要检查是否因为缓存问题导致。引用[5]中提到加入无效缓存指令后重新开启接收。 3. DMA接收模式:循环模式(Circular)可以持续接收,需要每次重新启动。但要注意缓冲区大小,防止溢出。如果一帧数据超过缓冲区大小,则需要进行分包处理。 四、HAL库回调函数(可选): 可以使用HAL库提供的回调函数机制。但是,空闲中断并没有直接的回调函数,所以需要在中断服务函数中自行处理。 另外,HAL库提供了DMA传输完成中断回调(HAL_UART_RxCpltCallback),但这里我们主要用空闲中断。 总结: 1. 配置串口DMA(循环模式接收,正常模式发送),使能空闲中断。 2. 启动DMA接收。 3. 在空闲中断中,计算接收长度,处理数据,并注意缓存一致性问题STM32H7需要处理Cache)。 4. 发送数据使用DMA发送函数。 以上方法可以实现高效、稳定的串口通信,特别适合处理定长数据。 </think>### STM32H743 串口DMA发送与空闲中断接收实现方法 在STM32H743上实现串口DMA发送和DMA空闲中断接收定长数据的关键步骤如下: #### 1. CubeMX配置 ```c // 引用[3]示例配置 USART2_Init(); DMA_RX_Init(); // 接收DMA配置为CIRCULAR模式 DMA_TX_Init(); // 发送DMA配置为NORMAL模式 __HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE); // 使能空闲中断 ``` #### 2. 关键代码实现 ```c // 启动接收(主函数初始化阶段) HAL_UART_Receive_DMA(&huart2, rx_buf, BUFFER_SIZE); // 空闲中断处理(在stm32h7xx_it.c中) void USART2_IRQHandler(void) { if(__HAL_UART_GET_FLAG(&huart2, UART_FLAG_IDLE)) { __HAL_UART_CLEAR_IDLEFLAG(&huart2); // 关键缓存同步操作[^5] SCB_InvalidateDCache_by_Addr((uint32_t*)rx_buf, BUFFER_SIZE); // 计算接收长度 uint16_t recv_len = BUFFER_SIZE - __HAL_DMA_GET_COUNTER(hdma_usart2_rx); // 处理数据 process_data(rx_buf, recv_len); // 重新启动接收[^3] HAL_UART_Receive_DMA(&huart2, rx_buf, BUFFER_SIZE); } HAL_UART_IRQHandler(&huart2); } // DMA发送函数 void uart_dma_send(uint8_t* data, uint16_t len) { HAL_UART_Transmit_DMA(&huart2, data, len); // 引用[4] } ``` #### 3. 关键注意事项 1. **缓存一致性处理** STM32H7需在空闲中断中添加缓存无效化指令: `SCB_InvalidateDCache_by_Addr()`[^5] 2. **DMA模式选择** - 接收DMA:必须配置为`CIRCULAR`模式 - 发送DMA:配置为`NORMAL`模式即可 3. **空闲中断使能** ```c __HAL_UART_ENABLE_IT(&huart, UART_IT_IDLE); // 引用[3] ``` 4. **接收长度计算** ```c recv_len = buffer_size - __HAL_DMA_GET_COUNTER(hdma_rx); ``` #### 4. 配置要点总结 | 功能 | 配置要求 | |-------------|--------------------------| | 接收DMA模式 | CIRCULAR(循环模式) | | 发送DMA模式 | NORMAL(普通模式) | | 中断使能 | 空闲中断+串口全局中断 | | 缓存管理 | 必须处理Cache一致性[^5] | > 实现原理:DMA在后台持续搬运串口数据到缓冲区,当总线出现空闲时触发中断,此时计算实际接收数据长度并处理数据。发送时DMA直接搬运数据到串口发送寄存器,释放CPU资源[^1][^2]。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值