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

FMC STM32H7 SDRAM

如何无痛使用片外SDRAM?

stm32 已经成功初始化了 STM32H7 上的外部 SDRAM(32MB) 如何在开发中无痛使用SDRAM 使它像普通 RAM 一样“自然地”使用?

[todo] 重要

MMT(Memory Management Tool) of STM32CubeMx

The Memory Management Tool (MMT) displays the memory map and defines memory attributes applied in user projects opened/created in STM32CubeMX. The tool is located in the “Tools” tab. It allows the user to declare memory regions at application level (referred to as “application region” or AppReg in this document). The hardware constraints related to TrustZone®, Memory Protection Unit and the memory granularity are handled by MMT and made transparent to the user, so that the focus can be put on the memory regions at the application level. A linker file is generated according to the application regions declared and configured by the user. The MMT key features are:

  • Memory map display
  • Application regions management
  • Linker file generation

MMT interacts with peripherals starting from the moment the user enters its interface:

  • Checks their settings
  • Updates other peripherals involved in memory map configuration

The peripherals are updated only when the first toggle button is ON. 

Apply periph.png


MMT updates the linker scripts only when the second toggle button is ON. 

Apply linker.png

STM32H7 FMC SDRAM 配置

嵌入式STM32上设置好外部SDRAM并不难。

