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),核心目的:

  • 硬件兼容性:让不同存储区域(如片内 Flash、片外 SRAM )可复用地址区间,通过配置寄存器切换映射源。
  • 灵活启动:支持从 Flash、SRAM 或系统存储器启动,只需修改映射关系(如 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 的读写。

    步骤:

    1. 硬件连接:SRAM 芯片(如 IS62WV51216 )的地址线、数据线、控制线连接到 STM32 FSMC 引脚。
    2. FSMC 初始化:配置时序和映射(如上述代码 )。
    3. 读写测试

      volatile uint8_t *sram = (volatile uint8_t *)0x60000000;  
      sram[0] = 0x55; // 写入数据  
      if (sram[0] == 0x55) { // 读取验证  
          // 读写正常  
      } else {  
          // 映射或硬件故障  
      }  
      
    场景 2:启动模式切换(从 SRAM 启动 )

    需求:系统默认从 Flash 启动,通过按键触发切换到 SRAM 启动。

    步骤:

    1. 固件准备:将测试程序编译为二进制,下载到 SRAM 地址(如 0x20000000 )。
    2. 映射切换

      void switch_to_sram(void) {  
          __HAL_RCC_SYSCFG_CLK_ENABLE();  
          SYSCFG->MEMRMP = SYSCFG_MEMRMP_SWP_FMC; // 切换到 SRAM 映射  
          NVIC_SystemReset(); // 复位系统,从新映射启动  
      }  
      
    3. 按键触发:按键中断中调用 switch_to_sram(),系统重启后从 SRAM 运行。

    四、避坑指南(地址与容量常见问题 )

    1. 地址越界访问
  • 现象:程序跑飞、HardFault 异常。
  • 原因:访问地址超出配置的存储区间(如访问 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. 多映射空间冲突
  • 现象:修改 Flash 数据却影响到 SRAM(因地址重映射 )。
  • 原因:未关闭不需要的映射,或切换映射后未刷新缓存。
  • 解决
  • 切换映射后执行内存屏障指令:

    __DSB(); // 数据同步屏障,确保映射生效  
    __ISB(); // 指令同步屏障,刷新指令缓存  
    
  • 3. 容量换算错误
  • 现象:申请 64 KB 内存却溢出(误将 65536 当 65535 )。
  • 原因:地址区间计算错误(忽略 “起始地址从 0 开始” )。
  • 解决
  • 用宏定义容量:

    #define SRAM_SIZE 65536 // 64 KB = 65536 字节  
    volatile uint8_t sram_buf[SRAM_SIZE] __attribute__((at(0x60000000)));  
    

  • 通过理解 “地址编码本质 + 映射逻辑 + 工程配置”,可精准控制内存访问,适配多存储场景(如片内 Flash、片外 SRAM、FSMC 扩展 ),解决硬件兼容、启动切换、数据读写等核心问题。

    作者:东方少爷

    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32内存地址原理详解及FSMC操作SRAM实战应用

    发表回复