STM32H7外接SPI FRAM:内存映射、读写函数与链接脚本配置咨询
1. 是否可将FRAM内存映射至系统内存/内存映射外设区域?
FM25L04B是SPI接口的串行存储芯片,无法直接硬件映射到STM32H7的内存/外设地址空间——它不是并行总线连接的内存映射型器件,没有对应的物理地址线直接关联到MCU地址总线。
但可以通过STM32H7的**MMU(内存管理单元)**实现虚拟地址到FRAM的“软映射”:将虚拟地址0x39000000及其后续512字节范围,通过MMU页表配置为需要缺页异常处理的区域;当CPU访问该地址范围时,会触发缺页异常,在异常处理函数中调用已实现的read_fram()/write_fram()完成实际的SPI读写操作,从而实现近乎透明的内存式访问(无需显式调用读写函数)。
如果不使用MMU,也可以通过链接脚本指定自定义内存段,但直接访问该段地址会触发总线错误,必须配合自定义的内存访问封装(比如宏或钩子函数),这种方式无法完全避免显式调用读写逻辑。
2. 读写函数应采用何种签名?
根据FRAM的512字节容量(地址范围0x000~0x1FF),推荐两种实用的函数签名:
单字节读写(适合零散地址操作)
uint8_t read_fram(uint16_t addr); void write_fram(uint16_t addr, uint8_t data);
addr用uint16_t足够覆盖全地址空间(0~511)- 返回值为读取到的单字节数据,写函数直接传入地址和待写入字节
多字节批量读写(适合连续地址的高效操作)
void read_fram_burst(uint16_t start_addr, uint8_t *buf, uint16_t len); void write_fram_burst(uint16_t start_addr, const uint8_t *buf, uint16_t len);
start_addr为起始地址,buf为数据缓冲区,len为操作字节数- 注意调用时需确保
start_addr + len不超过512(即地址不超过0x1FF)
3. 如何用arm-none-eabi-ld通过链接脚本分配起始地址为0x39000000的新内存区域?
修改STM32H7的链接脚本(通常后缀为.ld),添加自定义内存区域和对应段:
步骤1:在MEMORY块中添加FRAM区域定义
在原有FLASH、RAM等内存定义后,加入FRAM的地址和容量:
MEMORY { FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 2048K RAM (xrw) : ORIGIN = 0x24000000, LENGTH = 512K // 添加FRAM区域,起始地址0x39000000,大小512字节 FRAM (xrw) : ORIGIN = 0x39000000, LENGTH = 512 }
步骤2:在SECTIONS块中添加FRAM数据段
在原有段定义(如.text、.data)后,添加.fram_data段,指定其关联到FRAM区域:
SECTIONS { // 原有段定义保留... .fram_data : { . = ALIGN(4); // 按4字节对齐,匹配ARM架构访问特性 *(.fram_data .fram_data.*) // 匹配所有标记为.fram_data的代码段 . = ALIGN(4); } > FRAM // 将该段分配到FRAM内存区域 }
步骤3:在代码中声明FRAM变量
使用__attribute__((section(".fram_data")))和volatile关键字,将变量放入该段:
// 示例:声明一个32位变量存储在FRAM中 __attribute__((section(".fram_data"))) volatile uint32_t fram_config_var;
注意:如果未配置MMU及缺页异常处理,直接访问
fram_config_var会触发总线错误。必须配合MMU的缺页异常逻辑,将该地址的读写操作转发到SPI FRAM的驱动函数,才能实现无需显式调用读写函数的透明访问。
内容的提问来源于stack exchange,提问作者AlexLoss