// code verified OK STM32H743IIT6 - W9825G6KH-6I - 32MB
#include "main.h"
#include "SDRAM_W9825G6KH.h"   //externel sdram memory devicestatic void FMC_SDRAM_ClockInit(void);
static void FMC_SDRAM_GPIOInit(void);
void SDRAM_Init(void);/*
** 功能:FMC的SDRAM控制器初始化 + 外部SDRAM存储设备初始化
** 配置:1. 时钟源来自PLL2也需要适配。GPIO引脚也需要适配。
**       2. 初始化SDRAM控制器+外设时序初始化
** 注意:注意栈溢出。
*/
void SDRAM_Init(void)
{/*** 第一步:配置SDRAM控制器的时钟源*/FMC_SDRAM_ClockInit();/*** 第二步:配置SDRAM控制器与外部存储设备的引脚*/FMC_SDRAM_GPIOInit();/*** 第三步:FMC SDRAM 控制器初始化 -----------------------------*/FMC_SDRAM_TimingTypeDef  SDRAM_Timing = {0};SDRAM_HandleTypeDef      hsdram = {0};/* SDRAM device configuration */hsdram.Instance                = FMC_SDRAM_DEVICE;             /* SDRAM在BANK5,6 */hsdram.Init.SDBank             = FMC_SDRAM_BANK1;              /* 第一个SDRAM BANK */hsdram.Init.ColumnBitsNumber   = FMC_SDRAM_COLUMN_BITS_NUM_9;  /* 列数量 */hsdram.Init.RowBitsNumber      = FMC_SDRAM_ROW_BITS_NUM_13;    /* 行数量 */hsdram.Init.MemoryDataWidth    = FMC_SDRAM_MEM_BUS_WIDTH_16;   /* 数据宽度为16位 */       hsdram.Init.InternalBankNumber = FMC_SDRAM_INTERN_BANKS_NUM_4; /* 一共4个BANK */hsdram.Init.CASLatency         = FMC_SDRAM_CAS_LATENCY_2;      /* CAS延迟为2个周期 */hsdram.Init.WriteProtection    = FMC_SDRAM_WRITE_PROTECTION_DISABLE;  /* 失能写保护 */hsdram.Init.SDClockPeriod      = FMC_SDRAM_CLOCK_PERIOD_2;     /* SDRAM时钟=fmc_ker_ck/2=220M/2=110M=9.1ns */hsdram.Init.ReadBurst          = FMC_SDRAM_RBURST_ENABLE;      /* 使能突发访问 */hsdram.Init.ReadPipeDelay      = FMC_SDRAM_RPIPE_DELAY_1;      /* 读通道延迟1个fmc_ker_ck时钟周期 *//* Timing configuration for xxxMhz as SDRAM clock frequency */SDRAM_Timing.LoadToActiveDelay    = 2;  /* 加载模式寄存器到激活时间的延迟为2个时钟周期 */SDRAM_Timing.ExitSelfRefreshDelay = 8;  /* 退出自刷新延迟为8个时钟周期 */SDRAM_Timing.SelfRefreshTime      = 7;  /* 自刷新时间为7个时钟周期 */SDRAM_Timing.RowCycleDelay        = 7;  /* 行循环延迟为7个时钟周期 */SDRAM_Timing.WriteRecoveryTime    = 2;  /* 恢复延迟为2个时钟周期 */SDRAM_Timing.RPDelay              = 2;  /* 行预充电延迟为2个时钟周期 */SDRAM_Timing.RCDDelay             = 2;  /* 行到列延迟为2个时钟周期 */if(HAL_SDRAM_Init(&hsdram, &SDRAM_Timing) != HAL_OK){ Error_Handler();}/* Program the SDRAM external device -----------------------** 第四步:外部 SDRAM 存储设备的时序初始化*/__IO uint32_t tmpmrd =0;FMC_SDRAM_CommandTypeDef command;/* Step 1:  Configure a clock configuration enable command */command.CommandMode       = FMC_SDRAM_CMD_CLK_ENABLE;command.CommandTarget     = FMC_SDRAM_CMD_TARGET_BANK1;command.AutoRefreshNumber = 1;command.ModeRegisterDefinition = 0;HAL_SDRAM_SendCommand(&hsdram, &command, SDRAM_TIMEOUT);/* Step 2: 此时最小需要500us延迟;HAL库粒度为1ms,延迟1ms */HAL_Delay(1);/* Step 3: Configure a PALL (precharge all) command */command.CommandMode   = FMC_SDRAM_CMD_PALL;command.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK1;command.AutoRefreshNumber = 1;command.ModeRegisterDefinition = 0;HAL_SDRAM_SendCommand(&hsdram, &command, SDRAM_TIMEOUT);/* Step 4 : Configure a Auto-Refresh command */command.CommandMode   = FMC_SDRAM_CMD_AUTOREFRESH_MODE;command.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK1;command.AutoRefreshNumber = 8;command.ModeRegisterDefinition = 0;HAL_SDRAM_SendCommand(&hsdram, &command, SDRAM_TIMEOUT);/* Step 5: Program the external memory mode register */tmpmrd = (uint32_t)SDRAM_MODEREG_BURST_LENGTH_1          |SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL   |SDRAM_MODEREG_CAS_LATENCY_2           |SDRAM_MODEREG_OPERATING_MODE_STANDARD |SDRAM_MODEREG_WRITEBURST_MODE_SINGLE;command.CommandMode   = FMC_SDRAM_CMD_LOAD_MODE;command.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK1;command.AutoRefreshNumber = 1;command.ModeRegisterDefinition = tmpmrd;HAL_SDRAM_SendCommand(&hsdram, &command, SDRAM_TIMEOUT);/* Step 6: Set the refresh rate counter */HAL_SDRAM_ProgramRefreshRate(&hsdram, REFRESH_COUNT); }/* configure PLL2->R as output at 220MHz for FMC */
static void FMC_SDRAM_ClockInit(void)
{RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_FMC;PeriphClkInitStruct.PLL2.PLL2M = 25;PeriphClkInitStruct.PLL2.PLL2N = 440;PeriphClkInitStruct.PLL2.PLL2P = 2;PeriphClkInitStruct.PLL2.PLL2Q = 2;PeriphClkInitStruct.PLL2.PLL2R = 2;  //220MHz PLL2R OutputPeriphClkInitStruct.PLL2.PLL2RGE = RCC_PLL2VCIRANGE_0;PeriphClkInitStruct.PLL2.PLL2VCOSEL = RCC_PLL2VCOWIDE;PeriphClkInitStruct.FmcClockSelection = RCC_FMCCLKSOURCE_PLL2;if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK){Error_Handler();}/* Peripheral clock enable */__HAL_RCC_FMC_CLK_ENABLE(); 
}/*
1. 配置FMC内的SDRAM控制器对外部SDRAM存储设备物理连接引脚
*/
static void FMC_SDRAM_GPIOInit(void)
{GPIO_InitTypeDef GPIO_InitStruct ={0};/* 1. GPIO Ports Clock Enable */;//在MX_GPIO_Init函数里实现的。/** FMC GPIO ConfigurationPF0   ------> FMC_A0PF1   ------> FMC_A1PF2   ------> FMC_A2PF3   ------> FMC_A3PF4   ------> FMC_A4PF5   ------> FMC_A5PC0   ------> FMC_SDNWEPH2   ------> FMC_SDCKE0PH3   ------> FMC_SDNE0PF11  ------> FMC_SDNRASPF12  ------> FMC_A6PF13  ------> FMC_A7PF14  ------> FMC_A8PF15  ------> FMC_A9PG0   ------> FMC_A10PG1   ------> FMC_A11PE7   ------> FMC_D4PE8   ------> FMC_D5PE9   ------> FMC_D6PE10  ------> FMC_D7PE11  ------> FMC_D8PE12  ------> FMC_D9PE13  ------> FMC_D10PE14  ------> FMC_D11PE15  ------> FMC_D12PD8   ------> FMC_D13PD9   ------> FMC_D14PD10  ------> FMC_D15PD14  ------> FMC_D0PD15  ------> FMC_D1PG2   ------> FMC_A12PG4   ------> FMC_BA0PG5   ------> FMC_BA1PG8   ------> FMC_SDCLKPD0   ------> FMC_D2PD1   ------> FMC_D3PG15  ------> FMC_SDNCASPE0   ------> FMC_NBL0PE1   ------> FMC_NBL1*/GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_11|GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15;GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;GPIO_InitStruct.Alternate = GPIO_AF12_FMC;HAL_GPIO_Init(GPIOF, &GPIO_InitStruct);GPIO_InitStruct.Pin = GPIO_PIN_0;GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;GPIO_InitStruct.Alternate = GPIO_AF12_FMC;HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);GPIO_InitStruct.Pin = GPIO_PIN_2|GPIO_PIN_3;GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;GPIO_InitStruct.Alternate = GPIO_AF12_FMC;HAL_GPIO_Init(GPIOH, &GPIO_InitStruct);GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_8|GPIO_PIN_15;GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;GPIO_InitStruct.Alternate = GPIO_AF12_FMC;HAL_GPIO_Init(GPIOG, &GPIO_InitStruct);GPIO_InitStruct.Pin = GPIO_PIN_7|GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15|GPIO_PIN_0|GPIO_PIN_1;GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;GPIO_InitStruct.Alternate = GPIO_AF12_FMC;HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_14|GPIO_PIN_15|GPIO_PIN_0|GPIO_PIN_1;GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;GPIO_InitStruct.Alternate = GPIO_AF12_FMC;HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);}

Flexible memory controller (FMC)

stm32h743iit6 RM0433

The flexible memory controller (FMC) includes three memory controllers:

  • The NOR/PSRAM memory controller

  • The NAND memory controller

  • The Synchronous DRAM (SDRAM/Mobile LPSDR SDRAM) controller

The FMC functional block makes the interface with: synchronous and asynchronous static memories, SDRAM memories, and NAND flash memory. Its main purposes are: • to translate AXI transactions into the appropriate external device protocol • to meet the access time requirements of the external memory devices

关于静态存储器、动态存储器、Nand Flash

所有外部存储器都与FMC控制器共享地址、数据和控制信号。 每个外部设备都通过唯一的片选信号进行访问。FMC 每次只能对一个外部设备执行一次访问。

FMC 的主要特性如下:

• 与静态存储器映射设备接口,包括: – 静态随机存取存储器 (SRAM) – NOR 闪存/OneNAND 闪存 – PSRAM(4 个存储体) – 带有 ECC 硬件的 NAND 闪存,可校验高达 8 KB 的数据 • 与同步 DRAM(SDRAM/Mobile LPSDR SDRAM)存储器接口 • 支持突发模式,可更快地访问同步设备,例如 NOR 闪存、PSRAM 和 SDRAM • 可编程连续时钟输出,用于异步和同步访问 • 8、16 或 32 位宽数据总线 • 每个存储体均具有独立的片选控制 • 每个存储体均具有独立的配置 • 写使能和字节通道选择输出,可用于 PSRAM、SRAM 和 SDRAM 设备 • 外部异步等待控制 • 16 x 32 位深度的写 FIFO 写 FIFO 适用于所有存储器控制器,包括: – 写数据 FIFO,存储待写入存储器的数据 – 写地址 FIFO,用于存储地址(最多 28 位)加上数据大小(最多 2 位)。在突发模式下工作时,仅存储起始地址, 除非跨越页边界(对于 PSRAM 和 SDRAM)。在这种情况下,突发会被拆分成两个 FIFO 条目。 • 为 SDRAM 控制器提供 6 x64 位深度(6 x14 位地址标记)的可缓存读取 FIFO。

启动时,FMC 引脚必须由用户应用程序配置。应用程序未使用的 FMC I/O 引脚可用于其他用途。更详细地讲,

在系统启动时就需要根据外部设备类型和相关特性设置好FMC寄存器参数,并且直到系统再次复位或上电前不能再次修改FMC绝大多数的寄存器参数,除了极少部分on-the-fly特性的FMC寄存器比特位可以被再次修改,他们是:

  • FMC_PCR 寄存器中的 ECCEN 和 PBKEN 位

  • FMC_SR 寄存器中的 IFS、IRS 和 ILS 位

  • FMC_SDCMR 寄存器中的 MODE[2:0]、CTB1/CTB2、NRFS 和 MRD 位

  • FMC_SDRTR 寄存器中的 REIE 和 CRE 位。

当FMC使能时,需要按照如下流程去修改FMC的参数:

  1. 首先禁用 FMC,以防止在修改寄存器期间进一步访问任何内存控制器。

  2. 更新所有必需的配置。

  3. 再次启用 FMC。

使用 SDRAM 控制器时,如果在初始化阶段后需要修改 SDCLK 时钟比或刷新率,则必须遵循以下步骤。

  1. 将 SDRAM 设备置于自刷新模式。

  2. 通过复位 FMC_BCR1 寄存器中的 FMCEN 位来禁用 FMC。

  3. 更新所需参数。

  4. 一旦所有参数更新完成后,立即启用 FMC。

  5. 最后,发送“时钟配置启用”命令退出自刷新模式。

FMC 内部模块分析

FMC由以下几个内部硬件电路模块组成:

  • NOR Flash/PSRAM/SRAM 控制器;

  • NAND Flash 控制器;

  • SDRAM 控制器;

  • AXI 总线接口;

  • APB 总线接口(including the FMC configuration registers);内部模块框图如下:

image-20250607160942667

FMC 内部信号

image-20250607161312838

AHB interface

内部 CPU 核心通过 AHB 从机接口访问 FMC 寄存器。

AHB 总线时钟为访问 FMC 寄存器提供参考时钟。

AXI interface

内部 CPU 核心和其他总线上的主设备通过 AXI 从机接口访问外部存储设备。

AXI总线上的业务数据,会被转换成外部设备协议。由于 AXI 数据总线为 64 位宽,AXI 业务数据可能会根据访问数据的大小被拆分成多个连续的 32 位、16 位或 8 位访问。FMC 片选 (FMC_NEx) 不会在连续的访问之间切换。

当以下条件被触发后,FMC就会产生一个AXI从机错误:

  • 读取或写入未启用的 FMC 存储区(存储区 1 至 4)。

  • 在 FMC_BCRx 寄存器中的 FACCEN 位复位时读取或写入 NOR 闪存存储区。

  • 写入具有写保护的 SDRAM 存储区(FMC_SDCRx 寄存器中的 WP 位被置位)。

  • 超出 SDRAM 地址范围(访问保留地址范围)。

  • 在 SDRAM 存储区尚未初始化时尝试对其进行读/写访问。

当 BMAP[1:0] 配置的 FMC bank 基地址不支持 ADDR[31:28] 里的地址,FMC 便会产生一个 AXI 解码错误。

The kernel clock for the FMC is the asynchronous fmc_ker_ck clock (refer to Section 8: Reset and Clock Control (RCC) for fmc_ker_ck clock source selection).

AXI支持的存储和业务类型

通用规则

AXI总线上的请求业务数据尺寸可以是8/16/32/64位,然而接收访问的外部存储设备必须具有固定的数据宽度。

单词学习:whereas :compared with the fact that; but: 意思是:即使前面描述的是客观事实,但是xxxx

比如,You eat a huge plate of food for lunch, whereas I have just a sandwich. 你已然吃了很多午餐了,但我仅仅吃了一个三明治。

只有当AXI业务数据宽度与外部设备数据宽度一致时,才能发挥AXI总线数据传输的最大性能。

当AXI业务数据宽度与外部设备的数据宽度不一致时:

  1. AXI业务数据宽度大于设备数据宽度时,

    • 读写业务:FMC会将AXI业务数据宽度分割成较小的连续访问,以符合外部设备数据宽度。

  2. AXI业务数据宽度小于外部设备数据宽度且外部设备(SRAM, PSRAM, SDRAM)支持字节选择时,

    • 写业务:FMC 使用字节通道信号管理事务。

    • 读业务:FMC 根据外部设备的数据宽度返回所有字节。无用的字节将被系统丢弃。

  3. AXI业务数据宽度小于外部设备数据宽度且外部设备 (NOR and NAND flash memories)不支持字节选择时,

    • 写业务:FMC 写入一些不相关的字节,可能会损坏外部设备。

    • 读业务:FMC 根据外部设备的数据宽度返回所有字节。无用的字节将被系统丢弃。

注意:地址对齐

  1. FMC 不支持地址未对齐的读取业务请求(例如,从奇数地址开始的半字)。

  2. FMC 会根据外部设备是否支持字节选择特性来决定是否支持地址未对齐的写入事务。 如果外部设备不支持字节选择(NOR 和 NAND 闪存)特性,则FMC将不会支持窄写入事务和/或未对齐的写入事务,因为 FMC 会写入不相关的字节并损坏外部设备。

对 NOR 闪存/PSRAM 和 SDRAM 的封装支持

同步存储器必须配置为未定义长度的线性突发模式,因为并非所有主设备都能支持数据经过封装的业务。 如果主设备生成封装事务: • 读取操作将被拆分为两个线性突发事务。 • 如果启用了写入 FIFO,则写入操作将被拆分为两个线性突发事务;如果禁用了写入 FIFO,则写入操作将被拆分为多个线性突发事务。

外部设备地址的映射

从FMC的视角去看,外部存储设备被划分为数个256M字节大小的块区(bank)。

Bank1: NOR/PSRAM/SRAM,(4x64MB)可用于高达4个外部 NOR flash 或者 PSRAM 存储设备。此区块1被划分为4个 NOR/PSRAM 子存储区块,且支持子区块片选特性:

– Bank 1 - NOR/PSRAM 1 – Bank 1 - NOR/PSRAM 2 – Bank 1 - NOR/PSRAM 3 – Bank 1 - NOR/PSRAM 4

Bank2: SDRAM Bank1,(4x64MB)供外部 SDRAM 存储设备使用。SDRAM bank 1 or SDRAM bank 2 depending on BMAP bits configuration。

Bank3: NAND Flash, (4x64MB)用于外部 NAND flash 设备寻址。此区块的MPU存储属性必须配置成 software to Device。

Bank4: Not used ,(4x64MB)保留,FMC无法使用。

Bank5: SDRAM Bank1: (4x64MB) 用于 SDRAM 设备寻址(一个设备独占一个bank)。

Bank6: SDRAM Bank2: (4x64MB) 用于 SDRAM 设备寻址(一个设备独占一个bank)。

可以通过 FMC_BCR1 寄存器中的 BMAP[1:0] 位修改 FMC 存储体映射。表 154 显示了将 NOR/PSRAM 存储体与 SDRAM 存储体交换或重新映射 SDRAM 存储体 2 的配置,从而允许以两个不同的地址映射访问 SDRAM 存储体。

image-20250607181050249

习惯上,称一级bank为FMC bank + 数字标号,称二级bank为外部设备bank + 数字标号.

NOR/PSRAM 地址映射

[todo]

NAND flash 地址映射

[todo]

SDRAM 地址映射

[todo]

NOR flash/PSRAM 控制器

[todo]

NAND flash 控制器

[todo]

SDRAM 控制器

SDRAM 控制器主要特性

SDRAM 控制器支持如下主要特性:

  • 支持两个可独立配置 SDRAM banks;

  • 支持 8/16/32 数据总线宽度;

  • 13位数行地址,11位数列地址,4个内部子banks: 4x16Mx32bit (256 MB), 4x16Mx16bit (128 MB), 4x16Mx8bit (64 MB);

  • 支持以字、半字、字节形式访问;

  • SDRAM时钟可以是 fmc_ker_ck/2 or fmc_ker_ck/3;

  • 行和bank边界的自动化管理;

  • banks之间的来回往复访问;

  • 可编程的时序参数;

  • 支持可编程刷新率的自动刷新操作;

  • 自我刷新模式;

  • 掉电模式;

  • 通过软件方式上电初始化SDRAM;

  • 列地址选通延迟等级配置:等级1,等级2,等级3;

  • 可缓存的读 FIFO,深度为 6 行 x32 位(6 x14 位地址标签);

CAS(Column Address Strobe)Latency(列地址选通延迟)是 SDRAM 的核心时序参数,在 STM32H743 的 FMC SDRAM 控制器中,它决定了从发出读命令到数据输出的延迟时钟周期数

SDRAM 外部存储接口信号

最开始,必须配置那些规划被 "FMC SDRAM控制器"用来与“外部SDRAM存储设备”通信的 ”SDRAM 引脚” ,那些余下的 SDRAM 引脚可以另做他用。

image-20250607201413815

SDRAM 控制器功能描述

SDRAM控制器所有的输出(信号、地址、数据)都是在存储时钟(FMC_SDCLK)的下降沿做出改变。

初始化 SDRAM

SDRAM的初始化序列有软件负责管理。如果使用两个存储槽(banks),那么必须通过设置 FMC_SDCMR 寄存器里的命中存储槽Bank1/Bank2的对应的 CTB1 和 CTB2 位同时向Bank1和Bank2发出SDRAM的启动序列

  1. 将外部存储设备的特性写入到 FMC_SDCRx 寄存器内。 SDRAM 时钟频率、RBURST 和 RPIPE 是必须要写入到 FMC_SDCR1 寄存器内的。

  2. 将外部存储设备的时序特性写入到 FMC_SDTRx 寄存器内。TRP和TRC时序参数必须写入到 FMC_SDTR1 寄存器内。

  3. 设置 FMC_SDCMR 寄存器内的 MODE 位为 ‘001’ ,并配置其中的命中的bank位 Target Bank bits (CTB1 and/or CTB2),便开始向外部存储设备输送时钟信号(SDCKE被驱动为高)。

  4. 等待"规定"的延迟时间。典型延迟时间约为 100 μs(有关上电后所需的延迟时间,请参阅 SDRAM 数据手册)。

  5. 将 FMC_SDCMR 寄存器内的模式位设置为 ‘010’ 且配置好目标存储槽对应的比特位(CTB1 and/or CTB2),发出“预充所有”的命令。

  6. 将 MODE 位设置为“011”,并配置目标存储区位(CTB1 和/或 CTB2)以及 FMC_SDCMR 寄存器中的连续自动刷新命令 (NRFS) 数量。有关应发出的自动刷新命令数量,请参阅 SDRAM 数据手册。典型数量为 8。

  7. 配置 MRD 字段,将 MODE 位设置为“100”,并配置 FMC_SDCMR 寄存器中的目标存储区位(CTB1 和/或 CTB2),以发出“加载模式寄存器”命令并对 SDRAM 设备进行编程。具体来说,必须将突发长度 (BL) 设置为“1”,并选择 CAS 延迟。如果两个 SDRAM 存储区的模式寄存器不同,则必须重复此步骤两次,每个存储区各一次,并相应地设置目标存储区位。对于移动 SDRAM 设备,MRD 字段还用于在发出“加载模式寄存器”命令时配置扩展模式寄存器。

  8. 把刷新速率写入 FMC_SDRTR 寄存器内。 刷新率对应于刷新周期之间的延迟。其值必须正确适配外部的 SDRAM 存储设备。

此时,SDRAM 设备已准备好接受命令。如果在 SDRAM 接收访问期间发生系统复位,数据总线可能仍由 SDRAM 设备驱动。因此,在 NOR 闪存/PSRAM/SRAM 或 NAND 闪存控制器发出任何新的访问之前,必须复位紧接着重新初始化 SDRAM 设备。

注意:If two SDRAM devices are connected to the FMC, all the accesses performed at the same time to both devices by the Command Mode register (Load Mode Register command) are issued using the timing parameters configured for SDRAM Bank 1 (TMRD andTRAS timings) in the FMC_SDTR1 register. 如果 FMC 连接了两个 SDRAM 外部存储设备,那么使用命令模式寄存器同时对两个外部存储设备执行的所有访问,将都使用 FMC_SDTR1 寄存器内为SDRAM Bank 1配置的 TMRD andTRAS timings 位设定的时序发出。

SDRAM 控制器的写周期

SDRAM 控制器接受单次和突发写入请求,并将其转换为单次内存访问。在这两种情况下,SDRAM 控制器都会跟踪每个存储体的活动行,以便能够对不同的存储体执行连续的写入访问(多存储体乒乓访问)。

在执行任何写访问之前,必须通过清除 FMC_SDCRx 寄存器中的 WP 位来失能 SDRAM 的 Bank 写保护。

image-20250607215606016

SDRAM 控制器始终检查下一次访问。 • 如果下一次访问位于同一行或另一个活动行,则执行写入操作; • 如果下一次访问的目标是另一行(非活动行),则 SDRAM 控制器生成预充电命令,激活新行并发起写入命令。

SDRAM 控制器的读周期

SDRAM 控制器接受单次和突发读取请求,并将其转换为单次内存访问。在这两种情况下,SDRAM 控制器都会跟踪每个存储体中的活动行,以便能够在不同的存储体中执行连续的读取访问(多存储体乒乓访问)。

相关文章:

FMC STM32H7 SDRAM

如何无痛使用片外SDRAM? stm32 已经成功初始化了 STM32H7 上的外部 SDRAM(32MB) 如何在开发中无痛使用SDRAM 使它像普通 RAM 一样“自然地”使用? [todo] 重要 MMT(Memory Management Tool) of STM32CubeMx The Memory Management Tool (MMT) disp…...

部署DNS从服务器

部署DNS从服务器的目的 DNS域名解析服务中,从服务器可以从主服务器上获得指定的区域数据文件,从而起到备份解析记录与负载均衡的作用,因此通过部署从服务器可以减轻主服务器的负载压力,还可以提升用户的查询效率。 注意&#xf…...

Ubuntu 系统.sh脚本一键部署内网Java服务(组件使用docker镜像,宕机自启动)

#!/bin/bash# 更新系统并安装必要的依赖 sudo apt update sudo apt install -y apt-transport-https ca-certificates curl software-properties-common# 安装 Docker curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - sudo add-apt-repository …...

用 n8n 提取静态网页内容:从 HTTP Request 到 HTML 节点全解析

n8n 的 HTTP Request HTML 节点组合是个实用又高效的工具。这篇文章就带你一步步搞懂如何用它们提取静态网页内容,重点解析 HTML 节点参数和 CSS 选择器,让你轻松上手 。 一、整体流程概览 我们的目标是从静态网页中提取特定内容,流程分两…...

Android Camera Hal中通过Neon指令优化数据拷贝

背景描述: Camera apk普通相机模式录像操作时,一般是同时请求两个流,即预览流和录像流。对于两个流输出图像格式和分辨率相同的情况下,是不是可以通过一个流拷贝得到另一个流的数据,进而节省掉一个Sensor输出处理两次…...

C# winform教程(二)----button

一、button的使用方法 主要使用方法几乎都在属性内,我们操作也在这个界面 二、作用 用户点击时触发事件,事件有很多种,可以根据需要选择。 三、常用属性 虽然属性很多,但是常用的并不多 3.常用属性 名称内容含义AutoSize自动调…...

AcWing 3417:砝码称重——位集合

【题目来源】 3417. 砝码称重 - AcWing题库 【题目描述】 你有一架天平和 N 个砝码,这 N 个砝码重量依次是 W1,W2,⋅⋅⋅,WN。 请你计算一共可以称出多少种不同的正整数重量? 注意砝码可以放在天平两边。 【输入格式】 输入的第一行包含一个整数 N。 …...

我认为STM32输入只分为模拟输入 与 数字输入

核心概念解析 模拟输入 (Analog Input) 设计目的:直接连接模拟信号(如ADC采集电压、温度传感器输出) 硬件行为: ✅ 断开内部数字电路(施密特触发器禁用) ✅ 信号直通模拟外设(如ADC、运放&…...

Python编码格式化之PEP8编码规范

文章目录 概要PEP8编码风格py文本组织规范命名规范编码风格 PEP8编码检查工具pylintflake8PyCharm中配置检查工具 PEP8编码格式化工具blackautopep8PyCharm配置格式化工具本地git配置hook 总结 概要 在Python项目开发过程中,代码的可读性和一致性对于项目的长期维护…...

【Zephyr 系列 14】使用 MCUboot 实现 BLE OTA 升级机制:构建安全可靠的固件分发系统

🧠关键词:Zephyr、MCUboot、OTA 升级、BLE DFU、双分区、Bootloader、安全固件管理 📌面向读者:希望基于 Zephyr 为 BLE 设备加入安全 OTA 升级功能的开发者 📊预计字数:5200+ 字 🧭 前言:为什么你需要 OTA? 随着设备部署数量增多与产品生命周期延长,远程升级(…...

K8S认证|CKS题库+答案| 8. 沙箱运行容器 gVisor

目录 8. 沙箱运行容器 gVisor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作: 1)、切换集群 2)、官网找模板 3)、创建 RuntimeClass 4)、 将命名空间为 server 下的 Pod 引用 RuntimeClass 5&#xff09…...

