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.pswp.cn/web/91644.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/91644.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux I/O 系统调用完整对比分析

Linux I/O 系统调用完整对比分析 1. 概述 Linux 提供了丰富的 I/O 系统调用&#xff0c;每种都有其特定的用途和优势。本文将详细分析这些系统调用的特点、使用场景和性能特征。 2. 系统调用详细对比 2.1 基本读写函数 pread/pwrite #include <unistd.h>// 位置指定…

TiDB集群部署

架构&#xff1a; tidb–3台&#xff0c;pd–3台&#xff0c;tikv–3台 8c16g200g 1x2.2x.2x7.124 1x2.2x.2x7.148 1x2.2x.2x7.87 1x2.2x.2x7.93 1x2.2x.2x7.127 1x2.2x.2x7.104 pd-3台 4c8g100g 1x2.2x.2x7.143 1x2.2x.2x7.132 1x2.2x.2x7.91 1、下载安装包 #注&#xff1a;我…

C#中对于List的多种排序方式

在 C# 中给 List<AI> 排序&#xff0c;只要 明确排序规则&#xff08;比如按某个字段、某几个字段、或外部规则&#xff09;&#xff0c;就能用下面几种常见写法。下面全部基于这个示例类&#xff1a;public class AI {public int country; // 国家编号public int pr…

Spring框架中Bean的生命周期:源码解析与最佳实践

第1章&#xff1a;Spring Bean生命周期概述1.1 什么是Spring Bean生命周期&#xff1f;定义&#xff1a;Spring Bean生命周期是指从Bean的创建、初始化、使用到销毁的完整过程&#xff0c;由Spring容器严格管理 。核心思想是Spring容器通过IoC&#xff08;控制反转&#xff09;…

【51单片机6位数码管密码锁】2022-10-15

缘由六位密码器设计连接LED-嵌入式-CSDN问答 矩阵51单片机密码锁,回复:https://bbs.csdn.net/topics/392713242_智者知已应修善业的博客-CSDN博客 #include "REG52.h" unsigned char code smgduan[]{0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x…

‌我的第一个开源项目:跃动的心

还是一个编程初学者时&#xff0c;我怀着激动的心情完成了人生第一个开源项目——一个用HTML5 Canvas制作的动态跳动爱心效果。这个项目虽然简单&#xff0c;却让我深刻体会到了开源分享的快乐和技术创造的魅力。 壹、项目灵感 这个项目的灵感来源于浏览网页时&#xff0c;被各…

技术演进中的开发沉思-53 DELPHI VCL系列:windows的消息(下):TApplication窗体

今天我们梳理下关于TApplication的窗体消息下半部分的内容。前面也说过&#xff0c;在 Delphi 的世界里&#xff0c;TApplication 就像一位经验丰富的总工程师&#xff0c;而主窗体则是它倾注心血打造的核心建筑。如果你第一次在实验室里敲出 Delphi 代码时&#xff0c;屏幕上弹…

cesium FBO(四)自定义相机渲染到Canvas(离屏渲染)

前面几节的例子是将Cesium默认的相机渲染到纹理&#xff08;RTT&#xff09;或Canvas&#xff0c;这片文章讲解如何将自定义的一个camera的画面渲染到Canvas上&#xff0c;有了前面几篇的基础了&#xff0c;也能将自定义的画面渲染纹理、也可以灰度处理&#xff0c;原理是一样的…

双机并联无功环流抑制虚拟阻抗VSG控制【simulink仿真模型实现】

双机并联虚拟同步发电机&#xff08;VSG&#xff09;系统中&#xff0c;因线路阻抗不匹配及参数差异&#xff0c;易引发无功环流。本方案在传统VSG控制基础上&#xff0c;引入自适应虚拟阻抗环节。其核心在于&#xff1a;实时检测两机间无功环流分量&#xff0c;据此动态调节各…

python测试总结

测试题的基础知识点总结 1.循环求和 for循环步长&#xff08;range(2,101,2)&#xff09; while循环条件判断&#xff08;i%20&#xff09; 生成器表达式&#xff08;sum(i for i in range )&#xff09; 所以&#xff1a;sum(range(1,101,2))&#xff08;奇数和&#xff09;和…

