news 2026/7/19 13:43:21

STM32H733 OCTOSPI 驱动 HyperRAM 全流程:CubeMX 配置 + 内存映射 + 自测试代码

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
STM32H733 OCTOSPI 驱动 HyperRAM 全流程:CubeMX 配置 + 内存映射 + 自测试代码

前言

一、OCTOSPI简介

OCTOSPI是一个映射到专用AHB/AXI层的AHB/AXI从设备。HSPI是一个映射到专用AHB层的从设备。XSPI是一个映射到专用层的AXI从设备。这种映射方式使得OCTOSPI、HSPI和XSPI可以通过内存映射模式被访问,就像访问内部内存一样。

图1:STM32H7关于OCTOSPI总线位置

1.1 接口描述

每个 OCTOSPI 实例均支持单路/双路/四路/八路 SPI 格式;在同一总线上对单路/双路/四路/八路 SPI 进行复用,可通过集成的 Octo-SPI I/O 管理器(OCTOSPIIM)实现。

Octo-SPI接口主要使用以下线路:

  • OCTOSPINCS线路用于芯片选择
  • OCTOSPICLK线路用于时钟
  • OCTOSPI_NCLK用于支持1.8VHyperBus协议
  • OCTOSPI_DQS线路用于内存的数据同步/写屏蔽信号
  • OCTOSPI_IO[0…7]用于数据线

1.2 三种模式

无论使用哪种底层协议,OCTOSPI/HSPI/XSPI都可以在间接模式内存映射模式下工作。
当使用常规命令协议时,OCTOSPI/HSPI/XSPI可以在自动状态轮询模式下工作。
OCTOSPI 可在以下三种模式中的任意一种下运行:

  1. 间接模式
    所有操作均通过 OCTOSPI 寄存器执行;
    间接模式用于以下情况(无论HyperBus或常规命令协议):
    • 读/写/擦除操作
    • 如果AHB或AXI主设备不需要自主访问 OCTOSPI/HSPI/XSPI外设(在内存映射模式下可用)
    • 所有通过OCTOSPI/HSPI/XSPI数据寄存器执行的操作,使用CPU或DMA
    • 配置外部存储设备

  2. 状态轮询模式
    自动状态轮询模式允许硬件完全管理内存状态寄存器的自动轮询。此功能避免了软件开销,也无需执行软件轮询。在匹配时可以生成中断;

  3. 内存映射模式:外部存储器被映射为内存,系统将其视作支持读写操作的内部存储器。
    内存映射模式用于以下情况:
    • 读和写操作
    • 使用外部内存设备就像使用内部内存一样(以便任何AHB/AXI主机可
    以自主访问它)
    • 从外部存储设备执行代码
    在内存映射模式下,系统将外部内存视为内部内存。此模式允许所有AHB/AXI主机像访
    内部内存一样访问外部存储设备。CPU也可以从外部内存执行代码。
    这个也是本文章配置的模式
    在内存映射模式下:
    • OCTOSPI1可寻址空间从0x90000000到0x9FFFFFFF
    • OCTOSPI2可寻址空间从0x70000000到0x7FFFFFFF(b)
    • HSPI可寻址空间从0xA0000000到0xAFFFFFFF
    • XSPI1可寻址空间:从0x90000000到0x9FFFFFFF
    • XSPI2可寻址空间:从0x70000000到0x7FFFFFFF
    • XSPI3可寻址空间:从0x80000000到0x8FFFFFFF
    OCTOSPI 支持两种帧格式,适用于大多数外部串行存储器,例如串行 PSRAM、串行 NAND 和串行 NOR 闪存、Hyper RAM 及 Hyper flash 存储器。

多芯片封装(MCP)可组合上述任一提及的存储器类型,同样受支持:
• 经典帧格式,包含命令、地址、交替字节、空闲周期及数据阶段;
• HyperBus™ 帧格式。

