42. FSMC扩展SRAM
一、SRAM简介
静态随机存取存储器(Static Random-Access Memory,SRAM)是随机存取存储器的一种。所谓的 “静态”,是指这种存储器只要保持通电,里面储存的数据就可以恒常保持。相对之下,动态随机存取存储器(DRAM)里面所储存的数据就需要周期性地更新。然而,当电力供应停止时,SRAM 储存的数据还是会消失(被称为 volatile memory),这与在断电后还能储存资料的 ROM 或闪存是不同的。
二、SRAM读写流程
2.1、SRAM写时序
FSMC 模式 A 读时序图:
SRAM 写时序图:
时间参数 | IS62WV51216型号的时间要求 | 说明 |
---|---|---|
\(t_{WC}\) | 不小于 55ns | 写操作的总时间,即\((ADDSET+DATAST+1) * t_{HCLK} > 55ns\) |
\(t_{SA}\) | 大于 0ns | 从发送地址信号到给出写有使能信号的时间,即 \(ADDSET * t_{HCLK}> 0ns\) |
\(t_{PWE}\) | 不小于 40ns | 从接收到写使能信号到数据采样的时间,即\((DATAST + 1) * t_{HCLK} > 40 ns\) |
对于 F4,\(t_{HCLK} = \frac{1}{168M}s ≈ 6ns\),综上考虑,ADDSET≥0,DATAST≥6。
2.2、SRAM读时序
FSMC 模式 A 读时序图:
SRAM 读时序图:
在读写时序中,有几个比较重要的时间参数,在使用STM32 控制的时候需要参考。
时间参数 | IS62WV51216型号的时间要求 | 说明 |
---|---|---|
\(t_{RC}\) | 不小于 55ns | 读操作的总时间,即\((ADDSET+DATAST) * t_{HCLK} > 55ns\) |
\(t_{AA}\) | 最迟不大于 55ns | 从接收到地址信号到给出有效数据的时间,即\(ADDSET * t_{HCLK} < 55ns\) |
\(t_{DOE}\) | 最迟不大于 25ns | 从接收到读使能信号到给出有效数据的时间,即\(DATAST * t_{HCLK} < 25ns\) |
对于 F4,\(t_{HCLK} = \frac{1}{168M}s ≈ 6ns\),综上考虑,ADDSET≤9,DATAST≤4。
三、FSMC驱动SRAM步骤
3.1、使能FSMC和相关GPIO时钟
FSMC 时钟使能:
#define __HAL_RCC_FSMC_CLK_ENABLE() do { \
__IO uint32_t tmpreg = 0x00U; \
SET_BIT(RCC->AHB3ENR, RCC_AHB3ENR_FSMCEN);\
/* Delay after an RCC peripheral clock enabling */ \
tmpreg = READ_BIT(RCC->AHB3ENR, RCC_AHB3ENR_FSMCEN);\
UNUSED(tmpreg); \
} while(0U)
#define __HAL_RCC_GPIOD_CLK_ENABLE() do { \
__IO uint32_t tmpreg = 0x00U; \
SET_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIODEN);\
/* Delay after an RCC peripheral clock enabling */ \
tmpreg = READ_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIODEN);\
UNUSED(tmpreg); \
} while(0U)
#define __HAL_RCC_GPIOE_CLK_ENABLE() do { \
__IO uint32_t tmpreg = 0x00U; \
SET_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOEEN);\
/* Delay after an RCC peripheral clock enabling */ \
tmpreg = READ_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOEEN);\
UNUSED(tmpreg); \
} while(0U)
#define __HAL_RCC_GPIOF_CLK_ENABLE() do { \
__IO uint32_t tmpreg = 0x00U; \
SET_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOFEN);\
/* Delay after an RCC peripheral clock enabling */ \
tmpreg = READ_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOFEN);\
UNUSED(tmpreg); \
} while(0U)
#define __HAL_RCC_GPIOG_CLK_ENABLE() do { \
__IO uint32_t tmpreg = 0x00U; \
SET_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOGEN);\
/* Delay after an RCC peripheral clock enabling */ \
tmpreg = READ_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOGEN);\
UNUSED(tmpreg); \
} while(0U)
3.2、配置FSMC工作参数
设置 FSMC BANK1 区域 3 的相关寄存器。
HAL_StatusTypeDef HAL_SRAM_Init(SRAM_HandleTypeDef *hsram, FMC_NORSRAM_TimingTypeDef *Timing, FMC_NORSRAM_TimingTypeDef *ExtTiming);
形参 hsram 是 SRAM 句柄,它的定义如下:
typedef struct
{
FMC_NORSRAM_TypeDef *Instance; // FSMC寄存器基地址
FMC_NORSRAM_EXTENDED_TypeDef *Extended; // 扩展模式寄存器基地址
FMC_NORSRAM_InitTypeDef Init; // SRMA初始化结构体
HAL_LockTypeDef Lock; // SRAM锁对象结构体
__IO HAL_SRAM_StateTypeDef State; // SRMA设备访问状态结构体
DMA_HandleTypeDef *hdma; // DMA结构体
} SRAM_HandleTypeDef;
Instance:指向 FSMC 寄存器基地址。如果是 SRAM 设备,直接填写 FSMC_NORSRAM_DEVICE
标识符即可。
#define FSMC_NORSRAM_DEVICE FSMC_Bank1
Extended:指向 FSMC 扩展模式寄存器基地址。
#define FSMC_NORSRAM_EXTENDED_DEVICE FSMC_Bank1E
Init:用于对 FSMC 的初始化配置。它是 FSMC_NORSRAM_InitTypeDef
结构体指针类型。它的定义如下:
typedef struct
{
uint32_t NSBank; // NORSRAM存储区块号
uint32_t DataAddressMux; // 地址/数据复用使能
uint32_t MemoryType; // 存储器类型
uint32_t MemoryDataWidth; // 存储器数据宽度
uint32_t BurstAccessMode; // 突发模式配置
uint32_t WaitSignalPolarity; // 设置等待信号的极性
uint32_t WrapMode; // 突发下存储器传输使能
uint32_t WaitSignalActive; // 等待状态之前或等待状态期间
uint32_t WriteOperation; // 存储器写使能
uint32_t WaitSignal; // 使能或者禁止通过等待信号来插入等待状态
uint32_t ExtendedMode; // 使能或者禁止使能扩展模式
uint32_t AsynchronousWait; // 用于异步传输期间,使能或者禁止等待信号
uint32_t WriteBurst; // 用于使能或者禁止异步的写突发操作
uint32_t ContinuousClock; // 使能或者禁止FSMC向外部存储器输出时钟
uint32_t WriteFifo; // 使能或者禁止写FIFO
uint32_t PageSize; // 设置页大小
} FSMC_NORSRAM_InitTypeDef;
NSBank:用来指定使用到的存储块区号。
#define FSMC_NORSRAM_BANK1 (0x00000000U)
#define FSMC_NORSRAM_BANK2 (0x00000002U)
#define FSMC_NORSRAM_BANK3 (0x00000004U)
#define FSMC_NORSRAM_BANK4 (0x00000006U)
DataAddressMux:用来设置是否使能地址/数据复用,该变量仅对 NOR/PSRAM 有效。
#define FSMC_DATA_ADDRESS_MUX_DISABLE (0x00000000U)
#define FSMC_DATA_ADDRESS_MUX_ENABLE (0x00000002U)
MemoryType:用来设置存储器类型。
#define FSMC_MEMORY_TYPE_SRAM (0x00000000U)
#define FSMC_MEMORY_TYPE_PSRAM (0x00000004U)
#define FSMC_MEMORY_TYPE_NOR (0x00000008U)
MemoryDataWidth:用来设置存储器数据总线宽度,可选 8 位、16 位还是 32 位。
#define FSMC_NORSRAM_MEM_BUS_WIDTH_8 (0x00000000U)
#define FSMC_NORSRAM_MEM_BUS_WIDTH_16 (0x00000010U)
#define FSMC_NORSRAM_MEM_BUS_WIDTH_32 (0x00000020U)
BurstAccessMode:是否使能突发访问,仅对同步突发存储器有效。
#define FSMC_BURST_ACCESS_MODE_DISABLE (0x00000000U)
#define FSMC_BURST_ACCESS_MODE_ENABLE (0x00000100U)
WaitSignalPolarity:等待信号的极性,仅在突发模式访问下有用。
#define FSMC_WAIT_SIGNAL_POLARITY_LOW (0x00000000U)
#define FSMC_WAIT_SIGNAL_POLARITY_HIGH (0x00000200U)
WaitSignalActive:存储器是在等待周期之前的一个时钟周期还是等待周期期间使能 NWAIT。
#define FSMC_WAIT_TIMING_BEFORE_WS (0x00000000U)
#define FSMC_WAIT_TIMING_DURING_WS (0x00000800U)
WriteOperation:用来设置存储器写使能,也就是是否允许写入。
#define FSMC_WRITE_OPERATION_DISABLE (0x00000000U)
#define FSMC_WRITE_OPERATION_ENABLE (0x00001000U)
WaitSignal:等待使能位。
#define FSMC_WAIT_SIGNAL_DISABLE (0x00000000U)
#define FSMC_WAIT_SIGNAL_ENABLE (0x00002000U)
ExtendedMode:用来设置是否使能扩展模式,也就是是否允许读写使用不同时序。
#define FSMC_EXTENDED_MODE_DISABLE (0x00000000U)
#define FSMC_EXTENDED_MODE_ENABLE (0x00004000U)
AsynchronousWait:是否使能同步传输模式下的等待信号。
#define FSMC_ASYNCHRONOUS_WAIT_DISABLE (0x00000000U)
#define FSMC_ASYNCHRONOUS_WAIT_ENABLE (0x00008000U)
WriteBurst:是否禁止突发写。
#define FSMC_WRITE_BURST_DISABLE (0x00000000U)
#define FSMC_WRITE_BURST_ENABLE (0x00080000U)
形参 Timing 和形参 ExtTiming 都是 FSMC_NORSRAM_TimingTypeDef 结构体类型指针变量,其定义如下:
typedef struct
{
uint32_t AddressSetupTime; // 地址建立时间
uint32_t AddressHoldTime; // 地址保持时间
uint32_t DataSetupTime; // 数据建立时间
uint32_t BusTurnAroundDuration; // 总线周转阶段的持续时间
uint32_t CLKDivision; // CLK时钟输出信号的周期
uint32_t DataLatency; // 同步突发 NOR FLASH 的数据延迟
uint32_t AccessMode; // 异步模式配置
} FSMC_NORSRAM_TimingTypeDef;
AddressSetupTime:用来 设置地址建立时间,可以理解为 RD/WR 的高电平时间。
AddressHoldTime:用来 设置地址保持时间,模式 A 并没有用到。
DataSetupTime:用来 设置数据建立时间,可以理解为 RD/WR 的低电平时间。
BusTurnAroundDuration:用来 配置总线周转阶段的持续时间,NOR FLASH 用到。
CLKDivision:用来 配置 CLK 时钟输出信号的周期,以 HCLK 周期数表示。若控制异步存储器,该参数无效。
DataLatency:用来 设置同步突发 NOR FLASH 的数据延迟。若控制异步存储器,该参数无效。
AccessMode:用来 设置异步模式。
#define FSMC_ACCESS_MODE_A (0x00000000U)
#define FSMC_ACCESS_MODE_B (0x10000000U)
#define FSMC_ACCESS_MODE_C (0x20000000U)
#define FSMC_ACCESS_MODE_D (0x30000000U)
3.3、FSMC底层初始化
HAL 库中,提供 HAL_GPIO_Init() 函数用于配置 GPIO 功能模式,初始化 GPIO。该函数的声明如下:
void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init);
该函数的第一个形参 GPIOx 用来指定端口号,可选值如下:
#define GPIOD ((GPIO_TypeDef *) GPIOD_BASE)
#define GPIOE ((GPIO_TypeDef *) GPIOE_BASE)
#define GPIOF ((GPIO_TypeDef *) GPIOF_BASE)
#define GPIOG ((GPIO_TypeDef *) GPIOG_BASE)
第二个参数是 GPIO_InitTypeDef 类型的结构体变量,用来设置 GPIO 的工作模式,其定义如下:
typedef struct
{
uint32_t Pin; // 引脚号
uint32_t Mode; // 模式设置
uint32_t Pull; // 上下拉设置
uint32_t Speed; // 速度设置
uint32_t Alternate; // 复用功能设置
}GPIO_InitTypeDef;
成员 Pin 表示 引脚号,范围:GPIO_PIN_0 到 GPIO_PIN_15。
#define GPIO_PIN_0 ((uint16_t)0x0001) /* Pin 0 selected */
#define GPIO_PIN_1 ((uint16_t)0x0002) /* Pin 1 selected */
#define GPIO_PIN_2 ((uint16_t)0x0004) /* Pin 2 selected */
#define GPIO_PIN_3 ((uint16_t)0x0008) /* Pin 3 selected */
#define GPIO_PIN_4 ((uint16_t)0x0010) /* Pin 4 selected */
#define GPIO_PIN_5 ((uint16_t)0x0020) /* Pin 5 selected */
#define GPIO_PIN_6 ((uint16_t)0x0040) /* Pin 6 selected */
#define GPIO_PIN_7 ((uint16_t)0x0080) /* Pin 7 selected */
#define GPIO_PIN_8 ((uint16_t)0x0100) /* Pin 8 selected */
#define GPIO_PIN_9 ((uint16_t)0x0200) /* Pin 9 selected */
#define GPIO_PIN_10 ((uint16_t)0x0400) /* Pin 10 selected */
#define GPIO_PIN_11 ((uint16_t)0x0800) /* Pin 11 selected */
#define GPIO_PIN_12 ((uint16_t)0x1000) /* Pin 12 selected */
#define GPIO_PIN_13 ((uint16_t)0x2000) /* Pin 13 selected */
#define GPIO_PIN_14 ((uint16_t)0x4000) /* Pin 14 selected */
#define GPIO_PIN_15 ((uint16_t)0x8000) /* Pin 15 selected */
#define GPIO_PIN_All ((uint16_t)0xFFFF) /* All pins selected */
成员 Mode 是 GPIO 的 模式选择,有以下选择项:
#define GPIO_MODE_AF_PP 0x00000002U // 推挽式复用
成员 Pull 用于 配置上下拉电阻,有以下选择项:
#define GPIO_NOPULL 0x00000000U // 无上下拉
#define GPIO_PULLUP 0x00000001U // 上拉
#define GPIO_PULLDOWN 0x00000002U // 下拉
成员 Speed 用于 配置 GPIO 的速度,有以下选择项:
#define GPIO_SPEED_FREQ_LOW 0x00000000U // 低速
#define GPIO_SPEED_FREQ_MEDIUM 0x00000001U // 中速
#define GPIO_SPEED_FREQ_HIGH 0x00000002U // 高速
#define GPIO_SPEED_FREQ_VERY_HIGH 0x00000003U // 极速
成员 Alternate 用于 配置具体的复用功能,不同的 GPIO 口可以复用的功能不同,具体可参考数据手册。
#define GPIO_AF12_FSMC ((uint8_t)0x0C) /* FSMC Alternate Function mapping */
四、原理图
五、程序源码
/* SRAM基地址,根据SRAM_FSMC_NEX的设置来决定基址地址
* 我们一般使用FSMC的块1(BANK1)来驱动SRAM, 块1地址范围总大小为256MB,均分成4块:
* 存储块1(FSMC_NE1)地址范围: 0X6000 0000 ~ 0X63FF FFFF
* 存储块2(FSMC_NE2)地址范围: 0X6400 0000 ~ 0X67FF FFFF
* 存储块3(FSMC_NE3)地址范围: 0X6800 0000 ~ 0X6BFF FFFF
* 存储块4(FSMC_NE4)地址范围: 0X6C00 0000 ~ 0X6FFF FFFF
*/
#define SRAM_FSMC_NEX 3
#define SRAM_BASE_ADDRESS (0x60000000 + (0x4000000 * (SRAM_FSMC_NEX - 1)))
SRAM 初始化函数:
/**
* @brief SRAMc初始化
*
*/
void SRAM_Init(void)
{
SRAM_GPIO_Init();
FSMC_SRAM_Init();
}
FSMC 驱动 SRAM 函数:
/**
* @brief FSMC驱动SRMA初始化
*
*/
static void FSMC_SRAM_Init(void)
{
SRAM_HandleTypeDef hsram = {0};
FSMC_NORSRAM_TimingTypeDef FSMC_NORSRAM_TimingStruct = {0};
hsram.Instance = FSMC_NORSRAM_DEVICE;
hsram.Extended = FSMC_NORSRAM_EXTENDED_DEVICE;
hsram.Init.NSBank = (SRAM_FSMC_NEX == 1) ? FSMC_NORSRAM_BANK1 : \
(SRAM_FSMC_NEX == 2) ? FSMC_NORSRAM_BANK2 : \
(SRAM_FSMC_NEX == 3) ? FSMC_NORSRAM_BANK3 :
FSMC_NORSRAM_BANK4; // 根据配置选择FSMC_NE1~4
hsram.Init.DataAddressMux = FSMC_DATA_ADDRESS_MUX_DISABLE; // 地址/数据线不复
hsram.Init.MemoryType = FSMC_MEMORY_TYPE_SRAM; // SRAM
hsram.Init.MemoryDataWidth = FSMC_NORSRAM_MEM_BUS_WIDTH_16; // 16位数据宽度
hsram.Init.BurstAccessMode = FSMC_BURST_ACCESS_MODE_DISABLE; // 是使能突发访问,仅对同步突发存储器有效
hsram.Init.WaitSignalPolarity = FSMC_WAIT_SIGNAL_POLARITY_LOW; // 等待信号的极性,仅在突发模式访问下有
hsram.Init.WaitSignalActive = FSMC_WAIT_TIMING_BEFORE_WS; // 存储器是在等待周期之前的一个时钟周期还是等待周期期间使能NWAIT
hsram.Init.WriteOperation = FSMC_WRITE_OPERATION_ENABLE; // 存储器写使能
hsram.Init.WaitSignal = FSMC_WAIT_SIGNAL_DISABLE; // 等待使能位
hsram.Init.ExtendedMode = FSMC_EXTENDED_MODE_DISABLE; // 读写使用相同的时序
hsram.Init.AsynchronousWait = FSMC_ASYNCHRONOUS_WAIT_DISABLE; // 是否使能同步传输模式下的等待信号
hsram.Init.WriteBurst = FSMC_WRITE_BURST_DISABLE; // 禁止突发写
hsram.Init.ContinuousClock = FSMC_CONTINUOUS_CLOCK_SYNC_ASYNC;
FSMC_NORSRAM_TimingStruct.AccessMode = FSMC_ACCESS_MODE_A; // 模式A
FSMC_NORSRAM_TimingStruct.AddressSetupTime = 0x00; // 地址建立时间(ADDSET
FSMC_NORSRAM_TimingStruct.AddressHoldTime = 0x00; // 地址保持时间(ADDHLD)模式A未用到
FSMC_NORSRAM_TimingStruct.DataSetupTime = 0x18; // 数据保存时间
FSMC_NORSRAM_TimingStruct.BusTurnAroundDuration = 0x00;
HAL_SRAM_Init(&hsram, &FSMC_NORSRAM_TimingStruct, &FSMC_NORSRAM_TimingStruct);
}
SRAM 底层初始化函数:
/**
* @brief FSMC驱动SRAM底层初始化
*
*/
static void SRAM_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
__HAL_RCC_FSMC_CLK_ENABLE();
__HAL_RCC_GPIOD_CLK_ENABLE();
__HAL_RCC_GPIOE_CLK_ENABLE();
__HAL_RCC_GPIOF_CLK_ENABLE();
__HAL_RCC_GPIOG_CLK_ENABLE();
/** FSMC GPIO Configuration
PF0 ------> FSMC_A0 PF1 ------> FSMC_A1 PF2 ------> FSMC_A2
PF3 ------> FSMC_A3 PF4 ------> FSMC_A4 PF5 ------> FSMC_A5
PF12 ------> FSMC_A6 PF13 ------> FSMC_A7 PF14 ------> FSMC_A8
PF15 ------> FSMC_A9
PG0 ------> FSMC_A10 PG1 ------> FSMC_A11 PG2 ------> FSMC_A12
PG3 ------> FSMC_A13 PG4 ------> FSMC_A14 PG5 ------> FSMC_A15
PG10 ------> FSMC_NE3
PE7 ------> FSMC_D4 PE8 ------> FSMC_D5 PE9 ------> FSMC_D6
PE10 ------> FSMC_D7 PE11 ------> FSMC_D8 PE12 ------> FSMC_D9
PE13 ------> FSMC_D10 PE14 ------> FSMC_D11 PE15 ------> FSMC_D12
PE0 ------> FSMC_NBL0 PE1 ------> FSMC_NBL1
PD11 ------> FSMC_A16 PD12 ------> FSMC_A17 PD13 ------> FSMC_A18
PD0 ------> FSMC_D2 PD1 ------> FSMC_D3 PD8 ------> FSMC_D13
PD9 ------> FSMC_D14 PD10 ------> FSMC_D15 PD14 ------> FSMC_D0
PD15 ------> FSMC_D1
PD4 ------> FSMC_NOE PD5 ------> FSMC_NWE
*/
// PF0~PF5 PF12~PF15
GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 |
GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_12 | GPIO_PIN_13 |
GPIO_PIN_14 |GPIO_PIN_15;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF12_FSMC;
HAL_GPIO_Init(GPIOF, &GPIO_InitStruct);
// PG0~PG5 PG10
GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 |
GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_10;
HAL_GPIO_Init(GPIOG, &GPIO_InitStruct);
// PE7~PE15 PE0~PE1
GPIO_InitStruct.Pin = GPIO_PIN_7 | GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 |
GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 |
GPIO_PIN_15 | GPIO_PIN_0 | GPIO_PIN_1;
HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
// PD0~PD1 PD4~PD5 PD8~PD15
GPIO_InitStruct.Pin = GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 |
GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15 |
GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5;
HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
}
往 SRAM 写数据函数:
/**
* @brief 往SRAM指定地址写指定长度的数据
*
* @param address 开始写入的地址
* @param data 数据存储区
* @param length 写入的字节数
*/
void SRAM_WriteData(uint32_t address, uint8_t *data, uint32_t length)
{
for (; length != 0; length--)
{
*(volatile uint8_t *)(SRAM_BASE_ADDRESS + address) = *data++;
address++;
}
}
从 SRAM 读取数据函数:
/**
* @brief 从SRAM指定地址读取指定长度的数据
*
* @param address 开始读取的地址
* @param data 数据存储区
* @param length 要读取的长度
*/
void SRAM_ReadData(uint32_t address, uint8_t *data, uint32_t length)
{
for (; length != 0; length--)
{
*data++ = *(volatile uint8_t *)(SRAM_BASE_ADDRESS + address);
address++;
}
}
main() 函数内容如下:
uint8_t a1 __attribute__((section(".ext_sram")));
uint32_t buffer[1024] __attribute__((section(".ext_sram")));
int main(void)
{
uint8_t writeData[] = "Sakura";
uint8_t readData[15] = {0};
HAL_Init();
System_Clock_Init(8, 336, 2, 7);
Delay_Init(168);
UART_Init(&g_usart1_handle, USART1, 115200);
SRAM_Init();
a1 = 10;
printf("a1 address: %p, a1: %d\r\n", &a1, a1);
printf("buffer address: %p\r\n", buffer);
for (uint32_t i = 0; i < 1024; i++)
{
buffer[i] = i;
}
for (uint32_t i = 0; i < 1024; i++)
{
printf("%ld\t", buffer[i]);
}
printf("\r\n");
SRAM_WriteData(100, writeData, sizeof(writeData));
SRAM_ReadData(100, readData, sizeof(readData));
printf("readData: %s\r\n", readData);
while (1)
{
}
return 0;
}
如果使用的是 GCC 编译器的话,定义全局变量时,要想定义在外部 SRAM 中,需要修改 STM32F407ZGTx_FLASH.ld 文件的内容,给 GCC 添加外挂内存,这里我们起名字为:EXTSRAM。
/* Specify the memory areas */
MEMORY
{
...
EXTSRAM (rw) : ORIGIN = 0x68000000, LENGTH = 1024K
}
然后,添加 section 段:ext_sram(NOLOAD),注意 NOLOAD 的意思是储存在 ext_sram 段的内存变量,在启动的时候(就是 main() 之前的阶段)不需要初始化。这里容易出问题,标准 HAL 库是在进入 main() 才开始初始化(外挂 SRAM 需要调用自定义的 FSMC_SRAM_Init() 函数进行初始化后,才可以使用),不加 NOLOAD 的话,在启动阶段 GCC 默认加载内存变量(extern 或者 static 变量),找不到外挂 SRAM,导致 HardFault。
/* Define output sections */
SECTIONS
{
...
/* external sram data, do not initialize at startup */
.ext_sram(NOLOAD) :
{
. = ALIGN(4);
_sext_sram = .; /* create a global symbol at ext_sram start */
*(.ext_sram)
*(.ext_sram*)
. = ALIGN(4);
_eext_sram = .; /* define a global symbol at ext_sram end */
} >EXTSRAM
...
}
然后,我们要想把全部变量定义在外部 SRAM 中时,需要通过如下方法定义:
uint8_t a1 __attribute__((section(".ext_sram")));
uint32_t buffer[1024] __attribute__((section(".ext_sram")));
如果我们想把局部直接指定到外部 SRAM 中,可以通过指针的方式直接往 SRAM 对应的地址写数据即可。
*(uint32_t *)0x68000000 = 10;
data = *(uint32_t *)0x68000000;