【Redis】数据库与缓存一致性

目录 1、背景2、核心问题3、常见解决方案【1】缓存更新策略[1]旁路缓存模式(Cache-Aside)[2]写穿透模式(Write-Through)[3]写回模式 【2】删除与更新策略[1]先更新数据库再删除缓存[2]先删除缓存再更新数据库 【3】一致性保障机制…...

Selenium4+Python的web自动化测试框架

一、什么是Selenium? Selenium是一个基于浏览器的自动化测试工具,它提供了一种跨平台、跨浏览器的端到端的web自动化解决方案。Selenium主要包括三部分:Selenium IDE、Selenium WebDriver 和Selenium Grid。 Selenium IDE:Firefo…...

【论文解读】MemGPT: 迈向为操作系统的LLM

1st author: Charles Packer paper MemGPT[2310.08560] MemGPT: Towards LLMs as Operating Systems code: letta-ai/letta: Letta (formerly MemGPT) is the stateful agents framework with memory, reasoning, and context management. 这个项目现在已经转化为 Letta &a…...

vb监测Excel两个单元格变化,达到阈值响铃

需求 在Excel中实现监控两个单元格之间的变化范围,当达到某个设定的值的范围内时,实现自动响铃提示。 实现: 首先设置Excel,开启宏、打开开发者工具,点击visual Basic按钮,然后在左侧双击需要监测的shee…...