识别和分类恶意软件样本的工具YARA

YARA 是一个用于识别和分类恶意软件样本的工具,广泛应用于恶意软件分析、威胁情报、入侵检测等领域。它通过编写规则(YARA Rules)来匹配文件中的特定字符串、十六进制模式、正则表达式等特征。 一、YARA 的基本使用方法 1. 安装 YARA Linux(Ubuntu/Debian) sudo apt-ge…

GaussDB 约束的语法

1 约束的作用约束是作用于数据表中列上的规则&#xff0c;用于限制表中数据的类型。约束的存在保证了数据库中数据的精确性和可靠性。约束有列级和表级之分&#xff0c;列级约束作用于单一的列&#xff0c;而表级约束作用于整张数据表。下面是 GaussDB SQL 中常用的约束。NOT …

SecurityContextHolder 管理安全上下文的核心组件详解

SecurityContextHolder 管理安全上下文的核心组件详解在 Spring Security 中&#xff0c;SecurityContextHolder 是​​安全上下文&#xff08;Security Context&#xff09;的核心存储容器​​&#xff0c;其核心作用是​​在当前线程中保存当前用户的认证信息&#xff08;如用…

c++详解系列(引用指针)

目录 1.什么是引用 2.引用的定义 3.引用的特性 4.引用的使用 4.1引用传参 4.2传引用返回 5.const引用&#xff08;在引用的定义前用const修饰&#xff09; 5.1对于引用 5.2对于指针 6.引用&指针 总结 1.什么是引用 引用就是给变量起别名&#xff0c;一个变量可以…

深度学习loss总结(二)

对于目前深度学习主流任务学习,loss的设置至关重要。下面就不同任务的loss设置进行如下总结: (1)目标检测 2D/3D目标检测中的 Loss(损失函数)是训练模型时优化目标的核心,通常包括位置、类别、尺寸、方向等多个方面。以下是目前 常见的 2D 和 3D 目标检测 Loss 分类与…

【Linux网络】netstat 的 -anptu 各个参数各自表示什么意思?

netstat 是一个网络统计工具&#xff0c;它可以显示网络连接、路由表、接口统计、伪装连接和多播成员资格。在 netstat 命令中&#xff0c;不同的参数可以用来定制输出的内容。 你提到的 -anptu 参数组合各自的功能如下&#xff1a; -a (all): 显示所有活动的连接和监听端口。它…

[硬件电路-115]:模拟电路 - 信号处理电路 - 功能放大器工作分类、工作原理、常见芯片

功能放大器是以特定功能为核心的集成化放大电路&#xff0c;通过将运算放大器与外围电阻、电容等元件集成在单一芯片中&#xff0c;实现标准化、高性能的信号放大功能。其核心优势在于简化设计流程、提升系统稳定性&#xff0c;并针对特定应用场景优化性能参数。以下从定义、分…

双网卡UDP广播通信机制详解

UDP广播通信机制详解 一、通信流程分析 发送阶段 通过Client.Bind(192.168.0.3, 60000)将UDP套接字绑定到指定网卡和端口设置RemoteHost "255.255.255.255"实现全网段广播数据流向&#xff1a;192.168.0.3:60000 → 255.255.255.255:50000 接收阶段 设备响应数据应返…

从遮挡难题到精准测量:激光频率梳技术如何实现深孔 3D 轮廓的 2um 级重复精度?

一、深孔 3D 轮廓测量的遮挡困境深孔结构&#xff08;如航空发动机燃油喷嘴孔、模具冷却孔&#xff09;因孔深大&#xff08;常超 100mm&#xff09;、深径比高&#xff08;&#xff1e;10:1&#xff09;&#xff0c;其 3D 轮廓测量长期受限于光学遮挡难题。传统光学测量技术&a…

.NET 依赖注入(DI)全面解析

文章目录一、依赖注入核心原理1. 控制反转(IoC)与DI关系2. .NET DI核心组件二、服务生命周期1. 三种生命周期类型三、DI容器实现原理1. 服务注册流程2. 服务解析流程四、高级实现方法1. 工厂模式注册2. 泛型服务注册3. 多实现解决方案五、ASP.NET Core中的DI集成1. 控制器注入2…