当前位置: 首页 > news >正文

FSMC的配置和应用

一、FSMC 简介与工作原理

FSMC(Flexible Static Memory Controller)是 STM32 微控制器中用于与外部静态存储器(如 SRAM、PSRAM、NOR Flash、LCD 等)进行通信的一个外设模块。

1、支持的设备类型:

  • SRAM / PSRAM

  • NOR Flash

  • NAND Flash

  • PC 卡

  • 扩展 I/O 接口设备(如 TFT-LCD 控制器)

2、 工作原理:

FSMC 通过地址线、数据线和控制信号(如 nWE、nOE、nCS、ALE、CLE 等)对外扩展静态设备。其本质是将外设的访问映射到 MCU 的外部存储地址空间,实现类似访问内存的方式来读写外设。

二、典型应用场景

应用方向说明
外接SRAM作为内存扩展
外接LCD模块接带并口的TFT-LCD模块(例如8080协议)
外接Flash用于代码/数据的扩展存储
FPGA通信FSMC也常用于与FPGA的数据交互

三、开发步骤(基于 HAL 库) 

1、引脚配置

FSMC 依赖 GPIO,需要设置对应的引脚模式为 AF(Alternate Function)。常见管脚如:

  • 数据线 D0~D15

  • 地址线 A0~Axx

  • 控制线 NE1~NE4NOENWENADV

2、时钟使能

__HAL_RCC_FSMC_CLK_ENABLE();
__HAL_RCC_GPIOD_CLK_ENABLE();
// 其他GPIO时钟

3、 FSMC 初始化结构体配置

使用 FMC_NORSRAM_TimingTypeDefSRAM_HandleTypeDef

SRAM_HandleTypeDef hsram;
FMC_NORSRAM_TimingTypeDef Timing = {0};hsram.Instance = FSMC_NORSRAM_DEVICE;
hsram.Extended = FSMC_NORSRAM_EXTENDED_DEVICE;hsram.Init.NSBank = FSMC_NORSRAM_BANK1;
hsram.Init.DataAddressMux = FSMC_DATA_ADDRESS_MUX_DISABLE;
hsram.Init.MemoryType = FSMC_MEMORY_TYPE_SRAM;
hsram.Init.MemoryDataWidth = FSMC_NORSRAM_MEM_BUS_WIDTH_16;
hsram.Init.BurstAccessMode = FSMC_BURST_ACCESS_MODE_DISABLE;
hsram.Init.WaitSignalPolarity = FSMC_WAIT_SIGNAL_POLARITY_LOW;
hsram.Init.WrapMode = FSMC_WRAP_MODE_DISABLE;
hsram.Init.WaitSignalActive = FSMC_WAIT_TIMING_BEFORE_WS;
hsram.Init.WriteOperation = FSMC_WRITE_OPERATION_ENABLE;
hsram.Init.WaitSignal = FSMC_WAIT_SIGNAL_DISABLE;
hsram.Init.ExtendedMode = FSMC_EXTENDED_MODE_DISABLE;
hsram.Init.AsynchronousWait = FSMC_ASYNCHRONOUS_WAIT_DISABLE;
hsram.Init.WriteBurst = FSMC_WRITE_BURST_DISABLE;Timing.AddressSetupTime = 2;
Timing.AddressHoldTime = 1;
Timing.DataSetupTime = 5;
Timing.BusTurnAroundDuration = 0;
Timing.CLKDivision = 2;
Timing.DataLatency = 2;
Timing.AccessMode = FSMC_ACCESS_MODE_A;if (HAL_SRAM_Init(&hsram, &Timing, NULL) != HAL_OK)
{Error_Handler();
}

4、FSMC 地址映射

根据 FSMC 的 BANK地址映射,其起始地址为固定值,例如:

BANK地址起始控制引脚
BANK10x60000000NE1
BANK20x64000000NE2
BANK30x68000000NE3
BANK40x6C000000NE4

你可以通过访问 *(volatile uint16_t*)0x60000000 来读写连接的外设。

5、代码示例(操作LCD模块)

#define LCD_BASE_ADDR ((uint32_t)(0x60000000)) // NE1 映射static void LCD_WriteReg(uint16_t reg)
{*(volatile uint16_t*)(LCD_BASE_ADDR) = reg;
}static void LCD_WriteData(uint16_t data)
{*(volatile uint16_t*)(LCD_BASE_ADDR | (1 << 16)) = data; // A16=1 表示数据
}void LCD_Init(void)
{LCD_WriteReg(0x0001); // 假设这是LCD的初始化寄存器LCD_WriteData(0x1234);
}

四、示例

下面是一个 基于 STM32 + FSMC + DMA 访问外部 SRAM(以 IS61LV25616 为例) 的完整示例。此例程使用 HAL 库实现了对外部 SRAM 的初始化、DMA 读写操作。

