这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 活动中心 » 有奖活动 » 片外存储Flash使用方法(Arduino IDE环境)

共4条 1/1 1 跳转至

片外存储Flash使用方法(Arduino IDE环境)

助工
2026-06-25 22:49:58   被打赏 20 分(兑奖)     打赏

摘要:本文主要介绍在Arduino IDE开发环境下,如何快速上手使用外部Flash的相关功能(读取外部Flash的相关信息 / 将Flash中的程序,加载到RAM中执行)


一、Flash

Flash存储器,也称为Flash Memory,是一种非易失性存储设备,它结合了ROM和RAM的特点,不仅具有电子可擦除可编程(EEPROM)的性能,还能在断电后保持数据不丢失,类似于NVRAM的优势。


Flash存储器的类型

Flash存储器主要分为NOR Flash和NAND Flash两大类;

NOR Flash特点是具有较快的读速度和片上执行功能,但写入和擦除速度较慢,容量低且价格高,因此多用于代码存储;

NAND Flash以其快速的写入和擦除操作,大容量和低成本的优势,主要用于数据存储,如数码相机、MP3播放器和笔记本电脑;

1782397787430566.png


常用NOR Flash型号

大家可能经常使用都是Winbond(华邦)的W25Qxx系列, 除此之外还有国产的兆易创新(GigaDevice)的GD25Qxx系列等;

W25Qxx系列相关型号介绍

GD25Qxx系列相关型号介绍

同封装型号的GD25Qxx与W25Qxx兼容;

而且目前兆易的同款价格普遍低于华邦,非常适合进行平替;


Flash 特性

串行 NOR(SPI/DSPI/QSPI)

1782443411500851.png



二、开发环境搭建

1、下载Arduino IDE

1782397931641263.png


image-202606121101550872、安装相关功能库

1、安装STM32芯片支持库

image-20260622234739464.png

image-20260622234739464

2、安装WS25QxxFlash

该库提供Flash读取、写入等相关功能;

image-20260622175250398.png


1782398253796496.png




三、硬件设备(功能)

主要使用天空星STM32F407VET6开发板,以及焊接的外部Flash(GD25Q128);

通过WS25QxxFlash库,读取Flash的相关信息,以及当按下板载按键KEY时,将Flash中的点亮LED程序,加载到RAM中执行;


STM32F407VET6

1782398551546045.png



image-20260622175522535

SPI接口

image-20260622175444023.png


GD25Q128ESIGR

GD25Q128ESIGR是兆易创推出的128Mbit(16MB)串行 NOR Flash

存储结构:256 字节 / 页,4KB / 扇区,支持 32KB、64KB 块擦除及全片擦除;

全SPI扩展模式支持:标准SPI(单线)、Dual SPI(双线)、Quad SPI(四线)模式;

image-20260622162854991

image-20260622162854991.png

image-20260622170204033.png

1782398734858851.png



四、使用方法

Flash信息读取部分

// Flash 对应连接的引脚
#define SPI_SCK     PA5
#define SPI_MISO    PA6
#define SPI_MOSI    PA7
#define FLASH_CS    PA4

// 初始化SPI
SPI.setMISO(SPI_MISO);
SPI.setMOSI(SPI_MOSI);
SPI.setSCLK(SPI_SCK);
SPI.begin();
// 初始化Flash芯片 128Mbit (16MB)
flash.begin(&SPI, FLASH_CS, 128);

// 打印Flash信息
uint manufacturer, memType, capId;
if (flash.readChipInfo(&manufacturer, &memType, &capId)) {
        uint32_t totalBytes = 1UL << capId;
        Serial1.println("======= Flash 芯片信息 =======");
        Serial1.print("厂商ID:     0x");
        Serial1.println(manufacturer, HEX);
        Serial1.print("内存类型:   0x");
        Serial1.println(memType, HEX);
        Serial1.print("容量编码:   0x");
        Serial1.println(capId, HEX);
        Serial1.print("总容量:     ");
        Serial1.print(totalBytes);
        Serial1.print(" 字节 = ");
        Serial1.print(totalBytes / 1024);
        Serial1.print(" KB = ");
        Serial1.print(totalBytes / (1024UL * 1024UL));
        Serial1.print(" MB = ");
        Serial1.print((totalBytes * 8UL) / (1024UL * 1024UL));
        Serial1.println(" Mbit");
    } else {
        Serial1.println("读取Flash信息失败");
    }
    
    uint64_t uniqueId;
    if (flash.readUniqueId(&uniqueId)) {
        Serial1.print("64位唯一ID: 0x");
        Serial1.print((uint32_t)(uniqueId >> 32), HEX);
        Serial1.println((uint32_t)uniqueId, HEX);
    } else {
        Serial1.println("读取芯片唯一ID失败");
    }
    Serial1.println("");



示例代码

#include <SPI.h>
#include "MumanchuDebug.h"
#include "W25QxxFlash.h"


#ifdef DEBUG
void LogError(const char* msg, const char* filePath, uint line) {
    char buf[256];
    const char* fname = strrchr(filePath, '\\');
    fname = fname ? fname + 1 : filePath;
    sprintf(buf, "ERROR: %s : %s(%u)", msg, fname, line);
    Serial1.println(buf);
    Serial1.flush();
}
#endif

// SPI引脚定义 (SPI1)
#define SPI_SCK     PA5
#define SPI_MISO    PA6
#define SPI_MOSI    PA7
#define FLASH_CS    PA4

// 硬件引脚(LED/KEY)
#define LED_BUILTIN PB2
#define KEY_BUTTON  PA0

