固件刷完后不再响应串口,首先要判断丢失的是应用通信、下载入口还是硬件调试访问。它们的依赖不同:应用可能已经崩溃,ROM 下载仍可进入;调试口也可能因电源、连线或安全配置不可访问。不能用“JTAG/SWD 总能救砖”或“USB 能刷就能调试”概括。
| 能力 | 做什么 | 依赖什么 |
| 核心调试 | 暂停、单步、断点、寄存器/内存访问 | 芯片调试模块、权限、探针及服务支持 |
| 固件编程 | 擦除、写入并校验镜像 | 编程接口/算法、存储配置与工作条件 |
| 边界扫描 | 对覆盖的引脚/互连施加与采集测试状态 | 边界单元、BSDL、测试向量与板级配合 |
| ROM / Bootloader 下载 | 在启动阶段接收镜像 | 指定入口、传输协议及镜像格式 |
| 在线升级 | 运行中接收并切换固件 | 服务、验证、掉电恢复与版本策略 |
JTAG:传输入口不保证每一种测试能力
JTAG 常见信号为 TCK、TMS、TDI、TDO,TRST 是否存在取决于实现。TMS 在 TCK 边沿驱动 TAP 状态机,TDI/TDO 串行移入移出所选寄存器;调试模块和边界扫描是建立在相关结构上的不同用途。不是所有带 JTAG 的芯片都支持所需核心调试或完整边界扫描。
菊花链通常让一个器件 TDO 接下一个 TDI,主机需要知道链顺序及各器件指令寄存器长度等配置;其他器件可通过 BYPASS 减少移位开销,但不会自动从物理链消失。混合电压域也需匹配,不能假定所有 JTAG 目标都接受 5 V。
边界扫描可在不运行应用固件的情况下检查部分数字互连,但不是“直接控制每个引脚”。模拟、电源、高速专用引脚及没有边界单元的网络可能不覆盖;测试还需避免与板上其他驱动器争用。BSDL 描述器件边界结构,结合板级网表与向量才能定义实际覆盖率。OpenOCD:边界扫描介绍及工具支持边界
Microchip:TAP 状态机
移进去的位,什么时候真正生效?
从复位状态输入 TMS 序列 0、1、0、0 到达 Shift-DR,并完成 Capture-DR 的采样。此后每个 TCK 都会移位,包括 TMS=1 离开 Shift-DR 的那个周期。进入 Pause-DR 后可任意停留,已更新值不会改变;只有经过 Update-DR 才提交。这里保留协议本来的顺序约束,但每一步由你直接选择输入,可以走错路、暂停或重新同步。连续 5 个 TMS=1 可回到 TAP 复位状态,这不等于复位 CPU 或清除芯片数据。
正在呈现知识画面
移进去的位,什么时候真正生效?TAP 中移位与更新是不同状态。Shift-DR 改变临时位,Update-DR 才把它们送入更新寄存器。
{"id":"jtag-tap-update","title":"移进去的位,什么时候真正生效?","summary":"TAP 中移位与更新是不同状态。Shift-DR 改变临时位,Update-DR 才把它们送入更新寄存器。","height":1000,"html":"<h2 data-i18n=\"heading\"></h2><p class=\"intro\" data-i18n=\"intro\"></p><label class=\"check\"><input type=\"checkbox\" id=\"tdi\"><span data-i18n=\"tdi\"></span></label><div class=\"actions\"><button id=\"tms0\" data-i18n=\"tms0\"></button><button id=\"tms1\" data-i18n=\"tms1\"></button><button id=\"reset5\" data-i18n=\"reset5\"></button><button id=\"reset\" data-i18n=\"reset\"></button></div><div class=\"metrics\"><div><span data-i18n=\"state\"></span><strong id=\"state\"></strong></div></div><section class=\"panel\"><h3 data-i18n=\"next\"></h3><div id=\"next\"></div></section><div class=\"track-row\"><span data-i18n=\"shift\"></span><div id=\"bits\" class=\"track\"></div></div><div class=\"track-row\"><span data-i18n=\"updated\"></span><div id=\"committed\" class=\"track\"></div></div><div class=\"metrics\"><div><span data-i18n=\"tdo\"></span><strong id=\"tdo\"></strong></div></div><section class=\"panel\"><h3 data-i18n=\"history\"></h3><div id=\"history\"></div></section><details class=\"scope\"><summary data-i18n=\"scopeLabel\"></summary><p data-i18n=\"scope\"></p></details>","css":".intro{margin:8px 0 18px;color:var(--muted);font-size:14px}.caption,.hint{font-size:12px;color:var(--muted);margin-top:12px}.control{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:6px 12px;align-items:center;margin:14px 0;font-size:13px}.control label{font-size:13px}.control output{color:var(--accent);min-width:5em;text-align:right;font-variant-numeric:tabular-nums}.control input{grid-column:1/-1;width:100%;margin:0}.presets{display:flex;gap:4px;align-items:stretch}.presets button{flex:1 1 0;min-width:0;overflow-wrap:anywhere;font-size:13px}.actions{display:flex;gap:8px;flex-wrap:wrap;margin:14px 0}.actions button{flex:1;min-width:0;font-size:13px;min-height:42px}.copy{display:grid;font-size:14px;margin:16px 0}.copy>*{grid-area:1/1;margin:0}.reserve{visibility:hidden}.scope{margin-top:14px;color:var(--muted);font-size:12px}.scope summary{padding:8px 0;cursor:pointer}.scope p{margin-top:10px}.two{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin:16px 0}.card{border:1px solid var(--rule);border-radius:9px;padding:14px;min-width:0}.card header{font-size:14px;font-weight:600;margin-bottom:10px}.card button{font-size:12px;width:100%;min-width:0}.card small{display:block;font-size:12px;color:var(--muted)}.card code{font-size:12px;overflow-wrap:anywhere}.metrics{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin:16px 0}.metrics>div{border-left:2px solid var(--accent);padding-left:10px;min-width:0}.metrics span{display:block;min-height:3.2em;color:var(--muted);font-size:12px}.metrics strong{font-size:23px;display:block;font-weight:550;overflow-wrap:anywhere;font-variant-numeric:tabular-nums}.formula{border-top:1px solid var(--rule);padding-top:12px;margin-top:14px;font-size:13px;font-variant-numeric:tabular-nums;overflow-wrap:anywhere}.value{font:550 20px ui-monospace,monospace}.legend{display:flex;flex-wrap:wrap;gap:6px 14px;font-size:12px;color:var(--muted);margin:12px 0}.legend span:before{content:'';display:inline-block;width:8px;height:8px;border-radius:2px;background:var(--accent);margin-right:6px}.legend .second:before{background:var(--second)}.check{display:flex;align-items:center;gap:8px;font-size:13px;margin:12px 0}.check input{width:16px;height:16px;flex-shrink:0;accent-color:var(--accent)}select,input[type=text],input[type=number]{background:var(--paper);color:var(--ink);font:inherit;font-size:13px;padding:10px;border:1px solid var(--rule);border-radius:7px;max-width:100%;min-width:0}select{width:100%;min-height:42px}.band{height:20px;display:flex;background:var(--surface);border-radius:4px;overflow:hidden}.band i{height:100%;display:block;background:var(--accent);transition:width .25s}.band i.second{background:var(--second)}button:disabled{cursor:default}.status{font-size:13px;min-height:4.8em}.mono{font-family:ui-monospace,monospace;font-variant-numeric:tabular-nums}.muted{color:var(--muted)}\n@media(max-width:480px){.two{gap:8px}.card{padding:12px 9px}.actions button,.presets button{font-size:12px}.metrics strong{font-size:20px}.metrics{gap:8px}}\n@media(prefers-reduced-motion:reduce){*,*::before,*::after{transition:none!important;animation:none!important}}\n.track-row{margin:16px 0}.track-row>span{display:block;font-size:12px;color:var(--muted);margin-bottom:7px}.track{display:flex;gap:5px;min-width:0}.cell{flex:1;min-width:0;min-height:48px;border:1px solid var(--rule);border-radius:5px;background:var(--surface);display:flex;align-items:center;justify-content:center;text-align:center;padding:5px 3px;font-size:12px;overflow-wrap:anywhere;font-variant-numeric:tabular-nums}.cell.on{background:var(--accent-soft);border-color:var(--accent)}.cell.gold{background:var(--second-soft);border-color:var(--second)}.cell.empty{border-style:dashed;color:var(--muted)}.cell.error{border-color:var(--second);text-decoration:line-through}.choices{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:6px;margin:14px 0}.choices button{min-width:0;padding:10px 4px;font-size:12px;overflow-wrap:anywhere}.choices button[aria-current=true]{border-color:var(--accent);background:var(--accent-soft)}.relation{display:grid;grid-template-columns:minmax(0,1fr) 28px minmax(0,1fr);align-items:center;gap:6px;margin:12px 0}.relation .cell{min-height:72px}.relation>i{text-align:center;font-style:normal;color:var(--accent)}.readout{font-size:13px;white-space:pre-line;min-height:6.4em;overflow-wrap:anywhere}.plot{position:relative;border-left:1px solid var(--rule);border-bottom:1px solid var(--rule);padding:8px 0;margin:18px 0}.plot-row{height:28px;position:relative;margin:4px 0;background:var(--surface)}.plot-row i{position:absolute;inset-block:5px;background:var(--accent);min-width:2px;transition:width .2s,left .2s}.plot-row b{position:relative;padding:4px 6px;font:11px/28px ui-monospace,monospace;background:var(--paper);border-radius:3px}.axis{display:flex;justify-content:space-between;font-size:11px;color:var(--muted)}\n\n/* Action labels must remain readable across locales and narrow embeds. */\n.actions{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px}\n.actions button{overflow-wrap:anywhere;white-space:normal;min-height:58px;padding:8px}\nbutton{overflow-wrap:anywhere}\n@media(min-width:640px){.actions{grid-template-columns:repeat(3,minmax(0,1fr))}}\n\n.metrics span{overflow-wrap:anywhere}\n.panel{border:1px solid var(--rule);border-radius:9px;padding:12px;margin:14px 0;min-width:0}.panel h3{font-size:13px;margin:0 0 10px;color:var(--muted)}.source{font:13px/1.8 ui-monospace,monospace;white-space:pre-wrap;overflow-wrap:anywhere;margin:12px 0}.source mark{background:var(--accent-soft);color:var(--ink);border-bottom:2px solid var(--accent)}.source .fault{background:var(--second-soft);border-color:var(--second)}.diagram{display:block;width:100%;height:auto;max-height:320px;margin:14px 0;overflow:visible}.diagram text{fill:var(--ink);font:12px ui-monospace,monospace}.diagram .edge{stroke:var(--muted);stroke-width:1.5;fill:none}.diagram .edge.on{stroke:var(--accent);stroke-width:3}.diagram .node{fill:var(--surface);stroke:var(--rule);stroke-width:1.5}.diagram .node.on{fill:var(--accent-soft);stroke:var(--accent)}.diagram .node.gold{fill:var(--second-soft);stroke:var(--second)}.log{font:12px/1.8 ui-monospace,monospace;white-space:pre-line;overflow-wrap:anywhere;min-height:7.2em}.table{display:grid;gap:5px;margin:14px 0}.table .tr{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:5px}.table .tr>*{padding:8px 5px;background:var(--surface);border-radius:4px;min-width:0;overflow-wrap:anywhere;font-size:12px}.table .tr .on{background:var(--accent-soft)}.table .tr .gold{background:var(--second-soft)}.choices button{min-height:44px}.metrics strong{min-height:1.6em}.code-line{display:block}.code-line.on{background:var(--accent-soft);border-left:2px solid var(--accent);padding-left:6px}.status-badge{font-size:12px;color:var(--accent)}.tile-grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:6px}.tile-grid .cell{min-height:40px}.actions button{min-height:48px}.form-field{display:grid;gap:8px;margin:14px 0;font-size:13px}.form-field input{width:100%;font-family:ui-monospace,monospace}.metrics.three{grid-template-columns:repeat(3,minmax(0,1fr))}.formula{min-height:3.2em}.presets{flex-wrap:wrap}.presets button{flex-basis:25%;min-height:44px}\n\n.budget{display:flex;height:28px;margin:16px 0;background:var(--surface);border-radius:5px;overflow:hidden}.budget i{display:block;background:var(--accent);transition:width .2s}.budget i.gold{background:var(--second)}.intervals{margin:16px 0}.interval{position:relative;height:36px;background:var(--surface);margin:6px 0;border-radius:4px;overflow:hidden}.interval i{position:absolute;height:100%;background:var(--accent-soft);border-left:2px solid var(--accent)}.interval i.gold{background:var(--second-soft);border-color:var(--second)}.interval span{position:relative;z-index:1;font:12px/36px ui-monospace,monospace;padding-left:7px}.table .tr>*{min-height:4.8em}.formula{white-space:pre-line;min-height:7.2em}.actions:has(>button:only-child){grid-template-columns:1fr}.metrics strong{min-height:3.2em}.rangegrid{display:grid;grid-template-columns:repeat(12,minmax(0,1fr));gap:3px;margin:14px 0}.rangegrid .cell{min-height:30px;padding:3px 1px;font-size:10px}.message{min-height:7.2em;white-space:pre-line;font:13px/1.8 ui-monospace,monospace;overflow-wrap:anywhere}\n/* Allow CJK hanging punctuation without overflowing a metric's border box. */\n.metrics>div{padding-right:4px}.metrics span{line-break:anywhere}\n\n.chart-wrap{margin:18px 0}.chart-label{font-size:12px;color:var(--muted);min-height:3.2em}.curve{display:block;width:100%;height:auto;max-height:320px;overflow:visible}.curve text{font:11px ui-monospace,monospace;fill:var(--muted)}.gridline{stroke:var(--rule);stroke-width:1}.metrics strong{font-size:20px;min-height:3.2em}.formula{font:12px/1.8 ui-monospace,monospace;min-height:9em}.readout{min-height:8em}.presets button{flex-basis:30%}.scope p{overflow-wrap:anywhere} .unit-note{font-size:12px;color:var(--muted);margin:10px 0}\n\n.metrics span{word-break:break-all;line-break:strict}.metrics>div{padding-right:6px}\n\n#state{min-height:3.2em}.tr{grid-template-columns:1fr 2fr}.track .cell{flex:1}","js":"const $=s=>document.querySelector(s);const set=(id,v)=>$('#'+id).textContent=v;const t=k=>viz.t(k);function cells(id,values){$('#'+id).replaceChildren(...values.map(v=>{const e=document.createElement('div');e.className='cell '+(v.cls||'');e.textContent=v.text;e.title=v.title||v.text;return e;}));}function pressed(mode){document.querySelectorAll('[data-mode]').forEach(e=>e.setAttribute('aria-pressed',String(e.dataset.mode===mode)));}const esc=s=>String(s).replace(/[&<>\"']/g,c=>({'&':'&','<':'<','>':'>','\"':'"',\"'\":'''}[c]));const fmt=s=>'{'+[...s].sort().join(', ')+'}';function graph(id,nodes,edges){const el=$('#'+id);el.setAttribute('viewBox','0 0 360 200');el.innerHTML='<defs><marker id=\"arrow-'+id+'\" viewBox=\"0 0 10 10\" refX=\"8\" refY=\"5\" markerWidth=\"5\" markerHeight=\"5\" orient=\"auto-start-reverse\"><path d=\"M 0 0 L 10 5 L 0 10 z\" fill=\"var(--muted)\"/></marker></defs>'+edges.map(e=>{const a=nodes[e[0]],b=nodes[e[1]],dx=b.x-a.x,dy=b.y-a.y,d=Math.hypot(dx,dy)||1;if(edges.some(r=>r[0]===e[1]&&r[1]===e[0])){const nx=-dy/d,ny=dx/d;return '<path class=\"edge '+(e[2]?'on':'')+'\" d=\"M '+(a.x+dx/d*22+nx*12)+' '+(a.y+dy/d*22+ny*12)+' Q '+((a.x+b.x)/2+nx*38)+' '+((a.y+b.y)/2+ny*38)+' '+(b.x-dx/d*25+nx*12)+' '+(b.y-dy/d*25+ny*12)+'\" marker-end=\"url(#arrow-'+id+')\"/>';}return '<line class=\"edge '+(e[2]?'on':'')+'\" x1=\"'+(a.x+dx/d*25)+'\" y1=\"'+(a.y+dy/d*25)+'\" x2=\"'+(b.x-dx/d*28)+'\" y2=\"'+(b.y-dy/d*28)+'\" marker-end=\"url(#arrow-'+id+')\"/>';}).join('')+nodes.map(n=>'<g><circle class=\"node '+(n.cls||'')+'\" cx=\"'+n.x+'\" cy=\"'+n.y+'\" r=\"25\"/><text x=\"'+n.x+'\" y=\"'+(n.y+4)+'\" text-anchor=\"middle\">'+esc(n.name)+'</text>'+(n.sub?'<text x=\"'+n.x+'\" y=\"'+(n.y+43)+'\" text-anchor=\"middle\">'+esc(n.sub)+'</text>':'')+'</g>').join('');}function table(id,rows){$('#'+id).classList.add('table');$('#'+id).replaceChildren(...rows.map(r=>{const row=document.createElement('div');row.className='tr';row.style.gridTemplateColumns='repeat('+r.length+',minmax(0,1fr))';r.forEach(x=>{const c=document.createElement('div');if(typeof x==='object'){c.textContent=x.text;c.className=x.cls||'';}else c.textContent=x;row.append(c)});return row;}));}function band(id,parts,total){$('#'+id).replaceChildren(...parts.map((p,i)=>{const e=document.createElement('i');e.style.width=(100*p.value/total)+'%';e.className=i%2?'gold':'on';e.title=p.name+': '+p.value;return e}));}function intervals(id,items,total){$('#'+id).replaceChildren(...items.map(p=>{const row=document.createElement('div');row.className='interval';const bar=document.createElement('i');bar.style.left=(100*p.start/total)+'%';bar.style.width=(100*(p.end-p.start)/total)+'%';bar.className=p.cls||'';const label=document.createElement('span');label.textContent=p.label;row.append(bar,label);return row;}));}function plot(id,series,xr,yr,opts={}){const e=$('#'+id),X=x=>42+(x-xr[0])/(xr[1]-xr[0])*298,Y=y=>175-(y-yr[0])/(yr[1]-yr[0])*148;let out='<defs><clipPath id=\"clip-'+id+'\"><rect x=\"42\" y=\"27\" width=\"298\" height=\"148\"/></clipPath></defs>';for(let i=0;i<3;i++){let x=xr[0]+i*(xr[1]-xr[0])/2,y=yr[0]+i*(yr[1]-yr[0])/2;out+='<path class=\"gridline\" d=\"M '+X(x)+' 27V175M42 '+Y(y)+'H340\"/><text x=\"'+X(x)+'\" y=\"194\" text-anchor=\"middle\">'+esc(opts.xfmt?opts.xfmt(x):Number(x.toFixed(2)))+'</text><text x=\"36\" y=\"'+(Y(y)+4)+'\" text-anchor=\"end\">'+esc(opts.yfmt?opts.yfmt(y):Number(y.toFixed(2)))+'</text>';}out+='<g clip-path=\"url(#clip-'+id+')\">';for(const s of series){out+='<path fill=\"none\" stroke=\"'+(s.color||'var(--accent)')+'\" stroke-width=\"2.5\" '+(s.dash?'stroke-dasharray=\"5 4\"':'')+' d=\"'+s.pts.map((p,i)=>(i?'L':'M')+X(p[0]).toFixed(2)+','+Y(p[1]).toFixed(2)).join(' ')+'\"/>';}for(const p of opts.points||[])out+='<circle cx=\"'+X(p[0])+'\" cy=\"'+Y(p[1])+'\" r=\"4\" fill=\"var(--second)\" stroke=\"var(--paper)\" stroke-width=\"1.5\"/>';if(opts.cursor!==undefined)out+='<path d=\"M'+X(opts.cursor)+' 27V175\" stroke=\"var(--muted)\" stroke-dasharray=\"3 3\"/>';e.innerHTML=out+'</g>';e.dataset.curves=JSON.stringify(series.map(s=>s.pts));}const curve=(fn,lo,hi,n=120)=>Array.from({length:n+1},(_,i)=>{const x=lo+(hi-lo)*i/n;return[x,fn(x)]});function inputs(draw){document.querySelectorAll('input,select').forEach(e=>{e.addEventListener('input',draw);e.addEventListener('change',draw)});draw()}const n=id=>+$('#'+id).value;const show=(id,v,d=3)=>set(id,Number(v.toFixed(d)));const transitions={'Test-Logic-Reset':['Run-Test/Idle','Test-Logic-Reset'],'Run-Test/Idle':['Run-Test/Idle','Select-DR-Scan'],'Select-DR-Scan':['Capture-DR','Select-IR-Scan'],'Capture-DR':['Shift-DR','Exit1-DR'],'Shift-DR':['Shift-DR','Exit1-DR'],'Exit1-DR':['Pause-DR','Update-DR'],'Pause-DR':['Pause-DR','Exit2-DR'],'Exit2-DR':['Shift-DR','Update-DR'],'Update-DR':['Run-Test/Idle','Select-DR-Scan'],'Select-IR-Scan':['Capture-IR','Test-Logic-Reset'],'Capture-IR':['Shift-IR','Exit1-IR'],'Shift-IR':['Shift-IR','Exit1-IR'],'Exit1-IR':['Pause-IR','Update-IR'],'Pause-IR':['Pause-IR','Exit2-IR'],'Exit2-IR':['Shift-IR','Update-IR'],'Update-IR':['Run-Test/Idle','Select-DR-Scan']};let state='Test-Logic-Reset',bits=0,updated=0,tdo=null,hist=[state];function pulse(tms){if(state==='Capture-DR')bits=10;if(state==='Shift-DR'){tdo=bits&1;bits=(bits>>1)|($('#tdi').checked?8:0)}state=transitions[state][tms];if(state==='Update-DR')updated=bits;hist.push(state);hist=hist.slice(-5);draw()}function draw(){set('state',state);table('next',[['TMS=0',transitions[state][0]],['TMS=1',transitions[state][1]]]);cells('bits',bits.toString(2).padStart(4,'0').split('').map(text=>({text,cls:'on'})));cells('committed',updated.toString(2).padStart(4,'0').split('').map(text=>({text,cls:'gold'})));set('tdo',tdo===null?t('none'):tdo);table('history',Array.from({length:5},(_,i)=>[String(i+1),hist[i]||'—']));$('#state').dataset.values=JSON.stringify({state,bits,updated,tdo});}$('#tms0').onclick=()=>pulse(0);$('#tms1').onclick=()=>pulse(1);$('#reset5').onclick=()=>{for(let i=0;i<5;i++)pulse(1)};$('#reset').onclick=()=>{state='Test-Logic-Reset';bits=updated=0;tdo=null;hist=[state];draw()};draw();","audio":false,"strings":{"heading":"移进去的位,什么时候真正生效?","history":"最近的状态(旧 → 新)","intro":"直接选择 TMS 并施加一个 TCK 边沿;比较移位寄存器与已更新值。","next":"下一周期的状态","none":"尚未移出","reset":"重置","reset5":"TMS=1 连续 5 周期","scope":"完整 16 状态 TAP;假定选中一个 4 bit 教学数据寄存器,Capture-DR 采样固定 1010。每次按钮代表一个完整 TCK 周期,在适用边沿完成动作;不模拟 IR 位、具体指令及物理引脚驱动。","scopeLabel":"模型范围与约定","shift":"移位寄存器","state":"当前 TAP 状态","tdi":"TDI=1(否则 0)","tdo":"最近移出的 TDO","tms0":"TMS=0 · TCK","tms1":"TMS=1 · TCK","updated":"已更新值"}}
SWD:两根协议信号之外仍有电气条件
SWDIO 是双向数据线,SWCLK 是时钟。请求包含访问方向及 DP/AP 等信息,链路还需要响应、数据、奇偶校验和双向切换阶段。以 Arm 调试架构为例,DP 提供外部调试入口,AP 再访问相应内部资源;具体核心、多核和总线能力由实现决定。Arm Debug Interface 架构
“只需两线”只统计协议信号,板级通常还需 GND、目标电压参考,且为可靠复位/恢复预留 nRESET 常有价值。SWO 是可选的跟踪输出,不是 SWD 正常通信必需的第三根数据线;SWD 也不能普遍代替 JTAG 的边界扫描功能。
VTref 通常让探针感知目标 I/O 电平,不是给目标供电的输出。探针若另外提供供电功能,也需按接口定义使用,避免与板上电源冲突。短线和合理返回路径有助于信号质量,SWDIO 还要允许双向驱动,不能随意加单向缓冲。SEGGER VTref 定义
从 GDB 到 Flash,中间有哪些职责
GDB 使用匹配的 ELF 获取地址与调试信息,通过远程协议向 OpenOCD 等服务发请求;服务结合探针和目标配置访问芯片。探针支持 SWD,不代表其软件已经支持某颗新 MCU 的 Flash 控制器、安全模式或多核拓扑。
下载 Flash 往往还需要设备算法,有时会将小程序放入目标 RAM 执行。RAM 可用性、Flash 大小与 bank、时钟初始化及地址映射错误,都可能造成“能读 ID,却不能烧录”。J-Link、CMSIS-DAP、ST-Link 等也不是仅按品牌就功能相同,具体探针型号、固件、主机软件和目标支持要一起核对;“无限 Flash 断点”同样是附条件的软件能力,不是所有探针的普通硬件比较器无限多。
以下以 OpenOCD 的 ST-Link 接口配置与 STM32F4 目标配置为示例,只适用于已核对匹配的硬件和工具版本。命令会建立调试会话;其中复位和暂停会改变目标执行状态,排查现场故障时先保存需要的信息。
openocd -f interface/stlink.cfg -f target/stm32f4x.cfg
在另一个终端启动支持目标架构的 GDB,并加载与目标固件对应的 ELF:
arm-none-eabi-gdb firmware.elf
target extended-remote localhost:3333
monitor reset halt
info registers
x/8i $pc
这里没有提供自动擦除或解锁命令。reset halt 的实际效果依目标复位配置,端口也可配置;应按本地 OpenOCD 文档与目标设置核对。OpenOCD:GDB 连接与调试、复位配置
硬件断点/观察点数量有限;软件断点可能需要修改指令,Flash 中的实现由工具支持决定。暂停 CPU 后外设、DMA、看门狗或另一核心未必一起暂停,单步观察本身会改变时序,不能据此直接证明实时运行无竞争。
ROM、USB DFU、UF2 与 UART 不在同一个分类维度
ROM 表示代码所在位置,DFU 是一种固件升级协议名称,UART/USB 是传输相关接口,UF2 则是镜像块格式及常见加载方式。USB DFU 可以由 ROM 或可更新 Bootloader 实现;并非所有带 USB 的芯片出厂都有 DFU。
| 平台例子 | 可核对的实际行为 | 不能泛化的结论 |
| STM32 系统存储器下载 | 具体型号的启动模式、接口和引脚见 AN2606 | 不是全部 BOOT0 拉高就有 USART1 + USB DFU |
| RP2040 Pico BOOTSEL | ROM 入口可呈现虚拟 USB 大容量存储,接收 UF2 | UF2 拖放不等于 USB DFU 类协议 |
| nRF52 软件 DFU | 常见方案依赖已烧入的相应 Bootloader | 不能写成全系列出厂自带 USB ROM DFU |
| AVR / PIC 编程 | 接口按器件与工具选择 | ISP、ICSP、UPDI 不都是 AVR 六针同一种协议 |
STM32 的入口还可能受选项字节、芯片修订和安全配置影响,应该按精确料号查表。ST AN2606
Pico 的 BOOTSEL 代码位于 ROM,应用 Flash 损坏通常不会把这段 ROM 覆盖掉,但仍依赖正常供电、USB 连接与器件状态。Raspberry Pi Pico 文档
Nordic 的 nRF5 SDK DFU 工具要求对应软件 Bootloader;较新的软件平台也需按所用升级方案配置。不能把某块开发板预装的能力归到所有裸芯片。Nordic nRF Device Firmware Update
Microchip 的部分较新 AVR 使用 UPDI 进行编程与调试,PIC 的 ICSP 又有自己的器件协议和进入条件。Microchip AVR 接口
下载时间要把擦除、验证与传输分开
UART 115200 baud、8N1 时,每字节需要 10 bit,理想净字节率为 11520 B/s。256 KiB 镜像仅数据传输至少约 22.76 s,再加协议头、响应、重试、Flash 擦写与校验。提高串口波特率不能同比缩短被内部擦写支配的时间,USB 连接也不等于以 USB 标称总线带宽完成烧录。
镜像地址、向量表/入口、链接布局、Bootloader 分区及签名要求需一致。工具报告写入完成后,还要区分字节校验通过、启动验证通过和应用功能通过,这三件事不是同一结果。
连不上时,从依赖最少的一层逐步确认
先核对目标电源与 VTref、GND、接口方向、复位状态及主机对探针的识别,再核对传输选择、目标配置和时钟。降低调试速率可帮助判断连接质量,不能修复错误电压或安全锁定。
若应用启动后关闭调试引脚或进入低功耗,可按芯片支持尝试在复位下连接;这需要复位线和正确工具配置,并非所有器件都支持相同流程。读保护、认证和永久安全状态应按厂商定义处理,某些恢复会擦除数据,某些状态不可逆,不能把“解锁/全片擦除”当作保存故障证据前的默认动作。
能暂停后,先确认 ELF 与正在运行的镜像匹配,再看复位原因、PC、栈及异常上下文。仅有源码文件相同不够,优化、链接地址、库或生成配置不同都会让变量与行号误导判断。
总线排障则结合逻辑分析仪与示波器:一次 ACK 只表示某次字节被接收,不证明数据新鲜、后续事务或电气裕量一直正确。协议解码正常仍应按需要检查电平、上升沿和供电;逻辑分析仪也不能单独排除全部物理层问题。
开发、产线和现场按目标组织流程
开发阶段需要可观测性和恢复入口,产线需要可追溯镜像、校验、序列化与功能测试,现场升级需要验证、兼容与掉电恢复。量产可以直接用专用编程器批量烧录,也可以使用已建立的下载服务,不必固定为“先 SWD 烧 Bootloader,再 DFU”。
OTA 只说明镜像经过无线或网络到达,不自动提供真实性和可靠切换。接收、验证、写入候选区、启动确认与失败恢复应形成明确状态机;读保护、签名和调试授权是不同机制,最终配置要与产品维护策略一致。
复算传输下界
from math import isclose
baud, bits_per_byte, image_bytes = 115200, 10, 256*1024
payload_rate = baud/bits_per_byte
assert payload_rate == 11520
assert isclose(image_bytes/payload_rate, 22.755555555555556)
assert isclose(image_bytes*8/1e6, 2.097152)
print('UART 8N1 有效字节率和镜像纯传输时间下界通过')
传输时间只是下载链路的下界;编程验证、启动验证与实际功能验证应分别记录结果。
继续查阅