周辺回路の正しさは定常状態だけでなく、以下のような動的な状態変化にも現れます。ボタンにはバウンスが発生し、リセット期間中はGPIOが高インピーダンスになる可能性があります。コイルの遮断時にも電流が流れており、ソフトウェアがハングアップしても監視タスクは稼働し続けることがあります。これらの状態変化を回路およびソフトウェアモデルに組み込むことで、「部品は正しく接続されているのに、なぜか偶発的な誤動作が発生する」という現象を説明できます。
要件 核心メカニズム 本稿の重点
確実な起動 電源、リセット、クロック、デフォルト電位の連携 再現可能な初期状態の確立
ボタンで1回のみイベントを生成 連続した安定時間と状態変化 デバウンスは単なるディレイ後の繰り返し処理ではない
GPIOによるパワーロードの制御 MOSゲート駆動と電流ループ デフォルトオフ、フリーホイーリング、耐圧
絶縁境界を跨ぐ信号伝達 光伝送と出力電流能力 CTR、論理エッジ、両側の電源供給
LED調光 電流制限とデューティ比 定電流でも自動的に高効率になるわけではない
ソフトウェア異常からの回復 有効な進捗の証拠とリセット ウォッチドッグの条件、ウィンドウ、障害記録
起動前に、回路に明確な状態を持たせる
選択したMCUのリファレンスデザインに基づき、各電源ピン、デカップリングループ、アナログ電源、内部レギュレータ用コンデンサ、およびパワーオンシーケンスを確認します。デカップリングコンデンサは局所的な過渡電流を担うものであり、基板に大きなコンデンサが1つあるからといって電源ピン近傍のループを無視してはいけません。また、異なるMCUの特殊なコンデンサ端子に一律の値を適用してはいけません。
リセットは、電源がまだ安定していない段階をカバーする必要があります。単純なRC回路の解放時刻は、パワーオンスロープ、閾値、リーク電流、および急速な電源断と再起動の影響を受けます。保証された低電圧動作が必要な場合は、内蔵のBOR/PORまたは外部モニタと組み合わせるべきです。起動設定ピンのサンプリングタイミングはデータシートで確認し、外部ボタンや周辺機器がこのウィンドウ中にチップを誤った起動モードに引き込まないようにします。
外部クリスタルには、負荷コンデンサ、寄生要素、駆動レベル、発振器の利得マージンも関係しており、周波数が同じだからといって無条件に交換できるわけではありません。出力負荷はソフトウェアの初期化前に確実なデフォルト電位を持たせる必要があり、リセット期間中のピン状態は特に、リレーやモーターの意図しない動作を引き起こしてはいけません。
ボタンのデバウンス:状態変化を確認し、電位の繰り返し確認ではない
機械接点の瞬間的な開閉は複数のエッジを生成し、その持続時間はデバイスや操作によって異なります。ソフトウェアによるデバウンスでは、元の候補電位とその開始時刻を保存し、候補電位が設定されたウィンドウを超過して安定している場合にのみ、確認済み状態を更新します。確認済み状態が「解放」から「押下」に変化したときにのみ、1回のイベントを生成します。長押しと繰り返しトリガーは別途定義する必要があり、ループのたびに押下動作を実行してはいけません。
以下のモデルでは、1msごとにサンプリングし、ローレベルが押下を表すと仮定し、5msの安定ウィンドウを使用してアルゴリズムを示しています。実際のウィンドウはボタンの特性と応答要件に基づいて決定する必要があり、5msは汎用規格ではありません。また、離散サンプリングではサンプリングポイント間のすべてのスパイクを観察することはできません。
raw = [ 1 ] * 3 + [ 0 , 1 , 0 , 1 ] + [ 0 ] * 20 + [ 1 , 0 , 1 ] + [ 1 ] * 10
stable = candidate = 1
since = 0
presses , releases = [ ] , [ ]
for now , level in enumerate ( raw ) :
if level != candidate :
candidate , since = level , now
if candidate != stable and now - since >= 5 :
stable = candidate
( presses if stable == 0 else releases ) . append ( now )
assert presses == [ 12 ]
assert releases == [ 34 ]
print ( ' バウンスと長押しの軌跡:押下と解放は1回ずつのみ発生' )
MCU上では、適切な符号なし時間差を使用してカウンタのアンダーフロー/オーバーフローを処理し、比較ウィンドウが使用される時間基準の安全範囲内であることを保証する必要があります。外部割り込みはシステムをウェイクアップしたりチェックを開始したりできますが、すべての元の割り込みを即座にビジネスロジックのボタンイベントとして扱うべきではありません。
ハードウェアのRCは急速な変化を低減しますが、緩やかなエッジはシュミットトリガ入力または対応するデバウンスデバイスと連携させる必要があります。最も単純なプルアップR、ノード対地C、ボタン対地の回路では、解放時にコンデンサがRを介して充電され、押下時にボタンを介して急速に放電します。両方向の時定数が異なるため、必要に応じて電流制限抵抗を追加し、終値と閾値時間を再計算する必要があります。τ = RC は論理反転のディレイではなく、例えば 0 から 0.7VCC へ充電するには −RC ln(0.3) ≈ 1.204RC が必要です。TI:ボタンのデバウンスとシュミットトリガ
確認するのは安定レベルか新しい押下か?
安定Lowの途中一標本を1にすると候補計時は再開するが、確定押下が即解放されるわけではない。窓を短くするとバウンスがイベントになる場合がある。窓は応答遅延と誤検出の取引。
図を準備しています 再読み込み
確認するのは安定レベルか新しい押下か? 候補レベルの変化で計時を再開。確定状態の変化だけがイベントで、押し続けても再発火しない。
{"id":"debounce-stable-history","title":"確認するのは安定レベルか新しい押下か?","summary":"候補レベルの変化で計時を再開。確定状態の変化だけがイベントで、押し続けても再発火しない。","height":1000,"html":"<h2 data-i18n=\"heading\"></h2><p class=\"intro\" data-i18n=\"intro\"></p><div class=\"control\"><label for=\"window\" data-i18n=\"window\"></label><output for=\"window\" id=\"window-value\"></output><input id=\"window\" type=\"range\" min=\"1\" max=\"10\" value=\"5\" step=\"1\"></div><section class=\"panel\"><h3 data-i18n=\"raw\"></h3><div id=\"raw\"></div></section><div class=\"chart-wrap\"><div class=\"chart-label\" data-i18n=\"plot\"></div><svg id=\"plot\" class=\"curve\" viewBox=\"0 0 360 210\" role=\"img\" data-i18n-label=\"plot\"></svg></div><div class=\"metrics\"><div><span data-i18n=\"events\"></span><strong id=\"events\"></strong></div></div><div class=\"actions\"><button id=\"reset\" data-i18n=\"reset\"></button></div><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#raw{display:grid;grid-template-columns:repeat(6,1fr);gap:5px}#raw button{min-width:0;min-height:44px;padding:5px;font-size:12px}#events{min-height:5em}","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 initial=()=>Array.from({length:30},(_,i)=>i<3?1:i<8?(i%2):i<23?0:1);let raw=initial();function draw(){let window=n('window'),candidate=1,since=0,stable=1,confirmed=[],events=[];for(let i=0;i<30;i++){if(raw[i]!==candidate){candidate=raw[i];since=i}if(candidate!==stable&&i-since>=window){let prev=stable;stable=candidate;if(prev===1&&stable===0)events.push(i)}confirmed.push(stable)}show('window-value',window);if(!$('#raw').children.length)raw.forEach((_,i)=>{let b=document.createElement('button');b.onclick=()=>{raw[i]^=1;draw()};$('#raw').append(b)});[...$('#raw').children].forEach((e,i)=>{e.textContent=i+': '+raw[i];e.setAttribute('aria-pressed',String(raw[i]===0));e.setAttribute('aria-label',i+' ms: '+raw[i])});const pts=arr=>arr.flatMap((v,i)=>[[i,v],[i+1,v]]);plot('plot',[{pts:pts(raw)},{pts:pts(confirmed),color:'var(--second)'}],[0,30],[-.1,1.1]);set('events',events.length?events.join(', '):t('none'));$('#events').dataset.times=JSON.stringify(events);}$('#reset').onclick=()=>{raw=initial();draw()};inputs(draw);","audio":false,"strings":{"events":"押下イベント時刻(ms)","heading":"確認するのは安定レベルか新しい押下か?","intro":"各msサンプルをクリックして接点列を編集し、安定窓を変更。","none":"なし","plot":"横軸 ms、青:生レベル、金:確定レベル","raw":"生サンプル:左→右、上→下、0…29 ms","reset":"リセット","scope":"1 ms点サンプル、Lowが押下、初期解放。安定時間は現在時刻−候補開始時刻、サンプル間グリッチは不可視。","scopeLabel":"モデルの範囲と前提","window":"安定窓(ms)"}}
MOS負荷スイッチ:ゲート-ソース電圧とデフォルト状態
GPIOの駆動能力は、保証されたVOH/VOL、単ピンおよびポート全体の電流に基づいて確認する必要があり、絶対最大電流のみで設計してはいけません。大きな負荷はパワースイッチが担い、GPIOは制御を担当します。MOSが導通した後、直流ゲート電流はほぼ不要ですが、切り替え時にはゲート電荷を搬送する必要があります。
ローサイドNMOSでは、S端子をリターン端子に、D端子を負荷の下端に接続します。ゲート直列抵抗は充放電速度を制限し、リンギングの抑制に役立ちます。G-S間のプルダウンは、GPIOが高インピーダンス状態のときにデフォルトのオフ状態を提供します。抵抗値はミラー結合、リーク電流、駆動速度の両立も考慮する必要があり、プルダウンがあれば必ずしも誤導通しないわけではありません。ゲートクランプは S を基準にするべきで、すべてのハイサイドまたは浮遊スイッチに一律に「ゲート対地へのツェナーダイオード」を追加すべきではありません。
ハイサイドPMOSスイッチでは、通常S端子を正電源に、D端子を負荷に接続し、G端子をS端子にプルアップしてオフにし、適切なレベルシフト段によってローに引き下げてオンにします。ここでの端子配置は、専用の単一トランジスタによる逆接防止トポロジとは異なります。電源が12Vの場合、3.3VのGPIOではゲートを12V付近まで直接引き上げてオフにすることはできず、GPIOが損傷する可能性があります。レベルシフトが必要かどうかは、電源ドメインと実際のゲート-ソースウィンドウによって決まり、すべてのPMOSに同じ変換回路が必要なわけではありません。
オン抵抗は実際のVGSと温度に基づいて選択し、VGS(th) は微小なテスト電流で導通が始まることを示すだけであり、負荷が十分にオンになるための根拠としては使えません。耐圧は、電源、クランプ、寄生スパイクに基づいて決定し、「供給電圧×1.5」で完全なストレス予算を代替してはいけません。
DCリレー:遮断ループを完全に描く
+V コイル R、L NMOS D上・S下 K A 還流ダイオード Rg GPIO RGS 遮断後:コイル下端 → ダイオード → コイル上端 ローサイド駆動DCコイル:フリーホイーリングダイオードとコイルの並列接続 ダイオードは遮断電流のループを提供します。コイルに直列に接続されているわけでも、ドレインとグランド間に接続され ているわけでもありません。
正常な導通時、電流は電源からコイル、NMOSを経てリターン端に戻り、フリーホイーリングダイオードは逆バイアスされます。MOSが遮断されると、コイル電流は元の方向へ流れ続け、コイル下端の電圧が上昇し、ダイオードが下端から上端へ導通し、ローカルな閉ループを形成します。したがって、ダイオードの K(カソード)は電源側、A(アノード)はドレイン側 に接続され、コイルと並列になります。
5Vコイルの直流抵抗が70Ω、定常電流が約71.4mA、インダクタンスが0.1Hであると仮定すると、蓄エネルギーは約0.255mJです。0.7Vの一定逆方向電圧降下のみを考慮しコイル抵抗を無視して概算すると、電流の減衰時間は LI/V で約10.2msとなります。実際の電流はコイル抵抗、磁気回路の変化、クランプ特性の影響を受けるため、これはリレーの実測機械リリース時間ではありません。
通常のダイオードクランプは電圧が低く、電流減衰を延長し、結果としてリリース速度に影響を与えます。より高速なリリースが必要な場合は、より高い電圧のクランプを設計できますが、スイッチの耐圧と抑制器のエネルギーを再確認する必要があります。したがって、適切なエネルギー解放経路を確保することは必須ですが、すべてのコイルが同じフリーホイーリングダイオードを使用しなければならないわけではありません。OMRON:リレー抑制回路とリリース時間
コイル駆動の保護と、接点が切り替える負荷の保護は別の問題です。接点負荷にもインダクタンス、サージ、または大きな始動電流がある場合、接点の定格、アーク消弧、負荷側の抑制を別途評価する必要があります。駆動側にダイオードを追加しても、すべての接点ストレスを解決できるわけではありません。
フォトカプラ:絶縁境界とCTRの両方を閉じる
フォトカプラの入力LED電流は光に変換され、出力応答を生成します。両側に導電的な接続は必要ありません。しかし、電源、デバッグライン、またはグランドラインが両側をまたぐと、本来の絶縁境界を迂回する可能性があります。デバイスの耐圧テスト値は、PCB全体の長期的な動作絶縁能力を意味するものではなく、アプリケーションに必要なクリアランス、絶縁、および電源設計にも適合する必要があります。
フォトトランジスタ出力の場合、CTR = IC/IF であり、デバイスのビン分け、入力電流、温度、経年変化、テスト条件によって変化します。ある設計条件で利用可能な最低CTRが50%、IFが2mAであると仮定すると、出力電流の予算は1mAとなります。3.3Vを10kΩのプルアップで引き、ローレベルを0.2Vにしたい場合、約0.31mAが必要であり、静的な予算には余裕があります。ただし、CTRは通常指定されたVCEで測定されるため、飽和ローレベル能力と動的な伝播時間を確認する必要があり、この比率だけですべての論理指標が満たされることを証明してはいけません。Vishay フォトトランジスタフォトカプラ資料
プルアップ抵抗が大きすぎると、エッジが出力コンデンサによって遅延し、小さすぎるとより多くのシンク電流を要求します。深い飽和は遮断を延長する可能性もあります。高速論理用フォトカプラと通常のフォトトランジスタ出力構造は異なり、デバイスの電源とインターフェース仕様に基づいて接続する必要があり、LED電流を大きくするだけで任意のボーレートを実現できるわけではありません。
LED:PWMで平均値を制御、電流制限で瞬時値を制御
直列抵抗の電流は (V供給 − VF − スイッチ電圧降下)/R で計算され、最高供給電圧、最低VF、および許容誤差に基づいて最大電流を確認する必要があります。PWM調光は導通比率を変更するだけであり、電流制限を代替するものではありません。20%のデューティ比だからといって、瞬時電流を定格連続値の5倍に無制限に高めることはできず、パルス定格と熱制約を満たす必要があります。
定電流駆動にも電圧マージンと許容動作範囲が必要です。12V入力、3V LED、100mAの場合、リニア定電流器では約0.9Wを消費し、LEDの電気出力は0.3Wであるため、制御消費電力を無視すると効率は25%に過ぎません。スイッチング定電流方式は特定の条件下で効率を改善できますが、複雑さ、リップル、EMIも異なります。アドレス指定可能なRGB LEDは、制御と駆動を製品に統合しており、そのデータプロトコル、電圧、および電源過渡現象は別途確認する必要があります。
ウォッチドッグ:進捗によって健全性を証明する
メインループにウォッチドッグのリフレッシュ処理を配置することが必ずしも誤りではありません。ループがウォッチドッグの地点に到達する前にすべての重要な作業を完了する必要がある場合、ハングアップはリフレッシュを阻止します。逆に、独立した監視タスクが無条件でウォッチドッグをリフレッシュする場合、他のタスクがすべて停止していても、障害を永久的に隠蔽する可能性があります。
サンプリングタスク 有効なデータバッチの完了 処理タスク 処理ラウンドの完了 通信タスク 処理の進捗・回復 監視:新しい健全性の証拠は? 正常:更新、異常:記録してリセット待ち ウォッチドッグのリフレッシュは、単に監視スレッドが生きていることを示すだけ でなく、重要なタスクが進捗していることを証明すべきです ハートビートは周期ごとに更新され、一度の成功フラグを永久に保持してはいけません。オフラインなどの予期される状 態には、明確な健全性の定義が必要です。
各重要なタスクは、意味のある進捗を完了した後に、そのラウンドの健全性の証拠を更新し、監視側が集約してからリフレッシュする必要があります。外部入力を待機することが許可されているタスクについては、有界待機、タイムアウトリトライ、または制御されたオフラインを明確な状態として扱い、データを受信し続けることを要求してはいけません。健全性フラグは周期ごとにクリアするか、インクリメンタルシーケンスを使用する必要があり、一度セットされて永久に有効にしてはいけません。
タイムアウトとウィンドウは、最長の合法ブロック、起動、Flash消去、低消費電力状態、およびクロック許容誤差に基づいて設定され、最長タスクの2倍に固定してはいけません。独立したクロックと「ウィンドウチェック」は異なる属性であり、独立したウォッチドッグにもウィンドウ機能がある場合があります。具体的なスリープ/デバッグフリーズおよび起動後のウォッチドッグ無効化可否は、モデルごとのデータシートで確認してください。ST STM32WB 独立ウォッチドッグとウィンドウオプション
リセット後は、原因を早期に読み取り、必要な障害コンテキストを保持し、連続する異常再起動によるアクチュエータの動作を制限します。ウォッチドッグは制御不能からの回復に使用されるものであり、プログラムロジックの正しさを証明するものではなく、故障時のハードウェアデフォルト安全状態に代わるものではありません。
回路予算の再計算
from math import isclose , log
assert isclose ( - log ( 1 - 0. 7 ) , 1. 203972804325936 )
i , inductance = 5 / 70 , 0. 1
assert isclose ( i * 1000 , 71. 42857142857143 )
assert isclose ( 0. 5 * inductance * i * i * 1000 , 0. 2551020408163265 )
assert isclose ( inductance * i / 0. 7 * 1000 , 10. 20408163265306 )
assert isclose ( ( 3. 3 - 0. 2 ) / 10000 * 1000 , 0. 31 )
assert isclose ( ( 12 - 3 ) * 0. 1 , 0. 9 )
assert isclose ( 3 / 12 , 0. 25 )
print ( ' RC閾値、コイル蓄エネルギー、CTR負荷、リニアLED損失の計算が通過' )
デバウンス波形と数値計算でモデルの関係を確認できます。コイル、接点、絶縁の性能は実際の動作条件で測定します。
続きの参照