ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

瑞萨RZ/N2L QSPI启动与参数存储设计实战解析

瑞萨RZ/N2L QSPI启动与参数存储设计实战解析 做嵌入式这么多年第一次认真用瑞萨RZ/N2L做量产项目遇到的第一道坎就是启动方式。原先方案是SPI NOR存固件、I2C EEPROM存参数板子改版时客户要求少放器件干脆把参数也塞进同一颗QSPI Flash里。这个决定看起来省事实际做下来牵扯到启动模式配置、BootROM对Flash的初始化时序、RASC生成的驱动代码以及参数存储的磨损均衡和掉电保护一环扣一环每一环都有值得记录的坑。这篇文章就是这次实战的完整记录核心围绕两件事RZ/N2L怎么从QSPI Flash启动以及参数存储怎么做才能扛得住长时间运行。内容包括硬件接法、RASC工程配置、Keil环境搭建、底层驱动移植、参数区规划和常见故障排查适合准备用RZ/N2L做产品、或者想把代码和参数统一放到外部Flash上的朋友。如果你用过瑞萨RA6M5或者熟悉e2 studio那套操作上手RZ/N2L会更轻松因为配置思路是相通的但底层细节差异不小建议还是从头过一遍。1. 为什么我选了QSPI启动需求拆解与方案对比1.1 启动方式选型先把硬件资源算清楚RZ/N2L支持多种启动介质包括QSPI Flash、SD卡、串行接口等。项目里选择QSPI启动不是拍脑袋决定的主要从三个方面考量。第一是引脚开销。RZ/N2L作为MPU级别的芯片功能外设多引脚本来就不宽裕。QSPI接口只需要SCLK、CS、IO0到IO3六根信号线就能接一颗大容量NOR Flash而并行NOR需要几十根线在小型化板卡上完全不现实。第二是容量和成本。QSPI NOR Flash的单价和容量比EEPROM友好太多一颗16MB的W25Q128或者国产GD25Q128价格只有同容量并行Flash的零头空间还绰绰有余。固件、文件系统、参数区可以全放进去产品后续要加OTA升级也留得住空间。第三是XIP能力。QSPI控制器支持内存映射模式Flash可以被映射到CPU的地址空间代码可以直接在Flash上执行不需要上电后先搬运到SRAM再跑。这一点对启动速度帮助很大BootROM把QSPI初始化好之后CPU取指直接走内存映射通道省掉了先加载后跳转的那段等待。1.2 参数存储选择QSPI Flash到底合不合适很多人觉得参数存储应该老老实实用EEPROM担心Flash磨损和掉电丢数据。这个顾虑在十年前有道理但放到现在的应用场景只要设计合理QSPI NOR Flash做参数存储完全够用甚至更合适。NOR Flash的特点是按扇区擦除、按页写入擦除操作会把扇区全部置为0xFF写入只能把1写成0。这个特性决定了参数存储不能像EEPROM那样原地覆盖必须做双备份或者轮换写入。但换个角度想正因为擦写单位大反而逼着你去设计合理的写入策略最终的效果是数据可靠性比EEPROM方案更高因为每一份数据都会被校验、被备份、被轮换。参数存储对Flash的另一个要求是掉电保护。实际产品中参数写入瞬间掉电是常态如果只写一个区域断电后数据可能半新半旧。QSPI方案配合双Slot设计和CRC校验可以保证掉电后要么拿到旧数据要么拿到新数据不会出现中间态。2. 硬件设计与启动引脚配置先把底子打牢2.1 启动模式引脚和QSPI信号一个都不能接错RZ/N2L的启动介质选择由启动模式引脚的上下拉电平决定。具体是MD_BOOT还是其他命名一定要以芯片数据手册和硬件设计手册为准原理图阶段就要和FAE确认清楚。我这次就吃了个亏以为启动引脚默认拉低就是QSPI启动结果打样回来发现默认电平对应的是串行接口模式BootROM压根没去初始化QSPI调试器连上后CPU直接跑飞。启动模式引脚的电平需要在复位释放前稳定所以不要图省事直接接固定电平最好预留0欧电阻或者跳线方便量产阶段切换启动介质。比如调试时用串行接口模式烧写Bootloader测试完成后切回QSPI启动这个跳线能避免反复焊Flash。QSPI信号线的接法也要注意。WP和HOLD引脚在四线模式下会复用为IO2和IO3如果不用四线模式这两个引脚绝对不能悬空。悬空状态下Flash在复位或上电瞬间可能误进入HOLD状态导致通信一直挂起。正确的做法是分别通过10K电阻上拉到VCC或下拉到GND具体选择取决于Flash芯片手册。2.2 信号完整性和电源处理高频启动别翻车QSPI工作在几十MHz到上百MHz信号完整性比SPI模式要求高。PCB上QSPI四根数据线尽量等长误差控制在5mm以内SCLK和数据线之间不要差太多否则高速读时序会出现采样点偏移。走线避免跨分割底层如果有完整地平面信号质量会明显好过跨区走线。电源方面Flash的VCC引脚要就近放一个0.1uF陶瓷电容如果板上有空间再并一个1uF电容。QSPI Flash对电源纹波不算特别敏感但如果Flash供电和主控数字电源共用一条细线擦除瞬间的大电流冲击可能引起波形毛刺严重时造成通信错误。还有一个小细节RZ/N2L的QSPI控制器时钟源来自内部PLL还是外部晶振决定了启动时Flash最高能跑多快。BootROM初始化QSPI时用的是系统默认时钟如果你的外部Flash最高支持133MHz但BootROM阶段只跑到几十MHz这没关系只要应用代码里重新初始化QSPI把它切换到高速模式即可。启动阶段不用追求极限速度稳定才是第一位的。3. RASCKeil环境搭建从配置到第一行代码3.1 RASC创建工程先选对芯片型号和FSP版本瑞萨的图形化配置工具RASCRenesas Advanced Smart Configurator是RZ/N2L开发的主入口和e2 studio里的配置界面是同一套东西。如果你是从RA6M5这类RA系列转过来会发现操作方式很熟悉但RZ/N2L属于RZ系列FSP套件版本和RA系列不完全一样生成的代码结构也有差异。打开RASC后第一步是选择具体芯片型号。RZ/N2L下面还有多个子型号区别主要在封装、内置SRAM大小、外设数量上。选错型号会导致引脚分配不对后期改起来非常痛苦。我建议在原理图定稿后就把型号确认好RASC建工程时直接锁定。创建工程时还需要选目标编译器。RASC支持生成Keil、IAR、GCC等工具链的工程文件。我们用的是Keil MDK所以在RASC里选择对应的工具链生成后直接用MDK打开工程不需要手动移植。FSP版本方面不要盲目追求最新版。有些新版本对老型号的支持存在回归问题我踩过一次升级FSP后QSPI的片选时序配置项位置变了生成代码和手册对不上。稳妥做法是选择当前型号的长期支持版本至少等一个版本发布三个月后再考虑升级。3.2 QSPI外设配置重点看时序参数在RASC的Stacks配置页里添加QSPI驱动配置界面会列出一堆选项包括Bus Width总线宽度、Command Sequence命令序列、时钟分频等。这些参数不是随便填的要和实际Flash芯片的指令集对应上。例如W25Q128JV的页写指令有0x02单线和0x32四线页写部分厂商是0x32擦除指令有扇区擦除0x20、块擦除0xD8。RASC配置界面里会把对应的操作类型列出你需要把Flash手册上的指令码填进去。填错了不会立刻报错但读写数据会出现诡异性错误。时钟分频配置要略留余量。如果Flash标称最高支持104MHzQSPI时钟设置为80MHz到90MHz比较稳妥这个频率贴上限跑长时间工作加上温度变化时序余量不足会导致偶发读错。不要为了跑分好看把频率拉到极限嵌入式产品稳定压倒一切。RASC生成代码后QSPI驱动的初始化、读写接口都会被封装好实际使用时通常从R_QSPI_Open开始然后通过R_QSPI_Read、R_QSPI_Write、R_QSPI_Erase等接口操作Flash。具体函数名以生成代码为准但调用逻辑和标准FSP驱动是一致的熟悉一家以后换型号很容易上手。3.3 Keil工程导入与下载算法配置RASC生成完工程用Keil MDK打开后第一个要处理的问题是下载算法。默认工程里一般只包含片内Flash的编程算法外部QSPI Flash不在其中直接点下载会提示找不到算法。Keil里为外部Flash添加下载算法有两种方式。一种是选择Keil安装目录下现成的Flash算法文件比如针对W25Q系列通常能找到对应的.FLM文件另一种是通过调试器的QSPI Flash Loader实现J-Link的J-Flash软件支持大多数常见QSPI Flash型号配置好之后可以在线烧写。如果芯片的QSPI Flash型号比较冷门找不到现成下载算法还有一个通用办法先用串行接口启动模式通过UART把一段一级Bootloader烧到片内SRAM再由Bootloader通过QSPI驱动把完整的Bootloader和应用固件写到外部Flash。这个流程虽然绕但在量产阶段很实用尤其是调试器算法兼容性出问题时。下载算法配置好之后还有一个坑Keil的Flash Download选项里Programming Algorithm和RAM for Algorithm这两组地址空间要和芯片的QSPI映射地址范围匹配。RZ/N2L的QSPI内存映射地址通常是0x80000000开头的一段区间算法配置里的起始地址必须写对这个值否则程序能下载但上电后无法从Flash启动。4. QSPI底层驱动快速移植读、写、擦除完整实现4.1 QSPI控制器的工作方式先理解再动手RZ/N2L的QSPI控制器支持两种工作模式间接模式和内存映射模式。间接模式适合指令发送、状态读取、小块数据写入内存映射模式适合大块数据读取也就是XIP。理解这两种模式的区别对接下来的代码设计很重要。间接模式下代码像操作普通外设一样往控制器的传输寄存器里写数据触发QSPI控制器自动产生SCLK时序。这种方式灵活可以发任意指令缺点是效率偏低每笔数据传输都要软件参与。内存映射模式下Flash被映射到CPU地址空间CPU直接读地址就能拿到数据效率最高但不适合写入操作因为写操作需要完整的擦写流程。参数存储的优化思路就在这里读取参数时走内存映射模式直接以指针方式访问写入参数时切换到间接模式执行擦除、写使能、页写一系列命令。不少工程师忽略了这个区分全程用间接模式读Flash导致系统启动时读参数耗时几十毫秒完全没有发挥QSPI的性能优势。4.2 核心驱动代码忙检测、页写、扇区擦除QSPI NOR Flash的操作基础是命令序列。常见的命令包括写使能0x06、读状态寄存器0x05、页写0x02、扇区擦除0x20、块擦除0xD8、读数据0x03单线高速读0x0B。驱动设计时把这些命令封装成基础函数上层按需组合。下面这段代码是一个精简的忙等待和页写实现基于瑞萨FSP生成代码的基础上做的简化实际工程中要做超时保护/* 读QSPI Flash状态寄存器 */ static uint8_t qspi_read_status(void) { uint8_t status 0; uint8_t cmd 0x05; /* Read Status Register-1 */ R_QSPI_Command(cmd, 1, status, 1); return status; } /* 等待Flash空闲带超时 */ static int qspi_wait_busy(uint32_t timeout_ms) { uint32_t tick 0; while (tick timeout_ms) { if ((qspi_read_status() 0x01) 0) { return 0; } R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_MILLISECONDS); tick; } return -1; } /* 页写len为1~256字节 */ static int qspi_page_program(uint32_t addr, const uint8_t *buf, uint16_t len) { uint8_t write_enable 0x06; R_QSPI_Command(write_enable, 1, NULL, 0); return R_QSPI_Write(addr, buf, len); } /* 扇区擦除4KB */ static int qspi_sector_erase(uint32_t addr) { uint8_t write_enable 0x06; uint8_t erase_cmd 0x20; R_QSPI_Command(write_enable, 1, NULL, 0); R_QSPI_Command(erase_cmd, 1, NULL, 0); return qspi_wait_busy(500); }页写有个容易被忽略的坑如果写入数据跨过了256字节页边界Flash内部会自动回卷把后半段数据写到页首导致数据错乱。所以驱动里一定要做长度和跨页判断超过页边界就拆分成多次写入每次最多写到本页结尾。擦除等待时间也要给足。扇区擦除典型时间是50ms到400ms块擦除更长。等待循环里如果超时阈值设置太短会误报出错。我习惯把扇区擦除超时设置到500ms块擦除设置到2s给芯片留足余量。4.3 读性能优化内存映射模式一次搞定实际产品里参数结构体可能包含几十个字段分布在几百字节范围内。如果用间接模式一条条读每次都要拉起CS、发送命令效率非常低。正确做法是在系统初始化时把QSPI控制器切换到内存映射模式然后直接用指针读取#define QSPI_MAPPED_BASE 0x80000000UL #define PARAM_AREA_OFFSET 0x00100000UL const param_struct_t *param_ptr; param_ptr (const param_struct_t *)(QSPI_MAPPED_BASE PARAM_AREA_OFFSET); if (param_ptr-magic PARAM_MAGIC) { /* 参数有效直接使用 */ local_param *param_ptr; }这段代码的执行速度比间接模式快一个数量级读取时不占用CPU等待时间。写入时再切换回间接模式完成擦写后重新切回内存映射模式。需要注意的是切换操作要严格配对防止写操作执行到一半切模式导致总线异常。5. 参数存储优化实战磨损均衡与掉电保护5.1 存储区布局设计代码和参数分家参数存储设计的首要原则是参数区和代码区必须物理隔离不能混在一起。否则一次参数擦除失误可能把固件代码一同抹掉产品直接变砖。我习惯把QSPI Flash分成四个区域一级Bootloader、应用固件区、参数存储区、日志区。典型布局参考如下区域起始地址大小用途Bootloader0x00000064KB启动加载、异常恢复应用固件0x0100001MB主程序XIP运行参数存储0x200000256KB参数双Slot轮换日志区0x240000剩余空间运行日志、升级记录参数区划分256KB相对整个Flash容量不算大但用双Slot轮换设计已经非常宽裕。每个Slot对齐到扇区边界方便独立擦除。如果固件本身不足1MB可以把参数区扩大存储余量更充足。参数结构体建议这样定义typedef struct { uint32_t magic; /* 固定魔数识别参数有效性 */ uint32_t version; /* 参数版本号 */ uint32_t sequence; /* 递增序号区分新旧 */ uint32_t crc32; /* CRC校验值 */ uint8_t data[512]; /* 实际参数字段 */ } param_slot_t;magic用于快速判断区域是否被写过version支持不同版本固件之间参数格式升级sequence在双Slot设计中区分新旧数据crc32确保数据完整性。这四样缺一不可少了任何一个后续排查都会很痛苦。5.2 双Slot轮换写入掉电也不怕参数写入最怕掉电。设想一下写主Slot到一半断电主Slot的数据既不是旧值也不是新值如果系统只认主Slot这次断电就造成参数损坏。双Slot设计就是为了解决这个问题。写入流程这样设计读取当前有效Slot的sequence值记为old_seq。擦除空闲Slot把新参数和递增后的sequence写入该Slot。写入完成后读回校验CRC正确才认为写入成功。将另一个Slot标记为无效把magic清掉。上电时扫描两个Slot取magic有效且sequence较大的那个作为当前参数。这里的关键在于任何时候至少保留一份完整有效的参数。写入过程只动空闲Slot老数据始终在另一个Slot里躺着就算写一半掉电上电后仍能读到完整旧数据。实现时用一段简单的扫描函数static param_slot_t *find_valid_slot(void) { param_slot_t *slots[2]; int idx; slots[0] (param_slot_t *)(QSPI_MAPPED_BASE SLOT0_OFFSET); slots[1] (param_slot_t *)(QSPI_MAPPED_BASE SLOT1_OFFSET); /* 先判断magic是否都有效 */ if (slots[0]-magic ! PARAM_MAGIC slots[1]-magic ! PARAM_MAGIC) { return NULL; /* 两个槽都无效使用默认参数 */ } idx 0; if (slots[1]-magic PARAM_MAGIC) { if (slots[0]-magic ! PARAM_MAGIC || slots[1]-sequence slots[0]-sequence) { idx 1; } } if (crc32_check(slots[idx]) ! 0) { return NULL; /* CRC校验失败用默认参数并报警 */ } return slots[idx]; }这段逻辑还要配合CRC校验。CRC计算放在写入之前读回校验放在上电阶段。不要省CRC魔数只能在某种程度上防止未擦除区域被误读但无法防止数据位翻转或者部分写失败。CRC才是数据完整性的最终保障。5.3 磨损均衡计算Flash寿命到底够不够双Slot方案解决了掉电保护问题但磨损均衡的效果取决于Slot数量和写入频率。这里做一个简单计算把理论寿命算清楚。假设使用W25Q12816MB参数区256KB划分为两个Slot每个Slot占128KB一个Slot内包含32个4KB扇区。每次参数更新写一个扇区。Flash扇区擦写寿命为10万次。两个Slot始终各有一个扇区在参与写入磨损均衡后相当于有64个扇区轮流被擦写实际上每次写一个扇区后下次会优先选择擦写次数最少的扇区。那么使用寿命约为64个扇区 × 100,000次 / 每天写入次数 6,400,000 / 每天写入次数如果设备每10分钟写一次参数一天144次寿命约为44444天超过120年。就算每秒钟写一次一天86400次寿命也有74天对于参数保存来说完全够用。这个计算说明一个道理磨损均衡的效果取决于参与轮换的扇区数量。如果只用固定一个扇区擦写10万次寿命很快耗尽但只要把参与轮换的扇区数量做大寿命会线性增长。所以参数区不要划分得抠抠搜搜多给几个扇区产品生命周期内的磨损问题基本不用再管。6. 启动与存储故障排查我踩过的坑都在这里6.1 启动失败反复重启先查BootROM阶段遇到上电后系统反复重启或者完全没有响应按下面的顺序排查。首先检查启动模式引脚的最终电平是否和预期一致。用万用表量一下复位释放时的电平不要只看原理图。我遇到过原理图上标示正确但PCB布线时通过一个串联电阻接到了错误电平的情况。其次确认Flash芯片是否被BootROM正确识别。RZ/N2L的BootROM在执行QSPI启动时会从Flash的起始位置读取有效引导头引导头的结构在芯片应用手册里有明确说明。如果引导头内容不对BootROM会静默放弃之后的行为取决于其他启动源是否配置。第三步是检查Flash里是否确实烧写了Bootloader。用调试器读出Flash起始地址的二进制数据看是否有意料之外的全FF或者全00。全FF说明Flash没烧进去或者擦除后未编程全00说明写操作异常通常与写使能时序有关。6.2 参数偶发丢失CRC和掉电时序是重点参数偶发丢失是排查起来最头疼的问题因为问题不一定能稳定复现。我遇到过的原因主要有三类。第一类是CRC校验逻辑漏了字节。曾经有个同事把结构体里padding区也算进CRC结果不同编译优化级别下结构体填充字节不同导致同一份参数在不同固件下校验结果不一致。解决方案是定义一个固定布局的参数结构体显式添加保留字节CRC只计算固定区域。第二类是掉电瞬间的Flash内部状态被破坏。QSPI Flash在写入过程中掉电Flash内部的高压发生器可能处于中间状态虽然双Slot机制能保住有效数据但下次擦除这个损坏的Slot时如果写入电压不稳定擦除时间会异常拉长。解决办法是给Flash供电加一点储能电容让掉电后还能维持几十毫秒供电把正在进行的擦写流程走完。第三类是WP引脚保护意外开启。很多Flash有状态寄存器保护位一旦设置扇区或块无法擦除。如果代码里没有写状态寄存器的操作但有外部干扰触发了保护后续写操作会一直失败。排查时可以读取状态寄存器检查BP位是否被意外置位。6.3 擦写卡死和读取错乱信号问题和驱动问题两手抓擦写卡死最直接的原因是等待超时逻辑没有生效流程一直卡在忙轮询里。排查时要确认超时计数器是否运行正常以及是否在中断环境下调用了驱动且没有处理临界区。读取错乱常见于高速模式。把QSPI时钟从100MHz降到50MHz后故障消失基本可以断定是信号完整性问题。排查PCB布线、串联电阻取值、Flash供电滤波逐步降低时钟直到稳定。有时候问题不在硬件而是四线模式下的IO2/IO3引脚的复用配置不完整导致写命令发出时数据线状态错误。现象可能原因排查方法上电不启动复位循环启动模式引脚电平错误万用表量复位释放时引脚电平下载报错找不到Flash算法Keil缺少对应.FLM或地址配置不对核对QSPI映射地址替换下载算法参数偶尔全零或全FFCRC校验失误或双Slot逻辑缺陷检查CRC计算范围打印两个Slot有效状态擦除200ms后仍返回忙状态寄存器保护位被置位读取SR检查BP位数据高速读错低速正常信号完整性问题降低时钟、检查布线等长QSPI初始化后读写全失败命令序列配置和Flash型号不匹配对照Flash手册核对指令码排查这类问题效率最高的手段是提前在代码里埋好诊断信息。每步初始化、每次擦写执行、每个CRC校验都记录状态码。产品量产后出问题没有log根本无从下手。日志区划分出来的空间不是摆设就算只是异常时写一个16字节的错误码也比整片Flash拆下来读数据要靠谱。从RASC配置到Keil调试从间接模式到内存映射模式这套方案跑通之后RZ/N2L的QSPI启动和参数存储就不再是风险点了。以后再做新项目如果还是RZ系列配外部Flash我大概率会直接复用这套框架——把双Slot轮换逻辑做成独立模块底层驱动换成对应芯片的FSP接口上层业务逻辑几乎不用改。有一点想额外提醒量产固件里的QSPI时钟频率、超时时间这类参数一定要留成宏定义别散落在代码里。后期换Flash型号或者调整PCB布局只需要改一处配置重新编译就能用省去大范围排查的工夫。
返回列表