<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Electronics on TurboVision</title>
    <link>https://turbovision.in6-addr.net/tags/electronics/</link>
    <description>Recent content in Electronics on TurboVision</description>
    <generator>Hugo</generator>
    <language>en</language>
    <lastBuildDate>Tue, 21 Apr 2026 14:06:12 +0000</lastBuildDate>
    <atom:link href="https://turbovision.in6-addr.net/tags/electronics/index.xml" rel="self" type="application/rss&#43;xml" />
    
    
    
    <item>
      <title>Debouncing with Time and State, Not Hope</title>
      <link>https://turbovision.in6-addr.net/electronics/microcontrollers/debouncing-with-time-and-state/</link>
      <pubDate>Sun, 22 Feb 2026 00:00:00 +0000</pubDate>
      <lastBuildDate>Sun, 22 Feb 2026 22:43:31 +0100</lastBuildDate>
      <guid>https://turbovision.in6-addr.net/electronics/microcontrollers/debouncing-with-time-and-state/</guid>
      <description>&lt;p&gt;Button debouncing is one of the smallest problems in embedded systems and one of the most frequently mishandled. That combination makes it a perfect teaching case. Engineers know contacts bounce, yet many designs still rely on ad-hoc delays or lucky timing. These solutions pass demos and fail in real operation. A robust approach treats debouncing as a tiny state machine with explicit time policy.&lt;/p&gt;
&lt;p&gt;Mechanical bounce is not mysterious. On transition, contacts physically oscillate before settling. During that interval, GPIO sampling can see multiple edges. If firmware interprets every edge as intent, one press becomes many events. The correct objective is not “filter noise” in the abstract; it is to infer a human action from unstable electrical evidence with defined latency and false-trigger bounds.&lt;/p&gt;
&lt;p&gt;The naive pattern is edge interrupt plus &lt;code&gt;delay_ms(20)&lt;/code&gt; inside the handler. This feels simple but causes collateral damage: blocked interrupt handling, jitter in unrelated tasks, and poor power behavior. Worse, fixed delays are often too long for responsive UIs and still too short for worst-case switches. Delays treat symptoms while creating scheduling side effects.&lt;/p&gt;
&lt;p&gt;A better pattern separates observation from decision. Observation samples pin state periodically or on edge notifications. Decision logic advances through states: &lt;code&gt;Idle&lt;/code&gt;, &lt;code&gt;CandidatePress&lt;/code&gt;, &lt;code&gt;Pressed&lt;/code&gt;, &lt;code&gt;CandidateRelease&lt;/code&gt;. Each transition is gated by elapsed stable time. This design is cheap, deterministic, and testable. It also composes naturally with long-press and double-click features.&lt;/p&gt;
&lt;p&gt;Sampling frequency matters less than many assume. You do not need MHz polling for human input. A 1 ms tick is usually enough, and even 2–5 ms can be acceptable with careful thresholds. What matters is consistent sampling and explicit stability windows. If a signal remains stable for &lt;code&gt;N&lt;/code&gt; ticks, commit the state transition. If it flips early, reset candidate state.&lt;/p&gt;
&lt;p&gt;Interrupt-assisted designs can reduce average CPU cost without sacrificing rigor. Use GPIO interrupts only as wake hints, then confirm transitions in the debounce state machine on a scheduler tick. This hybrid model balances responsiveness and robustness. It avoids long ISR work while still minimizing idle polling overhead.&lt;/p&gt;
&lt;p&gt;Hardware assists are still useful. RC filters and Schmitt-trigger inputs reduce bounce amplitude and edge ambiguity. But hardware alone rarely removes the need for firmware logic, especially when you support varied switch vendors, cable lengths, or noisy environments. The best systems combine modest front-end conditioning with explicit software state handling.&lt;/p&gt;
&lt;p&gt;Testing debouncers should include adversarial scenarios, not only clean bench presses. Vary supply voltage, inject EMI near harnesses, test with gloved and rapid presses, and capture edge traces from different switch lots. Build a replay harness in firmware that feeds recorded edge sequences into your debounce logic and asserts expected events. This turns “seems fine” into measurable confidence.&lt;/p&gt;
&lt;p&gt;Latency trade-offs should be stated in requirements. If you require sub-20 ms press detection while tolerating noisy switches, design thresholds accordingly and verify under worst-case bounce profiles. Teams often optimize for false-trigger elimination and accidentally create sluggish interfaces. Users notice sluggishness immediately. Good debouncing balances reliability with perceived immediacy.&lt;/p&gt;
&lt;p&gt;State-machine debouncing also scales better for many inputs. Instead of per-button delay hacks, you run a compact table of states and timestamps. This structure keeps complexity linear and enables uniform behavior across keys. It also simplifies telemetry: you can log per-button transition timing and detect degrading switches before field failures escalate.&lt;/p&gt;
&lt;p&gt;Power-conscious designs must integrate debouncing with sleep states. Wake-on-edge can trigger from bounce bursts. Firmware should treat wake events as tentative, verify stable states, and return to low power quickly when no valid action is confirmed. Without this, noisy inputs can destroy battery life while appearing functionally correct in brief lab tests.&lt;/p&gt;
&lt;p&gt;The biggest lesson is methodological. Debouncing rewards explicit models over folklore. Define states. Define thresholds. Define expected outcomes. Then test those outcomes with recorded traces and timing variation. This is the same engineering pattern used for larger systems, just in miniature. If a team is sloppy on debouncing, it is often sloppy elsewhere too.&lt;/p&gt;
&lt;p&gt;So treat button handling as more than boilerplate. It is a compact reliability exercise that improves firmware architecture, testing discipline, and UX quality. Time and state beat hope every time.&lt;/p&gt;
&lt;p&gt;If you are mentoring juniors, debouncing is an ideal first design review topic. It is small enough to reason about completely, yet rich enough to expose habits around requirements, state modeling, timing assumptions, and test quality. Teams that do debouncing well usually do larger stateful systems well too.&lt;/p&gt;
&lt;h2 id=&#34;tiny-reference-implementation-pattern&#34;&gt;Tiny reference implementation pattern&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;9
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;raw&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;last_raw&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;last_change_ms&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;now_ms&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;last_raw&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;raw&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;now_ms&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;last_change_ms&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;stable_ms&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;debounced&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;raw&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;debounced&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;raw&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nf&#34;&gt;emit_event&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;debounced&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;?&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;EV_PRESS&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;EV_RELEASE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Simple, explicit, and testable. This pattern is often enough for reliable human-input paths.&lt;/p&gt;
&lt;p&gt;Related reading:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://turbovision.in6-addr.net/electronics/microcontrollers/state-machines-that-survive-noise/&#34;&gt;State Machines That Survive Noise&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://turbovision.in6-addr.net/electronics/microcontrollers/timer-capture-without-an-rtos/&#34;&gt;Timer Capture Without an RTOS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://turbovision.in6-addr.net/electronics/ground-is-a-design-interface/&#34;&gt;Ground Is a Design Interface&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
    <item>
      <title>Debugging Noisy Power Rails</title>
      <link>https://turbovision.in6-addr.net/electronics/debugging-noisy-power-rails/</link>
      <pubDate>Sun, 22 Feb 2026 00:00:00 +0000</pubDate>
      <lastBuildDate>Sun, 22 Feb 2026 22:48:03 +0100</lastBuildDate>
      <guid>https://turbovision.in6-addr.net/electronics/debugging-noisy-power-rails/</guid>
      <description>&lt;p&gt;Noisy power rails cause some of the most frustrating hardware bugs because the symptoms look random while the root cause is often deterministic. A board that &amp;ldquo;usually works&amp;rdquo; at room temperature can fail after five minutes under load, pass again after reboot, and mislead you into chasing firmware ghosts for days.&lt;/p&gt;
