STM32外扩SRAM

第1章 基础知识

Cortex-M4 处理器使用 32 位地址总线,这意味着它能够直接访问 4GB(0x00000000 到 0xFFFFFFFF) 的地址空间。ARM 公司为了方便芯片厂商设计和提高软件的可移植性,为这 4GB 空间制定了一个标准的"模板"划分规则,而具体的芯片厂商(如 ST 或 GD32)会在这个模板上填充自己的硬件。总之,单片机是通过地址来访问寄存器、FLASH,ARM寻址范围4GB。

1.1 Cortex-M4 内核的 4GB 寻址空间划分

1.1.1 CODE 区(代码区)

  1. 地址范围: 0x00000000 - 0x1FFFFFFF (512MB)
  2. 作用: 主要用于存放程序代码和常量数据。
  3. 实际情况: 在 GD32 芯片中,这里通常映射的是片内的 Flash(闪存)。比如您的程序下载进去后,就放在这里运行。
  4. 特点: 支持代码直接运行,且通常有预读缓存机制,以加快 CPU 取指令的速度。

1.1.2 SRAM 区(片内数据区)

  1. 地址范围: 0x20000000 - 0x3FFFFFFF (512MB)
  2. 作用: 用于存放程序运行时产生的数据,比如全局变量、局部变量、堆栈(Stack)等。
  3. 实际情况: 对应 GD32 芯片内部的 物理 SRAM 。例如,如果一颗 GD32 芯片有 96KB 的 SRAM,这 96KB 就会映射在 0x20000000 开始的区域。
  4. 特点: 读写速度极快,掉电后数据丢失。

1.1.3 片上外设区(Peripheral)

  1. 地址范围: 0x40000000 - 0x5FFFFFFF (512MB)
  2. 作用: 所有的片上外设寄存器都映射在这个区域。
  3. 实际情况: 当您写代码去控制 GPIO、USART、定时器等时,实际上是在访问这个地址段内的特定地址。
  4. 特点: 每个外设占据一段地址,通过读写这些地址来控制硬件的动作。

1.1.4 EXMC 区(外部存储器控制器)

  1. 地址范围: 0x60000000 - 0x9FFFFFFF (1GB)
  2. 作用: 这是 GD32 独有的外部存储器扩展接口。
  3. 实际情况:
    1. EXMC 是 GD32 对 ARM FSMC 的增强版。
    2. 它允许您通过引脚在芯片外部扩展 SRAM、NAND Flash 或 NOR Flash。
    3. 图中绿色的大区域就是专门留给这些外部存储设备的。访问这里时,MCU 会自动将其转换为外部设备的读写时序。
  4. 注意: 虽然这里标了 EXMC,但部分高性能 GD32 芯片的 SDRAM 实际上映射在更靠上的位置(如 0xC0000000)。

1.1.5 未使用区(Reserved)

  1. 地址范围: 0xA0000000 - 0xDFFFFFFF (896MB)
  2. 作用: 保留地址空间,目前没有分配具体用途。
  3. 注意: 不要尝试访问这部分地址,否则可能会触发"HardFault"硬件错误。部分特定型号的 SDRAM 可能会占用 0xC0000000 开始的地址。

1.1.6 Cortex-M4 内核寄存器区

  1. 地址范围: 0xE0000000 - 0xFFFFFFFF (512MB)
  2. 作用: 包含 CPU 核心自身的控制、调试和中断系统。
  3. 实际情况:
    1. NVIC(嵌套向量中断控制器): 控制所有中断的优先级和使能。
    2. SysTick: 系统滴答定时器,常用于操作系统的任务调度。
    3. MPU / FPU: 内存保护单元和浮点运算单元的配置寄存器。

1.2 STM32的片上外设FSMC

STM32 中用于扩展外部存储器的片上外设是 FSMC,而 GD32 中对应的外设称为 EXMC。两者功能类似,都可以作为 MCU 访问外部存储器的总线控制器。