跨域请求解决方案全解析

跨域请求可以通过多种技术方案实现,核心是绕过浏览器的同源策略限制。以下是主流解决方案及具体实现方式: 一、CORS(跨域资源共享) 最常用的标准化方案,通过服务器设置HTTP响应头实现: Access-Control-Al…...

【前端】vue3性能优化方案

以下是Vue 3性能优化的系统性方案,结合核心优化策略与实用技巧,覆盖渲染、响应式、加载、代码等多个维度: ⚙️ 一、渲染优化 精准控制渲染范围 v-if vs v-show: v-if:条件为假时销毁DOM,适合低频切换场景&…...

node 进程管理工具 pm2 的详细说明 —— 一步一步配置 Ubuntu Server 的 NodeJS 服务器详细实录 7

前言 我以 Ubuntu Server 打造的 NodeJS 服务器为主题的系列文章,经过五篇博客,我们顺利的 安装了 ubuntu server 服务器,并且配置好了 ssh 免密登录服务器,安装好了 服务器常用软件安装, 配置好了 zsh 和 vim 以及 通过 NVM 安装…...

Flask与Celery 项目应用(shared_task使用)

目录 1. 项目概述主要功能技术栈 2. 项目结构3. 环境设置创建虚拟环境并安装依赖主要依赖 4. 应用配置Flask应用初始化 (__init__.py)Celery应用初始化 (make_celery.py) 5. 定义Celery任务 (tasks.py)任务说明 6. 创建API端点 (views.py)API端点说明 7. 前端界面 (index.html)…...

