STM32内存地址原理详解及FSMC操作SRAM实战应用
地址编码本质、存储容量换算、映射逻辑关联、工程应用实操 四个维度,详细解析内存地址与数据大小的原理,并结合实际场景说明应用方法:
一、内存地址与数据大小的底层原理
1. 地址编码的物理本质
内存地址本质是 硬件译码电路的 “选择信号”,32 位地址总线(如 Cortex – M3 )能生成 232 个唯一地址(0x00000000 – 0xFFFFFFFF ),每个地址对应 1 字节(8 bit ) 存储单元。
bit 与字节的关系:
1 字节(Byte)= 8 比特(bit),内存地址通过 “字节寻址” 管理数据,即使操作 32 位整数(4 字节 ),CPU 也会通过连续 4 个字节地址完成读写。
地址范围与容量换算:
地址区间的大小(容量)= 结束地址 – 起始地址 + 1(因地址从 0 开始)。
示例:0x20020000 - 0x2002FFFF 区间:
容量 = 0x2002FFFF - 0x20020000 + 1 = 0x10000(十进制 65536 )= 64 KB(1 KB = 1024 字节,65536=64×1024 )。
2. 存储映射的 “多空间关联” 逻辑
表格中 “主 Flash”“嵌入式 SRAM”“系统存储器”“FSMC” 的映射,本质是 同一物理地址空间的 “别名映射”(Remap),核心目的:
FSMC 重映射 )。3. 64 KB 容量的数学验证(以 0x20020000 - 0x2002FFFF 为例 )
十进制计算:
起始地址:0x20020000 = 2×167+0×166+0×165+2×164+0+0+0+0 = 537,395,200
结束地址:0x2002FFFF = 2×167+0×166+0×165+2×164+15×163+15×162+15×16+15 = 537,460,735
容量 = 537,460,735−537,395,200+1=65536 字节 = 64 KB(65536=64×1024 )。
十六进制简化计算:
地址区间长度 = 结束地址 – 起始地址 + 1 = 0x2002FFFF - 0x20020000 + 1 = 0x10000(十六进制 )。
因 0x10000=216 = 65536 字节 = 64 KB(65536÷1024=64 )。
二、工程应用方法(结合 STM32 FSMC 场景 )
1. 地址区间与存储容量的 “硬件配置”
以 STM32 FSMC 扩展片外 SRAM 为例,需通过 寄存器配置地址映射,确保 CPU 访问 0x60000000 开始的区间时,实际操作片外 SRAM:
FSMC 初始化代码(HAL 库示例 ):
FSMC_NORSRAM_TimingTypeDef Timing = {0};
Timing.AddressSetupTime = 15; // 地址建立时间(HCLK 周期数 )
Timing.AddressHoldTime = 15; // 地址保持时间
Timing.DataSetupTime = 255; // 数据建立时间
FSMC_NORSRAM_InitTypeDef Init = {0};
Init.NSBank = FSMC_NORSRAM_BANK1; // 选择 BANK1
Init.DataAddressMux = FSMC_DATA_ADDRESS_MUX_DISABLE;
Init.MemoryType = FSMC_MEMORY_TYPE_SRAM;
Init.MemoryDataWidth = FSMC_MEMORY_DATA_WIDTH_8BIT; // 8 位数据宽度
Init.BurstAccessMode = FSMC_BURST_ACCESS_MODE_DISABLE;
Init.WaitSignalPolarity = FSMC_WAIT_SIGNAL_POLARITY_LOW;
// ... 其他配置
HAL_FSMC_NORSRAM_Init(&hsram1, &Timing, &Timing);
地址映射逻辑:
配置后,访问 0x60000000 - 0x6000FFFF(64 KB )地址时,FSMC 会将请求路由到片外 SRAM,实现 “内存地址 = 片外 SRAM 物理地址”。
2. 多映射空间的 “启动模式切换”
表格中 “主 Flash”“嵌入式 SRAM” 的同地址映射,支持 系统启动模式切换,通过修改 SYSCFG 寄存器(如 STM32 )实现:
从 SRAM 启动配置:
__HAL_RCC_SYSCFG_CLK_ENABLE(); // 使能 SYSCFG 时钟
SYSCFG->MEMRMP |= SYSCFG_MEMRMP_SWP_FMC; // 切换映射:FSMC 重映射到 SRAM
应用场景:
程序升级(IAP )时,先将新固件下载到 SRAM,切换映射后从 SRAM 启动运行,实现 “边运行边升级”。
3. 地址与数据的 “精准操作”(以位操作为例 )
内存地址可精确到 “位(bit)”,通过 位掩码(Bit Mask ) 操作字节内的单个 bit,示例:
设置 0x20020000 地址的第 3 位为 1:
volatile uint8_t *addr = (volatile uint8_t *)0x20020000;
*addr |= (1 << 3); // 位掩码操作:第 3 位置 1
工程价值:
控制硬件寄存器的单个 bit(如 GPIO 输出、外设使能 ),无需操作整个字节,减少冗余读写。
4. 大容量存储的 “地址分段管理”
若需访问超过 64 KB 的空间(如 1 MB Flash ),需 分段映射 + 指针跳转,示例:
遍历 1 MB Flash(地址 0x08000000 - 0x080FFFFF ):
volatile uint8_t *flash_addr = (volatile uint8_t *)0x08000000;
for (uint32_t i = 0; i < 1024 * 1024; i++) { // 遍历 1 MB
uint8_t data = flash_addr[i]; // 逐字节读取
// 处理数据...
}
优化技巧:
使用 32 位指针 批量读写(如 uint32_t *addr = (uint32_t *)0x08000000; ),减少循环次数,提升效率。
三、典型场景实战(地址计算 + 映射切换 )
场景 1:FSMC 扩展 SRAM 读写测试
需求:通过 FSMC 扩展 64 KB 片外 SRAM,验证地址 0x60000000 - 0x6000FFFF 的读写。
步骤:
- 硬件连接:SRAM 芯片(如 IS62WV51216 )的地址线、数据线、控制线连接到 STM32 FSMC 引脚。
- FSMC 初始化:配置时序和映射(如上述代码 )。
- 读写测试:
volatile uint8_t *sram = (volatile uint8_t *)0x60000000; sram[0] = 0x55; // 写入数据 if (sram[0] == 0x55) { // 读取验证 // 读写正常 } else { // 映射或硬件故障 }
场景 2:启动模式切换(从 SRAM 启动 )
需求:系统默认从 Flash 启动,通过按键触发切换到 SRAM 启动。
步骤:
- 固件准备:将测试程序编译为二进制,下载到 SRAM 地址(如
0x20000000)。 - 映射切换:
void switch_to_sram(void) { __HAL_RCC_SYSCFG_CLK_ENABLE(); SYSCFG->MEMRMP = SYSCFG_MEMRMP_SWP_FMC; // 切换到 SRAM 映射 NVIC_SystemReset(); // 复位系统,从新映射启动 } - 按键触发:按键中断中调用
switch_to_sram(),系统重启后从 SRAM 运行。
四、避坑指南(地址与容量常见问题 )
1. 地址越界访问
0x60010000 但 SRAM 仅 64 KB )。ASSERT 校验地址:
#define SRAM_END 0x6000FFFF
void write_sram(uint32_t addr, uint8_t data) {
assert_param(addr <= SRAM_END); // 地址校验
volatile uint8_t *p = (volatile uint8_t *)addr;
*p = data;
}
2. 多映射空间冲突
__DSB(); // 数据同步屏障,确保映射生效
__ISB(); // 指令同步屏障,刷新指令缓存
3. 容量换算错误
#define SRAM_SIZE 65536 // 64 KB = 65536 字节
volatile uint8_t sram_buf[SRAM_SIZE] __attribute__((at(0x60000000)));
通过理解 “地址编码本质 + 映射逻辑 + 工程配置”,可精准控制内存访问,适配多存储场景(如片内 Flash、片外 SRAM、FSMC 扩展 ),解决硬件兼容、启动切换、数据读写等核心问题。
作者:东方少爷