&lt;p&gt;A useful mindset shift is this: unstable power is not a side issue. It is a primary signal path. If voltage integrity is poor, every digital subsystem becomes statistically unreliable, and software symptoms are just the final expression.&lt;/p&gt;
&lt;p&gt;My default workflow starts with measurement hygiene before diagnosis:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;short ground spring on probe, not long alligator wire&lt;/li&gt;
&lt;li&gt;scope bandwidth limit toggled on/off to compare high-frequency noise&lt;/li&gt;
&lt;li&gt;capture at startup, idle, peak load, and transient edges&lt;/li&gt;
&lt;li&gt;document probe points physically on board photos&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Bad probing creates fake ripple. Good probing reveals real coupling.&lt;/p&gt;
&lt;p&gt;First pass checks are simple:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;DC level within regulator tolerance&lt;/li&gt;
&lt;li&gt;ripple amplitude against component and MCU limits&lt;/li&gt;
&lt;li&gt;transient droop during load step&lt;/li&gt;
&lt;li&gt;recovery time after transient&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If rail droop aligns with brownout resets, you are already close to root cause.&lt;/p&gt;
&lt;p&gt;Many failures come from layout, not component choice. Long return paths, poor decoupling placement, and shared high-current loops inject noise into sensitive domains. The classic mistake is placing bulk capacitance &amp;ldquo;on the board&amp;rdquo; but not near the switching current loop that actually needs it.&lt;/p&gt;
&lt;p&gt;Decoupling strategy must be layered:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;bulk capacitors for low-frequency energy&lt;/li&gt;
&lt;li&gt;mid-value ceramics for mid-band support&lt;/li&gt;
&lt;li&gt;small ceramics close to IC pins for high-frequency edges&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You cannot substitute one category for another and expect broad-band stability.&lt;/p&gt;
&lt;p&gt;Another frequent issue is regulator operating mode. Some switchers enter pulse-skipping or burst modes at light loads, creating ripple patterns that vanish under bench tests with constant load but reappear in real duty cycles. If your device has sleep/wake behavior, you must test rails during those transitions explicitly.&lt;/p&gt;
&lt;p&gt;Grounding is equally important. &amp;ldquo;Common ground&amp;rdquo; in schematic does not mean common impedance in reality. If ADC reference return shares noisy digital current paths, measurements drift. If RF front-end return shares switching loops, sensitivity collapses. Separate returns and tie at controlled points where possible.&lt;/p&gt;
&lt;p&gt;Temperature is the hidden multiplier. ESR changes, regulator compensation margins shrink, and borderline systems cross failure thresholds. Always run a thermal variance pass:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;cold start&lt;/li&gt;
&lt;li&gt;nominal ambient&lt;/li&gt;
&lt;li&gt;warmed board&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If behavior changes sharply with temperature, inspect compensation and component derating assumptions.&lt;/p&gt;
&lt;p&gt;I also recommend intentional stress tests:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;rapid load toggling&lt;/li&gt;
&lt;li&gt;USB cable swaps with different resistance&lt;/li&gt;
&lt;li&gt;long harness injection&lt;/li&gt;
&lt;li&gt;intentional supply sag within safe bounds&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Robust designs degrade gracefully. Fragile ones fail theatrically.&lt;/p&gt;
&lt;p&gt;When debugging mixed analog-digital boards, isolate domains in experiments. Power analog from clean bench source while digital remains on board regulator, then reverse. This quickly identifies whether the coupling direction is analog-to-digital, digital-to-analog, or both.&lt;/p&gt;
&lt;p&gt;Firmware can help hardware diagnosis without becoming a crutch. Add telemetry:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;brownout counters&lt;/li&gt;
&lt;li&gt;rail ADC snapshots before reset&lt;/li&gt;
&lt;li&gt;timestamped fault reasons&lt;/li&gt;
&lt;li&gt;load-state markers around heavy operations&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Telemetry does not fix power integrity, but it shortens hypothesis cycles dramatically.&lt;/p&gt;
&lt;p&gt;One common anti-pattern is over-filtering after the fact. Engineers add ferrite beads and extra capacitors everywhere until symptoms soften, then ship. This can mask a fundamental loop stability or return-path problem. Prefer first-principles fixes: loop minimization, proper decoupling placement, compensation review, domain partitioning.&lt;/p&gt;
&lt;p&gt;Board revision discipline matters too. Keep change batches small and attributable:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;rev A: decoupling placement change only&lt;/li&gt;
&lt;li&gt;rev B: regulator compensation update only&lt;/li&gt;
&lt;li&gt;rev C: return path reroute only&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you change ten variables per spin, you learn almost nothing.&lt;/p&gt;
&lt;p&gt;A practical &amp;ldquo;done&amp;rdquo; checklist for rail stability:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ripple within target across states&lt;/li&gt;
&lt;li&gt;transient droop below brownout threshold margin&lt;/li&gt;
&lt;li&gt;no unexplained resets over long stress runs&lt;/li&gt;
&lt;li&gt;ADC/reference stability within spec&lt;/li&gt;
&lt;li&gt;behavior stable across temperature and load profiles&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Until all five pass, call the board &amp;ldquo;diagnostic,&amp;rdquo; not &amp;ldquo;production-ready.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Power integrity work is rarely glamorous, but it is where reliable products are born. Teams that treat rails as first-class design artifacts ship fewer mysteries, write less defensive firmware, and spend less time in late-stage panic labs.&lt;/p&gt;
&lt;p&gt;If you remember one sentence: measure the rail where the current switches, not where the schematic is pretty. That single habit catches a surprising number of expensive mistakes early.&lt;/p&gt;
&lt;h2 id=&#34;firmware-telemetry-example&#34;&gt;Firmware telemetry example&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;log_power_snapshot&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;snapshot&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;vdd_mv&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;read_adc_mv&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;VDD_CH&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;snapshot&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;brownout_count&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;read_reset_counter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;snapshot&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;load_state&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;current_load_state&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nf&#34;&gt;emit_snapshot&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;snapshot&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Telemetry does not replace probing, but it shortens the path from symptom to actionable hypothesis.&lt;/p&gt;
&lt;p&gt;Related reading:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://turbovision.in6-addr.net/electronics/ground-is-a-design-interface/&#34;&gt;Ground Is a Design Interface&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://turbovision.in6-addr.net/electronics/microcontrollers/state-machines-that-survive-noise/&#34;&gt;State Machines That Survive Noise&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://turbovision.in6-addr.net/electronics/microcontrollers/spi-signals-that-lie/&#34;&gt;SPI Signals That Lie&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
    <item>
      <title>Ground Is a Design Interface</title>
      <link>https://turbovision.in6-addr.net/electronics/ground-is-a-design-interface/</link>
      <pubDate>Sun, 22 Feb 2026 00:00:00 +0000</pubDate>
      <lastBuildDate>Sun, 22 Feb 2026 22:48:21 +0100</lastBuildDate>
      <guid>https://turbovision.in6-addr.net/electronics/ground-is-a-design-interface/</guid>
      <description>&lt;p&gt;Many circuit failures are not caused by “bad signals.” They are caused by bad assumptions about ground. Designers often treat ground as a neutral reference that exists automatically once a symbol is placed. In reality, ground is a physical network with resistance, inductance, and shared current paths. If we ignore that, measurements lie, interfaces become unstable, and debugging turns into superstition.&lt;/p&gt;
