4、J-Flash命令行进阶:指定设备型号、设置烧录地址、自动序列号烧录

上一章我们聊了J-Flash命令行怎么跑起来。说白了,那只是热身。

真正到产线上,你不可能每烧一片板子都手动敲一遍命令。你得让脚本自己知道:烧什么芯片、烧到哪个地址、序列号怎么递增。

这一章,我就把这三个核心点拆开讲透。

4.1 指定设备型号:-device 参数

J-Flash命令行里,指定设备型号用的是 -device 参数。格式很简单:

JFlash.exe -device NRF52840_XXAA -open prj.jflash

但这里有个坑——设备名必须跟J-Flash内部数据库里的名字完全一致,大小写都不能错。

⚠ 我曾经踩过的坑:

有一次产线反馈说烧录失败,我查了半天,发现是设备名写成了 NRF52840,少了 _XXAA 后缀。J-Flash直接报错说找不到设备。从那以后,我每次写脚本都会先手动打开J-Flash,在设备选择框里把名字复制出来,再贴到脚本里。

怎么确认设备名对不对?我习惯用这个命令先验证一下:

JFlash.exe -device ? | findstr "NRF52"

这个命令会列出所有名字里带 NRF52 的设备。你找到目标型号,直接复制就行。

4.2 设置烧录地址:-startaddr 与 -endaddr

默认情况下,J-Flash会把整个固件烧到芯片的起始地址(通常是0x08000000或0x00000000)。但实际项目中,你经常需要指定烧录范围。

比如,你只想更新Bootloader区域,或者只烧写某个特定分区。这时候就要用 -startaddr 和 -endaddr。

JFlash.exe -device STM32F407VG -startaddr 0x08000000 -endaddr 0x08020000 -open prj.jflash -auto -exit

这个命令的意思是:只烧录从0x08000000到0x08020000这段地址。超出这个范围的数据会被忽略。

💡 我的个人习惯:

我一般会先读一下芯片的Flash布局图,确认分区边界。比如STM32F4的Bootloader通常占前128KB,那 -endaddr 就设成 0x08020000。这样即使固件文件里有多余数据,也不会误烧到应用区。

还有一个场景:你烧录的是加密固件,解密后数据量比原始文件大。这时候指定地址范围能防止溢出。

4.3 自动序列号烧录:产线实战核心

产线烧录最头疼的是什么?序列号管理。

每片板子都要有一个唯一的序列号,而且这个序列号通常要烧到芯片的特定位置(比如OTP区域或Flash末尾)。手动改?不现实。用J-Flash命令行配合脚本,可以轻松实现自动化。

4.3.1 序列号存储位置

我见过两种主流做法:

方案 存储位置 优点 缺点
方案A 固件末尾预留4字节 实现简单,不需要额外操作 每次升级固件可能覆盖
方案B OTP区域或独立扇区 序列号不会丢失 需要额外烧录步骤

我个人更推荐方案B。为什么?因为产线上经常有返修板,重新烧录固件时,方案A的序列号就被覆盖了。方案B把序列号放在独立区域,固件升级不影响。

4.3.2 脚本实现自动序列号

下面是一个Windows批处理脚本的例子。它每次烧录前,从文件里读取当前序列号,烧录完成后自动加1。

@echo off
setlocal enabledelayedexpansion

set SERIAL_FILE=serial.txt
set /p CURRENT_SERIAL=<%SERIAL_FILE%

echo 当前序列号: %CURRENT_SERIAL%

:: 生成包含序列号的二进制文件
echo %CURRENT_SERIAL% | certutil -decodehex -f - format > serial.bin

:: 烧录固件
JFlash.exe -device NRF52840_XXAA -open firmware.jflash -auto -exit

:: 烧录序列号到OTP区域
JFlash.exe -device NRF52840_XXAA -open serial.jflash -auto -exit

:: 序列号递增
set /a NEXT_SERIAL=%CURRENT_SERIAL%+1
echo %NEXT_SERIAL% > %SERIAL_FILE%

echo 序列号 %CURRENT_SERIAL% 烧录完成
🔑 关键点:

这个脚本里,我用了两个J-Flash调用:第一个烧固件,第二个烧序列号。为什么分开?因为固件和序列号的烧录地址不同,分开更清晰,也方便排查问题。

4.3.3 序列号格式处理

序列号不一定是纯数字。有时候是MAC地址格式(如 00:11:22:33:44:55),有时候是带校验位的字符串。

我遇到过最麻烦的情况:客户要求序列号包含生产日期、产线编号和流水号。比如 20240507-A-00123。这种格式用批处理处理起来比较麻烦,我一般会写一个Python脚本辅助。

# generate_serial.py
import sys
from datetime import datetime

def generate_serial(counter):
    date_str = datetime.now().strftime("%Y%m%d")
    line_id = "A"
    serial = f"{date_str}-{line_id}-{counter:05d}"
    return serial

if __name__ == "__main__":
    counter = int(sys.argv[1])
    print(generate_serial(counter))

然后在批处理里调用:

for /f %%i in ('python generate_serial.py %CURRENT_SERIAL%') do set SERIAL_STR=%%i

4.4 完整流程:从设备选择到序列号烧录

把上面三个知识点串起来,一个完整的产线烧录流程应该是这样的:

  1. 初始化:读取序列号文件,获取当前值
  2. 生成序列号数据:转换成二进制格式
  3. 烧录固件:指定设备型号和地址范围
  4. 烧录序列号:写入OTP或独立扇区
  5. 校验:读取烧录内容,确认序列号正确
  6. 递增:更新序列号文件,准备下一片

下面这张图展示了整个流程:

J-Flash 命令行自动序列号烧录流程 读取 serial.txt 生成序列号二进制文件 JFlash -device -startaddr -endaddr JFlash 烧录序列号到OTP 校验序列号 序列号 +1 写入文件 关键参数 -device: 设备型号 -startaddr: 起始地址 -endaddr: 结束地址

4.5 避坑指南

最后,分享几个我在产线上踩过的坑:

  • 序列号文件并发问题:如果多台烧录器同时读取同一个 serial.txt,会出现重复序列号。我后来改用数据库或锁文件机制解决。
  • 地址越界:有一次我忘了检查固件大小,结果烧录地址超出了芯片Flash范围。J-Flash不会报错,但烧进去的数据是错的。现在我的脚本都会先计算固件大小,再跟芯片容量对比。
  • 设备名拼写:前面说过了,大小写和符号必须完全匹配。我建议在脚本里加一个 -device ? 的预检步骤。
💡 我的建议:

刚开始做自动序列号烧录时,先用手动模式跑一遍完整流程。确认每一步都正确后,再写成脚本。这样能省掉很多调试时间。