ARTICLE DETAIL

资讯详情

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

使用J-Link和pylink直接读取Nordic与EFR32的MAC地址

使用J-Link和pylink直接读取Nordic与EFR32的MAC地址 做蓝牙开发这几年我发现自己被问得最多的一类问题不是怎么配对、怎么广播而是——“我这个板子的MAC地址是多少”设备还没烧固件、不开机、甚至程序压根没写就急着要那6字节的蓝牙MAC。一般人的第一反应是跑例程把地址打印出来。但产测、备货、写标签、绑定SN的时候哪有时间等固件跑起来。正确做法是拿调试器直接读芯片里的出厂信息区J-Link搞定Nordic配合pylink脚本把流程自动化Silicon Labs的EFR32系列也一样能读到。这篇文章就把我实际操作的整个思路、寄存器地址和避坑点都捋清楚给做产测导入、固件开发、工装软件的朋友一个直接能抄的方案。这个内容不是只讲“在哪敲一条命令”还会说明为什么地址要倒着读、为什么有的芯片读出来是8个字节以及连接不上、读出来全是F这些常见问题到底出在哪。无论你手头是nRF52832、nRF52840还是EFR32BG22、EFR32MG21只要支持SWD接口、没有被调试锁死都可以用这套方法快速拿到MAC地址。1. 项目背景什么场景下需要调试器直接读MAC1.1 产测、贴标、绑定SN时的刚需单台设备看固件日志拿MAC很容易但一上产线就完全不是一回事。流水线上的板子往往是空片或者只烧了Bootloader主程序还没下载这时候你想通过串口打印MAC是根本做不到的。更常见的场景是组装厂需要在产品外壳或包装上贴MAC标签标签数据是提前从PCBA板子上读出来的总不能每块板子都先烧一套带测试固件的程序读完再擦掉那样效率太低还容易把固件版本搞混。我见过不少工厂的处理方式用一个支持J-Link的工装把板子的SWD测试点夹住脚本自动读取芯片出厂MAC然后写入产测系统生成标签和测试记录整个过程1到2秒一块板。这种方案的前提就是能够通过调试口直接访问芯片内部的出厂信息区而不是依赖应用层固件。所以掌握用J-Link直接读MAC的方法从开发调试到工厂导入都用得上。1.2 MAC地址在芯片里到底存哪儿不同芯片厂商存储出厂MAC的位置完全不一样这也是很多人一开始找不到地址的原因。Nordic的nRF51、nRF52系列把蓝牙MAC放在FICRFactory Information Configuration Registers区域这个区域是芯片出厂时烧录的一次性配置区用户程序无法修改非常稳定。MAC相关的寄存器有两个DEVICEADDRTYPE和DEVICEADDR。DEVICEADDR的地址是0x100000A8长度48位6字节就是蓝牙设备地址本身DEVICEADDRTYPE地址是0x100000A4表示这个地址的类型比如公共地址还是随机静态地址。Silicon Labs的EFR32系列则不同它把EUI-48也就是MAC放在Flash信息页Info Page的Device Info结构体里我的实际经验里经常从0x0FE08130这个偏移开始读长度为6字节。不同系列的具体偏移略有差异但通常就在0x0FE08130附近读不到的时候可以把整个信息页拉出来人工比对几秒钟就能定位。1.3 J-Link和pylink为什么能读到这些数据J-Link通过SWDSerial Wire Debug接口访问芯片内部的调试访问端口进而读取系统总线上的任意内存映射地址。FICR和Info Page都映射在芯片的地址空间中所以调试器可以直接读。这里的关键点是读取过程不需要CPU运行任何程序芯片上电后SWD接口就是可用的除非启用了调试锁。pylink是SEGGER官方提供的Python绑定库它封装了J-Link的DLL接口。你可以把pylink理解为用Python代码来控制J-Link就像你在J-Link Commander里手动敲命令一样。安装后只需要几行代码就能连接目标芯片、读取内存、控制CPU暂停和复位。用pylink而不是纯Commander的好处是容易做循环一块板读完后自动读下一块还能把结果直接写入Excel或数据库。2. 开工前的准备驱动、接线与工具链里的坑2.1 J-Link驱动安装与版本核对官方驱动从SEGGER官网下载在Windows下安装后会自动包含J-Link Commander和DLL库pylink就是靠这个DLL工作的。Win11下安装时需要注意装完最好重新拔插一次J-Link并且在设备管理器里确认枚举出来的是“J-Link”而不是带黄色感叹号的未知设备。如果驱动装不上多半是系统强制驱动签名导致的可以先试试以管理员身份运行安装包。版本选择上我建议直接装最新版但要注意一个特别常见的坑如果你用的是市面上某些非正规渠道的J-Link V9新版驱动的DLL校验很严格主动弹出“J-Link is a clone”之类提示。遇到这种情况要么换正版工具要么老老实实用旧版本驱动。如果是公司产线我强烈建议采购正版J-Link或者Segger的批量授权山寨工具在产线上出问题排查起来会让人崩溃。另外和你电脑上已有的工具链版本也会有冲突。比如你先装了Nordic的nrfjprog它自带了一版J-Link驱动之后你又装SEGGER新版驱动两者可能会覆盖彼此的DLL。症状就是J-Link Commander能连但nrfjprog报错或者反过来。解决办法是全部重装同一版本的SEGGER驱动再装nrfjprog时不要勾选“Include SEGGER J-Link driver”之类的选项。2.2 SWD接口定义与线序核对J-Link的接口有两种常见物理形态一种是20pin JTAG排针另一种是10pin 1.27mm SWD排针。实际读MAC只需要4根线SWDIO、SWCLK、GND、VTref。VTref是从目标板采样回来的参考电压用于电平匹配一定要接否则J-Link无法判断目标板电压。20pin接口的常用定义如下Pin名称说明1VTref目标板参考电压2TMS / SWDIOSWD数据线3GND地4TCK / SWCLKSWD时钟线6TDO / SWO可接读MAC不需要10nRESET复位信号建议接10pin接口则更紧凑常见于Nordic DK板、Silicon Labs开发板以及很多量产的测试点设计。它的定义一般是这样1脚VTref2脚SWDIO3脚GND4脚SWCLK5脚GND6脚SWO10脚nRESET。不同厂家的丝印可能略有差异但万变不离其宗只要保证SWDIO、SWCLK、地、参考电压四根线没错就行。我踩过的一个坑是目标板上有两个SWD接口一个在板载调试器上另一个是外部调试器的排针两者接反了。如果你用板载J-Link读的是板载调试器连接的MCU你想用外部J-Link读板上的目标MCU时记得先断开板载调试器的连接否则总线有冲突。2.3 pylink库安装与验证pylink库用pip安装就行推荐在虚拟环境里装避免和系统Python环境互相污染。pip install pylink安装完成后可以先用一小段代码验证J-Link是否被识别import pylink jlink pylink.JLink() jlink.open() print(J-Link serial:, jlink.serial_number) jlink.close()这段代码如果正常执行说明驱动和pylink库都能工作。如果报错提示找不到DLL检查一下SEGGER驱动是否安装以及Python进程是32位还是64位pylink需要和J-Link DLL的位数匹配。我自己有一次在Python 32位环境下跑J-Link DLL是64位的折腾了半天才反应过来Python环境统一用64位能省掉大部分麻烦。3. Nordic篇nRF52系列读取MAC的完整操作3.1 用J-Link Commander命令行直接读DEVICEADDR最快速的方式是用J-Link Commander手动操作。连接nRF52832时命令行这样写JLink.exe -device nRF52832_xxAA -if SWD -speed 4000 -autoconnect 1不同芯片的型号字符串可以在SEGGER支持列表里查到比如nRF52840是nRF52840_xxAAnRF51822是nRF51822_xxAA。进入J-Link交互界面后执行mem8 0x100000A8 6这条命令的意思是从0x100000A8地址开始读取6个字节的内存。在我实际项目中某块nRF52832板子的输出是50 7A 42 01 72 C8这一串并不是最终要的MAC字符串因为DEVICEADDR寄存器在FICR里是小端存放的内存里读到的低地址字节其实是MAC的低位必须倒过来读真实MAC应该是C8:72:01:42:7A:50这个倒序问题坑了非常多人。你只要记住一个原则nRF52的FICR DEVICEADDR在内存里的字节顺序和打印出来的MAC完全相反。想验证的话把读到的结果和你拿手机扫描到的板子蓝牙MAC对照一下能对上就说明顺序对了。3.2 用pylink脚本读取并自动转换字节序手动敲命令适合临时验货批量操作还是得靠脚本。下面这段是我在产测工装里实际用的代码做了最小化精简import pylink from pylink.enums import JLinkInterfaces DEVICE_ADDR_REG 0x100000A8 def read_nordic_mac(devicenRF52832_xxAA): jlink pylink.JLink() jlink.open() jlink.connect(device, interfaceJLinkInterfaces.SWD, speed4000) jlink.halt() raw jlink.memory_read8(DEVICE_ADDR_REG, 6) mac_bytes raw[::-1] # FICR小端倒序才是真实MAC jlink.go() jlink.close() mac_str :.join(f{b:02X} for b in mac_bytes) return mac_str if __name__ __main__: print(read_nordic_mac())这段代码有几个关键细节。halt()是为了让CPU停下来避免调试访问和总线操作竞争走之前调go()让设备恢复执行避免下一块板子上电后处于暂停状态。memory_read8返回字节列表[::-1]实现倒序。如果你是Windows环境连接速度4000kHz一般比较稳如果遇到驱动能力弱的板子降到1000kHz往往能解决问题。3.3 和nrfjprog的结果对照验证为了确认读出来的MAC是对的我习惯再用Nordic官方工具nrfjprog交叉验证一遍nrfjprog --memrd 0x100000A8 --n 6或者更直接新版nrfjprog支持nrfjprog --devices但那打印的是Device ID不是MAC。最简单的还是读内存自己解析。nrfjprog读出来的原始字节同样需要倒序所以不要以为换个工具就不用来回倒了。我之前测试过同块板子用nrfjprog、J-Link Commander和pylink读出来的数据完全一致倒序后和手机扫描到的MAC也一致。这样两边一比对基本可以确认地址、读法和解析逻辑都正确。4. Silicon Labs篇EFR32系列读取MAC4.1 EFR32的Device Info和EUI48在哪Silicon Labs的EFR32系列芯片包括BG22、BG13、MG21这些常见型号出厂信息存放在Flash信息页Info Page的Device Info结构体中。EUI48就是这个结构体里的48位MAC地址字段它通常代表芯片的全球唯一MAC。对EFR32系列我的经验里取值地址从0x0FE08130开始读6个字节即可。需要注意EFR32的MAC存储方式不像Nordic那样“简单粗暴地反序”大多数型号内存中读出的字节顺序就是打印顺序也就是你从0x0FE08130读到的第1个字节是MAC的最高位最后一个字节是最低位。这一点和Nordic正好相反所以换平台时千万不要套用同一个脚本逻辑。4.2 用J-Link Commander操作EFR32连接EFR32BG22时命令行这样写JLink.exe -device EFR32BG22C224F512IM40 -if SWD -speed 4000 -autoconnect 1进入交互界面后读取mem8 0x0FE08130 6我手上的EFR32BG22模块读出来的输出是E8 94 F6 2C 15 68这个直接就是MACE8:94:F6:2C:15:68。Silicon Labs和Nordic的字节序规则不一样所以不是说所有芯片读出来都要反转而是要看厂商具体实现。如果0x0FE08130读出来感觉不太像有效MAC或者全F全0可以扩大范围读取mem8 0x0FE08100 128把整个Device Info结构拉出来人工找一下6字节的EUI48。通常它就在Unique ID后面而且和芯片外壳丝印或出厂标签上的MAC一致很好认。4.3 pylink脚本读取EFR32并与Simplicity Commander对照对应pylink脚本如下import pylink from pylink.enums import JLinkInterfaces EUI48_ADDR 0x0FE08130 def read_efr32_mac(deviceEFR32BG22C224F512IM40): jlink pylink.JLink() jlink.open() jlink.connect(device, interfaceJLinkInterfaces.SWD, speed4000) jlink.halt() mac_bytes jlink.memory_read8(EUI48_ADDR, 6) jlink.go() jlink.close() mac_str :.join(f{b:02X} for b in mac_bytes) return mac_str if __name__ __main__: print(read_efr32_mac())Silicon Labs官方也提供Simplicity Commander工具可以直接在命令行确认MACcommander device info输出的信息里包括Unique ID和MAC Address用它对照pylink读出来的结果一目了然。这两条路径我都试过数据一致。如果你手头既装了Simplicity Studio又装了J-Link注意Commander默认用的是Silicon Labs自带的驱动和SEGGER J-Link互不冲突可以直接并存。5. 常见问题与排查技巧实录5.1 连接不上目标芯片卡在Cannot Connect这个是遇到最多的问题。J-Link Commander报“Cannot connect to target”时我一般按下面顺序排查先看VTref是否被检测到如果J-Link软件界面显示目标电压为0说明参考电压没接好或者是目标板根本没上电。然后用万用表量一下SWDIO和SWCLK有没有被拉低很多开发板上的按键、外设把这两个引脚复用了导致调试口被占住。最后把速度往下调4000kHz不行换1000kHz再不行换100kHz低速一般都能连上。另外目标芯片如果被启用了读写保护或者调试锁SWD会被禁掉。Nordic里如果设置了APPROTECTEFR32里如果设置了Secure Lock调试器都进不去这种情况只能先通过全片擦除或者厂商的解锁流程恢复。产线上如果碰到一致性的“连不上”优先怀疑是不是上一道工序把锁开了。5.2 读出来全是0xFF或者全是0x00内存读出来全是F基本是调试器访问的地址无效或者芯片没有正确连接。先说地址无效Nordic的FICR地址0x100000A8只对nRF51、nRF52系列有效nRF53、nRF70的地址空间完全不同EFR32的EUI48偏移也随型号变化不要拿通用地址硬套。再有一个可能是芯片被保护后内存读出来会被掩码成F或者0。如果读出来全是0一般是地址对但读的方式不对。比如用mem32去读6字节的MAC读出来的32位数据和16位数据拼在一起很容易搞错。统一用mem8按字节读然后自己拼顺序最稳妥。5.3 读到的MAC和标签、实际广播地址对不上这个现象其实要分两层看。第一层是字节序问题Nordic需要倒序这个前面已经反复强调。第二层是“MAC地址”这个概念本身有歧义芯片出厂信息区里的EUI48或DEVICEADDR是硬件出厂地址但BLE设备在运行时完全可以使用随机静态地址或私有地址不一定广播真实的MAC。比如nRF52的FICR里除了DEVICEADDR还有一个DEVICEADDRTYPE如果地址类型是随机静态地址那么设备每次上电可能动态生成地址。Silicon Labs的蓝牙协议栈也允许设备不采用EUI48作为公共地址运行时的可发现地址可能是另一个值。所以你要是发现“读出来的和手机扫码看到的不一致”先搞清楚你扫描到的是不是公共地址类型再判断是否读取有误。6. 顺手把它做成产测工具6.1 一个完整的批量读取脚本把Nordic和EFR32的逻辑整合到一个脚本里是产测工装比较理想的形态。下面是一个简化版的可运行示例支持通过命令行参数指定平台和数量import argparse import time import pylink from pylink.enums import JLinkInterfaces def make_reader(device, reg, reverseFalse): def read(): j pylink.JLink() j.open() j.connect(device, interfaceJLinkInterfaces.SWD, speed4000) j.halt() raw j.memory_read8(reg, 6) j.go() j.close() data raw[::-1] if reverse else raw return :.join(f{b:02X} for b in data) return read if __name__ __main__: parser argparse.ArgumentParser() parser.add_argument(--platform, choices[nordic, efr32], requiredTrue) parser.add_argument(--count, typeint, default1) args parser.parse_args() if args.platform nordic: reader make_reader(nRF52832_xxAA, 0x100000A8, reverseTrue) else: reader make_reader(EFR32BG22C224F512IM40, 0x0FE08130, reverseFalse) for i in range(args.count): mac reader() print(f{i 1}\t{mac}) time.sleep(0.3)这个脚本每读一块板子会等待300ms让工装气动夹爪或操作员有时间换板子。实际产线里我会把print替换成写入CSV或数据库再关联一个SN二维码扫码枪输入形成完整绑定链路。6.2 后续扩展的一些思路一次读一块还是不够快的话可以同时挂多路J-Link到同一台电脑pylink支持枚举当前连接的所有J-Link循环打开、读取、关闭。每一路对应一个工位产线节拍能再提一截。除了MAC同一个调试会话里还能一次性读芯片唯一ID、Flash大小、硬件版本甚至校验固件烧录结果这些都可以作为产测数据的一部分写进数据库避免为了不同参数来回切换工具。配合CMake或者CI系统甚至可以在固件编译完成后自动连接开发板烧录、读MAC、跑基础功能测试整个流程下来能节省大量手工操作时间。我个人在实际项目中的体会是读MAC这件事看起来只是“敲一条命令”但真正落地到产线时要考虑字节序、器件型号差异、锁保护策略、数据怎么和SN绑定这些细节。先用J-Link Commander手动验证地址和顺序再上pylink自动脚本最后再迭代人机交互流程是风险最小的推进路径。最后再分享一个小技巧无论Nordic还是EFR32首次在一款新芯片上操作时都先读一块样片来验证地址和解析方式如果你发现读出的6字节和外壳标签或官方工具输出一致再批量套用脚本。毕竟不同批次、不同封装、不同固件烧录策略都可能让你所谓的“通用地址”失效留一手验证环节永远不亏。
返回列表