|
一、片内RAM用完了?; b: z* Y6 u( N% B( w 曾有两个不同的STM32用户反馈了相似的问题,他们在对STM32F7Cube库里的工程例程进行编译时,发现了一个令人很不解的事。编译的结果提示芯片内的RAM几乎都耗光了。但以他们对工程基本功能的了解,按理说不应该那样,想知道是怎么回事。 " W; e. d5 u, t& E* \" d' V 具体情况是这样的:他们先在STM32CubeF7的固件库里打开了一个有关lwip应用的工程示例。比如下面目录处的一个工程,使用ARM MDK进行编译。0 `- H% r" l) X: g2 O: `; R7 \' A, J \STM32Cube_FW_F7_V1.15.0\Projects\STM32746G-Discovery\Applications\LwIP\LwIP_HTTP_Server_Netconn_RTOS\MDK-ARM。 结果发现编译结果提示片内RAM的空间已经基本用完了。但感觉应该用不了那么多RAM内存。客户还想添加自己的其它用户程序,还需使用RAM呢。) H, I. d. ^$ q( z, {6 P. G s0 A! L9 o% v/ X5 ^* }2 `# a* k 打开相应软件工程,使用ARM MDK进行编译,发现编译结果跟客户反馈的一致。
3 J$ w$ Y. f* E 从编译结果来看,感觉RAM真的被用去了320多K,那RAM用到哪里去了呢?打开对应的MAP文件进一步查看,可得到如下数据。* A& H0 f0 h4 y- o: A : {7 W6 j6 Y& C4 ?5 @8 V
: J3 c3 e% n& l' K3 _! { 先看看芯片内部RAM情况。目前使用的芯片是STM32F746NG,查看其数据手册可知其内部系统RAM容量为320KB【1KB=1024B】,分别由如下三块RAM区域组成,各区域容量及地址范围如下:2 ~+ r9 e! |# S3 Z; u % m1 U# ^+ i3 X4 U
/ Q# X( J. ^: I$ ~0 f) M% @ 结合编译结果和上面数据信息来看,貌似RAM真的用完了。既然这样,只好硬着头皮继续查看MAP文件的其它细节,看看RAM到底消耗在哪些地方去了。后来发现在某个地址段有个巨大的PAD补丁填充区。/ Z% j5 z- w1 j5 q$ A
* b% v8 ^! l1 i 即从0x2000fd08到0x2004bfff这段区域,共246,520Bytes【即上图黄色区域所指】。 + C" a& v/ q3 N) r# b1 x m 一般来讲,代码编译后产生的PAD补丁块往往是因为地址对齐方面的原因导致的一些不便使用的零散内存碎片,正常来讲,这些补丁块不会大面积的集中在一起。1 s# x6 X! J( a" ?. O1 X, h2 b3 r- j1 z : T! x" N, s q( s' n 比如使用下面结构体变量的时候会插入padding. # y" ?5 A9 z; s1 E4 i% h& }! a
/ a" \: c0 _3 E, F) A; ^' V 但这里提示的补丁块也太大了,高达200多KB而且是连续空间!感觉是哪里误会了。从分析来看,初步判断这个PAD区间应该还是可以被用户使用的。于是尝试在现有代码里随意增加一块16KB的RAM有效使用量,编译一切正常,编译后显示的内存用量结果跟之前几乎一模一样,依然显示RAM用完了。 2 v8 G' @3 M+ H8 _6 x7 `/ { 但通过查看MAP文件,可以发现上面提到的那个大补丁块空间也随之减小了16KB。显然,RAM并非真正用完了,只是编译器把它当作类似对齐原因导致的补丁块了。现在问题是,怎么会被编译器误判成这么大的一个补丁块呢? 进一步查看MAP和部分代码源文件,我们可以发现有一块RAM区域,即芯片内的SRAM2区域被用户使用attribute关键字自行做了内存使用分配了,即这块内存空间不是交给编译器安排的。【下图中绿色方框内的内存分配】5 I. q, s: I! P5 e7 I2 Z/ r0 h
; r& {1 E5 I# F& b6 p# [ 结合上面的分析,那个巨大的pad区域正是经编译器分配使用到的RAM空间的末尾地址开始到SRAM2起始地址【0X2004C000】之前的那段空间。 这里让人想到一个地方,那就是MDK IDE选项配置中Target配置的这个地方:2 v5 Y9 b4 j7 I) g7 {
8 h7 B n5 r1 ^$ J1 o7 T, \3 @ 这里片内RAM配置是这样的,意味着从0x20000000到0x2004ffff的全部RAM空间交由编译器分配管理。# o. D1 }6 ~ k( w3 Q) {: z* F 0 _3 Z8 _2 x# [* M: ~+ I
1 A9 N: E$ Q O( E+ l8 u % Q2 k# @9 H+ K$ v/ _ 而在实际应用代码中,编译器从0x20000000开始分配内存,而SRAM2区域则由用户自行安排使用的。这样的话,经编译器所分配所用到的RAM内存末尾到0x2004BFFF这段未用区域被视为了pad区域。看来只是误会一场。" h( I, g) G. `6 Y ! X1 c" z6 }# Z) ]! e 那如何消除这个误会呢?我们可以将上面的内存配置项稍微修改下,让SRAM2区域不再让编译器分配管理,这样就避免了编译器分配的内存末尾到SRAM2区域起始地址的这段空间被视为补丁区。像下面一样修改: / _) ~2 J. D6 S0 n; L
这样修改后再做编译,结果如下,不再给人RAM都用光的感觉了,只用到70多KB的片内RAM。( `6 y* y- x( P6 N* I' Z3 B8 {0 k 0 Z/ ?) ~: U. C% ^$ B, B
8 E+ }- R. s4 f0 S* _/ g5 G7 A 二、Flash编程算法用不了?: d2 E" Y0 a7 P& b! i5 J ( t+ W1 W9 c# q( C7 w, l 用人使用STM32F7开发产品,发现编译时找不到合适的Flash算法文件,或者MDK自带的现有FLM文件用不了。具体情况是这样的: 有人使用STM32F750V8开发产品,编译完毕后欲进行下载调试,结果没法完成程序下载,提示FLASH下载错误。 # @" }' N, `& o6 B
, C) Y. f$ C. ~ $ d& S4 K% f# m& E6 x9 { 检查了各个配置后,怀疑FLASH算法文件是否有问题。 打开MDK选项配置里flash_download的编程配置页面,那里已经添加了相关FLASH编程算法文件。, v) U# s. r$ C# A' E2 p6 m6 V 0 h. K) J; Z5 L( L! z2 u6 H3 h
3 P6 I: r) k( e% ] H4 u5 j2 y' N# E: A9 Q, O 不过,如果使用STM32芯片做过开发的人可能比较容易发现有个地方有点刺眼,就是起始地址那个地方。用过STM32F0/F1/F4等系列的人可能比较容易发现,那个FLASH起始地址一般是在0x8000000这个地方,这里却是0x00200000。我们再看看MDK选项配置有关内存分配的那个地方,如下图所示:: R! L0 T0 E* c8 k" l# X9 I, R 9 g. g) c! ?0 y2 k, O% ]
这里的片内FLASH默认起始地址却是0x8000000。显然这里跟FLASH编程算法文件的起始地址定义不一致,初步判断应该是这个地方导致的问题。. k3 W! c' v! Y! l6 M& _2 W ( r: e' l. q% }# V0 v7 u3 T) t 到此,我们有必要看看STM32F7的相关技术手册了关于内部FLASH地址分配的内容。下图是STM32F750片内主从外设及总线的框架图,对于片内FLASH,CPU可以有两条总线通路访问它,一条是通过AXIM接口,经过AXI-AHB桥以64位总线访问,另一条是通过ITCM接口,经过片内ART加速器后访问它。【如下图所示】
% L T, F& [0 O7 z* W/ m: Z 我们通过进一步查看手册,可以得知CPU访问该FLASH区域时因使用不同总线接口而安排了不同的地址访问区域。& b( x+ j6 i7 V5 @$ A& s* G 8 {0 o4 e% e" i # @$ s. G6 I* x% ]3 Z
从上面表格可以清楚地看到,CPU从不同接口访问FLASH时其对应地址是不一样的。通过AXIM接口访问内部flash的起始地址为0x08000000,通过ITCM接口访问内部flash的起始地址是0x00200000. 5 G+ C& u' V, d% X2 Z 结合到上面案例,MDK选项配置中Target页面的片内FLASH地址安排与Flash算法定义的地址不一致导致程序下载失败。那么,我们可以将二者的地址及大小调整得一致,具体根据实际开发需求来调整,即根据你希望CPU通过哪个接口来访问FLASH来做相应调整。 # T, ~8 W+ Z$ S7 f+ K) L: ?, M 1、如果希望走ITCM接口来访问,我们就将Target页面的FLASH存储起始地址与FLASH算法文件定义的起始地址都设置为0x00200000,如下图所示:$ b% z7 G6 i& c+ x, ] 1 C9 S4 U6 _. I+ L
2、如果希望走AXIM接口来访问,我们就将FLASH算法文件定义的起始地址与Target页面定义得一致,这里就是0x08000000. % K! L4 n# a8 @ + e) h- Y" {- m 基于MDK提供的FLASH算法文件我们可以直接修改其起始地址及大小。 - X9 y1 L' B) |5 g5 E; M 比如,如果刚加载进来的算法文件是基于ITCM接口的地址定义,如下图所示。 5 k% r6 E) B4 W; P+ P4 i 2 L3 k \2 j/ O3 u ^
我们可以直接基于上面算法文件参数进行起始地址修改,即修改椭圆里的起始地址数据,修改完毕后点击下方的OK按钮即可。操作如下图所示: 4 a% F0 j. C# P2 Z9 N4 x - f+ W2 u8 f+ W, G3 u$ p( y
【注:我所用ARM MDK版本为5.28。】 ' i5 |7 P$ K# J2 F- y 基于修改后的配置,再进行编译后即可进行下载调试。 * `( V m) R8 x# ~# r( O
8 d# q' u2 y' ` \( q |
| 学习一下。 |
微信公众号
手机版