发新帖本帖赏金 35.00元(功能说明)我要提问
返回列表
打印
[APM32F4]

APM32F407如何把程序的所有数据存储在SDRAM

[复制链接]
834|4
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
luobeihai|  楼主 | 2024-11-18 00:26 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 luobeihai 于 2024-11-18 00:28 编辑


#申请原创# @21小跑堂

0. 前言

最近在做的一个项目,由于需要极大的RAM空间存储程序的数据,了解到极海的APM32F407芯片型号,刚好可以外接SDRAM芯片,可以把RAM存储空间扩展到2MB的大小,非常符号项目的需要。

下面介绍下如何把程序中的所有数据段(全局数据、还有栈等)都存储在外扩的SDRAM中。

要把所有数据都存储到SDRAM,我们只要修改链接文件,把RAM区域的地址都设置到SDRAM的地址即可。但是麻烦就在于SDRAM在还没有初始化之前是不能正常使用的,而SDRAM还不能正常使用之前,就说明栈也还不能正常使用,所以我们需要先写一段汇编代码对SDRAM进行初始化,因为如果使用C语言进行初始化SDRAM,那么使用使用到栈。

但是由于APM32F407内部还有可用的RAM,那么我们可以先使用内部的RAM作为栈空间使用,然后对SDRAM进行初始化之后,就把栈空间切换到SDRAM地址即可。这样我们就能使用C语言实现SDRAM的初始化代码,而不用汇编代码。

1. 修改sct链接文件

既然要把所有数据都存到SDRAM的地址空间,那么我们首先需要修改链接文件的RAM地址为SDRAM的地址,如下:

; *************************************************************
; *** 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 0x60000000 0x00200000  {  ; RW data
   .ANY (+RW +ZI)
  }
}

其中,RW_IRAM1 的可执行域,就是需要填写SDRAM的起始地址0x60000000和0x00200000的大小。

2. SDRAM初始化代码

SDRAM的初始化代码,我们使用C语言实现,这个直接从极海官方的SDK示例代码拿过来使用即可。

