本博客是源于一次使用H7的串口DMA发送出现了BUG,中间也踩了一些坑,用来记录一下。我这里使用的芯片是STM32H723VGT6,开发使用的IDE是Keil,如果不是使用Keil可能会少踩一些坑。

RAM的DMA问题

以下部分内容参考:《安富莱STM32V7开发板用户手手册》第25章 STM32H7的TCM,SRAM等五块内存基础知识

首先先把STM32H7X3的系统架构图放上来:

在这里插入图片描述
根据上面的框图,可以知道芯片总共可以分为三个域:D1 DomainD2 DomainD3 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;
  • SRAM1SRAM2SRAM3区:位于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中。

完。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