&lt;p&gt;The mental shift is simple but profound: ground is not the absence of design. Ground is part of the design interface. Every subsystem communicates through it, injects noise into it, and depends on its stability. Once you frame ground this way, layout and topology decisions stop feeling cosmetic and start feeling architectural.&lt;/p&gt;
&lt;p&gt;A common early mistake is routing sensitive analog return currents through the same narrow paths used by switching loads. The board may pass basic tests, then fail under realistic activity when motor drivers, DC-DC converters, or digital bursts modulate the local reference. The symptom appears as random ADC jitter or intermittent threshold misfires. The root cause is shared impedance, not firmware.&lt;/p&gt;
&lt;p&gt;Star-ground strategies can help in some low-frequency or mixed-signal contexts, but they are often misapplied as a universal rule. Solid planes usually win for modern digital work because they minimize return path impedance and give high-frequency currents predictable local loops under signal traces. The key is intentional current-path thinking, not slogan-driven layout.&lt;/p&gt;
&lt;p&gt;Measurement technique also determines whether you see truth or artifacts. Using long oscilloscope ground clips on fast edges can invent ringing that is mostly probe loop inductance. Engineers then “fix” a problem that exists in the measurement setup. Short ground springs, proper probe compensation, and awareness of reference path are not optional details; they are prerequisites for trustworthy diagnosis.&lt;/p&gt;
&lt;p&gt;Connector strategy reveals ground philosophy quickly. Boards with inadequate ground pins in high-speed or noisy interfaces force return currents through awkward paths, increasing emissions and susceptibility. Good connector pinout design alternates signals and returns where possible, reserves dedicated quiet returns for sensitive channels, and accounts for cable behavior, not just schematic neatness.&lt;/p&gt;
&lt;p&gt;Power integrity is entangled with ground integrity. Decoupling capacitors are often discussed as local energy reservoirs, which is true, but their effectiveness depends on short, low-inductance loops into ground. A perfectly valued capacitor placed with poor return routing underperforms dramatically. Placement and loop geometry dominate textbook capacitance calculations more often than teams expect.&lt;/p&gt;
&lt;p&gt;Grounding errors also create software illusions. Firmware engineers may chase race conditions when the true issue is reference movement that shifts logic thresholds under load. Timing fixes sometimes appear to work because they reduce simultaneous switching activity, not because they solved software logic. Cross-disciplinary debugging prevents this misattribution and saves weeks.&lt;/p&gt;
&lt;p&gt;Board bring-up benefits from a ground-first checklist:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Confirm continuity and low-resistance paths for primary returns.&lt;/li&gt;
&lt;li&gt;Verify high-current loops are short and segregated from sensitive nodes.&lt;/li&gt;
&lt;li&gt;Inspect decoupling loop geometry physically, not just in CAD netlists.&lt;/li&gt;
&lt;li&gt;Probe critical points with low-inductance techniques.&lt;/li&gt;
&lt;li&gt;Correlate signal anomalies with load events.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This sequence catches issues earlier than random parameter sweeps.&lt;/p&gt;
&lt;p&gt;In mixed-voltage systems, ground partitioning decisions become even more delicate. Isolation boundaries, level shifters, and external peripherals can introduce unexpected return paths through shields, USB grounds, or measurement equipment. Teams should document intended return routes explicitly and validate them in lab setups that mirror field wiring. Bench-only success with ideal lab grounding often collapses in deployed environments.&lt;/p&gt;
&lt;p&gt;EMC behavior is often where weak ground design is finally exposed. Boards that “work” functionally may fail emissions or immunity tests because return paths were treated as afterthoughts. Retrofitting fixes at that stage is expensive: ferrites, shield tweaks, stitching vias, and cable rework can help, but they are compensations. The cheaper path is to design current return intentionally from the first layout pass.&lt;/p&gt;
&lt;p&gt;Ground discipline is also a communication tool. When schematics and layout notes name current paths and reference assumptions, teams align faster. Reviewers can reason about failure modes before prototypes exist. Firmware and hardware engineers share a common model instead of debating symptoms from different abstractions. This shortens iteration and improves reliability.&lt;/p&gt;
&lt;p&gt;If there is one practical takeaway, it is this: whenever a circuit behaves inconsistently, ask “where does the return current actually flow?” before changing code, values, or component vendors. That question reframes debugging around physics instead of folklore. Ground is not background. Ground is the interface all your interfaces rely on.&lt;/p&gt;
&lt;h2 id=&#34;measurement-snippet-for-repeatable-captures&#34;&gt;Measurement snippet for repeatable captures&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Point: MCU VDD pin (not regulator output only)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Probe: x10, short spring ground
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Capture windows:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  - cold startup
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  - idle
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  - peak switching load
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  - load step edge
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Record:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  - ripple p-p
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  - droop minimum
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  - recovery time&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Consistency in measurement setup is what makes comparisons meaningful across board revisions.&lt;/p&gt;
&lt;p&gt;Related reading:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://turbovision.in6-addr.net/electronics/debugging-noisy-power-rails/&#34;&gt;Debugging Noisy Power Rails&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://turbovision.in6-addr.net/electronics/prototyping-with-failure-budgets/&#34;&gt;Prototyping with Failure Budgets&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://turbovision.in6-addr.net/electronics/microcontrollers/spi-signals-that-lie/&#34;&gt;SPI Signals That Lie&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
    <item>
      <title>Prototyping with Failure Budgets</title>
      <link>https://turbovision.in6-addr.net/electronics/prototyping-with-failure-budgets/</link>
      <pubDate>Sun, 22 Feb 2026 00:00:00 +0000</pubDate>
      <lastBuildDate>Sun, 22 Feb 2026 22:18:40 +0100</lastBuildDate>
      <guid>https://turbovision.in6-addr.net/electronics/prototyping-with-failure-budgets/</guid>
      <description>&lt;p&gt;Most prototype plans assume success too early. Schedules are built around happy-path bring-up, and risk is represented as a vague buffer at the end. In practice, hardware projects move faster when failure is budgeted explicitly from the beginning.&lt;/p&gt;