EXMC 主要用于驱动外部扩展的静态存储器,例如 SRAM、NOR Flash 和 NAND Flash;部分 GD32 型号的 EXMC 还支持 SDRAM。但是FSMC 本身只支持静态存储器,而 GD32 的 EXMC 在此基础上扩展了对 SDRAM 的支持。

硬件连接方面,外部 SRAM 只需将其地址线、数据线、片选、读/写使能等信号对应连接到 MCU 的 EXMC 引脚即可完成基本连接。配置好 EXMC 后,外部 SRAM 会被映射到 MCU 的地址空间中,程序可以像访问内部 RAM 一样直接读写外部 SRAM。

1.3 FSMC的地址映射

单片机的外扩 RAM 区域(即 FSMC 地址空间)共划分为四个 Bank:Bank1、Bank2、Bank3 和 Bank4 。其中,Bank1 专门用于访问静态存储器(如 SRAM、NOR Flash 等),它又被细分为四个子区域(Region),每个子区域拥有独立的片选信号(NE1~NE4),可以分别映射一片独立的 SRAM 芯片。

例如,工程中需要扩展 4 片 SRAM 时,可以将它们的地址线、数据线以及读写控制线并联到 FSMC 的对应引脚上,然后分别使用 Bank1 的四个片选信号 NE1~NE4 来选中不同的 SRAM 芯片。这样,四片 SRAM 就会被映射到四个不同的地址区间,程序通过访问不同的地址段即可读写对应的芯片

1.4 STM32的FSMC外设应用

一个Bank分为4个区域(Region),每个区域表示着不同的外设。每个外设通过不同的GPIO来选中。

每一个Bank的4个区域

STM32端口 片选哪个Region区 基地址
PD7 NE1 0x60000000
PG9 NE2 0x64000000
PG10 NE3 0x68000000
PG12 NE4 0x6C000000

1.5 STM32操作片上外设(内存映射)

cpp 复制代码
static BYTE g_buffer[1 * 1024] __attribute__((at(0x6C000000), zero_init));

at() : 指定变量存放在绝对地址(外部SRAM起始处)

zero_init: 禁止启动代码在main()之前清零该区域(此时FSMC尚未初始化,

若启动时去写0x6C000000会导致总线错误/硬件错误)

在此,我们只将存储区域开辟了1K的大小。如果你们SRAM芯片的容量大小是1M的,那么这儿就可以写为:1*1024*1024的数组容量。

在程序内部对这块存储区域进行读写操作,就相当于操作外扩的SRAM。或者其他片上外设。

从上图再次验证,我们开辟的存储区域,确实在片上SRAM中。

第2章 工程实践

2.1 头文件

cpp 复制代码
/*************************************************************************
* 
*   Copyright (C), 2017-2037, BPG. Co., Ltd.
* 
*   文件名称: EX_SRAM.h
*   软件模块: 驱动
*   版 本 号: 1.0
*   生成日期: 2026-10-13
*   作    者: lium
*   功    能: 驱动模块头文件
* 
*************************************************************************/
#ifndef __EX_SRAM__
#define __EX_SRAM__

#ifdef __cplusplus
extern "C"{
#endif

#include "Drivers.h"

void EXSRAMDrvInit(void);
void EXSRAMDrvTest(void);

#ifdef __cplusplus
}
#endif

#endif /* __EX_SRAM__ */
/*************************************************************************
* 改动历史纪录:
Revision 1.0, 2026-10-13, lium
describe: 初始创建.
*************************************************************************/

2.2 源文件

cpp 复制代码
/*************************************************************************
*
*   Copyright (C), 1999-2004, . Co., Ltd.
*
*   文件名: EX_SRAM.c
*   所属模块: 驱动
*   版    本: 1.0
*   创建日期: 2026-10-13
*   作    者: lium
*   备    注: 片外SRAM驱动(STM32F103 FSMC)
*
*************************************************************************/
#include "EX_SRAM.h"