1、GPIO引脚初始化配置:
void SDRAM_GPIOConfig(void)
{
    GPIO_Config_T gpioConfig;

    RCM_EnableAHB1PeriphClock(RCM_SDRAM_GPIO_PERIPH);

    gpioConfig.speed = GPIO_SPEED_50MHz;
    gpioConfig.mode = GPIO_MODE_AF;
    gpioConfig.otype = GPIO_OTYPE_PP;
    gpioConfig.pupd = GPIO_PUPD_NOPULL;

    gpioConfig.pin = GPIO_PIN_10 | GPIO_PIN_12 |
                     GPIO_PIN_13 | GPIO_PIN_14 |
                     GPIO_PIN_15;
    GPIO_Config(GPIOD, &gpioConfig);
   
    GPIO_ConfigPinAF(GPIOD, GPIO_PIN_SOURCE_10, GPIO_AF_FSMC);
    GPIO_ConfigPinAF(GPIOD, GPIO_PIN_SOURCE_12, GPIO_AF_FSMC);
    GPIO_ConfigPinAF(GPIOD, GPIO_PIN_SOURCE_13, GPIO_AF_FSMC);
    GPIO_ConfigPinAF(GPIOD, GPIO_PIN_SOURCE_14, GPIO_AF_FSMC);
    GPIO_ConfigPinAF(GPIOD, GPIO_PIN_SOURCE_15, GPIO_AF_FSMC);

    gpioConfig.pin = GPIO_PIN_0 | GPIO_PIN_1 |
                     GPIO_PIN_2 | GPIO_PIN_3 |
                     GPIO_PIN_4 | GPIO_PIN_6 |
                     GPIO_PIN_7 | GPIO_PIN_8 |
                     GPIO_PIN_9 | GPIO_PIN_10 |
                     GPIO_PIN_11;
    GPIO_Config(GPIOF, &gpioConfig);
   
    GPIO_ConfigPinAF(GPIOF, GPIO_PIN_SOURCE_0, GPIO_AF_FSMC);
    GPIO_ConfigPinAF(GPIOF, GPIO_PIN_SOURCE_1, GPIO_AF_FSMC);
    GPIO_ConfigPinAF(GPIOF, GPIO_PIN_SOURCE_2, GPIO_AF_FSMC);
    GPIO_ConfigPinAF(GPIOF, GPIO_PIN_SOURCE_3, GPIO_AF_FSMC);
    GPIO_ConfigPinAF(GPIOF, GPIO_PIN_SOURCE_4, GPIO_AF_FSMC);
    GPIO_ConfigPinAF(GPIOF, GPIO_PIN_SOURCE_6, GPIO_AF_FSMC);
    GPIO_ConfigPinAF(GPIOF, GPIO_PIN_SOURCE_7, GPIO_AF_FSMC);
    GPIO_ConfigPinAF(GPIOF, GPIO_PIN_SOURCE_8, GPIO_AF_FSMC);
    GPIO_ConfigPinAF(GPIOF, GPIO_PIN_SOURCE_9, GPIO_AF_FSMC);
    GPIO_ConfigPinAF(GPIOF, GPIO_PIN_SOURCE_10, GPIO_AF_FSMC);
    GPIO_ConfigPinAF(GPIOF, GPIO_PIN_SOURCE_11, GPIO_AF_FSMC);

    gpioConfig.pin = GPIO_PIN_1 | GPIO_PIN_2 |
                     GPIO_PIN_3 | GPIO_PIN_4 |
                     GPIO_PIN_5 | GPIO_PIN_6 |
                     GPIO_PIN_8 | GPIO_PIN_15;
    GPIO_Config(GPIOG, &gpioConfig);
   
    GPIO_ConfigPinAF(GPIOG, GPIO_PIN_SOURCE_1, GPIO_AF_FSMC);
    GPIO_ConfigPinAF(GPIOG, GPIO_PIN_SOURCE_2, GPIO_AF_FSMC);
    GPIO_ConfigPinAF(GPIOG, GPIO_PIN_SOURCE_3, GPIO_AF_FSMC);
    GPIO_ConfigPinAF(GPIOG, GPIO_PIN_SOURCE_4, GPIO_AF_FSMC);
    GPIO_ConfigPinAF(GPIOG, GPIO_PIN_SOURCE_5, GPIO_AF_FSMC);
    GPIO_ConfigPinAF(GPIOG, GPIO_PIN_SOURCE_6, GPIO_AF_FSMC);
    GPIO_ConfigPinAF(GPIOG, GPIO_PIN_SOURCE_8, GPIO_AF_FSMC);
    GPIO_ConfigPinAF(GPIOG, GPIO_PIN_SOURCE_15, GPIO_AF_FSMC);

    gpioConfig.pin = GPIO_PIN_3 | GPIO_PIN_5 |
                     GPIO_PIN_8 | GPIO_PIN_10 |
                     GPIO_PIN_13 | GPIO_PIN_15;
    GPIO_Config(GPIOH, &gpioConfig);
   
    GPIO_ConfigPinAF(GPIOH, GPIO_PIN_SOURCE_3, GPIO_AF_FSMC);
    GPIO_ConfigPinAF(GPIOH, GPIO_PIN_SOURCE_5, GPIO_AF_FSMC);
    GPIO_ConfigPinAF(GPIOH, GPIO_PIN_SOURCE_8, GPIO_AF_FSMC);
    GPIO_ConfigPinAF(GPIOH, GPIO_PIN_SOURCE_10, GPIO_AF_FSMC);
    GPIO_ConfigPinAF(GPIOH, GPIO_PIN_SOURCE_13, GPIO_AF_FSMC);
    GPIO_ConfigPinAF(GPIOH, GPIO_PIN_SOURCE_15, GPIO_AF_FSMC);

    gpioConfig.pin = GPIO_PIN_3 | GPIO_PIN_7 |
                     GPIO_PIN_8 | GPIO_PIN_9 |
                     GPIO_PIN_10 | GPIO_PIN_11;
    GPIO_Config(GPIOI, &gpioConfig);
   
    GPIO_ConfigPinAF(GPIOI, GPIO_PIN_SOURCE_3, GPIO_AF_FSMC);
    GPIO_ConfigPinAF(GPIOI, GPIO_PIN_SOURCE_7, GPIO_AF_FSMC);
    GPIO_ConfigPinAF(GPIOI, GPIO_PIN_SOURCE_8, GPIO_AF_FSMC);
    GPIO_ConfigPinAF(GPIOI, GPIO_PIN_SOURCE_9, GPIO_AF_FSMC);
    GPIO_ConfigPinAF(GPIOI, GPIO_PIN_SOURCE_10, GPIO_AF_FSMC);
    GPIO_ConfigPinAF(GPIOI, GPIO_PIN_SOURCE_11, GPIO_AF_FSMC);
}