&lt;p&gt;A failure budget is not pessimism. It is resource planning for uncertainty:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;time for bad assumptions&lt;/li&gt;
&lt;li&gt;time for measurement mistakes&lt;/li&gt;
&lt;li&gt;time for rework&lt;/li&gt;
&lt;li&gt;time for supply surprises&lt;/li&gt;
&lt;li&gt;time for documentation repair&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Without these budgets, teams call normal engineering iteration &amp;ldquo;delay.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;The first step is failure classification. Not all failures are equal:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Design failures&lt;/strong&gt; - wrong topology, wrong margins, incorrect assumptions.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Integration failures&lt;/strong&gt; - interfaces disagree despite locally valid modules.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Manufacturing failures&lt;/strong&gt; - assembly defects, tolerances, placement variance.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Operational failures&lt;/strong&gt; - behavior differs under real workload/temperature/noise.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Each class needs different mitigation strategy, so one generic &amp;ldquo;debug week&amp;rdquo; is rarely effective.&lt;/p&gt;
&lt;p&gt;In early prototype phases, I allocate explicit percentages:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;40% planned build/measurement&lt;/li&gt;
&lt;li&gt;40% planned failure handling&lt;/li&gt;
&lt;li&gt;20% contingency&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The exact numbers vary, but the principle is fixed: failure handling is first-class work.&lt;/p&gt;
&lt;p&gt;Teams often underestimate setup friction too. The first useful measurement of a new board may require:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;probe fixture adaptation&lt;/li&gt;
&lt;li&gt;firmware instrumentation pass&lt;/li&gt;
&lt;li&gt;calibration checks&lt;/li&gt;
&lt;li&gt;power sequencing scripts&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;None of this ships to customers, but all of it determines debugging velocity. Budget it.&lt;/p&gt;
&lt;p&gt;A good failure-budget workflow begins with hypothesis inventory. Before fabrication, write down the top assumptions that would hurt most if wrong:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;regulator stability over load profile&lt;/li&gt;
&lt;li&gt;oscillator startup margin&lt;/li&gt;
&lt;li&gt;ADC reference noise limits&lt;/li&gt;
&lt;li&gt;interface timing at worst-case cable length&lt;/li&gt;
&lt;li&gt;thermal dissipation under sustained duty&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Then attach verification plans and fallback options to each assumption.&lt;/p&gt;
&lt;p&gt;This shifts the team from reactive debugging to prepared debugging.&lt;/p&gt;
&lt;p&gt;Another powerful habit is &amp;ldquo;one-risk-per-revision&amp;rdquo; where feasible. If rev A changes power stage and connector pinout and clock source and firmware boot mode at once, post-failure attribution becomes slow and political. Smaller change batches reduce ambiguity and improve learning rate.&lt;/p&gt;
&lt;p&gt;Failure budgets also improve communication with stakeholders. Instead of saying &amp;ldquo;we are late,&amp;rdquo; you can say:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;planned design-risk budget consumed at 70%&lt;/li&gt;
&lt;li&gt;integration-risk budget consumed at 40%&lt;/li&gt;
&lt;li&gt;new unknown introduced by vendor BOM substitution&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is honest, actionable reporting.&lt;/p&gt;
&lt;p&gt;There is a cultural benefit too. When failure time is budgeted, engineers stop hiding uncertainty. They surface problems earlier because discovery is expected, not punished. Early truth beats late heroics.&lt;/p&gt;
&lt;p&gt;Measurement quality must be part of the budget. I have seen teams burn days on fake signals from bad probing. Allocate time for measurement validation:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;sanity checks with known references&lt;/li&gt;
&lt;li&gt;probe compensation verification&lt;/li&gt;
&lt;li&gt;alternate instrument cross-checks&lt;/li&gt;
&lt;li&gt;repeatability check by second engineer&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If measurements are unreliable, all downstream conclusions are suspect.&lt;/p&gt;
&lt;p&gt;Software teams have similar patterns in reliability engineering. Hardware teams can borrow them directly:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;failure budget burn rate&lt;/li&gt;
&lt;li&gt;rollback criteria&lt;/li&gt;
&lt;li&gt;pre-declared stop conditions&lt;/li&gt;
&lt;li&gt;postmortem with concrete follow-up&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The vocabulary may differ, the operational logic is identical.&lt;/p&gt;
&lt;p&gt;A practical board-level failure budget dashboard can be simple:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;open high-risk assumptions&lt;/li&gt;
&lt;li&gt;failed verification count by class&lt;/li&gt;
&lt;li&gt;mean time from failure report to hypothesis&lt;/li&gt;
&lt;li&gt;mean time from hypothesis to validated fix&lt;/li&gt;
&lt;li&gt;unresolved supplier-related risks&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Even lightweight metrics make iteration quality visible.&lt;/p&gt;
&lt;p&gt;Another common miss is treating documentation as optional during prototyping. Under pressure, teams skip notes &amp;ldquo;to go faster,&amp;rdquo; then repeat mistakes because context is lost. Allocate explicit documentation time in the failure budget:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;what failed&lt;/li&gt;
&lt;li&gt;why it failed&lt;/li&gt;
&lt;li&gt;how it was verified&lt;/li&gt;
&lt;li&gt;what changed&lt;/li&gt;
&lt;li&gt;what remains uncertain&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This transforms prototype rounds into reusable knowledge.&lt;/p&gt;
&lt;p&gt;Supply chain volatility deserves dedicated budget lines now. Alternate parts with nominally equivalent values can change behavior materially. If your prototype depends on one fragile component source, include time for qualification variants before it becomes an emergency.&lt;/p&gt;
&lt;p&gt;Budgeting for failure does not mean accepting low quality. It means treating quality as an outcome of controlled iteration. The fastest teams are not those with few failures. They are those that detect, classify, and resolve failures with minimal confusion.&lt;/p&gt;
&lt;p&gt;A useful decision checkpoint at each milestone:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;are we failing in new ways (learning), or same ways (process issue)?&lt;/li&gt;
&lt;li&gt;are unresolved failures shrinking in severity?&lt;/li&gt;
&lt;li&gt;are we increasing confidence in system margins?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If answers trend poorly, stop adding features and stabilize fundamentals.&lt;/p&gt;
&lt;p&gt;Failure budgets are especially effective for interdisciplinary projects where electrical, firmware, and mechanical decisions interact. Shared budget language prevents one domain from appearing blocked by another when the real issue is cross-domain assumption mismatch.&lt;/p&gt;
&lt;p&gt;In the long run, failure budgeting creates calmer projects. Less panic, fewer surprises, better prioritization, cleaner postmortems. The prototype stage becomes what it should be: a deliberate learning phase that converges toward robust production behavior.&lt;/p&gt;
&lt;p&gt;If you want one immediate change, add a &amp;ldquo;planned failure work&amp;rdquo; line to your next prototype plan and protect it from feature pressure. That single line can prevent weeks of late-stage scrambling.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>SPI Signals That Lie</title>
      <link>https://turbovision.in6-addr.net/electronics/microcontrollers/spi-signals-that-lie/</link>
      <pubDate>Sun, 22 Feb 2026 00:00:00 +0000</pubDate>
      <lastBuildDate>Sun, 22 Feb 2026 22:09:16 +0100</lastBuildDate>
      <guid>https://turbovision.in6-addr.net/electronics/microcontrollers/spi-signals-that-lie/</guid>
      <description>&lt;p&gt;SPI looks simple on paper: clock, data out, data in, chip select. Four wires, deterministic timing, done. In real projects, SPI failures often appear as &amp;ldquo;sometimes wrong bytes,&amp;rdquo; &amp;ldquo;first transfer fails,&amp;rdquo; or &amp;ldquo;only breaks on production boards.&amp;rdquo; These are the kind of bugs that waste days because the bus seems healthy at first glance.&lt;/p&gt;