1、示例目标

  • 通过 FSMC 接口扩展外部 SRAM

  • 使用 DMA 进行高效数据传输(写入和读取)

  • SRAM 芯片示例:IS61LV25616,256K x 16bit = 512KB

2、硬件前提(假设如下):

信号接口说明
数据线PD14PD15 + PE7PE15(D0~D15)
地址线地址线 A0~A17
控制信号NOE, NWE, NE1
总线宽度16-bit
SRAM映射地址0x60000000(BANK1)

3、工程结构

  • main.c: 主函数

  • sram.c/h: FSMC+DMA 初始化 & 操作函数

  • 使用 STM32CubeMX 自动生成 HAL 框架,添加 SRAM 相关内容

Step 1: 配置 STM32CubeMX

  1. 开启 FSMC 外设(有些型号叫 FMC)

  2. 配置为:

    • SRAM

    • Bank1

    • 数据总线宽度:16bit

    • 地址/数据复用:关闭

    • 写操作:启用

  3. 开启 DMA 通道(如 DMA2_Stream0,MemoryToMemory,优先级 High)

  4. 启用对应的 GPIO(PD, PE, PF)

生成代码后在 sram.c/h 中编写以下逻辑。

#ifndef __SRAM_H
#define __SRAM_H#include "stm32f4xx_hal.h"#define SRAM_BANK_ADDR ((uint32_t)0x60000000) // Bank1 -> NE1
#define SRAM_SIZE      (512 * 1024)           // 512KB SRAMextern SRAM_HandleTypeDef hsram1;void SRAM_Init(void);
HAL_StatusTypeDef SRAM_DMA_Write(uint32_t offset, uint8_t *src, uint32_t size);
HAL_StatusTypeDef SRAM_DMA_Read(uint32_t offset, uint8_t *dst, uint32_t size);#endif

sram.c代码

#include "sram.h"SRAM_HandleTypeDef hsram1;
DMA_HandleTypeDef hdma_memtomem;// 初始化 FSMC SRAM + DMA
void SRAM_Init(void)
{FMC_NORSRAM_TimingTypeDef Timing = {0};/*** FSMC Configuration ***/hsram1.Instance = FMC_NORSRAM_DEVICE;hsram1.Extended = FMC_NORSRAM_EXTENDED_DEVICE;hsram1.Init.NSBank = FMC_NORSRAM_BANK1;hsram1.Init.DataAddressMux = FMC_DATA_ADDRESS_MUX_DISABLE;hsram1.Init.MemoryType = FMC_MEMORY_TYPE_SRAM;hsram1.Init.MemoryDataWidth = FMC_NORSRAM_MEM_BUS_WIDTH_16;hsram1.Init.BurstAccessMode = FMC_BURST_ACCESS_MODE_DISABLE;hsram1.Init.WaitSignalPolarity = FMC_WAIT_SIGNAL_POLARITY_LOW;hsram1.Init.WrapMode = FMC_WRAP_MODE_DISABLE;hsram1.Init.WaitSignalActive = FMC_WAIT_TIMING_BEFORE_WS;hsram1.Init.WriteOperation = FMC_WRITE_OPERATION_ENABLE;hsram1.Init.WaitSignal = FMC_WAIT_SIGNAL_DISABLE;hsram1.Init.ExtendedMode = FMC_EXTENDED_MODE_DISABLE;hsram1.Init.AsynchronousWait = FMC_ASYNCHRONOUS_WAIT_DISABLE;hsram1.Init.WriteBurst = FMC_WRITE_BURST_DISABLE;Timing.AddressSetupTime = 2;Timing.AddressHoldTime = 1;Timing.DataSetupTime = 5;Timing.BusTurnAroundDuration = 1;Timing.CLKDivision = 2;Timing.DataLatency = 2;Timing.AccessMode = FMC_ACCESS_MODE_A;if (HAL_SRAM_Init(&hsram1, &Timing, NULL) != HAL_OK){Error_Handler();}/*** DMA Configuration ***/__HAL_RCC_DMA2_CLK_ENABLE();hdma_memtomem.Instance = DMA2_Stream0;hdma_memtomem.Init.Channel = DMA_CHANNEL_0;hdma_memtomem.Init.Direction = DMA_MEMORY_TO_MEMORY;hdma_memtomem.Init.PeriphInc = DMA_PINC_ENABLE;hdma_memtomem.Init.MemInc = DMA_MINC_ENABLE;hdma_memtomem.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;hdma_memtomem.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;hdma_memtomem.Init.Mode = DMA_NORMAL;hdma_memtomem.Init.Priority = DMA_PRIORITY_HIGH;hdma_memtomem.Init.FIFOMode = DMA_FIFOMODE_DISABLE;if (HAL_DMA_Init(&hdma_memtomem) != HAL_OK){Error_Handler();}__HAL_LINKDMA(&hsram1, hdma, hdma_memtomem);
}// 使用 DMA 写入外部 SRAM
HAL_StatusTypeDef SRAM_DMA_Write(uint32_t offset, uint8_t *src, uint32_t size)
{if ((offset + size) > SRAM_SIZE) return HAL_ERROR;uint8_t *dest = (uint8_t *)(SRAM_BANK_ADDR + offset);return HAL_DMA_Start(&hdma_memtomem, (uint32_t)src, (uint32_t)dest, size);
}// 使用 DMA 从外部 SRAM 读取
HAL_StatusTypeDef SRAM_DMA_Read(uint32_t offset, uint8_t *dst, uint32_t size)
{if ((offset + size) > SRAM_SIZE) return HAL_ERROR;uint8_t *src = (uint8_t *)(SRAM_BANK_ADDR + offset);return HAL_DMA_Start(&hdma_memtomem, (uint32_t)src, (uint32_t)dst, size);
}