二、HyperBUS简介

  • HYPERBUSTM是一种低信号计数DDR接口,可实现高速读写吞吐量。
  • HyperBus 使用高速 8 位 DDR 接口来处理地址和数据。此外,每个存储器件都有相应的差分时钟、读/写锁存信号和片选。
  • HyperBus 可在同一总线上支持外部闪存和 RAM,并且可配合任何具有 HyperBus 兼容外设接口的微控制器使用。
  • HyperBus 可设置成主/从接口,其中一个主机可以连接至总线上的一个或多个从存储器件。 HyperBus 闪存器件称为 HyperFlash™,HyperBus DRAM 存储器件称为 HyperRAM™。

2.1 HyperBUS逻辑框图

图2 :HyperRAM框图

关于引脚描述

引脚类型描述
CS#主输出,从输入芯片选择。总线事务以高电平到低电平跳变启动,以低电平到高电平跳变终止。主设备为每个从设备配备独立的片选信号(CS#)。
CK,CK#主输出,从输入差分时钟
DQ[7:0]输入\输出数据输入\输出接口
RWDS输入输出读写数据选通:在所有总线事务的命令/地址部分期间,RWDS 为从设备输出信号,用于指示是否需要额外的初始延迟。在读取数据传输期间,从设备输出的数据与 RWDS 边沿对齐;在写入数据传输期间,从设备输入作为数据掩码使用。(HIGH = 需要额外延迟,LOW = 无需额外延迟)。
RESET#主输出、从输入、内部上拉硬件复位。当 Reset# 为低电平时,从设备将自动初始化并返回至待机状态;当 Reset# 为低电平时,RWDS 和 DQ[7:0] 将被置为高阻态(HIGH-Z)。从设备的 Reset# 输入端包含弱上拉电阻;若 Reset# 未连接,则会被上拉至高电平。

RWDS是一个双向信号,用于指示:

  • 读取传输中数据开始从HYPERRAMTM器件传输到主件的时间(初始读取延迟)
  • 当数据在读取传输期间从HYPERRAMT?器件传输到主件时(作为源同步读取数据选通)
  • 当数据开始从主件传输到HYPERRAMTM器件进行写入传输时(初始写入延迟)
  • 写入数据传输期间的数据屏蔽

    在读或写传输的CA传输部分期间,RWDS充当HYPERRAMTM设备的输出,以指示传输中是否需要额外的初始读取延迟。
    在读数据传输过程中,RWDS是一种读数据选通,数据值随RWDS的转换而边沿对齐。

每个传输都以CS和指令地址(CA)信号的置位开始,然后开始时钟转换以传输6个CA字节,接着是初始读取延迟和读取或写入数据传输,直到CS被取消置位。

图3:读取传输,单次初始延迟计数

图4: 读取传输,额外延迟计数

图5:写入传输,单次初始延迟计数

2.2 HyperBUS传输详情

