STM32 外部 SRAM/PSRAM 扩展:FMC 接口详解
👁 6 阅读 · 2026-08-16 · STM32 存储
当片内 RAM 不够用时,通过 FMC 接口外扩 SRAM/PSRAM 是常见做法。本文深入剖析 STM32 FMC 存储控制器原理,包括总线结构、时序配置与硬件连接,并给出基于 HAL 库的完整驱动代码和注意事项,帮助开发者快速上手外部存储器扩展。
## 1. FMC 是什么?
FMC(Flexible Memory Controller)是 STM32 系列中的灵活存储控制器,它源于早期的 FSMC(Flexible Static Memory Controller),在 F4/F7/H7 等系列中更名为 FMC,但功能更加强大。FMC 可以连接 NOR Flash、SRAM、PSRAM、SDRAM 以及 LCD 并行接口,并支持 8/16/32 位数据总线宽度。
对于 SRAM/PSRAM,FMC 本质上是一个外置总线主控:CPU 发起访问时,FMC 自动生成地址、数据、读/写使能等时序信号,从而把片外存储器映射到 ARM 的寻址空间。
## 2. SRAM/PSRAM 接口原理
典型异步 SRAM 接口包含以下信号:
- **地址线** A[0:N-1]
- **数据线** DQ[0:M-1]
- **芯片使能** CE#(FMC 中对应 NE 片选)
- **读使能** OE#(FMC 中对应 NOE)
- **写使能** WE#(FMC 中对应 NWE)
- **字节选择** LB#/UB#(FMC 中对应 NBL[1:0])
FMC 把片外 SRAM 映射到 AHB 地址空间,例如 STM32F4 的 Bank1 起始地址 0x60000000。访问地址时,FMC 会驱动相应的片选信号并产生读写时序。
### 关键时序参数
异步访问需要配置以下时间参数(单位为 HCLK 周期):
- **AddressSetup**:地址建立时间(从地址有效到片选有效/命令有效)
- **AddressHold**:地址保持时间
- **DataSetup**:数据建立时间(读时用于采样,写时用于数据有效时间)
- **BusTurnaround**:总线周转时间,防止读/写切换冲突
对于高速 PSRAM 通常使用**同步突发模式**,而普通 SRAM 则使用异步模式。开发时需要参考 SRAM 数据手册中的时序图来调节这些参数。
## 3. 硬件连接示例
以 16 位异步 SRAM(如 IS62WV51216)为例,连接 STM32F407ZGT6:
- 数据线:D[15:0] -> FSMC_D[15:0]
- 地址线:A[18:0] -> FSMC_A[18:0]
- 片选:CE# -> FSMC_NE1(Bank1,起始地址 0x60000000)
- 读使能:OE# -> FSMC_NOE
- 写使能:WE# -> FSMC_NWE
- 字节选择:UB# -> FSMC_NBL1,LB# -> FSMC_NBL0
注意,若 SRAM 为 16 位,STM32 的 FSMC_A[0] 通常内部连接到存储器的 A[1],即地址左移一位,以支持字节选择。这在使用指针访问时需留意地址对齐。
## 4. 配置步骤
### 4.1 使用 STM32CubeMX 配置
1. 启用 FMC,选择 Bank1,SRAM/PSRAM 模式。
2. 设置数据总线宽度(8/16 位)。
3. 配置时序参数:根据 SRAM 手册计算。典型值(HCLK=168MHz,周期约 6ns):
- AddressSetup = 9(约 54ns)
- DataSetup = 9(约 54ns)
- AddressHold = 1
- BusTurnaround = 0
4. 生成代码,HAL 库会自动生成 `MX_FMC_Init()` 和 `HAL_SRAM_Init()`。
### 4.2 手动代码配置(HAL)
如果不想用 CubeMX,也可以手动编写初始化函数:
```c
#include "stm32f4xx_hal.h"
SRAM_HandleTypeDef hsram;
FMC_NORSRAM_TimingTypeDef SramTiming;
void FMC_SRAM_Init(void)
{
// 使能相关时钟
__HAL_RCC_FMC_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
// 省略 GPIO 初始化,按实际引脚配置为复用功能 AF12
hsram.Instance = FMC_NORSRAM_DEVICE;
hsram.Extended = FMC_NORSRAM_EXTENDED_DEVICE;
hsram.Init.DataAddressMux = FMC_DATA_ADDRESS_MUX_DISABLE;
hsram.Init.MemoryType = FMC_MEMORY_TYPE_SRAM;
hsram.Init.MemoryDataWidth = FMC_NORSRAM_MEM_BUS_WIDTH_16;
hsram.Init.BurstAccessMode = FMC_BURST_ACCESS_MODE_DISABLE;
hsram.Init.WriteOperation = FMC_WRITE_OPERATION_ENABLE;
hsram.Init.WaitSignal = FMC_WAIT_SIGNAL_DISABLE;
hsram.Init.ExtendedMode = FMC_EXTENDED_MODE_DISABLE;
hsram.Init.WriteBurst = FMC_WRITE_BURST_DISABLE;
hsram.Init.ContinuousClock = FMC_CONTINUOUS_CLOCK_SYNC_ASYNC;
hsram.Init.WriteFifo = FMC_WRITE_FIFO_DISABLE;
hsram.Init.PageSize = FMC_PAGE_SIZE_NONE;
// 时序参数,单位:HCLK 周期数
SramTiming.AddressSetupTime = 9;
SramTiming.AddressHoldTime = 1;
SramTiming.DataSetupTime = 9;
SramTiming.BusTurnAroundDuration = 0;
SramTiming.CLKDivision = 16;
SramTiming.DataLatency = 17;
SramTiming.AccessMode = FMC_ACCESS_MODE_A;
hsram.Init.ContinuousClock = FMC_CONTINUOUS_CLOCK_SYNC_ASYNC;
HAL_SRAM_Init(&hsram, &SramTiming, &SramTiming);
// 开启 FMC 写保护解除(某些 STM32 默认写保护)
__HAL_FMC_NORSRAM_ENABLE(&hsram);
}
```
## 5. 完整代码示例:读写外部 SRAM
初始化后,外部 SRAM 的地址映射在 0x60000000。可以直接通过指针访问,也可以使用链接脚本定义段。
```c
#define SRAM_BASE 0x60000000U
#define SRAM_SIZE (1024U * 512U) // 假设 512KB
void SRAM_Test(void)
{
uint16_t *sram = (uint16_t *)SRAM_BASE;
uint32_t i;
uint32_t errors = 0;
// 写入 0xAA55 模式
for (i = 0; i < SRAM_SIZE / 2; i++)
{
sram[i] = 0xAA55;
}
// 读出并校验
for (i = 0; i < SRAM_SIZE / 2; i++)
{
if (sram[i] != 0xAA55)
{
errors++;
}
}
if (errors == 0)
{
// 测试通过
}
else
{
// 错误处理
}
}
```
注意:对于 16 位 SRAM,使用 `uint16_t` 指针可确保每次访问 16 位数据,地址自动递增 2 字节。若要按字节访问,请使用 `uint8_t` 指针,但 FMC 会通过 NBL 引脚自动控制高/低字节。
## 6. 注意事项
### 6.1 时序调校
不同 SRAM 速度等级不同,时序参数需仔细查阅数据手册。若时序过紧,数据采样会出错;若过松,则性能下降。推荐先用示波器观察 NOE/NWE/地址有效沿,再微调参数。
### 6.2 电压电平匹配
STM32 FMC 引脚电压必须与外部 SRAM 的 I/O 电压一致。若 SRAM 为 3.3V,STM32 选用 VDD=3.3V;若为 1.8V SRAM,需选用对应供电的 MCU 或加电平转换芯片。
### 6.3 PCB 布线
外部总线的信号完整性至关重要。数据线、地址线尽量等长,控制信号(NOE/NWE/NE)要避免过长走线,建议加串联电阻(如 22Ω)抑制过冲。电源去耦电容靠近 SRAM 电源引脚。
### 6.4 缓存一致性与 DMA
如果使用 STM32F7/H7,CPU 的 D-Cache 会导致 DMA 与 CPU 访问外部 SRAM 时数据不一致。处理方式:
- 对 SRAM 区域配置为**设备/强序模式**(Device or Strongly-ordered),关闭缓存;
- 或使用 `SCB_CleanDCache()` / `SCB_InvalidateDCache()` 手动维护一致性。
### 6.5 片选与地址重叠
确保 FMC 的多个外设(如 LCD、NOR Flash)不使用同一个 Bank 或相同片选,否则会访问冲突。
## 7. 总结
FMC 将复杂的外部存储器时序转换为简单的内存映射访问,极大简化了设计。正确配置时序参数和硬件连接后,外部 SRAM/PSRAM 可以像内部 RAM 一样使用。对于需要大容量平滑数据缓冲(如音频、LCD 帧缓冲)的应用,这是一项非常实用的技能。