Fetch API 使用详解:Bearer Token 与 localStorage 实践

Fetch API:现代浏览器内置的用于发送 HTTP 请求的 API,Bearer Token:一种基于令牌的身份验证方案,常用于 JWT 认证,localStorage:浏览器提供的持久化存储方案,用于在客户端存储数据。 token是我…...

vue3 vite.config.js 引入bem.scss文件报错

[sass] Can’t find stylesheet to import. ╷ 1 │ use “/bem.scss” as *; │ ^^^^^^^^^^^^^^^^^^^^^^ ╵ src\App.vue 1:1 root stylesheet 分析 我们遇到了一个在Vue3项目中使用Vite时,在vite.config.js中引入bem.scss文件报错的问题。错误信息指出在App.vue…...

二叉树-226.翻转链表-力扣(LeetCode)

一、题目解析 翻转可以理解为树的左右子树交换,从根到叶子节点,但是这里交换的是链接的指针,而不是单纯的交换值,当出现nullptr时,也是可以交换链接的,交换值的话就不行了。 二、算法原理 依旧的递归&…...

HarmonyOS Next 弹窗系列教程(3)

HarmonyOS Next 弹窗系列教程(3) 选择器弹窗 (PickerDialog) 介绍 选择器弹窗通常用于在用户进行某些操作(如点击按钮)时显示特定的信息或选项。让用户可以进行选择提供的固定的内容。 以下内容都属于选择器弹窗: …...

