【STM32开发】STM32H7的DMA发送
本博客是源于一次使用H7的串口DMA发送出现了BUG,中间也踩了一些坑,用来记录一下。我这里使用的芯片是STM32H723VGT6,开发使用的IDE是Keil,如果不是使用Keil可能会少踩一些坑。
RAM的DMA问题
以下部分内容参考:《安富莱STM32V7开发板用户手手册》第25章 STM32H7的TCM,SRAM等五块内存基础知识
首先先把STM32H7X3的系统架构图放上来:

根据上面的框图,可以知道芯片总共可以分为三个域:D1 Domain、D2 Domain和D3 Domain。各个域之间是通过总线进行连接的。其次,STM32H7有如下几块内存:
TCM区:首先是与CPU最紧密的Tightly-Coupled Memory,紧密耦合内存。这块内存的特点是跟内核速度一样,而片上的其他RAM基本上都达不到这个速度,都会有降频处理。速度:400 MHz;DTCM地址:0x2000 0000,大小128 KB,ITCM地址:0x0000 0000,大小64 KB(期间踩到的坑就是将DMA发送的数据定义在了DTCM上,但是DMA访问不了这块内存,DMA的状态就一直处于BUSY状态。从上面的框图可以看出来DMA与TCM无法连通);AXI SRAM区:位于D1域,数据带宽是64 bit,挂载在AXI总线上。这块内存除了D3域中的BDMB主控不能访问,其它都可以访问此RAM;速度:200 MHz;地址:0x2400 0000,大小512 KB;SRAM1,SRAM2,SRAM3区:位于D2域,数据带宽是32 bit,挂载在AHB总线上。除了D3域中的BDMB主控不能访问,其它都可以访问此RAM;速度:200 MHz;SRAM1地址:0x3000 0000,大小:128 KB;SRAM2地址:0x3002 0000,大小:128 KB;SRAM3地址:0x3004 0000,大小:32 KB;SRAM4区:位于D3域,数据带宽32 bit,挂载在AHB总线上,大部分主控都能够访问这块内存;速度:200 MHz,地址:0x3800 0000,大小:64 KB。
进一步,通过上述的总线互联图,可以知道:
- DTCM和ITCM不支持DMA1,DMA2和BDMA,仅支持MDMA;
- AXI SRAM,SRAM1,SRAM2,SRAM3不支持BDMA,支持MDMA,DMA1和DMA2;
- SRAM4支持所有DMA,即MDMA,DMA1,DMA2和BDMA。
上述总结非常重要!!!
通过MAP文件查看RAM
以下部分内容参考:《安富莱STM32V7开发板用户手手册》第10章 STM32H7的FLASH,RAM和栈使用情况
通过MAP文件,可以查看工程中所定义的函数、变量等占用RAM或者FLASH等情况。首先要生成MAP文件,Keil中如下选型要勾选上:

MAP文件包含以下几部分:
Section Cross References:展示不同文件中函数的调用关系;Removing Unused input sections from the image:主要是被删除的冗余函数,也就是添加到工程里面,但是没有调用到的;Image Symbol Table:主要分为两类,分别是Local Symbols和Global Symbols;Local Symbols记录了用static声明的全局变量地址和大小,C文件中函数的地址和用static声明的函数代码大小;Global Symbols记录了全局变量的地址和大小,C文件中函数的地址及其代码大小;Memory Map of the image:映像文件可以分为加载域(Load Region)和运行域(Execution Region):加载域反映了ARM可执行映像文件的各个段存放在存储器中的位置关系,也就是程序在FLASH中的实际存储;运行域反映了ARM可执行映像文件各个段真正执行时在存储器中的位置关系,也就是芯片上电后程序的运行状态;Image component sizes:简单来说就是对工程中的各个文件进行的总体分析,例如代码占用了多少字节等等。
修改Keil的sct文件
在 Keil MDK(ARMCC / ARMCLANG)环境中开发 STM32H7 时,不会使用 .ld 链接脚本文件(那是 GCC / CubeIDE 使用的)。Keil 使用的是 .sct(scatter file),通常位于工程目录下。

如果Scatter File选项栏显示是灰色,那说明勾选了Use Memory Layout from Target Dialog,也就是Keil会自动根据Target页的RAM/FLASH配置生成新的Scatter文件,因此会忽略用户手动对sct文件的修改。
Keil自动生成的sct文件内容为:
; *************************************************************
; *** Scatter-Loading Description File generated by uVision ***
; *************************************************************
LR_IROM1 0x08000000 0x00100000 { ; load region size_region
ER_IROM1 0x08000000 0x00100000 { ; load address = execution address
*.o (RESET, +First)
*(InRoot$$Sections)
.ANY (+RO)
.ANY (+XO)
}
RW_IRAM1 0x20000000 0x00020000 { ; DTCM (NO DMA)
.ANY (+RW +ZI)
}
RW_IRAM2 0x24000000 0x00020000 { ; AXI SRAM
.ANY (+RW +ZI)
}
}
可以看到默认的sct文件只含有DTCM和AXI SRAM这两块内存。这里新建一个sct文件,并对SRAM2进行定义:
; *************************************************************
; *** Scatter-Loading Description File generated by uVision ***
; *************************************************************
LR_IROM1 0x08000000 0x00100000 { ; load region size_region
ER_IROM1 0x08000000 0x00100000 { ; load address = execution address
*.o (RESET, +First)
*(InRoot$$Sections)
.ANY (+RO)
.ANY (+XO)
}
RW_IRAM1 0x20000000 0x00020000 { ; DTCM (NO DMA)
.ANY (+RW +ZI)
}
RW_IRAM2 0x24000000 0x00020000 { ; AXI SRAM
.ANY (+RW +ZI)
}
; ✅ 新增的 D2 RAM 段
RW_D2RAM 0x30000000 0x00048000 { ; 288 KB D2 SRAM ✅ DMA 强烈推荐
*(.RAM_D2)
}
}
修改后,需要在Linker中取消勾选了Use Memory Layout from Target Dialog,并修改Scatter File的路径,修改为包含D2 RAM段定义的sct文件。然后再重新构建工程(踩到的另一个坑就是忘记取消勾选了,结果就是无论如何修改sct文件也没有作用)。
为了测试设置是否成功,首先在main中定义了一个全局变量:
__attribute__((section(".RAM_D2"))) uint8_t send_jetson_buf[256] = {0};
编译后,查看MAP文件,查询该变量的地址:

根据上图可知该变量的地址在0x3000 0000,设置成功。如果变量地址仍然为0x2000 0000,那么说明该变量仍然定义在DTCM中。
完。
更多推荐

所有评论(0)