main.c代码

#include "main.h"
#include "sram.h"
#include <string.h>
#include <stdio.h>uint8_t txBuffer[64];
uint8_t rxBuffer[64];int main(void)
{HAL_Init();SystemClock_Config();SRAM_Init();// 初始化数据for (int i = 0; i < 64; i++) {txBuffer[i] = i;}// 写入SRAMif (SRAM_DMA_Write(0x0000, txBuffer, sizeof(txBuffer)) != HAL_OK) {printf("SRAM DMA Write Failed!\n");}HAL_Delay(10);// 读取SRAMif (SRAM_DMA_Read(0x0000, rxBuffer, sizeof(rxBuffer)) != HAL_OK) {printf("SRAM DMA Read Failed!\n");}HAL_Delay(10);// 校验if (memcmp(txBuffer, rxBuffer, sizeof(txBuffer)) == 0) {printf("SRAM Read/Write OK.\n");} else {printf("SRAM Data Mismatch!\n");}while (1);
}

五、开发注意事项

项目内容
地址线对齐LCD常用 A16 作为数据命令选择信号
时序调整DataSetupTime、AddressSetupTime 需根据实际器件手册配置
数据总线宽度若使用 8 位数据总线,需调整 FSMC_NORSRAM_MEM_BUS_WIDTH_8
DMA 支持FSMC 支持 DMA 访问,加快数据刷新效率
多设备冲突使用多个BANK时注意各设备地址映射不可重叠

六、调试与排查问题技巧

1. 总线不响应

  • 检查 GPIO 是否配置为复用模式

  • 检查 FSMC 时钟是否开启

2. 数据错误或乱码

  • 检查数据总线宽度是否正确(8/16 位)

  • 检查读写时序设置(尤其是 DataSetupTime

  • 检查地址线是否配置正确(A16 通常用于寄存器/数据切换)

3. 无法访问外设

  • 使用示波器抓取 NE, NOE, NWE,是否跳变

  • 使用 volatile 强制访问,避免优化器优化掉访问行为

http://www.xdnf.cn/news/1210465.html

相关文章:

  • SpringBoot集成deepseek
  • Export useForm doesn‘t exist in target module
  • vue3组件通信的几种方法,详解
  • 05动手学深度学习(下)
  • Linux - 权限的理解(深入浅出,详细细微)
  • 书籍推荐算法研究
  • gRPC性能陷阱:低延迟网络下的客户端瓶颈揭秘
  • Spark SQL 数组函数合集:array_agg、array_contains、array_sort…详解
  • Zynq SOC FPGA嵌入式裸机设计和开发教程自学笔记:GPIO扩展与中断控制技术,万字详解!!
  • 【变更性别】
  • TCPDump实战手册:协议/端口/IP过滤与组合分析指南
  • ESP32学习-1.第一个程序helloworld
  • 子数组和 问题汇总
  • FPGA实现SRIO高速接口与DSP交互,FPGA+DSP异构方案,提供3套工程源码和技术支持
  • Linux_库制作与原理浅理解
  • Python高效历史记录管理:保存最后N个元素的完整指南
  • 【CSS】盒子类型
  • 功率场效应晶体管MOSFET关键指标
  • leaflet中绘制轨迹线的大量轨迹点,解决大量 marker 绑定 tooltip 同时显示导致的性能问题
  • 车载刷写架构 --- 刷写思考扩展
  • Redis的持久化策略-AOF和RDB(详细图解)
  • Java面试宝典:MySQL8新特性底层原理
  • Vue2 vs Vue3:核心差异与升级亮点
  • DeepSeek MoE 技术解析:模型架构、通信优化与负载均衡
  • 飞书 —— 多维表格 —— AI生成
  • 系统学习算法:专题十五 哈希表
  • 数据库02 网页html01 day44
  • 抵御酒店管理系统收银终端篡改攻击 API 加密的好处及实现——仙盟创梦IDE
  • 如何创建一个 Solana 钱包?
  • 文件操作与IO流