/*---------------------------------------------------------------------------
 * 注意: GD32 与 STM32 的存储区编号不同
 *   GD32  EXMC  Bank0 Region0~3 (NE0~NE3), 0x6C000000 = GD32 的 Region3/NE3
 *   STM32 FSMC  Bank1 Region1~4 (NE1~NE4), 0x6C000000 = STM32 的 Region4/NE4
 *   两者片选引脚同为 PG12, 物理连线一致, 只是编号偏移了 1。
 *-------------------------------------------------------------------------*/
#define BANK1_REGION4_ADDR       0x6C000000              /* Bank1 Region4(NE4)起始地址 */
#define EX_SRAM_BUFFER_SIZE      (1 * 1024 * 1024)       /* 外部SRAM容量, 1M字节 */
#define EX_SRAM_BEGIN_ADDR       (BANK1_REGION4_ADDR)
#define EX_SRAM_END_ADDR         (BANK1_REGION4_ADDR + EX_SRAM_BUFFER_SIZE - 1)  /* 0x6C0FFFFF */


/* FSMC SRAM句柄 */
static SRAM_HandleTypeDef s_hsram;

/*---------------------------------------------------------------------------
 * 函数名: EXSRAM_ConfigFSMCGPIO
 * 功  能: 配置FSMC相关GPIO引脚(地址线/数据线/控制线)
 * 参  数: 无
 * 返  回: 无
 *-------------------------------------------------------------------------*/
static void EXSRAM_ConfigFSMCGPIO(void)
{
	GPIO_InitTypeDef gpioInitStruct;

	/* 使能相关GPIO时钟 */
	__HAL_RCC_GPIOD_CLK_ENABLE();
	__HAL_RCC_GPIOE_CLK_ENABLE();
	__HAL_RCC_GPIOF_CLK_ENABLE();
	__HAL_RCC_GPIOG_CLK_ENABLE();

	/* FSMC引脚统一配置为复用推挽输出, 最高速度 */
	gpioInitStruct.Mode  = GPIO_MODE_AF_PP;
	gpioInitStruct.Speed = GPIO_SPEED_FREQ_HIGH;

	/* GPIOF: 地址线 A0~A9 (PF0~PF5, PF12~PF15) */
	gpioInitStruct.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;
	HAL_GPIO_Init(GPIOF, &gpioInitStruct);

	/* GPIOG: 地址线 A10~A15 (PG0~PG5) + 片选 FSMC_NE4 (PG12) */
	gpioInitStruct.Pin = GPIO_PIN_0  | GPIO_PIN_1  | GPIO_PIN_2  | GPIO_PIN_3  |
	                     GPIO_PIN_4  | GPIO_PIN_5  | GPIO_PIN_12;
	HAL_GPIO_Init(GPIOG, &gpioInitStruct);

	/* GPIOD: 地址线 A16~A18 (PD11~PD13) + 数据线 D0~D3/D13~D15
	 *        + 读/写信号 FSMC_NOE(PD4) / FSMC_NWE(PD5) */
	gpioInitStruct.Pin = GPIO_PIN_0  | GPIO_PIN_1  | GPIO_PIN_4  | GPIO_PIN_5  |
	                     GPIO_PIN_8  | GPIO_PIN_9  | GPIO_PIN_10 | GPIO_PIN_11 |
	                     GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15;
	HAL_GPIO_Init(GPIOD, &gpioInitStruct);

	/* GPIOE: 数据线 D4~D12 (PE7~PE15) + 字节使能 FSMC_NBL0(PE0)/FSMC_NBL1(PE1) */
	gpioInitStruct.Pin = GPIO_PIN_0  | GPIO_PIN_1  | 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;
	HAL_GPIO_Init(GPIOE, &gpioInitStruct);
}

/*---------------------------------------------------------------------------
 * 函数名: EXSRAM_ConfigBank1Region4
 * 功  能: 配置并启用FSMC Bank1 Region4(NE4, 0x6C000000)
 * 参  数: 无
 * 返  回: 无
 *-------------------------------------------------------------------------*/