&lt;p&gt;The core lesson is that SPI integrity is not just protocol correctness. It is electrical timing, firmware sequencing, and peripheral state management combined.&lt;/p&gt;
&lt;p&gt;Common failure classes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;clock polarity/phase mismatch masked by forgiving devices&lt;/li&gt;
&lt;li&gt;chip-select timing violations near transaction boundaries&lt;/li&gt;
&lt;li&gt;signal integrity problems at higher edge rates&lt;/li&gt;
&lt;li&gt;peripheral state not reset between commands&lt;/li&gt;
&lt;li&gt;DMA and interrupt races corrupting transfer order&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Any one can produce plausible-but-wrong data.&lt;/p&gt;
&lt;p&gt;I start with protocol truth first. Confirm CPOL/CPHA mode from datasheets, then verify with logic analyzer captures of command/response boundaries. Do not rely on &amp;ldquo;it worked with another sensor.&amp;rdquo; Different devices tolerate different mistakes.&lt;/p&gt;
&lt;p&gt;Chip-select discipline is frequently underestimated. Some peripherals require minimum setup/hold time around CS transitions. If firmware toggles CS too quickly under optimization changes, a previously stable transfer can degrade silently. Enforce timing explicitly, not by incidental delays.&lt;/p&gt;
&lt;p&gt;Signal integrity matters earlier than many assume. At modest board lengths and strong GPIO drive settings, ringing and overshoot can create false edges. Scope captures at the receiver pin, not just MCU pin, are essential. What leaves the MCU is not always what arrives at the device.&lt;/p&gt;
&lt;p&gt;Practical board-level mitigations include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;series resistors near source on high-edge lines&lt;/li&gt;
&lt;li&gt;clean return paths&lt;/li&gt;
&lt;li&gt;reduced edge rate where available&lt;/li&gt;
&lt;li&gt;controlled trace length matching for sensitive links&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These are cheap changes with high payoff.&lt;/p&gt;
&lt;p&gt;On firmware side, transaction framing should be explicit. Wrap transfers in one API that controls:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CS assert/deassert&lt;/li&gt;
&lt;li&gt;mode and speed selection&lt;/li&gt;
&lt;li&gt;optional guard delays&lt;/li&gt;
&lt;li&gt;retry and timeout policy&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Scattered raw register writes across drivers create hidden divergence and fragile maintenance.&lt;/p&gt;
&lt;p&gt;DMA introduces its own failure modes. If buffer ownership and completion signaling are unclear, stale or partially updated data appears intermittently. Use strict ownership rules and assert expected transfer length at completion.&lt;/p&gt;
&lt;p&gt;Interrupt interactions can also corrupt sequencing. If high-priority ISRs preempt between CS assert and first clock edge, timing contracts may break. Critical sections around transaction start are often justified in tight timing designs.&lt;/p&gt;
&lt;p&gt;Another subtle trap: mixed-speed peripherals on shared bus. Reconfiguration bugs happen when one driver leaves bus speed or mode altered for the next device. Centralized bus arbitration prevents this class of bug.&lt;/p&gt;
&lt;p&gt;Diagnostic strategy that works well:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;lock one known-good frequency and mode&lt;/li&gt;
&lt;li&gt;disable DMA and run blocking transfers&lt;/li&gt;
&lt;li&gt;validate deterministic test vectors&lt;/li&gt;
&lt;li&gt;reintroduce DMA and concurrency incrementally&lt;/li&gt;
&lt;li&gt;increase bus speed in controlled steps&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;When failures reappear, you know which complexity layer introduced them.&lt;/p&gt;
&lt;p&gt;I strongly recommend adding protocol-level self-checks where possible:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;read-back register after write&lt;/li&gt;
&lt;li&gt;device ID verification at startup&lt;/li&gt;
&lt;li&gt;command echo checks&lt;/li&gt;
&lt;li&gt;CRC where supported&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These catch latent bus corruption before higher-level logic misbehaves.&lt;/p&gt;
&lt;p&gt;Power and reset sequencing also influence SPI reliability. Some peripherals accept clocks before internal state is ready, then remain in undefined mode until hard reset. Ensure boot initialization obeys datasheet timing windows.&lt;/p&gt;
&lt;p&gt;For production robustness, perform variability tests:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;temperature sweep&lt;/li&gt;
&lt;li&gt;supply voltage corners&lt;/li&gt;
&lt;li&gt;cable/harness variants where applicable&lt;/li&gt;
&lt;li&gt;repeated long-run stress with error counters&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If an SPI link passes only nominal lab conditions, it is not finished.&lt;/p&gt;
&lt;p&gt;Logging can help in deployed systems:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;transaction error counts&lt;/li&gt;
&lt;li&gt;timeout counts&lt;/li&gt;
&lt;li&gt;last failing opcode&lt;/li&gt;
&lt;li&gt;bus-reset events&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These metrics turn rare field failures into diagnosable patterns.&lt;/p&gt;
&lt;p&gt;The big mindset shift: SPI bugs are often systems bugs, not line-by-line coding bugs. You solve them fastest by combining electrical captures, protocol verification, and firmware sequencing analysis, not by focusing on one layer alone.&lt;/p&gt;
&lt;p&gt;If you keep one rule, keep this: trust captured timing and measured waveforms over assumptions. SPI rarely lies; our interpretation of partial evidence does.&lt;/p&gt;
&lt;p&gt;If a design ships to production, add one recovery path too: a bus reinitialization routine that can safely reset peripheral state after repeated transaction failure. Rare field glitches become survivable when recovery is deterministic and observable rather than hidden behind random retries.&lt;/p&gt;
&lt;p&gt;Design for recoverability, then verify it under stress.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>State Machines That Survive Noise</title>
      <link>https://turbovision.in6-addr.net/electronics/microcontrollers/state-machines-that-survive-noise/</link>
      <pubDate>Sun, 22 Feb 2026 00:00:00 +0000</pubDate>
      <lastBuildDate>Sun, 22 Feb 2026 22:39:14 +0100</lastBuildDate>
      <guid>https://turbovision.in6-addr.net/electronics/microcontrollers/state-machines-that-survive-noise/</guid>
      <description>&lt;p&gt;A lot of embedded bugs are not algorithm failures. They are state-management failures under imperfect signals. Inputs bounce, clocks drift, interrupts cluster, and peripherals report transitional nonsense. Firmware that assumes clean edges and ideal timing eventually fails in the field where noise is normal.&lt;/p&gt;
