luobeihai 发表于 2024-11-18 00:26

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

本帖最后由 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
               EXPORTReset_Handler            
      IMPORTSystemInit
      IMPORT__main
      IMPORTSDRAM_GPIOConfig
      IMPORTSDRAM_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来实现这种场景,我下面把这个工程上传了,以供大家参考。






呐咯密密 发表于 2024-11-20 10:01

代码全部储存在SDRAM,运行还是在单片机的RAM里面吧
页: [1]
查看完整版本: APM32F407如何把程序的所有数据存储在SDRAM