所有 HYPERRAM™ 总线传输都可以分为读或写。当时钟处于空闲状态(CK = 低电平且 CK# = 高电平)时,CS# 变为低电平,总线传输开始。
图6:
指令-地址CA序列
CA0、CA1、CA2定义的信息有:

  • 读取或写入命令。
  • 地址空间:存储空间或寄存器空间
  • 寄存器空间用于访问器件标识符(ID)寄存器和配置寄存器(CR),用于识别器件特性并确定在HYPERBUSTM扩展I/O接口上读取和写入传输的从机特定行为。
  • 命令是否使用线性或回卷突发序列。
  • 目标行(和半页)地址(上位地址)
  • 目标列(半页内的单词)地址(低阶地址)

图7:DQ通道和CA地址映射


CA位含义

CA位位名称位含义
47R/W#识别该事务为读操作或写操作。R/W# = 1 表示读事务;R/W# = 0 表示写事务。
46Address space(AS)指示读取或写入事务是访问内存空间还是寄存器空间。AS = 0 表示内存空间 AS= 1 表示寄存器空间寄存器空间用于访问设备 ID 和配置寄存器。
45Burst type指示突发数据流是线性还是环形。突发类型 = 0 表示环形突发;突发类型 = 1 表示线性突发。
44-16Row and upper column address目标地址的行与高位列组件:系统字地址位 A31–A3。主机控制器主接口应将任何未被特定设备密度使用的高位行地址位设为 0。行数及列地址之间的地址位边界大小取决于从设备。
15-3Reserved扩展预留。当前 HYPERBUS™ 设备中,预留位无实际作用,但为确保未来兼容性,应由主机控制器主接口将其设为 0。
2-0Lower column address目标地址的低位列组件。系统字地址位 A2–A0 用于选择半页内的起始字。

三、使用硬件介绍

3.1 HyperRAM硬件

本文使用的HyperRAM硬件是 ISSI 的IS67WVH4M8FALL,主要性能:
HyperBus™ 低信号计数接口

IS67WVH4M8FALL的命名含义

  • IS66:3.0V 电压版本;IS67:1.8V 电压版本
  • 4M8:容量规格 = 4M × 8bit = 32Mbit = 4MB 字节
  • H:HyperBus 接口协议
  • FALL / BLL:封装、温度等级、驱动强度等细分参数

● 1.8 V / 3.0 V 接口支持
○ 单端时钟(CK)— 11 根总线信号
○ 可选差分时钟(CK, CK#)— 12 根总线信号
● 芯片选择(CS#)
● 8 位数据总线(DQ[7:0])
● 读写数据选通(RWDS)
○ 双向数据选通 / 屏蔽
○ 所有事务起始处输出,用于指示刷新延迟
○ 读事务期间输出,作为读数据选通
● 可配置输出驱动强度

● 最高200 MHz时钟频率
● 双倍数据速率(DDR)— 每个时钟周期传输两笔数据
● 数据吞吐量最高达 400 MB/s(3,200 Mbps)
○ 可配置突发特性
- 包装突发长度:
• 16 字节(8 个时钟周期)
• 32 字节(16 个时钟周期)
• 64 字节(32 个时钟周期)
• 128 字节(64 个时钟周期)
○ 线性突发
○ 混合突发 — 一次包装突发后接线性突发

性能摘要
最大时钟频率(1.8 V VCC/VCCQ):200 MHz
最大时钟频率(3.0 V VCC/VCCQ):200 MHz
最大访问时间:35 ns

3.2 主控芯片

主控芯片使用的是:STM32H733ZGT6

相关介绍网上资源较多,此处省略…

四、STM32CubeMX配置及说明

4.1配置图

  • Memory Type:选择HyperBus(TM),将 OCTOSPI 切换为 HyperBus 协议帧格式,适配 HyperRAM 器件

  • 接口复用:通过 OCTOSPI I/O 管理器(OCTOSPIM)将 OCTOSPI1 映射到Port2端口,与硬件原理图的 PF/PG 组引脚一一对应

  • 信号分配

    • CLK:单端时钟输出,提供 HyperRAM 工作时钟

    • NCLK:差分互补时钟输出,HyperBus 协议支持差分时钟,提升高速下信号抗干扰能力

    • NCS:片选信号,低电平有效,每次总线事务由 CS 拉低启动

    • DQS (RWDS):数据选通信号,对应 HyperBus 的 RWDS 引脚,读操作作为数据同步选通,写操作作为写屏蔽

    • Data[3:0] / Data[7:4]:组成 8 位双向数据总线,支持 DDR 双倍数据速率传输


#### 通用参数

参数配置值说明
Fifo Threshold1FIFO 阈值设为 1 字节,数据到达即触发传输,适配小数据随机访问
Dual Quad modeDisable不使用双四路模式,HyperBus 为单 8 线通道
Device Size22地址位宽 22bit,对应2^22 = 4MB容量,匹配 IS67WVH4M8FALL 规格
Device TypeRAM外部器件类型为 RAM,区别于 Flash 的擦写逻辑
Chip Select High Time5片选高电平最小保持 5 个时钟周期,保证两次传输间器件复位
Free Running ClockDisable仅传输时输出时钟,空闲时关闭以降低功耗
Clock ModeLow时钟空闲电平为低,匹配 HyperBus 协议时钟极性
Clock Prescaler2外设时钟 2 分频,200MHz 输入时钟下得到100MHz工作时钟
Delay Hold Quarter CycleEnable开启 1/4 周期保持,优化 DDR 模式下数据建立 / 保持时间
Refresh241刷新周期配置,适配 HyperRAM 自刷新需求,保证数据不丢失


输出速度等级:VERY_HIGH(非常高速)


  • CPU I-Cache / CPU D-Cache:全部开启。指令缓存与数据缓存可大幅提升外部存储器访问效率,内存映射模式下可直接在 HyperRAM 上运行代码、读写数据。

  • Speculation default mode:开启,优化总线预取与推测访问,提升连续访问性能。

  • 基地址0x90000000,对应 STM32H7 系列 OCTOSPI1 内存映射起始地址

  • 区域大小:4MB

  • 访问权限:全权限读写,支持指令读取(可执行代码)

  • 内存属性

    • Cacheable:可缓存,允许 DCache 缓存该区域数据

    • Bufferable:可缓冲,允许写缓冲合并写入,提升写性能

    • Shareable:非共享,单主控访问无需总线同步

  • TEX Level 1:配置为 Normal 内存类型,配合缓存策略实现最高访问性能



我的时钟配置

五、代码生成

1. 主函数

****************************************************************************** * @file : main.c ****************************************************************************** int main() { HyperRAM_EnableMemMapped(); /* 进入内存映射模式 */ HyperRAM_SelfTest(); /* HyperRAM 自测试 */ while (1) { } }

2. HyperRAM.c

/* * HyperRAM.c */ #include "HyperRAM.h" #include "mlog.h" /** * @brief 进入 HyperRAM 内存映射模式 * @note 调用后 HyperRAM 可通过 OCTOSPI1_BASE (0x90000000) 直接访问 */ void HyperRAM_EnableMemMapped(void) { OSPI_HyperbusCmdTypeDef s_cmd = {0}; OSPI_MemoryMappedTypeDef s_mmap = {0}; /* 切换到 HyperBus 内存地址空间 */ s_cmd.AddressSpace = HAL_OSPI_MEMORY_ADDRESS_SPACE; s_cmd.AddressSize = HAL_OSPI_ADDRESS_32_BITS; s_cmd.DQSMode = HAL_OSPI_DQS_ENABLE; s_cmd.Address = 0; s_cmd.NbData = 1; if (HAL_OSPI_HyperbusCmd(&hospi1, &s_cmd, 1000) != HAL_OK) { Error_Handler(); } /* 关闭超时计数器,提升连续访问性能 */ s_mmap.TimeOutActivation = HAL_OSPI_TIMEOUT_COUNTER_DISABLE; if (HAL_OSPI_MemoryMapped(&hospi1, &s_mmap) != HAL_OK) { Error_Handler(); } } /* ========================== 自测试程序 ========================== */ #define HYPERRAM_BASE_ADDR 0x90000000UL #define HYPERRAM_SIZE (4UL * 1024 * 1024) /* 4MB */ /* 测试地址:起始、中间、末尾附近 */ static const uint32_t test_addrs[] = { 0x000000, /* 起始 */ 0x001000, /* 4KB 边界 */ HYPERRAM_SIZE / 2 - 256, /* 中间 */ HYPERRAM_SIZE - 256, /* 末尾 */ }; /** * @brief HyperRAM 自测试 * @note 必须在 HyperRAM_EnableMemMapped() + OCTOSPI_DelayBlock_Calib() 之后调用 * 测试内容: * 1. 单字节读写 * 2. 多种数据模式(0x00/0xFF/0xAA55/行走1) * 3. 多地址边界测试 * 4. 大块顺序读写 + 性能测量 */ void HyperRAM_SelfTest(void) { __IO uint8_t *base = (__IO uint8_t *)HYPERRAM_BASE_ADDR; uint32_t pass = 0, fail = 0; uint32_t tick_start, tick_end; uint8_t err_cnt[4]; uint16_t fail_offset[2]; uint8_t expect_val[2], actual_val[2]; /* ---- 测试1:单字节读写 ---- */ MLOG_I("HRAM", "[Test1] Single byte read/write"); { uint8_t err = 0; base[0] = 0xA5; if (base[0] != 0xA5) err++; base[1] = 0x5A; if (base[1] != 0x5A) err++; base[100] = 0x33; if (base[100] != 0x33) err++; err_cnt[0] = err; if (err == 0) { MLOG_I("HRAM", " PASS"); pass++; } else { MLOG_I("HRAM", " FAIL (errors=%d)", err); fail++; } } /* ---- 测试2:数据模式测试 ---- */ MLOG_I("HRAM", "[Test2] Data pattern test (256 bytes @ 0x1000)"); { #define PATTERN_LEN 256 uint8_t write_buf[PATTERN_LEN]; uint8_t read_buf[PATTERN_LEN]; uint8_t err = 0; /* 模式: 0x00, 0xFF, 0xAA, 0x55, 行走1 */ const uint8_t patterns[] = { 0x00, 0xFF, 0xAA, 0x55, 0x01, 0x80 }; const char *pattern_names[] = { "0x00", "0xFF", "0xAA", "0x55", "walk1_L", "walk1_H" }; for (uint8_t p = 0; p < sizeof(patterns); p++) { /* 填充写入缓冲区 */ for (uint16_t i = 0; i < PATTERN_LEN; i++) { if (patterns[p] == 0x01) write_buf[i] = (uint8_t)(1u << (i % 8)); else if (patterns[p] == 0x80) write_buf[i] = (uint8_t)(0x80u >> (i % 8)); else write_buf[i] = patterns[p]; } /* 写入 HyperRAM */ for (uint16_t i = 0; i < PATTERN_LEN; i++) { base[0x1000 + i] = write_buf[i]; } /* 回读校验 */ for (uint16_t i = 0; i < PATTERN_LEN; i++) { read_buf[i] = base[0x1000 + i]; if (read_buf[i] != write_buf[i]) err++; } err_cnt[1] = err; if (err == 0) MLOG_I("HRAM", " pattern %s: PASS", pattern_names[p]); else { MLOG_I("HRAM", " pattern %s: FAIL (errors=%d)", pattern_names[p], err); fail++; } if (err == 0) pass++; err = 0; } #undef PATTERN_LEN } /* ---- 测试3:多地址边界测试 ---- */ MLOG_I("HRAM", "[Test3] Address boundary test (64 bytes each)"); { #define BOUNDARY_LEN 64 uint8_t err = 0; for (uint8_t a = 0; a < sizeof(test_addrs) / sizeof(test_addrs[0]); a++) { __IO uint8_t *ptr = (__IO uint8_t *)(HYPERRAM_BASE_ADDR + test_addrs[a]); /* 写入地址值作为数据 */ for (uint16_t i = 0; i < BOUNDARY_LEN; i++) { ptr[i] = (uint8_t)((test_addrs[a] + i) & 0xFF); } /* 回读校验 */ for (uint16_t i = 0; i < BOUNDARY_LEN; i++) { if (ptr[i] != (uint8_t)((test_addrs[a] + i) & 0xFF)) { fail_offset[0] = i; expect_val[0] = (uint8_t)((test_addrs[a] + i) & 0xFF); actual_val[0] = ptr[i]; err++; break; } } err_cnt[2] = err; if (err == 0) { MLOG_I("HRAM", " addr=0x%06lX: PASS", test_addrs[a]); pass++; } else { MLOG_I("HRAM", " addr=0x%06lX: FAIL (offset=%d expect=0x%02X read=0x%02X)", test_addrs[a], fail_offset[0], expect_val[0], actual_val[0]); fail++; } err = 0; } #undef BOUNDARY_LEN } /* ---- 测试4:大块顺序读写 + 性能测量 ---- */ MLOG_I("HRAM", "[Test4] Bulk sequential R/W (1KB) + timing"); { #define BULK_SIZE 1024 uint8_t write_buf[BULK_SIZE]; uint8_t err = 0; /* 填充递增数据 */ for (uint16_t i = 0; i < BULK_SIZE; i++) { write_buf[i] = (uint8_t)(i & 0xFF); } /* 写入计时 */ tick_start = HAL_GetTick(); for (uint16_t i = 0; i < BULK_SIZE; i++) { base[0x2000 + i] = write_buf[i]; } tick_end = HAL_GetTick(); MLOG_I("HRAM", " Write 1KB: %lu ms", tick_end - tick_start); /* 回读校验 + 计时 */ tick_start = HAL_GetTick(); for (uint16_t i = 0; i < BULK_SIZE; i++) { if (base[0x2000 + i] != write_buf[i]) { fail_offset[1] = i; expect_val[1] = write_buf[i]; actual_val[1] = base[0x2000 + i]; err++; break; } } tick_end = HAL_GetTick(); MLOG_I("HRAM", " Read 1KB: %lu ms", tick_end - tick_start); err_cnt[3] = err; if (err == 0) { MLOG_I("HRAM", " PASS"); pass++; } else { MLOG_I("HRAM", " FAIL (offset=0x%03X expect=0x%02X read=0x%02X)", 0x2000 + fail_offset[1], expect_val[1], actual_val[1]); fail++; } #undef BULK_SIZE } /* ---- 汇总 断电打在这里查看err_cnt的值---- */ MLOG_I("HRAM", "========== HyperRAM Self Test DONE =========="); MLOG_I("HRAM", " PASS: %lu, FAIL: %lu", pass, fail); if (fail > 0) { MLOG_I("HRAM", "*** TEST FAILED - check hardware/timing ***"); } else { MLOG_I("HRAM", "*** ALL TESTS PASSED ***"); } }

3. HyperRAM.h

/* * HyperRAM.h */ #ifndef HYPERRAM_HYPERRAM_H_ #define HYPERRAM_HYPERRAM_H_ #include "stm32h7xx_hal.h" #include "stm32h7xx_ll_delayblock.h" #include "octospi.h" #include "string.h" #include "main.h" void HyperRAM_EnableMemMapped(void); void HyperRAM_SelfTest(void); #endif /* HYPERRAM_HYPERRAM_H_ */

4. octospi.c的配置可以对照看一下

/* USER CODE BEGIN Header */ /** ****************************************************************************** * @file octospi.c * @brief This file provides code for the configuration * of the OCTOSPI instances. ****************************************************************************** * @attention * * Copyright (c) 2026 STMicroelectronics. * All rights reserved. * * This software is licensed under terms that can be found in the LICENSE file * in the root directory of this software component. * If no LICENSE file comes with this software, it is provided AS-IS. * ****************************************************************************** */ /* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ #include "octospi.h" /* USER CODE BEGIN 0 */ /* USER CODE END 0 */ OSPI_HandleTypeDef hospi1; /* OCTOSPI1 init function */ void MX_OCTOSPI1_Init(void) { /* USER CODE BEGIN OCTOSPI1_Init 0 */ /* USER CODE END OCTOSPI1_Init 0 */ OSPIM_CfgTypeDef sOspiManagerCfg = {0}; OSPI_HyperbusCfgTypeDef sHyperBusCfg = {0}; /* USER CODE BEGIN OCTOSPI1_Init 1 */ /* USER CODE END OCTOSPI1_Init 1 */ hospi1.Instance = OCTOSPI1; hospi1.Init.FifoThreshold = 1; hospi1.Init.DualQuad = HAL_OSPI_DUALQUAD_DISABLE; hospi1.Init.MemoryType = HAL_OSPI_MEMTYPE_HYPERBUS; hospi1.Init.DeviceSize = 22; hospi1.Init.ChipSelectHighTime = 5; hospi1.Init.FreeRunningClock = HAL_OSPI_FREERUNCLK_DISABLE; hospi1.Init.ClockMode = HAL_OSPI_CLOCK_MODE_0; hospi1.Init.WrapSize = HAL_OSPI_WRAP_NOT_SUPPORTED; hospi1.Init.ClockPrescaler = 2; hospi1.Init.SampleShifting = HAL_OSPI_SAMPLE_SHIFTING_NONE; hospi1.Init.DelayHoldQuarterCycle = HAL_OSPI_DHQC_ENABLE; hospi1.Init.ChipSelectBoundary = 0; hospi1.Init.DelayBlockBypass = HAL_OSPI_DELAY_BLOCK_USED; hospi1.Init.MaxTran = 0; hospi1.Init.Refresh = 241; if (HAL_OSPI_Init(&hospi1) != HAL_OK) { Error_Handler(); } sOspiManagerCfg.ClkPort = 2; sOspiManagerCfg.DQSPort = 2; sOspiManagerCfg.NCSPort = 2; sOspiManagerCfg.IOLowPort = HAL_OSPIM_IOPORT_2_LOW; sOspiManagerCfg.IOHighPort = HAL_OSPIM_IOPORT_2_HIGH; if (HAL_OSPIM_Config(&hospi1, &sOspiManagerCfg, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { Error_Handler(); } sHyperBusCfg.RWRecoveryTime = 4; sHyperBusCfg.AccessTime = 4; sHyperBusCfg.WriteZeroLatency = HAL_OSPI_LATENCY_ON_WRITE; sHyperBusCfg.LatencyMode = HAL_OSPI_FIXED_LATENCY; if (HAL_OSPI_HyperbusCfg(&hospi1, &sHyperBusCfg, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { Error_Handler(); } /* USER CODE BEGIN OCTOSPI1_Init 2 */ /* USER CODE END OCTOSPI1_Init 2 */ } void HAL_OSPI_MspInit(OSPI_HandleTypeDef* ospiHandle) { GPIO_InitTypeDef GPIO_InitStruct = {0}; RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0}; if(ospiHandle->Instance==OCTOSPI1) { /* USER CODE BEGIN OCTOSPI1_MspInit 0 */ /* USER CODE END OCTOSPI1_MspInit 0 */ /** Initializes the peripherals clock */ PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_OSPI; PeriphClkInitStruct.PLL2.PLL2M = 2; PeriphClkInitStruct.PLL2.PLL2N = 32; PeriphClkInitStruct.PLL2.PLL2P = 2; PeriphClkInitStruct.PLL2.PLL2Q = 2; PeriphClkInitStruct.PLL2.PLL2R = 2; PeriphClkInitStruct.PLL2.PLL2RGE = RCC_PLL2VCIRANGE_3; PeriphClkInitStruct.PLL2.PLL2VCOSEL = RCC_PLL2VCOWIDE; PeriphClkInitStruct.PLL2.PLL2FRACN = 0; PeriphClkInitStruct.OspiClockSelection = RCC_OSPICLKSOURCE_PLL2; if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK) { Error_Handler(); } /* OCTOSPI1 clock enable */ __HAL_RCC_OCTOSPIM_CLK_ENABLE(); __HAL_RCC_OSPI1_CLK_ENABLE(); __HAL_RCC_GPIOF_CLK_ENABLE(); __HAL_RCC_GPIOG_CLK_ENABLE(); /**OCTOSPI1 GPIO Configuration PF0 ------> OCTOSPIM_P2_IO0 PF1 ------> OCTOSPIM_P2_IO1 PF2 ------> OCTOSPIM_P2_IO2 PF3 ------> OCTOSPIM_P2_IO3 PF4 ------> OCTOSPIM_P2_CLK PF5 ------> OCTOSPIM_P2_NCLK PF12 ------> OCTOSPIM_P2_DQS PG0 ------> OCTOSPIM_P2_IO4 PG1 ------> OCTOSPIM_P2_IO5 PG10 ------> OCTOSPIM_P2_IO6 PG11 ------> OCTOSPIM_P2_IO7 PG12 ------> OCTOSPIM_P2_NCS */ GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3 |GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_12; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate = GPIO_AF9_OCTOSPIM_P2; HAL_GPIO_Init(GPIOF, &GPIO_InitStruct); GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_11; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate = GPIO_AF9_OCTOSPIM_P2; HAL_GPIO_Init(GPIOG, &GPIO_InitStruct); GPIO_InitStruct.Pin = GPIO_PIN_10|GPIO_PIN_12; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate = GPIO_AF3_OCTOSPIM_P2; HAL_GPIO_Init(GPIOG, &GPIO_InitStruct); /* OCTOSPI1 interrupt Init */ HAL_NVIC_SetPriority(OCTOSPI1_IRQn, 0, 0); HAL_NVIC_EnableIRQ(OCTOSPI1_IRQn); /* USER CODE BEGIN OCTOSPI1_MspInit 1 */ /* USER CODE END OCTOSPI1_MspInit 1 */ } } void HAL_OSPI_MspDeInit(OSPI_HandleTypeDef* ospiHandle) { if(ospiHandle->Instance==OCTOSPI1) { /* USER CODE BEGIN OCTOSPI1_MspDeInit 0 */ /* USER CODE END OCTOSPI1_MspDeInit 0 */ /* Peripheral clock disable */ __HAL_RCC_OCTOSPIM_CLK_DISABLE(); __HAL_RCC_OSPI1_CLK_DISABLE(); /**OCTOSPI1 GPIO Configuration PF0 ------> OCTOSPIM_P2_IO0 PF1 ------> OCTOSPIM_P2_IO1 PF2 ------> OCTOSPIM_P2_IO2 PF3 ------> OCTOSPIM_P2_IO3 PF4 ------> OCTOSPIM_P2_CLK PF5 ------> OCTOSPIM_P2_NCLK PF12 ------> OCTOSPIM_P2_DQS PG0 ------> OCTOSPIM_P2_IO4 PG1 ------> OCTOSPIM_P2_IO5 PG10 ------> OCTOSPIM_P2_IO6 PG11 ------> OCTOSPIM_P2_IO7 PG12 ------> OCTOSPIM_P2_NCS */ HAL_GPIO_DeInit(GPIOF, GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3 |GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_12); HAL_GPIO_DeInit(GPIOG, GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_10|GPIO_PIN_11 |GPIO_PIN_12); /* OCTOSPI1 interrupt Deinit */ HAL_NVIC_DisableIRQ(OCTOSPI1_IRQn); /* USER CODE BEGIN OCTOSPI1_MspDeInit 1 */ /* USER CODE END OCTOSPI1_MspDeInit 1 */ } } /* USER CODE BEGIN 1 */ /* USER CODE END 1 */
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/19 13:41:30

嵌入式实时调试利器:PC Trace原理、配置与实战应用解析

1. 嵌入式实时分析与诊断&#xff08;ERAD&#xff09;与PC Trace核心价值解析在嵌入式系统&#xff0c;尤其是像TI C2000系列这样的实时微控制器开发中&#xff0c;最头疼的问题往往不是代码写不出来&#xff0c;而是代码跑起来后&#xff0c;你根本不知道它在“想”什么。传统…

作者头像 李华
网站建设 2026/7/19 13:36:12

Ryujinx:用C重新定义Switch游戏体验的终极指南

Ryujinx&#xff1a;用C#重新定义Switch游戏体验的终极指南 【免费下载链接】Ryujinx 用 C# 编写的实验性 Nintendo Switch 模拟器 项目地址: https://gitcode.com/GitHub_Trending/ry/Ryujinx 还在为无法在PC上体验Switch游戏而烦恼吗&#xff1f;Ryujinx这款完全用C#编…

作者头像 李华