找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 730|回复: 0
收起左侧

STM32单片机外部存储器的使用资料

[复制链接]
ID:609322 发表于 2023-12-4 09:35 | 显示全部楼层 |阅读模式
STM32外部存储器使用资料,很有参考价值
最近在学习一些gui方面的操作,并且是要跑os的;突然就发现自己的硬件平台的内部SRAM资源不够用了(硬件平台:战舰V2,stm32f103ze、外扩SRAM:1M);怎么破,那只能把外部的SRAM给用起来,不用就是浪费啊,要知道SRAM可是不便宜啊;
         然后就开始借鉴了配套例程:外部SRAM的使用;通过fsmc来控制实现,外部sram可以正常使用了,但是我想尝试下其他的方法来使用外部的SRAM:
1、  不使用attribute关键字来实现变量等在外部的分配,让编译器自己来完全支配,这样我们就不用计算我们指定的地址是否会重合等问题;
2、  让内部的64K SRAM、外部的1M SRAM都能用,而且让编译器自己处理使用;
3、  完全不使用内部的SRAM,只使用外部的SRAM

上网上查阅一些资料后,发现这个需要在2010年就有了,而且当时网友遇到的问题,我在实验中也遇到了,关键是前面的帖子,也没有很明确或是清晰的给出解释,有的帖子给的方法是可以实现要求的,但是没有说为什么这样可以,所以我自己一边实验一边验证,下面把自己遇到的问题和现象做一个记录:
1、  按照网友的方法来配置,让编译器自动处理SRAM的使用
(1)       实验前提,例程采用配套例程的:37外部SRAM实验;外部SRAM的初始化使用system_stm32f10x.c官方提供的fsmc初始化;
(2)       工程配置
开启system_stm32f10x.c中的fsmc初始化外部SRAM函数;
配置工程中SRAM的分配,注意:SRAM部分有两块,两个√
注意去掉main文件中的testsram[]的属性定义
(3)       实验结果,程序正常执行;
链接加载控制文件内容:
工程的map文件,如果对map文件不熟悉的可以自行查阅资料
是不是很明显:一大堆的变量和数据段都分配在了内部的SRAM,只有一个.bss段分配到了外部的SRAM 0x68000000地址处,为什么会这样,因为内部放不下了啊,另外咱们的testsram数组是没有初始化的,你试一下在定义时就初始化,看看地址有什么变化;要理解这里的原理,就需要对分散加载文件有深入的了解了,自行查找;

2、  完全不使用内部SRAM,只使用外部SRAM
(1)       工程配置和上面的实验一致,只修改一个部分,去掉内部SRAM的配置
(2)       编译测试
编译是没有任何问题的,下载验证吧,程序跑不起来,失败了;
在线jlink调试一下:
看到了吧,死在这里和,硬件fault了;先不解释为什么;
(3)       解决问题的过程
之前在网上搜到一些建议,但是我还是选择了,先用jlink调试一下看看;
             我在用jlink在线调试操作过程是:
             第一步:单步调试,看一下是哪里进入了硬件fault;我在void SystemInit (void)函数中增加了一些调试
            
目的是看一下,这的fsmc对外部的SRAM的操作是否正确,结果是正确的,说明官方的fsmc初始化没有问题,此时外部的SRAM已经可以正常使用了;
第二步:继续单步调试,发现是在这个地方进入了硬件fault了:static void SetSysClockTo72(void)函数的退出时
到这里你在执行单步一次,就会进入硬件fault,可以确定了是在函数返回时进入硬件fault,这里先不解释原因;

第三步:我借鉴了一下网友的建议,如下:
         工程配置不用改动任何地方,还是只配置外部SRAM作为唯一的选择,注意这里和 的配置是不一样的,它的同时配置了外部和内部SRAM,如果这样那他所做的操作就有些多余了,不用修改启动文件就可以啊,就和我上面第1中的工程配置一样啊,编译器自己就可以处理了,不用你在修改启动文件啊,这也是该贴下面很多网友说不用修改启动文件中的
__initial_sp    EQU     0x20000000 + Stack_Size 也可以正常运行;如果你按照下面的工程配置,再不添加启动文件中的__initial_sp    EQU     0x20000000 + Stack_Size设置,你试一下可以正常运行吗?答案是:不能;
         工程配置不变:
         
         修改启动文件startup_stm32f10x_hd.s中的关于栈的位置定义:等价于强制把复位后的栈指针指向内部的SRAM位置处

好了,其它一切不变,编译下载吧

第四步:下载验证
成功运行程序
Sct文件内容:完全是把数据段放在了外部的SRAM中;
Map文件内容:所有数据段都在了外部SRAM上面;

到这里,你会有疑问吧,那我们刚才设置启动文件中__initial_sp    EQU     0x20000000 + Stack_Size的目的是啥啊,为啥在配置不变情况下,不加程序就不正常,加了就可以正常运行;后面解释,先附张图,自己先分析一下


这里分享一个巧合事件:
我在2、完全不使用内部SRAM,只使用外部SRAM   ->  (3)解决问题的过程  -> 第三步 中给出的结论:如果你按照我的工程配置,只启用外部sram配置选项,然后你又不设置启动文件中栈指针的设置,程序是不会正常启动的;
有的朋友可能在实验中会出现,即按照只使用外部sram配置,且不添加启动文件设置,程序也正常启动了,会说我的结论是错的,其实结论是对的,你这个配置下能正常运行程序,完全是个巧合,下面给出巧合呈现方法:
         第一步:按照下面的配置、下载、运行程序,系统正常运行,没有任何问题
         

                       第二步:你为了推翻我的结论,在工程配置不变的情况下,只修改了启动文件的配置,删除了对栈指针的设置