编程笔记---问题小计

编程笔记 qml ProgressBar 为什么valuemodel.progress / 100 在QML中,ProgressBar的value属性用于表示进度条的当前进度值,其范围通常为0到1(或0%到100%)。当使用model.progress / 100来设置value时,这样做的原因是为…...

【docker】Windows安装docker

环境及工具(点击下载) Docker Desktop Installer.exe (windows 环境下运行docker的一款产品) wsl_update_x64 (Linux 内核包) 前期准备 系统要求2: Windows 11:64 位系统&am…...

无人机避障——感知部分(Ubuntu 20.04 复现Vins Fusion跑数据集)胎教级教程

硬件环境:NVIDIA Jeston Orin nx 系统:Ubuntu 20.04 任务:跑通 EuRoC MAV Dataset 数据集 展示结果: 编译Vins Fusion 创建工作空间vins_ws # 创建目录结构 mkdir -p ~/vins_ws/srccd ~/vins_ws/src# 初始化工作空间&#xf…...

人工智能--大型语言模型的存储

好的,我现在需要回答用户关于GGUF文件和safetensors文件后缀的差别的问题。首先,我得先确认这两个文件格式的具体应用场景和它们各自的优缺点。用户可能是在处理大模型时遇到了这两种文件格式,想了解它们的区别以便正确使用。 首先&#xff…...

OD 算法题 B卷【删除字符串中出现次数最少的字符】

文章目录 删除字符串中出现次数最少的字符 删除字符串中出现次数最少的字符 实现删除字符串中出现次数最少的字符,若(最少的)有多个字符出现次数一样,则都删除。输出删除后的字符串,其他字符保持原有顺序;…...

如何安装并使用RustDesk

参考: 搭建 RustDesk Server:打造属于自己的远程控制系统,替代 TeamViewer 和 ToDesk! 向日葵、ToDesk再见!自己动手,自建RustDesk远程服务器真香! 通俗易懂:RustDesk Server的搭…...

机器学习——随机森林算法

随机森林算法是一种强大的树集成算法,比使用单个决策树效果要好得多。 以下是生成树集成的方法:假设有一个大小为m的训练集,然后对于b1到B,所以执行B次,可以使用有放回抽样来创建一个大小为m的训练集。所以如果有10个…...