static void EXSRAM_ConfigBank1Region4(void)
{
	FSMC_NORSRAM_TimingTypeDef timing;
	FSMC_NORSRAM_InitTypeDef   init;

	/* 使能FSMC时钟 */
	__HAL_RCC_FSMC_CLK_ENABLE();

	/* 异步SRAM读写时序(模式A)
	 * 注意: 数值n对应(n+1)个HCLK周期, 需按外部SRAM数据手册与HCLK频率调整 */
	timing.AddressSetupTime       = 2;    /* 地址建立时间 */
	timing.AddressHoldTime        = 2;    /* 地址保持时间 */
	timing.DataSetupTime          = 4;    /* 数据建立时间 */
	timing.BusTurnAroundDuration  = 0;    /* 总线周转时间(仅复用模式使用) */
	timing.CLKDivision            = 0;    /* 同步时钟分频(异步模式无效) */
	timing.DataLatency            = 0;    /* 同步数据延迟(异步模式无效) */
	timing.AccessMode             = FSMC_ACCESS_MODE_A;

	/* Bank1 Region4(NE4)配置 */
	init.NSBank             = FSMC_NORSRAM_BANK4;              /* 使用Region4 */
	init.DataAddressMux     = FSMC_DATA_ADDRESS_MUX_DISABLE;   /* 地址/数据不复用 */
	init.MemoryType         = FSMC_MEMORY_TYPE_SRAM;           /* 存储器类型: SRAM */
	init.MemoryDataWidth    = FSMC_NORSRAM_MEM_BUS_WIDTH_16;   /* 数据宽度: 16位 */
	init.BurstAccessMode    = FSMC_BURST_ACCESS_MODE_DISABLE;  /* 禁用突发访问 */
	init.WaitSignalPolarity = FSMC_WAIT_SIGNAL_POLARITY_LOW;   /* 等待信号低电平有效 */
	init.WrapMode           = FSMC_WRAP_MODE_DISABLE;          /* 禁用回绕突发 */
	init.WaitSignalActive   = FSMC_WAIT_TIMING_BEFORE_WS;      /* 等待信号时序 */
	init.WriteOperation     = FSMC_WRITE_OPERATION_ENABLE;     /* 使能写操作 */
	init.WaitSignal         = FSMC_WAIT_SIGNAL_DISABLE;        /* 禁用等待信号 */
	init.ExtendedMode       = FSMC_EXTENDED_MODE_DISABLE;      /* 禁用扩展模式 */
	init.AsynchronousWait   = FSMC_ASYNCHRONOUS_WAIT_DISABLE;  /* 禁用异步等待 */
	init.WriteBurst         = FSMC_WRITE_BURST_DISABLE;        /* 禁用写突发 */
	init.PageSize           = FSMC_PAGE_SIZE_NONE;             /* 页大小: 无 */

	/* FSMC控制寄存器基地址 */
	s_hsram.Instance = FSMC_NORSRAM_DEVICE;
	s_hsram.Extended = FSMC_NORSRAM_EXTENDED_DEVICE;

	/* 初始化并启用Bank1 Region4 */
	s_hsram.Init = init;
	HAL_SRAM_Init(&s_hsram, &timing, &timing);
}

/*---------------------------------------------------------------------------
 * 函数名: EXSRAMDrvInit
 * 功  能: 外部SRAM初始化
 * 参  数: 无
 * 返  回: 无
 *-------------------------------------------------------------------------*/
void EXSRAMDrvInit(void)
{
	EXSRAM_ConfigFSMCGPIO();
	EXSRAM_ConfigBank1Region4();
}


/****************************************************/
/*                  测试代码                        */
/****************************************************/
#define __DEBUG__
#ifdef __DEBUG__

/* 测试缓冲区大小(字节) */
#define BUFFER_SIZE              1024

