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
英文地址,請注明出處:http://en.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…