&lt;p&gt;Robust systems treat noise as a design input, not a test surprise.&lt;/p&gt;
&lt;h2 id=&#34;why-finite-state-machines-still-win&#34;&gt;Why finite state machines still win&lt;/h2&gt;
&lt;p&gt;State machines are sometimes dismissed as &amp;ldquo;old-school&amp;rdquo; in modern embedded stacks. That is a mistake. They remain one of the best tools for making behavior explicit under uncertainty:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;legal transitions are visible&lt;/li&gt;
&lt;li&gt;invalid transitions can be handled deliberately&lt;/li&gt;
&lt;li&gt;timeout behavior is encoded, not implied&lt;/li&gt;
&lt;li&gt;recovery paths are first-class&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Most importantly, state machines force you to name ambiguous phases that ad-hoc boolean logic usually hides.&lt;/p&gt;
&lt;h2 id=&#34;a-practical-pattern-event-queue--transition-table&#34;&gt;A practical pattern: event queue + transition table&lt;/h2&gt;
&lt;p&gt;A resilient architecture separates interrupt capture from policy:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;ISR captures minimal event.&lt;/li&gt;
&lt;li&gt;Main loop dequeues event.&lt;/li&gt;
&lt;li&gt;Transition function updates state.&lt;/li&gt;
&lt;li&gt;Output actions run from resulting state.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;typedef&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;enum&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ST_IDLE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ST_ARMED&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ST_ACTIVE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ST_FAULT&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;state_t&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;typedef&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;enum&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;EV_EDGE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;EV_TIMEOUT&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;EV_CRC_FAIL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;EV_RESET&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;event_t&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;state_t&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;step&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;state_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;event_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;switch&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;ST_IDLE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;   &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;e&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;EV_EDGE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;    &lt;span class=&#34;o&#34;&gt;?&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;ST_ARMED&lt;/span&gt;  &lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ST_IDLE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;ST_ARMED&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;  &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;e&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;EV_TIMEOUT&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;?&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;ST_ACTIVE&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;e&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;EV_CRC_FAIL&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;?&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;ST_FAULT&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ST_ARMED&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;ST_ACTIVE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;e&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;EV_RESET&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;   &lt;span class=&#34;o&#34;&gt;?&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;ST_IDLE&lt;/span&gt;   &lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ST_ACTIVE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;ST_FAULT&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;  &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;e&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;EV_RESET&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;   &lt;span class=&#34;o&#34;&gt;?&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;ST_IDLE&lt;/span&gt;   &lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ST_FAULT&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ST_FAULT&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;This is intentionally simple. Complexity belongs in explicit transitions, not in hidden timing side effects.&lt;/p&gt;
&lt;h2 id=&#34;debounce-is-a-state-problem-not-just-delay&#34;&gt;Debounce is a state problem, not just delay&lt;/h2&gt;
&lt;p&gt;Naive debounce logic (&lt;code&gt;delay then read&lt;/code&gt;) often passes bench tests and fails with variable load. Better approach:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;maintain input state&lt;/li&gt;
&lt;li&gt;require stable duration threshold&lt;/li&gt;
&lt;li&gt;transition only when threshold satisfied&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This aligns with &lt;a href=&#34;https://turbovision.in6-addr.net/electronics/microcontrollers/debouncing-with-time-and-state/&#34;&gt;Debouncing with Time and State&lt;/a&gt; and extends it into full system behavior.&lt;/p&gt;
&lt;h2 id=&#34;timeouts-are-architectural-not-patchwork&#34;&gt;Timeouts are architectural, not patchwork&lt;/h2&gt;
&lt;p&gt;Every state that waits on external behavior should define timeout semantics:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;what timeout means&lt;/li&gt;
&lt;li&gt;whether retry is allowed&lt;/li&gt;
&lt;li&gt;max retry budget&lt;/li&gt;
&lt;li&gt;fallback state&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Undefined timeout behavior is one of the most expensive firmware ambiguities in production debugging.&lt;/p&gt;
&lt;h2 id=&#34;top-aligned-diagnostics-in-firmware-logs&#34;&gt;Top-aligned diagnostics in firmware logs&lt;/h2&gt;
&lt;p&gt;When logging transitions, keep entries normalized:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ts | old_state | event | new_state | action | error_code&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;This format turns logs into analyzable traces instead of prose fragments. You can then diff expected transition sequences against observed ones in automated tests.&lt;/p&gt;
&lt;h2 id=&#34;guarding-against-interrupt-storms&#34;&gt;Guarding against interrupt storms&lt;/h2&gt;
&lt;p&gt;Interrupt storms can starve policy logic if ISR work is too heavy. Keep ISR minimal:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;capture timestamp&lt;/li&gt;
&lt;li&gt;capture source id&lt;/li&gt;
&lt;li&gt;queue event&lt;/li&gt;
&lt;li&gt;exit&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Any parsing, retry decisions, or multi-step logic belongs in cooperative main-loop context where execution order is controlled.&lt;/p&gt;
&lt;h2 id=&#34;noise-aware-testing-strategy&#34;&gt;Noise-aware testing strategy&lt;/h2&gt;
&lt;p&gt;A strong test suite includes adversarial input timing:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;burst edges near threshold boundaries&lt;/li&gt;
&lt;li&gt;delayed acknowledgments&lt;/li&gt;
&lt;li&gt;missing edges&lt;/li&gt;
&lt;li&gt;duplicate events&lt;/li&gt;
&lt;li&gt;out-of-order event injections&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If your machine cannot survive these, it is not ready for hardware reality.&lt;/p&gt;
&lt;h2 id=&#34;cross-references-for-this-design-style&#34;&gt;Cross references for this design style&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://turbovision.in6-addr.net/electronics/microcontrollers/timer-capture-without-an-rtos/&#34;&gt;Timer Capture Without an RTOS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://turbovision.in6-addr.net/electronics/microcontrollers/spi-signals-that-lie/&#34;&gt;SPI Signals That Lie&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://turbovision.in6-addr.net/electronics/ground-is-a-design-interface/&#34;&gt;Ground Is a Design Interface&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These pieces describe the same principle at different layers: uncertainty is part of the interface contract.&lt;/p&gt;
&lt;h2 id=&#34;implementation-details-that-pay-off&#34;&gt;Implementation details that pay off&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Keep state enum in one header, shared by firmware and test harness.&lt;/li&gt;
&lt;li&gt;Use explicit &amp;ldquo;unexpected event&amp;rdquo; handler, never silent ignore.&lt;/li&gt;
&lt;li&gt;Version your transition table so behavior changes are reviewable.&lt;/li&gt;
&lt;li&gt;Add build-time switch for transition tracing in debug builds.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This sounds procedural because reliability is procedural.&lt;/p&gt;
&lt;h2 id=&#34;final-thought&#34;&gt;Final thought&lt;/h2&gt;
&lt;p&gt;Embedded systems do not get judged by elegance under ideal inputs. They get judged by behavior under messy electrical and timing conditions. State machines that survive noise are not conservative design. They are aggressive risk management.&lt;/p&gt;
&lt;p&gt;If you are choosing between adding one more feature and hardening transitions around existing behavior, harden first. Field failures almost always happen at transitions, not in the center of stable states.&lt;/p&gt;
&lt;p&gt;Document each state transition in one sentence that an on-call engineer can understand at 3 AM. If the sentence is unclear, the transition is probably underspecified in code as well.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Timer Capture Without an RTOS</title>
      <link>https://turbovision.in6-addr.net/electronics/microcontrollers/timer-capture-without-an-rtos/</link>
      <pubDate>Sun, 22 Feb 2026 00:00:00 +0000</pubDate>
      <lastBuildDate>Sun, 22 Feb 2026 22:15:51 +0100</lastBuildDate>
      <guid>https://turbovision.in6-addr.net/electronics/microcontrollers/timer-capture-without-an-rtos/</guid>
      <description>&lt;p&gt;One of the most useful embedded skills is measuring external timing accurately without hiding behind a heavy runtime stack. You do not need an RTOS to capture pulse widths, frequency drift, or event latency with high reliability. You need a clear timing model, disciplined interrupt design, and careful data handoff.&lt;/p&gt;