接下来编译、烧写程序,进行验证(注意这个过程前提是,你的平台还在上次的程序下正常上电运行,这里直接按照第二步的设置编译后,用jlink下载验证,为了说明问题我把两次的程序作了小处理,说明确实是连个不同的程序;上面的LCD上显示是"WarShip ZG008",这里我们显示是 "WarShip ZG009")
程序正常运行了,你的结论是错的,别急:此时你把平台的电源断掉,在上电看看,程序能正常运行吗,不能吧;
原因我还没有深究,预估是,当你的平台正常运行下,你用jilink在线下载程序时,外设的配置还会保持之前的状态,至少fsmc的配置没有改变,仍然可以操作外部的sram,所以你删除了启动文件的配置后,下载程序仍正常运行,断电后在上电,就不行了;这里只是我的猜测,还没有验证;

到这里,大家是否明白了,这一切的设置,是不是都离不开一个东西:sp

3、  上面实验现象分析
第一步:要清楚,cm3的内核是在上电后进入复位中断处理,但是复位中断的pc值可不是存储在0x0000 0000 地址,而是下一个4字节的位置,0x0000 0000存储的是sp的栈指针;
第二步:通过调试,你可以看到,程序进入上电复位中断后,先跳转到SystemInit()函数,调试截图如下:
LDR     R0, =SystemInit
    BLX     R0   
当单步执行 BLX  r0后,注意观察左边寄存器的变化,以及上面的汇编语句:
看到了:R13的sp值改变了,因为发生了函数调用,需要保存上下文环境,所有有PUSH的操作,切记,这里对栈进行了操作,即入栈,那么当你从systeminit()函数返回时,就要涉及到出站,这点很重要,这就是你进入硬件fault的根源;

第三步:分析上面工程配置1中的设置,
这种配置,你只改变了SRAM的配置,让外部和内部的都启用,让编译器自己解决分配问题;通过map文件你可以很清楚:编译器把大部分的数据段都还是放在了内部SRAM中,包括主要的__initial_sp栈顶指针,只把一个很大的内部sram放不下的main.c中定义的testsram[]放在了外部sram;
此时,堆栈指针在内部sram中,所以在复位中断中的函数调用引发的入栈、出栈可以完全正常运行;

第四步:分析上面工程2中的配置
         
       这里分两种情况,工程配置不变下,是否设置启动文件的__initial_sp    EQU     0x20000000 + Stack_Size;
(1)       不修改启动文件中的栈的设置
此时通过编译后的map和调试文件可以知道:
当调用LDR     R0, =SystemInit
          BLX     R0         
            后会发生入栈的push操作,注意观察此时的R13的数值:0x680F 4AF8;已经指向了外部的sram;
此处发生了push入栈操作,但是重点是此时我们还没有对外部sram进行操作啊,试问你这个入栈能成功吗,不能,那么里面的数值是函数返回后的下一条PC值吗,肯定不是,是个垃圾值;然后,你调用pop操作,将一个垃圾值给了PC,那只能进入硬件fault了;

那我们找到了问题的根源,那咋改进呢:首先要明白一点,开发板硬件上电复位前,我们是无法完成STM32对外部SRAM的配置,只能上电后进行配置了才可以使用。也就是说,只有正常的硬件复位序列完成后,我们可以在Reset_Handler(复位中断服务程序)中完成对SRAM的配置,但是在这之前,局部变量和函数调用用到的Stack栈空间,我们都得使用STM32内部SRAM,明白了这点,很重要;
(2)       修改启动文件,设置 __initial_sp    EQU     0x20000000 + Stack_Size
此时,我们分析编译后的map、调试信息:
注意观察:此时的map中数据段信息,没有__initial_sp的值;我们在看一下,调试时的信息:
很清晰:此时发生函数调用时,我们使用的栈指向了内部的sram控件,R13 = 0x2000 0400;所以当发生push、pop操作时,数据都是正确的,PC也可以得到正确的数值,从而按流程执行;
            所以我们在启动文件中添加的那一条:__initial_sp    EQU     0x20000000 + Stack_Size很重要,就是强制把栈定义在了内部的sram中了,虽然通过map文件我们可以看到,在外部的sram中我们也分配了heap、stack的空间,但是我们的sp指针没有指向他们,其实这里还有很多扩展的空间;

到这里,分析完了,外部sram的用法,以及为什么会出现这种现象,其中涉及到map文件的解析;以及分散加载。Sct的知识,没有做仔细分析,合理安排、设计分散加载文件可以让你的工程的链接、加载更加符合你的设计要求,所以在工程设置中,IDE才允许我们设置使用自己定义的.sct文件;有时间,后面再细分析;


以上图文的Word格式文档下载(内容和本网页上的一模一样,方便大家保存):
12 STM32的外部SRAM使用的总结.docx (1.78 MB, 下载次数: 3)

评分

参与人数 1黑币 +50 收起 理由
admin + 50 共享资料的黑币奖励!

查看全部评分

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表