本帖最后由 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)
|
摆脱单片机RAM不足的束缚,使用外扩SDRAM储存程序数据。