&lt;p&gt;Timer input-capture peripherals are built for this job. They latch counter values on configured edges and let firmware process deltas later. The hardware does the precise timestamping; software handles interpretation.&lt;/p&gt;
&lt;p&gt;A robust architecture starts with three decisions:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;counter clock source and prescaler&lt;/li&gt;
&lt;li&gt;edge policy (rising, falling, both)&lt;/li&gt;
&lt;li&gt;overflow handling strategy&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If these are vague, accuracy claims will be vague too.&lt;/p&gt;
&lt;p&gt;Choose timer frequency from measurement goals, not convenience. Too slow and quantization error dominates. Too fast and overflow complexity increases, especially on narrow counters. A good target is where one tick is clearly below your required resolution with margin for jitter analysis.&lt;/p&gt;
&lt;p&gt;Input capture ISR design should be minimal:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;read captured value&lt;/li&gt;
&lt;li&gt;read/track overflow epoch&lt;/li&gt;
&lt;li&gt;write compact event record into ring buffer&lt;/li&gt;
&lt;li&gt;return&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Do not compute expensive statistics inside ISR unless absolutely necessary. Deterministic ISR duration keeps timestamping reliable.&lt;/p&gt;
&lt;p&gt;The ring buffer is the bridge between hard realtime edges and softer application logic. Make it explicit:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;fixed-size, lock-free where possible&lt;/li&gt;
&lt;li&gt;head/tail updates with clear ownership&lt;/li&gt;
&lt;li&gt;overflow counter for dropped samples&lt;/li&gt;
&lt;li&gt;sequence IDs for gap detection&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If sampling can outrun processing, design for graceful loss reporting instead of silent corruption.&lt;/p&gt;
&lt;p&gt;Overflow math is where many implementations become flaky. A 16-bit timer at high clock rate wraps frequently. You need either:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;software epoch extension in overflow ISR, or&lt;/li&gt;
&lt;li&gt;wider hardware timer if available&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Then reconstruct absolute timestamps as &lt;code&gt;(epoch &amp;lt;&amp;lt; counter_bits) | capture_value&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Validate overflow handling with deliberate stress:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;low-frequency signals to force many wraps between edges&lt;/li&gt;
&lt;li&gt;bursty high-frequency signals near ISR capacity&lt;/li&gt;
&lt;li&gt;mixed duty cycles&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If only one scenario is tested, hidden edge cases survive to production.&lt;/p&gt;
&lt;p&gt;Debounce and input conditioning matter too. Electrical noise can generate false captures. Hardware filtering, Schmitt inputs, or digital filter settings on capture channels often improve reliability more than post-processing hacks.&lt;/p&gt;
&lt;p&gt;For pulse width measurement, both-edge capture is ideal:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;capture rising edge timestamp&lt;/li&gt;
&lt;li&gt;capture falling edge timestamp&lt;/li&gt;
&lt;li&gt;subtract with wrap-safe arithmetic&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For frequency measurement, rising-only with period averaging is often cleaner.&lt;/p&gt;
&lt;p&gt;Averaging strategy should reflect signal characteristics. Fixed-window averaging smooths noise but can blur short transients. Exponential filters react faster but need careful coefficient tuning. Choose based on what errors are expensive for your application.&lt;/p&gt;
&lt;p&gt;No RTOS does not mean no scheduling discipline. Use a simple cooperative loop:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;drain capture buffer&lt;/li&gt;
&lt;li&gt;update derived metrics&lt;/li&gt;
&lt;li&gt;publish snapshots atomically&lt;/li&gt;
&lt;li&gt;run non-critical tasks opportunistically&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This model is predictable and usually enough for single-MCU measurement nodes.&lt;/p&gt;
&lt;p&gt;Atomic publication is important when data is consumed by other contexts (serial output, control loop, diagnostics). Use double-buffered snapshots or short critical sections to avoid torn reads.&lt;/p&gt;
&lt;p&gt;Instrumentation should be built in early:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;dropped-sample count&lt;/li&gt;
&lt;li&gt;max ISR latency observed&lt;/li&gt;
&lt;li&gt;max buffer depth reached&lt;/li&gt;
&lt;li&gt;timestamp monotonicity checks&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Without instrumentation, &amp;ldquo;seems stable&amp;rdquo; can hide near-overload behavior.&lt;/p&gt;
&lt;p&gt;Another practical pattern is calibration hooks. If timer clock derives from an internal RC oscillator, drift can distort measurements. Add a calibration path using known references where possible, or at least expose drift estimation telemetry so users understand uncertainty.&lt;/p&gt;
&lt;p&gt;When integrating with control logic, separate measurement confidence from measurement value. For each computed metric, carry metadata:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;valid/invalid&lt;/li&gt;
&lt;li&gt;sample count&lt;/li&gt;
&lt;li&gt;age&lt;/li&gt;
&lt;li&gt;error flags&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Control decisions should degrade safely on low-confidence inputs.&lt;/p&gt;
&lt;p&gt;Testing must include real signal generators and ugly signals:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;clean square waves for baseline&lt;/li&gt;
&lt;li&gt;jittered waveforms&lt;/li&gt;
&lt;li&gt;missing pulses&lt;/li&gt;
&lt;li&gt;slow edges near threshold&lt;/li&gt;
&lt;li&gt;EMI-contaminated lines&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Embedded timing code that only passes clean-lab signals is unfinished.&lt;/p&gt;
&lt;p&gt;One reason people reach for RTOS early is fear of concurrency complexity. That fear is understandable. But for focused timing tasks, a disciplined interrupt-plus-buffer model is simpler, faster, and easier to audit. You can always layer a scheduler later if system scope grows.&lt;/p&gt;
&lt;p&gt;A compact bring-up checklist:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;verify edge timestamps with logic analyzer correlation&lt;/li&gt;
&lt;li&gt;force overflow and confirm wrap-safe math&lt;/li&gt;
&lt;li&gt;saturate input rate and observe drop accounting&lt;/li&gt;
&lt;li&gt;validate end-to-end latency from edge to published metric&lt;/li&gt;
&lt;li&gt;confirm behavior after long-duration runs&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If all five pass, you have a reliable timing subsystem.&lt;/p&gt;
&lt;p&gt;The deeper lesson is architectural: put precision where it belongs. Let hardware timestamp edges. Let ISR move minimal data. Let foreground logic compute and publish. Clean boundaries produce reliable systems.&lt;/p&gt;
&lt;p&gt;This design style scales from small sensor interfaces to motor control telemetry and protocol timing diagnostics. It also teaches excellent habits: deterministic ISR design, explicit loss accounting, and confidence-aware outputs.&lt;/p&gt;
&lt;p&gt;You do not need an RTOS to do serious timing work. You need explicit constraints, measurable behavior, and the discipline to keep fast paths simple.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>RISC-V on a 10-Cent Chip</title>
      <link>https://turbovision.in6-addr.net/electronics/microcontrollers/riscv-on-ch32v003/</link>
      <pubDate>Fri, 30 Jan 2026 00:00:00 +0000</pubDate>
      <lastBuildDate>Sun, 22 Feb 2026 15:48:47 +0100</lastBuildDate>
      <guid>https://turbovision.in6-addr.net/electronics/microcontrollers/riscv-on-ch32v003/</guid>
      <description>&lt;p&gt;The WCH CH32V003 costs less than a stamp and runs a 32-bit RISC-V core