// 外部Flash中代码的存储地址
#define FIRMWARE_FLASH_ADDR   0x00000000

// RAM执行缓冲区,强制4字节对齐
uint8_t ram_code_buf[64] __attribute__((aligned(4)));

// 按键消抖配置
#define KEY_DEBOUNCE_MS   20
unsigned long last_key_tick = 0;
bool key_pressed_flag = false;

/*
  功能:将 PB2 置高电平点亮LED,执行完毕后返回主程序
  对应汇编:
    ldr  r0, [pc, #4]   ; 加载 GPIOB_BSRR 寄存器地址
    movs r1, #4          ; 1<<2,对应 PB2 置位
    str  r1, [r0]        ; 写入寄存器,点亮LED
    bx   lr              ; 返回调用者
    DCD  0x40020418      ; GPIOB_BSRR 硬件地址
*/
const uint8_t led_on_binary[] = {
  0x01, 0x48,  // ldr r0, [pc, #4]
  0x04, 0x21,  // movs r1, #4
  0x01, 0x60,  // str r1, [r0]
  0x70, 0x47,  // bx lr
  // GPIOB_BSRR 地址 0x40020418,小端序存储
  0x18, 0x04, 0x02, 0x40
};

HardwareSerial Serial1(PA10, PA9); 
W25QxxFlash flash;

void setup() {
    Serial1.begin(115200);
    delay(1000);
    Serial1.flush();

    pinMode(LED_BUILTIN, OUTPUT);
    digitalWrite(LED_BUILTIN, LOW); // 初始熄灭
    pinMode(KEY_BUTTON, INPUT_PULLDOWN);

    // 初始化SPI总线
    SPI.setMISO(SPI_MISO);
    SPI.setMOSI(SPI_MOSI);
    SPI.setSCLK(SPI_SCK);
    SPI.begin();

    // 初始化Flash芯片 128Mbit (16MB)
    flash.begin(&SPI, FLASH_CS, 128);

    // 打印 Flash 信息
    uint manufacturer, memType, capId;
    if (flash.readChipInfo(&manufacturer, &memType, &capId)) {
        uint32_t totalBytes = 1UL << capId;
        Serial1.println("======= Flash 芯片信息 =======");
        Serial1.print("厂商ID:     0x");
        Serial1.println(manufacturer, HEX);
        Serial1.print("内存类型:   0x");
        Serial1.println(memType, HEX);
        Serial1.print("容量编码:   0x");
        Serial1.println(capId, HEX);
        Serial1.print("总容量:     ");
        Serial1.print(totalBytes);
        Serial1.print(" 字节 = ");
        Serial1.print(totalBytes / 1024);
        Serial1.print(" KB = ");
        Serial1.print(totalBytes / (1024UL * 1024UL));
        Serial1.print(" MB = ");
        Serial1.print((totalBytes * 8UL) / (1024UL * 1024UL));
        Serial1.println(" Mbit");
    } else {
        Serial1.println("读取Flash信息失败");
    }
    
    uint64_t uniqueId;
    if (flash.readUniqueId(&uniqueId)) {
        Serial1.print("64位唯一ID: 0x");
        Serial1.print((uint32_t)(uniqueId >> 32), HEX);
        Serial1.println((uint32_t)uniqueId, HEX);
    } else {
        Serial1.println("读取芯片唯一ID失败");
    }
    Serial1.println("");

    // 写入代码到外部Flash
    uint8_t check_byte;
    flash.readData(FIRMWARE_FLASH_ADDR, &check_byte, 1);
    if (check_byte != led_on_binary[0]) {
        Serial1.println("首次运行,正在写入LED代码到外部Flash...");
        flash.eraseSector(0);          // 擦除第0个4KB扇区
        flash.waitWhileBusy(1000);
        flash.writeData(FIRMWARE_FLASH_ADDR, led_on_binary, sizeof(led_on_binary));
        flash.waitWhileBusy(1000);
        Serial1.println("代码写入到外部Flash完成");
    } else {
        Serial1.println("外部Flash中已写入代码");
    }
    Serial1.println("按下Key 触发Flash代码加载执行");
    Serial1.println("");
    Serial1.flush();
}

void loop() {
    // 按键消抖检测
    if (digitalRead(KEY_BUTTON) == HIGH) {
        if (millis() - last_key_tick > KEY_DEBOUNCE_MS) {
            if (!key_pressed_flag) {
                key_pressed_flag = true;
                Serial1.println("按键触发:开始从Flash加载代码到SRAM...");

                // 把Flash中的二进制机器码读到RAM缓冲区
                flash.readData(FIRMWARE_FLASH_ADDR, ram_code_buf, sizeof(led_on_binary));

                // 转换为函数指针,地址最低位置1 (Cortex-M Thumb状态位要求)
                void (*exec_led_func)(void) = (void (*)(void))((uint32_t)ram_code_buf | 0x01);

                // 跳转执行RAM中的代码,执行完毕自动返回
                exec_led_func();
                Serial1.println("LED已点亮");
            }
        }
    } else {
        key_pressed_flag = false;
        last_key_tick = millis();
    }
}






五、效果演示


Flash.gif

image-20260624214556258

Flash








关键词: Flash     GD25Q128    

院士
2026-06-26 15:43:38     打赏
2楼

谢谢分享。


专家
2026-06-29 13:51:36     打赏
3楼

谢谢分享!有点像计算机操作系统执行程序了,哈哈哈。不过这种处理只适用于二进制代码量比较小的处理吧?


高工
2026-06-29 13:55:10     打赏
4楼

立创的这个spi flash支持xip吗?


共4条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]