问题描述:开启D-Cache后,从USART DMA通道口接收、发送的数据与实际数据不对;
原因分析:1.D-Cach内部缓存报错数据,而没有写入RAM
2.DMA从RAM内存加载数据
4种解决方案:1.禁用D-Cache,操作最简单,但会牺牲一部分性能;可以用于调试,分析问题是否与D-Cache有关。
2.关闭部分内存的D-Cache。 这可以通过配置内存保护单元(MPU)来实现。 缺点是MPU区域有特定的对齐限制,您需要将DMA缓冲区放置到内存的特定部分。 每个工具链(GCC、IAR、KEIL)需要以不同的方式配置。
注意MPU区域可以重叠,高的区域号优先。 与子区域禁用位一起,这可以用来软化对齐和大小限制。
注意,设备和强顺序内存类型不允许对内存的非对齐访问。
3.将部分内存配置为透写。 只能用于TX DMA。 类似于前面的选项。
4.使用缓存维护操作。 对于特定的地址范围,可以将存储在缓存中的数据写回内存("clean"操作),也可以丢弃存储在缓存中的数据("invalidate"操作)。 缺点是这些操作的缓存行大小是32个字节,所以你不能从缓存中清除单个字节或使其失效。 当RX缓冲区与其他数据或TX缓冲区“共享”缓存线时,这可能会导致错误(请参见下图)。
注意,对于未初始化的D-Cache,维护操作“clean”或“clean and invalidate”可能会导致BusFault异常。 这是由于上电复位后ECC(纠错码)未初始化造成的。 如果你的项目需要大量的维护操作,想要暂时关闭D-Cache,你可以使用SCB_InvalidateDCache功能,它会清理缓存,设置正确的ECC,而不是启用它。
我使用了第四种方法:
接收使用:Invalid
DMABuffer_t rxBuffer;
getRxBuffer(&buffer);
SCB_InvalidateDCache_by_Addr((uint32_t *)rxBuffer.addr,rxBuffer.size);
if (HAL_UART_Receive_DMA(huart, (uint8_t *)rxBuffer.addr, rxBuffer.size) != HAL_OK)
{
/* Transfer error in transmission process */
Error_Handler();
}
发送使用:Clean
static DMABuffer_t buffer;
getTxBuffer(&buffer);
SCB_CleanDCache_by_Addr((uint32_t *)buffer.addr,buffer.size);//SCB_CleanDCache();
if (HAL_UART_Transmit_DMA(huart, (uint8_t *)buffer.addr, buffer.size) != HAL_OK)//
{
/* Transfer error in transmission process */
Error_Handler();
}
数据使用32byte对齐
uint8_t m_rxBuffer[MAX_RX_BUFFER_SIZE] __attribute__ ((aligned (32)));
uint8_t m_txBuffer[MAX_TX_BUFFER_SIZE] __attribute__ ((aligned (32)));
参考引用出处,详见:https://community.st.com/s/article/FAQ-DMA-is-not-working-on-STM32H7-devices