at 48 MHz. It has 2 KB of RAM, 16 KB of flash, and a surprisingly complete
peripheral set: USART, SPI, I²C, ADC, timers.&lt;/p&gt;
&lt;p&gt;We set up the open-source MounRiver toolchain, flash a UART echo program
over the single-wire debug interface, and measure current consumption in
sleep mode: 8 µA. For battery-powered sensors, this chip is hard to beat.&lt;/p&gt;
&lt;p&gt;The interesting part is not only the price. It is what this device teaches
about writing firmware with hard limits. With 2 KB RAM, every buffer is a
design decision. With 16 KB flash, libraries have to justify their existence.
That pressure tends to produce cleaner code than &amp;ldquo;just add another package.&amp;rdquo;&lt;/p&gt;
&lt;h2 id=&#34;bring-up-notes-that-save-time&#34;&gt;Bring-up notes that save time&lt;/h2&gt;
&lt;p&gt;My shortest path to first success:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Get a known-good blink or UART echo working first.&lt;/li&gt;
&lt;li&gt;Verify clock configuration before touching peripherals.&lt;/li&gt;
&lt;li&gt;Keep interrupts disabled until polling logic is stable.&lt;/li&gt;
&lt;li&gt;Add one peripheral at a time and re-test power draw.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Most early failures are clock, pin mux, or toolchain path problems, not
&amp;ldquo;mystical hardware bugs.&amp;rdquo; If serial output is dead, confirm GPIO mode and
baud assumptions before rewriting half the project.&lt;/p&gt;
&lt;h2 id=&#34;why-this-chip-is-useful-in-practice&#34;&gt;Why this chip is useful in practice&lt;/h2&gt;
&lt;p&gt;CH32V003 is ideal for disposable probes, tiny sensor nodes, and protocol
bridges where BOM cost matters. You can still keep a disciplined structure:
small drivers, explicit init sequence, and one integration test per module.
That gives reliability without heavyweight frameworks.&lt;/p&gt;
&lt;p&gt;Related reading:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://turbovision.in6-addr.net/electronics/microcontrollers/avr-bare-metal/&#34;&gt;AVR Bare-Metal Blinking&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://turbovision.in6-addr.net/electronics/soldering-smd-by-hand/&#34;&gt;Hand-Soldering 0402 Components&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
    <item>
      <title>Hand-Soldering 0402 Components</title>
      <link>https://turbovision.in6-addr.net/electronics/soldering-smd-by-hand/</link>
      <pubDate>Sun, 28 Dec 2025 00:00:00 +0000</pubDate>
      <lastBuildDate>Mon, 09 Mar 2026 09:46:27 +0100</lastBuildDate>
      <guid>https://turbovision.in6-addr.net/electronics/soldering-smd-by-hand/</guid>
      <description>&lt;p&gt;0402 passives measure 1.0 × 0.5 mm. They&amp;rsquo;re barely visible to the naked
eye, yet hand-soldering them is doable with the right technique: flux,
a fine conical tip, thin solder wire, and patience.&lt;/p&gt;
&lt;p&gt;The key is to tin one pad first, tack the component down, then solder the
other side. A stereo microscope helps but isn&amp;rsquo;t strictly necessary if you
have good lighting and steady hands.&lt;/p&gt;
&lt;p&gt;What usually fails is not dexterity, but process order. If you approach 0402
work like through-hole soldering, parts tombstone, slide, or disappear into
the carpet. If you stage the work correctly, the joints become boringly
repeatable.&lt;/p&gt;
&lt;h2 id=&#34;workflow-that-keeps-rework-low&#34;&gt;Workflow that keeps rework low&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Clean pads with isopropyl alcohol.&lt;/li&gt;
&lt;li&gt;Add liquid flux before touching solder.&lt;/li&gt;
&lt;li&gt;Pre-tin exactly one pad with a tiny amount.&lt;/li&gt;
&lt;li&gt;Hold the part with tweezers, reflow that pad, and &amp;ldquo;tack&amp;rdquo; alignment.&lt;/li&gt;
&lt;li&gt;Solder the second pad with minimal dwell time.&lt;/li&gt;
&lt;li&gt;Revisit the first pad only if wetting looks poor.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The microscope is optional, but magnification changes quality control. Even a
cheap USB scope catches bridges and cold joints before power-on.&lt;/p&gt;
&lt;h2 id=&#34;common-mistakes&#34;&gt;Common mistakes&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Too much solder: creates hidden bridges under the body.&lt;/li&gt;
&lt;li&gt;Too little flux: oxidized pads and grainy joints.&lt;/li&gt;
&lt;li&gt;Too much heat: lifted pads, especially on cheap proto boards.&lt;/li&gt;
&lt;li&gt;Mechanical pressure while heating: parts shoot away or skew.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;My rule is simple: if the joint takes more than a few seconds, stop, re-flux,
and try again. Fighting a dry joint with temperature only makes damage faster.&lt;/p&gt;
&lt;p&gt;Related reading:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://turbovision.in6-addr.net/electronics/microcontrollers/riscv-on-ch32v003/&#34;&gt;RISC-V on a 10-Cent Chip&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://turbovision.in6-addr.net/retro/hardware/restoring-a-286/&#34;&gt;Restoring an AT 286&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
  </channel>
</rss>