2、SDRAM时序配置:
void SDRAM_Init(void)
{
//    uint32_t sdramCapacity;
    DMC_Config_T dmcConfig;
    DMC_TimingConfig_T timingConfig;

    RCM_EnableAHB3PeriphClock(RCM_AHB3_PERIPH_EMMC);
    RCM_ConfigSDRAM(RCM_SDRAM_DIV_4);

    timingConfig.latencyCAS = DMC_CAS_LATENCY_3;        //!< Configure CAS latency period
    timingConfig.tARP       = DMC_AUTO_REFRESH_10;      //!< Configure auto refresh period
    timingConfig.tRAS       = DMC_RAS_MINIMUM_2;        //!< Configure line activation and precharging minimum time
    timingConfig.tCMD       = DMC_ATA_CMD_1;            //!< Configure active to active period
    timingConfig.tRCD       = DMC_DELAY_TIME_1;         //!< Configure RAS To CAS delay Time
    timingConfig.tRP        = DMC_PRECHARGE_1;          //!< Configure precharge period
    timingConfig.tWR        = DMC_NEXT_PRECHARGE_2;     //!< Configure time between the Last Data and The Next Precharge for write
    timingConfig.tXSR       = 3;                        //!< Configure XSR0
    timingConfig.tRFP       = 0x2F9;                    //!< Configure refresh Cycle

    dmcConfig.bankWidth     = DMC_BANK_WIDTH_1;         //!< Configure bank address width
    dmcConfig.clkPhase      = DMC_CLK_PHASE_REVERSE;    //!< Configure clock phase
    dmcConfig.rowWidth      = DMC_ROW_WIDTH_11;         //!< Configure row address width
    dmcConfig.colWidth      = DMC_COL_WIDTH_8;          //!< Configure column address width
    dmcConfig.timing        = timingConfig;

    DMC_Config(&dmcConfig);
    DMC_ConfigOpenBank(DMC_BANK_NUMBER_2);
    DMC_EnableAccelerateModule();

    DMC_Enable();
}

3. 修改启动代码

最重要的一个步骤是修改启动代码,由于我们前面已经修改了sct链接文件,把所有的数据都储存在了SDRAM的地址空间。但是,SDRAM在还未初始化之前是不能正常使用的,所以我们在启动代码需要进行修改。在跳转运行SDRAM代码之前,我们先设置SP栈指针指向APM32F407芯片内部的RAM空间,然后跳转执行SDRAM的初始化代码。初始化完成之后,SDRAM即可正常使用,这时重新设置SP栈指针指向SDRAM的存储空间。修改的汇编代码如下:

Reset_Handler    PROC
                 EXPORT  Reset_Handler             [WEAK]
        IMPORT  SystemInit
        IMPORT  __main
        IMPORT  SDRAM_GPIOConfig
        IMPORT  SDRAM_Init
               
                 LDR     SP, =0x20020000   // 先设置SP指向APM32F407芯片内部RAM的最顶部
                 LDR     R0, =SystemInit
                 BLX     R0

                 // 这里跳转到SDRAM的初始化代码
                 BL SDRAM_GPIOConfig
                 BL SDRAM_Init

                 // SDRAM已经进行初始化,SDRAM可以正常使用。重新设置SP指向SDRAM空间
                 LDR     SP, =__initial_sp
                 LDR     R0, =__main
                 BX      R0
                 ENDP

做完以上步骤,我们就可以把全部的数据都使用SDRAM来存储了。

我们可以在代码中定义一个全局变量,然后把这个全局变量的地址打印出来,看看其地址是否在SDRAM的0x60000000地址空间。

执行示例结果如下:


为了方便大家使用这个Demo来实现这种场景,我下面把这个工程上传了,以供大家参考。

APM32F4xx_SDK_V1.4_SDRAM_Data.zip (922.15 KB)




使用特权

评论回复

打赏榜单

21小跑堂 打赏了 35.00 元 2024-11-25
理由:恭喜通过原创审核!期待您更多的原创作品~~

评论
21小跑堂 2024-11-25 16:49 回复TA
摆脱单片机RAM不足的束缚,使用外扩SDRAM储存程序数据。 
沙发
呐咯密密| | 2024-11-20 10:01 | 只看该作者
代码全部储存在SDRAM,运行还是在单片机的RAM里面吧

使用特权

评论回复
评论
luobeihai 2024-11-20 21:14 回复TA
代码烧录在芯片内部Flash,也在Flash执行代码。这里只是吧数据全部存储在SDRAM,而没有使用芯片内部的RAM存储运行的数据。 
板凳
XXX-YYY| | 2024-11-26 11:46 | 只看该作者
非常实用,留言收藏

使用特权

评论回复
发新帖 本帖赏金 35.00元(功能说明)我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

19

主题

71

帖子

2

粉丝