/* 
 * 定义一个变量g_buffer;将其放置在外扩的SRAM区域上
 * 
 * 
 * 外部SRAM测试缓冲区
 * at()     : 指定变量存放在绝对地址(外部SRAM起始处)
 * zero_init: 禁止启动代码在main()之前清零该区域(此时FSMC尚未初始化,
 *            若启动时去写0x6C000000会导致总线错误/硬件错误) */
static BYTE g_buffer[BUFFER_SIZE] __attribute__((at(EX_SRAM_BEGIN_ADDR), zero_init));



/*---------------------------------------------------------------------------
 * 函数名: EXSRAMDrvTest
 * 功  能: 外部SRAM读写自检, 写入/读回数据均通过串口打印
 * 参  数: 无
 * 返  回: 无
 *-------------------------------------------------------------------------*/
void EXSRAMDrvTest(void)
{
	WORD i;

	printf("********************************\r\n");
	printf("********外部SRAM测试开始********\r\n");
	printf("********************************\r\n");

	/* 写入测试数据并打印 */
	printf("写入数据:\r\n");
	for (i = 0; i < BUFFER_SIZE; i++)
	{
		g_buffer[i] = (BYTE)i;
		printf("%02X ", g_buffer[i]);
		if ((i % 16) == 15)
		{
			printf("\r\n");
		}
	}
	if ((BUFFER_SIZE % 16) != 0)
	{
		printf("\r\n");
	}

	/* 读回并打印 */
	printf("读回数据:\r\n");
	for (i = 0; i < BUFFER_SIZE; i++)
	{
		printf("%02X ", g_buffer[i]);
		if ((i % 16) == 15)
		{
			printf("\r\n");
		}
	}
	if ((BUFFER_SIZE % 16) != 0)
	{
		printf("\r\n");
	}

	/* 读回校验 */
	for (i = 0; i < BUFFER_SIZE; i++)
	{
		if (g_buffer[i] != (BYTE)i)
		{
			printf("外部SRAM测试失败: 地址0x%X 写入0x%02X 读回0x%02X\r\n",
			       (unsigned int)(EX_SRAM_BEGIN_ADDR + i),
			       (unsigned int)(i & 0xFF),
			       (unsigned int)g_buffer[i]);
			return;
		}
	}

	printf("**************************************************\r\n");
	printf("********外部SRAM测试通过, 外部SRAM测试结束********\r\n");
	printf("**************************************************\r\n");
}
#endif /* __DEBUG__ */

/*************************************************************************
* 修改历史记录:
Revision 1.0, 2026-10-13, lium
describe: 初始版本.
*************************************************************************/

参考文献

FSMC---扩展外部SRAMhttps://doc.embedfire.com/mcu/stm32/f103badao/std/zh/latest/book/FSMC.html

相关推荐
似是燕归来3 小时前
Rtthread OS 功能开发之 socket udp协议程序开发
网络·stm32·网络协议·udp·socket·rtthread
Zw-awa5 小时前
先把轮子转稳,才不会烧心:编码器速度环 PID 怎么接回 PWM
stm32
小李不想当小白5 小时前
USART串口数据包(STM32标准库学习笔记)
经验分享·笔记·stm32·单片机·嵌入式硬件·学习
无敌贵点大王20 小时前
嵌入式软件面试经(自用)
c语言·stm32·单片机·学习·mongodb·面试·职场和发展
创界工坊工作室1 天前
29-基于单片机的万年历设计
stm32·单片机·嵌入式硬件·proteus
zhangzhangkeji1 天前
stm32 HAL 库改写 江协科大的程序
stm32
沐欣工作室_lvyiyi1 天前
基于STM32的社区养老控制系统设计(论文+源码)
stm32·单片机·嵌入式硬件·社区养老控制系统·保定理工学院·华德学院·淮南师范学院
创界工坊工作室1 天前
26-基于51单片机的智能热水器控制系统设计
stm32·单片机·嵌入式硬件·毕业设计·51单片机·proteus
sayang_shao1 天前
用 Rust 开发 STM32H743:从零搭建一个能烧录、能看日志的项目
stm32·单片机·rust