DMA在STM32H7上不工作的解决办法
问题
将STM32F0的DMA代码移植到STM32H7上时发现DMA ErrorCode寄存器为1,无法正常工作
原因
DMA缓冲区内存被放置在DTCM,最好将DMA缓冲区放在D2域(SRAM1,SRAM2和SRAM3)内,因为D2到D1桥会增加额外的延迟。
解决方案:
将缓冲区放在RAM_D2内存部分中
实现
修改linker script(*.ld)文件,将下面代码块放进SECTIONS中
.dma_buffer :
{
KEEP(*(.dma_buffer))
} >RAM_D2
在项目源码中添加DMA_BUFFER宏定义
#if defined( __ICCARM__ )
#define DMA_BUFFER \
_Pragma("location=\".dma_buffer\"")
#else
#define DMA_BUFFER \
__attribute__((section(".dma_buffer")))
#endif
注:请注意DMA需要4字节对齐,如有需要可能需要加 __attribute__ ((aligned (4)))
例:
#define DMA_BUFFER \
__attribute__((section(".dma_buffer"))) __attribute__ ((aligned (4)))
在声明变量时使用
DMA_BUFFER uint32_t buffer[10];
// ...
HAL_TIM_PWM_Start_DMA(&htim2,TIM_CHANNEL_1,buffer,10);
注:如果在初始化之前定义的buffer,其初始值可能不会被拷贝进RAM_D2中,这个解决办法有两个:
一是在方法中使用memset或手动更改其值,二是修改startup.s手动进行拷贝。
由D-Cache带来的问题
STM32H7中有一个D-cache,D-cache会影响DMA传输的功能,因为只能搬运RAM中的数据,D-cache会将新数据保存在内部缓存中,而不会将新数据写入RAM中。有几种办法可以解决D-cache带来的影响。
禁用全局D-Cache
在CubeMX中关闭D-Cache,或使用代码来关闭。
SCB_DisableDCache();
清除并刷新D-Cache
使用SCB_CleanDCache_by_Addr方法来刷新D-Cache缓存,需要注意的是该方法并不是禁用某一段地址的缓存,而是会将缓存写入内存中。所以每次修改了buffer值都需要调用该方法去刷新缓存!
/* Clean D-cache */
SCB_CleanDCache_by_Addr(buffer, 10);
注:该方法也是需要地址四字节对齐,对齐代码来自参考
#define TX_LENGTH (16)
uint8_t tx_buffer[TX_LENGTH];
/* Write data */
tx_buffer[0] = 0x0;
tx_buffer[1] = 0x1;
/* Clean D-cache */
/* Make sure the address is 32-byte aligned and add 32-bytes to length, in case it overlaps cacheline */
SCB_CleanDCache_by_Addr((uint32_t*)(((uint32_t)tx_buffer) & ~(uint32_t)0x1F), TX_LENGTH+32);
参考
https://community.st.com/s/article/FAQ-DMA-is-not-working-on-STM32H7-devices
https://www.keshikan.net/gohantabeyo/?p=563
https://github.com/keshikan/STM32H7_DMA_sample