4、NOR Flash 选型:XIP(片上执行)特性,Quad/Octal SPI 接口,容量与扇区大小选择

各位做嵌入式开发的朋友,咱们今天聊聊 NOR Flash 选型。说实话,这玩意儿看着简单,但坑是真不少。我这些年经手的项目,少说也有几十个,每次选 NOR Flash 都得打起十二分精神。

先说说我的核心建议:代码存储,首选 NOR Flash。为什么?因为它能 XIP(片上执行),说白了就是 CPU 可以直接在上面跑代码,不用先搬到 RAM 里。这个特性在嵌入式系统里太重要了。

XIP 特性:为什么代码存储离不开它?

XIP,全称是 Execute In Place。你想想看,系统上电后,CPU 要做的第一件事是什么?读第一条指令。如果代码存在 NOR Flash 里,CPU 可以直接通过地址总线访问它,就像访问内存一样。

我在项目中遇到过这样的情况:有个同事非要用 NAND Flash 存代码,结果每次启动都要先把代码搬到 RAM 里,不仅启动慢,还多占了一颗 RAM 芯片。后来我建议他换成 NOR Flash,启动时间从 2 秒降到了 0.3 秒。嗯,这就是 XIP 的魅力。

关键点:NOR Flash 的读时序和 SRAM 类似,CPU 可以直接通过地址线读取指令。而 NAND Flash 是块设备,必须通过控制器间接访问,没法 XIP。

Quad/Octal SPI 接口:速度与成本的平衡

早期的 NOR Flash 用 SPI 接口,一次只能传 1 位数据。后来有了 Quad SPI(四线),再后来是 Octal SPI(八线)。我个人习惯,根据系统对读速度的要求来选择:

  • 标准 SPI:适合低速应用,比如存些配置参数。时钟跑 20MHz 就够用了。
  • Quad SPI:目前最主流的选择。四线同时传输,速度能到 80MB/s 以上。我大部分项目都用这个。
  • Octal SPI:追求极致速度时用。八线传输,速度能飙到 200MB/s 以上。但要注意,这种 Flash 通常更贵,而且 PCB 布线也更复杂。

我记得有个项目,客户要求系统启动时间小于 100ms。用 Quad SPI 的 NOR Flash,读速度勉强够。后来换成 Octal SPI,启动时间直接砍半。但代价是 BOM 成本涨了 30%。所以选型时一定要权衡。

我的建议:如果系统对启动速度要求不高,用 Quad SPI 就够了。别盲目追求 Octal,省下的钱可以买杯咖啡。

容量与扇区大小选择:别小看这个细节

NOR Flash 的容量从几 Mb 到几 Gb 都有。但选容量时,我建议你多留个心眼:

  • 代码大小:先估算你的固件有多大。一般留 50% 的余量,方便后续升级。
  • 扇区大小:这个特别容易踩坑。NOR Flash 的擦除操作是按扇区来的,扇区越大,擦除时间越长。

我曾经在一个项目里选了 64KB 扇区的 NOR Flash,结果每次 OTA 升级都要擦除整个扇区,哪怕只改了几个字节。后来换成 4KB 小扇区的型号,升级效率提升了好几倍。

扇区大小 优点 缺点 适用场景
4KB 擦除粒度细,适合频繁小数据更新 管理复杂,需要更多元数据 参数存储、OTA 升级
64KB 管理简单,擦除效率高 擦除粒度粗,浪费空间 代码存储、大块数据
256KB 适合大容量存储 擦除时间长,不适合频繁更新 固件镜像、文件系统

避坑指南:我曾经选了一款 256KB 扇区的 NOR Flash 存配置文件,结果每次改一个参数都要擦除 256KB 的数据,写入时间长达 500ms。后来换成 4KB 扇区的型号,写入时间降到 30ms。所以,如果你的系统需要频繁更新小数据,一定要选小扇区的 Flash。

代码示例:如何配置 Quad SPI 读操作

下面是一个简单的 Quad SPI 读操作配置示例,基于 STM32 的 HAL 库:

// 配置 Quad SPI 读操作
void QSPI_Read_Config(void)
{
    QSPI_CommandTypeDef sCommand;
    
    // 设置读命令
    sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE;
    sCommand.Instruction = 0xEB;  // Quad Output Fast Read
    sCommand.AddressMode = QSPI_ADDRESS_4_LINES;
    sCommand.AddressSize = QSPI_ADDRESS_24_BITS;
    sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
    sCommand.DataMode = QSPI_DATA_4_LINES;
    sCommand.DummyCycles = 6;     // 需要 6 个 dummy cycle
    sCommand.NbData = 256;        // 读取 256 字节
    
    // 发送命令
    if (HAL_QSPI_Command(&hqspi, &sCommand, HAL_QSPI_TIMEOUT_DEFAULT) != HAL_OK)
    {
        Error_Handler();
    }
    
    // 读取数据
    uint8_t buffer[256];
    if (HAL_QSPI_Receive(&hqspi, buffer, HAL_QSPI_TIMEOUT_DEFAULT) != HAL_OK)
    {
        Error_Handler();
    }
}

这段代码里,我用了 Quad Output Fast Read 命令(0xEB),地址和数据都走四线。注意那个 DummyCycles,不同厂商的 Flash 可能不一样,一定要看数据手册。

总结一下我的选型思路

嗯,说了这么多,最后总结几点:

  1. 代码存储,首选 NOR Flash,因为 XIP 特性无可替代。
  2. 接口选 Quad SPI,性价比最高。除非你对速度有极致要求,再考虑 Octal。
  3. 容量留 50% 余量,别抠门。扇区大小根据你的更新频率来选。
  4. 多看数据手册,特别是时序参数和命令集。不同厂商的兼容性可能有问题。

我建议你选型时,先画个表格,把需求列清楚:代码大小、更新频率、启动时间要求、成本预算。然后拿着这个表去选 Flash,基本不会出大错。

好了,这一章就聊到这儿。下一章咱们讲讲 NAND Flash 选型,那个坑更多,到时候再细说。