ARTICLE DETAIL

资讯详情

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

DTM命令实战:绕过HCI/ACI直接控制射频芯片收发测试

DTM命令实战:绕过HCI/ACI直接控制射频芯片收发测试 做射频测试的朋友应该都有这个体会一颗芯片拿在手里想让它老老实实吐一个单载波很多人第一反应是翻HCI命令表或者打开厂商SDK调ACI接口。但在产线和实验室里这两条路经常走不通——芯片还没加载固件协议栈根本没跑起来HCI链路压根建立不了ACI接口又依赖整个系统服务SDK一套下来小半天没了。这时候你就会明白DTM命令才是真正干糙活儿的工具。今天这篇东西不是给你复述标准文档而是把我在几个项目里实际控制DTM的方式、踩过的坑、整理出来的套路一次性讲透。适合三类人看做射频测试的工程师、写产测固件的嵌入式开发、以及被WiFi芯片aci测试流程绕晕的测试开发。核心就一件事在不依赖HCI/ACI的前提下如何用DTM命令直接控制芯片的收发机完成发射、接收、灵敏度等一系列底层测试。1. 控制面选型真相为什么DTM命令能绕过HCI/ACI1.1 HCI和ACI的控制链路到底依赖什么先厘清概念。HCI全称Host Controller Interface是蓝牙协议栈里主机和控制器之间的标准接口。你用电脑连蓝牙耳机时电脑里的协议栈通过HCI把“扫描”“连接”“发数据”这些指令交给控制器。ACI在WiFi/BT combo芯片里更常见全称通常是Application Controller Interface或厂商自定义的私有问题本质上也是给应用处理器控制无线芯片的管理通道。这两条通道都有个共同特点它们跑在完整的协议栈之上至少需要一个可用的Host。标准HCI要等控制器固件加载完LMP层初始化完成才能收发命令ACI更麻烦往往要求系统里已经跑着蓝牙协议栈或WiFi驱动你在PC端调个小工具发命令只是表面动作底层SDK会做一堆状态机切换。有些量产芯片甚至把HCI/ACI物理引脚复用掉了根本不给外部主机留口子。所以测试工程师遇到的第一堵墙就是我手里只有一颗裸芯片或者只有个最小系统板连固件都是临时烧的这时候HCI/ACI就是镜花水月。协议栈没起来这套高层接口就是空中楼阁。1.2 DTM命令的本质从协议栈里抽一根直连PHY的线DTM是Direct Test Mode的缩写中文常叫直接测试模式。它最初是蓝牙规范定义的一种测试状态作用是让测试仪或主机能直接控制芯片的射频收发机不碰协议栈、不建立连接、不跑加密和跳频。但注意规范里给出的DTM控制命令通常被映射到HCI的LE Transmitter Test / Receiver Test指令上这也是很多人一查资料就绕回HCI的原因。而我这篇要说的DTM commands是很多芯片厂商在标准HCI之外额外提供的一套“私有DTM命令”直接挂在UART测试串口上。它们不依赖HCI控制器逻辑也不依赖ACI系统服务而是由芯片里的测试模式固件直接解析。你可以把它理解成不用去按房间里各种智能开关面板HCI/ACI而是直接进配电箱合闸灯一样能亮而且没有面板背后的逻辑限制。这套命令的好处非常实际响应快命令字少适合产线快速切换测试项。不依赖外部协议栈芯片上电就能用。可以绕过HCI/ACI的权限锁、固件版本限制。有些芯片在量产前连协议栈固件都没有但测试固件已经内置DTM命令。1.3 什么时候必须用DTM而不是HCI/ACI我总结了几类典型场景你可以对照一下自己是不是也遇到过。第一类是芯片进入量产测试阶段。产线上的DUT往往只烧了一个最小测试固件为了省flash压根不放完整协议栈。HCI和ACI都没有代码实体唯一可用的就是DTM测试固件里的命令解析器。第二类是射频性能debug。比如客户报某个频点功率偏低你想在不被协议栈干扰的情况下让芯片连续发单载波或固定调制包。用HCI发LE Transmitter Test也可以但如果控制器固件版本太老标准命令的参数范围不够用比如无法设置任意频率点、无法连续自定义包长这时候厂商私有的DTM命令往往提供更大的灵活度。第三类是WiFi/BT combo芯片里的WiFi部分要做aci测试。现在很多测试同学把“WiFi芯片aci测试”和“蓝牙DTM测试”混在一起谈实际上两者控制面不同ACI测的是WiFi控制信道与数据面交互DTM测的是蓝牙/射频前端底层的收发能力。当你想单独验证2.4G射频前端是否正常又不想让WiFi/BT协议栈参与时DTM命令就是最干净的控制手段。2. 进入DTM的硬件入口与命令帧骨架2.1 测试串口与测试引脚的识别DTM命令在物理链路上绝大多数芯片用的是UART。原因很简单UART便宜、调试工具多、波特率灵活而且产线测试板很容易把UART引到排针或者探针点上。你需要拿到硬件原理图找到三个信号DTM_TX / DTM_RX测试串口的收发引脚。DTM_EN或TEST_MODE进入DTM模式的使能引脚。DTM_GND与PC串口共地别忽略。有些芯片会复用普通GPIO通过上拉/下拉配置进入DTM。比如内部bootloader在上电时检测某个pin的电平如果拉高就直接进入DTM命令循环。这个引脚在量产测试板上通常会被单独拉出来方便测试治具用探针接触。一个容易踩的坑不要想当然地把DTM_RX接到PC的RXD。UART是交叉连接的PC的TXD接芯片的RXDPC的RXD接芯片的TXD。很多新手第一块板子没反应查到最后就是两根线接反了。2.2 一条DTM命令由哪些字段组成各家芯片的DTM命令格式并不完全统一但骨架大同小异。这里我给出一个最通用的抽象模型你拿到厂商命令表后可以按这个框架去套。一般命令帧会包含同步头常见为固定字节如0xAA 0xAA或0x55 0x55用来让芯片识别一条命令的开始。方向字段表明是下行命令还是上行响应有些芯片用bit位有些直接固定数值。命令字比如0x01代表发射测试0x02代表接收测试0x03代表读取结果。参数区长度不定包含分频因子、信道号、发射功率、包类型等。校验字段常见为累加和、CRC或简单的取反校验。以某芯片的发射命令为例它的格式可能是0xAA 0xAA | 0x01 | 0x10 | 0x0F 0x00 0x00 0x00 | 0x2A 同步头 | 下行 | TX命令| 频率字 功率字 包类型| 累加和我给的是示意不是具体芯片真实命令。但你一定要建立这个认知频率、功率、包类型这几个参数几乎在所有DTM命令里都是核心。频率往往直接给一个内部分频值或者信道号功率给的是寄存器增益值而不是dBm包类型决定你发的是单载波还是PRBS9调制包。2.3 DTM命令和标准HCI测试命令的差异标准蓝牙HCI里也有LE Transmitter Test、LE Receiver Test命令但它们走的是HCI分组结构包类型是0x01、0x02频率参数通过LL channel index表示而且必须在Controller已经跑起来的前提下由Host通过HCI传输层发送。厂商私有DTM命令则灵活得多。我可以把频率参数直接写成2402 MHz对应的寄存器值或者用非标信道号映射可以连续发送自定义数量的包可以单独控制调制开关输出纯载波还可以在TX和RX之间快速切换不需要先Stop Test再Start Test。但代价是不通用。你换一颗芯片命令字、同步头、参数顺序可能全变了。所以我建议你把DTM命令控制层封装成一个独立模块不要和业务逻辑混在一起。后面我会给出封装思路先继续往下看。3. 搭建DTM控制环境并跑通第一条发射命令3.1 软硬件清单与连接方式先说硬件。最低限度你需要一块被测板DUT保证DTM引脚能访问到。一个USB转UART小板尽量选带CP2102/FT232方案的稳定性比CH340稍好一点但CH340也能用。杜邦线若干或者一个简易治具。频谱仪或综测仪用来验证发射是否成功。软件方面串口助手sscom、SecureCRT都行用于手工调试PythonPyserial用于后续自动化。连接顺序是USB转UART小板插入PC小板TXD接DUT的RXD小板RXD接DUT的TXD共地。然后把DTM_EN引脚电平拉高或拉低具体看芯片手册。注意供电有些测试板从USB口取电如果DUT射频发射时瞬间电流较大USB供电不足会导致输出功率不稳。量产测试板一定要单独稳压供电。3.2 进入DTM模式的时序DTM模式不是插上串口就能用的。通常有两种进入方式第一种是上电即进入。芯片的bootloader在reset释放时检查DTM_EN电平若满足条件则跳过正常启动流程直接运行测试固件。这时你只需要先把DTM_EN设置到正确电平再给DUT上电。第二种是运行中切换。芯片已经跑着正常固件通过某个GPIO中断或特殊串口序列进入DTM。这种方式调试方便但容易误触产线上不推荐。最容易翻车的是时序。我见过有人在主控复位前就拉高了DTM_EN结果芯片上电初始化时引脚状态还没稳定bootloader漏检直接进入了正常启动流程。正确做法是用测试治具控制DUT电源确保电源稳定后延迟几十ms再把DTM_EN置为有效电平最后拉一下reset引脚或者直接重新上电。伪代码逻辑就是这样DUT_OFF set DTM_EN ACTIVE DUT_ON sleep 50ms assert reset pulse (至少10ms低电平) sleep 200ms等芯片内部测试固件起来后你通过串口发任何无效命令它都应该有响应或至少不产生无关数据。3.3 用Python/PySerial发送第一条TX命令进入DTM后用Python发命令是最快的验证方式。先装库pip install pyserial然后写一个最小函数import serial import time ser serial.Serial( portCOM9, # Windows baudrate115200, # 波特率以芯片手册为准 timeout0.5, parityN, stopbits1, bytesize8 ) def send_dtm_command(cmd): ser.reset_input_buffer() ser.write(bytes.fromhex(cmd)) time.sleep(0.05) resp ser.read(64) print(cmd, -, resp.hex()) return resp # 进入发射模式示例频率2402MHz功率寄存器值0x0F调制包PRBS9 send_dtm_command(AAAA 01 10 0F00 0000 000F 2A)命令字和同步头只是示意重点在于流程先清空输入缓冲发送延时读响应。不要发送后立刻读芯片UART解析也需要时间。如果多命令连续切换中间最好加20ms以上的间隔方便内部状态机稳定。3.4 在频谱仪上验证命令是否生效命令发出后别急着看代码返回值。第一步应该是看频谱仪有没有反应。把频谱仪的中心频率设到命令设定的频率SPAN设宽一点比如5MHzRBW设100kHzVBW可以比RBW大十倍。如果命令生效你会看到一条干净的谱线或者调制包谱。这里有个经验如果看到频谱仪上有跳动的噪声但始终没有稳定的信号大概率是命令里的频率字或者功率字解析不对。先用单载波命令测试把功率设为最大可调值信号应该非常明显。确认信号出来后再记录频谱仪实测的功率和频率和配置参数对比。第一次跑通通常就卡在这几步跑通之后DTM控制就成功了一大半。4. TX/RX测试命令详解与WiFi芯片ACI测试的分工4.1 发射测试工具频率、功率、包类型怎么配合发射测试是DTM命令最主要的应用场景。不同的测试项需要不同的包类型这点很多人忽略。频率校准/晶振校准用单载波CWContinuous Wave输出。芯片内部调制器关闭射频前端直接输出一个点频正弦波频谱仪上就是一根很窄的谱线。通过对比实际频点与目标频点的偏差可以算出晶振误差。功率校准也用单载波功率检测最准。某些芯片的调制包会引入PAPR峰均比导致峰值功率和平均功率差别较大而单载波可以直接读平均功率。调制质量测试需要发调制包通常是PRBS9或PN9伪随机序列覆盖0/1跳变和极限符号序列。这时频谱仪要开对应蓝牙标准的模板比如BT的发射模板、ACPR、EVM等。在命令参数上你需要先弄明白芯片的数据手册里“功率寄存器值”和“实际dBm”的对应关系。很多芯片不是线性映射只是分档寄存器值。量产标定程序会扫描多个功率寄存器值从频谱仪读回实际功率做成一张校准表。这就是DTM命令在产测中的核心价值给一个寄存器值测一个实际功率形成校准曲线。4.2 接收测试与PER/RSSI回传值的解析接收测试比发射测试略复杂。常见的做法是测试仪综测仪发送已知数据包DUT进入接收模式接收统计接收到的包数量、CRC错误数、RSSI然后把统计结果回传到DTM串口。DTM命令控制DUT进入RX模式后芯片会在内部统计一段时间内的结果。有些芯片支持手动查询有些芯片会在统计结束后自动上报。你的控制程序要能区分这两种行为。假设手动查询流程一般是发命令进入接收模式。设置测试时长或包数量。等待测试完成。发送查询命令读取回传值。解析PER包错误率和RSSI。响应数据里通常包含接收总包数单位可能是一个或多个字段错误包数RSSI平均值同步丢失次数解析时注意字节序。比如芯片返回两个字节的包数统计如果低字节在前你要按小端解析。举个例子def parse_rx_result(raw): # raw 假设从这里开始是统计数据 total_packets int.from_bytes(raw[0:2], byteorderlittle) crc_error int.from_bytes(raw[2:4], byteorderlittle) rssi_raw int.from_bytes(raw[4:6], byteorderlittle, signedTrue) per (crc_error / total_packets) * 100 if total_packets else -1 return total_packets, crc_error, rssi_raw, per这个解析逻辑是通用的具体字段偏移以手册为准。拿到PER之后和蓝牙规范里灵敏度测试的极限值对比就能直接判断DUT的接收性能是否达标。4.3 为什么WiFi芯片aci测试不能替代DTM接收测试做WiFi/BT combo芯片项目的朋友最近经常听到“wifi芯片aci测试”这个词。ACI测试主要验证WiFi控制接口、命令队列、数据路径和连接管理功能它工作在芯片正常通信状态下需要完整固件和协议栈支撑。DTM接收测试则完全不同。它直接让射频前端进入接收模式不建链、不关联AP、不走TCP/IP栈只看物理层能不能收到并正确解调信号。两者的测试目的不一样。我曾经见过一个项目在WLAN吞吐测试里发现WiFi芯片的灵敏度下降了3dB第一反应是WiFi射频前端有问题。但通过DTM方式单独测WiFi所在的2.4G频段接收PER结果完全正常最后发现是ACI配置里的接收天线分集参数被改错了。如果只用ACI测试这问题大概率要被定位成硬件问题浪费好几块板子。所以正确的思路是物理层指标用DTM命令验证控制面和数据面用ACI或HCI验证两者互补不能互相替代。5. DTM调试中的高频踩坑点与我的排障路径5.1 命令发出去了芯片却没有任何反应这是最常见的现象也是排查链路最长的一个坑。按照下面路径走能解决90%的问题。第一步确认DUT真的进入了DTM模式。可以发一个芯片特有的查询命令比如版本号查询看是否有固定长度响应。如果连查询命令都没反应直接检查DTM_EN时序和reset流程。第二步检查UART接线。用示波器量芯片RXD引脚的波形看PC发过来的数据是否到达芯片端。如果波形都看不到就是接线或转接板问题。第三步检查波特率。有些芯片在DTM模式下波特率固定为9600或115200但可能有上电自动检测机制。如果检测机制支持自定义波特率你的串口软件会有一个“自动检测”选项但量产脚本不要依赖这个直接固定波特率。第四步确认命令校验。校验字段算错的话芯片会认为命令无效有些芯片会返回错误帧有些芯片干脆什么都不回。可以在发送前打印命令十六进制串手工算一遍累加和排除代码bug。第五步如果仍然无反应用逻辑分析仪抓串口数据看芯片有没有在收到命令后拉低TX。如果TX一直为高那就是芯片测试固件没起来或者UART引脚配置不对。5.2 进入DTM后无法退出或设备卡死有些芯片进入DTM后是一个死循环只有复位才能退出。如果你发的命令格式不对芯片可能跑到一个未知分支里卡死这时再发什么都无效。我的做法是在控制脚本里专门加一个recover函数先拉低DTM_EN然后对DUT硬件复位重新进入DTM模式。不要指望发一条软复位命令就能退出很多厂商根本没实现。另外一个常见问题是DTM模式占用了某些GPIO导致测试板上的LED、按键功能失效。这时不要困惑这是正常的。DTM模式下所有非必要外设都可能被关掉。5.3 频率和功率实测值偏差过大如果你发的发射命令明确设置了2402MHz但频谱仪上看到中心频率偏了几百kHz甚至1MHz先不要怀疑命令不对。先查晶振。DTM模式下的频率精度完全依赖参考时钟。如果DUT使用的是外部晶振检查晶振的实际频率和标称频率是否一致误差可能来自匹配电容。如果DUT使用的是内部RC振荡器那频率误差大是正常的这种芯片量产时一般需要先做射频校准把晶振频率修正确实写入efuse或OTP。功率偏差也是类似的排查逻辑。先确认供电电压是否稳定再确认频谱仪线损校准是否做了。很多人在实验室没有做线损补偿导致实测功率比芯片输出功率低1-2dB这不是芯片的问题。5.4 串口被别的工具抢占DTM命令夹杂HCI垃圾数据调试combo芯片时同一颗芯片上可能有多个串口号资源一个用于DTM另一个用于HCI日志或ACI测试。有时候你用串口助手打开了DTM串口但另一个软件占用了HCI串口DTM串口是独立的按理说不会冲突。但如果是同一个UART通过软件切换作为HCI还是DTM口就会出问题。比如你上一轮用ACI测试往串口发了一堆字符串命令没有正确退出ACI模式然后切换到DTM后芯片buffer里还残留数据DTM命令解析器会被残留数据干扰导致响应异常。这种问题最有效的办法是每次切换控制模式前把DUT完整复位重新进入目标模式。另外测试程序不要同时打开同一串口关掉所有串口工具再运行脚本。6. 从手工DTM命令到自动化产测脚本6.1 先封装一个设备控制类手工调通DTM命令后接下来就是产线自动化。我建议不要直接在测试流程里写一堆send_dtm_command(AAAA...)这种代码而是封装成设备类把命令字、参数转换、响应解析都收进去。一个简单的类设计class DTMController: def __init__(self, port, baudrate115200, timeout0.5): self.ser serial.Serial(port, baudrate, timeouttimeout) self.serial_number self.get_version() def _send(self, data): self.ser.reset_input_buffer() self.ser.write(data) time.sleep(0.05) return self.ser.read(64) def tx_start(self, freq_khz, power_reg): # 构造命令并发送 cmd self.pack_tx_command(freq_khz, power_reg) resp self._send(cmd) return self.parse_resp(resp) def rx_start(self, freq_khz): cmd self.pack_rx_command(freq_khz) resp self._send(cmd) return self.parse_resp(resp) def rx_result(self): cmd self.pack_rx_result_command() resp self._send(cmd) return self.parse_rx_result(resp)这样的好处是换芯片平台时只需要改pack_tx_command、pack_rx_command这些方法上层测试逻辑不用动。6.2 与频谱仪联动形成Pass/Fail闭环产测不能只看芯片返回的“命令已经被接受”必须把外部仪表测量的结果拉进判定逻辑。常用做法是让Python通过SCPI/VISA控制频谱仪每次DTM命令发射后从频谱仪读取中心频率和峰值功率然后和阈值比较。伪代码import pyvisa rm pyvisa.ResourceManager() sa rm.open_resource(TCPIP0::192.168.1.100::INSTR) sa.write(:FREQ:CENT 2402MHz) sa.write(:FREQ:SPAN 200kHz) sa.write(:CALC:MARK1:MAX;) freq_meas float(sa.query(:CALC:MARK1:X?)) power_meas float(sa.query(:CALC:MARK1:Y?)) # 判定 if abs(freq_meas - 2402e6) 30e3: fail(频率偏差超过30kHz) elif power_meas -5: fail(功率过低) else: pass在DUT发射命令和频谱仪测量之间一定要加延时一般给100ms等频谱仪扫描稳定。不要用固定延时去替代底部的“等待测量完成”查询最好读仪表的operation complete状态。6.3 混合方案RF校准走DTM信令吞吐走ACI最后谈谈工程实践中更成熟的产测流程。一颗WiFi/BT combo芯片往往要同时覆盖两条测试路径。第一条路径是射频校准走DTM命令。在DUT刚上电、协议栈还没加载时先用DTM命令扫频率、扫功率、测PER把晶振修正值和功率校准表写入芯片存储。这个阶段完全不依赖HCI/ACI速度快、稳定性高。第二条路径是功能测试走ACI或HCI。校准完成后给DUT加载完整固件通过ACI配置WiFi连接测试AP跑吞吐数据通过HCI执行蓝牙配对、连接、音频传输测试。这一阶段验证的是协议栈和上层应用功能。两条路径的切换要干净。我建议在产测程序里用两个独立的进程或线程因为DTM和ACI会共用一些底层资源不能同时执行。先跑完DTM全部项断电重启再进ACI测试流程这样最不会出幺蛾子。我在几个量产项目里都是这样落地的。一开始图省事想把DTM和ACI命令混在同一个串口流程里结果状态机一团乱麻后来强制分段测试时间反而更短误测率也降下来不少。如果你正准备做类似的测试系统我建议你从最简单的场景开始先把一块DUT的DTM发射命令手动调通记录下频率实测值和功率实测值然后封装成脚本再扩展到全频段扫描。不要一上来就想着把整套产测自动化做完。DTM命令看起来简单但不同芯片、不同板卡、不同仪表组合在一起细节非常多。把第一步走稳了后面的路自然就顺了。
返回列表