Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions PCB/diagrams/power_control_layout.html
Original file line number Diff line number Diff line change
Expand Up @@ -276,11 +276,11 @@ <h2>What's on this sheet</h2>
<tbody>
<tr><td class="ref">Q_PWR1</td><td class="val">DMG2305UX</td><td class="val">SOT-23</td><td class="role">Main power switch — VSYS to PWR_EN, carries full system current</td></tr>
<tr><td class="ref">R_PWR_EN_PD1</td><td class="val">100kΩ</td><td class="val">0402</td><td class="role">PWR_EN pull-down to GND — the buck's EN pin has no internal bias and must not be left floating (callout G)</td></tr>
<tr><td class="ref">Q_LATCH1</td><td class="val">2N7002</td><td class="val">SOT-23</td><td class="role">Pulls Q_PWR1's gate low to turn it on</td></tr>
<tr><td class="ref">Q_LATCH1</td><td class="val">AO3400A</td><td class="val">SOT-23</td><td class="role">Pulls Q_PWR1's gate low to turn it on; low-Vth (≤1.45V) so a press still turns it on at the bottom of the battery range (callout D)</td></tr>
<tr><td class="ref">D_OR1 / D_OR2</td><td class="val">1N4148W</td><td class="val">SOD-123</td><td class="role">Diode-OR: PWR_BTN_TRIGGER (actual button press, via the inverter) or MCU hold, either one latches power on</td></tr>
<tr><td class="ref">R_GATE1</td><td class="val">100kΩ</td><td class="val">0402</td><td class="role">Q_PWR1 gate pull-up — off by default</td></tr>
<tr><td class="ref">R_LATCH_G1</td><td class="val">1MΩ</td><td class="val">0402</td><td class="role">Q_LATCH1 gate pull-down — off by default; sized to not starve the diode-OR's drive (callout D)</td></tr>
<tr><td class="ref">R_BTN_PU1</td><td class="val">100kΩ</td><td class="val">0402</td><td class="role">Button sense pull-up — MCU reads pressed (0V) vs idle (VSYS)</td></tr>
<tr><td class="ref">R_BTN_PU1</td><td class="val">100kΩ</td><td class="val">0402</td><td class="role">Button sense pull-up to 3V3 (not VSYS) — MCU reads pressed (0V) vs idle (3.3V); callout H</td></tr>
<tr><td class="ref">R_INV_BASE1</td><td class="val">100kΩ</td><td class="val">0402</td><td class="role">Dedicated VSYS pull-up for Q_INV1's base — decoupled from PWR_BTN_SENSE (callout F)</td></tr>
<tr><td class="ref">D_INV_ISO1</td><td class="val">BAT54W</td><td class="val">SOD-123</td><td class="role">Schottky isolator: lets a press pull Q_INV1's base low without loading PWR_BTN_SENSE</td></tr>
<tr><td class="ref">Q_INV1</td><td class="val">MMBT3904</td><td class="val">SOT-23</td><td class="role">Inverter: idle (button up) holds PWR_BTN_TRIGGER low; pressed lets R_INV_PU1 pull it high</td></tr>
Expand Down Expand Up @@ -399,7 +399,7 @@ <h2>Suggested floorplan</h2>
<!-- Q_LATCH1 -->
<rect x="195" y="250" width="30" height="50" rx="4" fill="#20241f" stroke="#4a5044" stroke-width="1.5"/>
<text x="210" y="243" text-anchor="middle" font-family="JetBrains Mono, monospace" font-weight="700" font-size="11" fill="#e0b563">Q_LATCH1</text>
<text x="210" y="315" text-anchor="middle" font-family="JetBrains Mono, monospace" font-size="9" fill="#7fa08d">2N7002 (NMOS)</text>
<text x="210" y="315" text-anchor="middle" font-family="JetBrains Mono, monospace" font-size="9" fill="#7fa08d">AO3400A (NMOS)</text>
<text x="238" y="279" text-anchor="start" font-family="JetBrains Mono, monospace" font-size="9" fill="#c9d6cd">G</text>

<!-- R_LATCH_G1 -->
Expand Down Expand Up @@ -429,7 +429,7 @@ <h2>Suggested floorplan</h2>

<!-- R_BTN_PU1 -->
<rect x="273" y="408" width="14" height="26" rx="2" fill="#20241f" stroke="#4a5044" stroke-width="1.5"/>
<text x="262" y="382" text-anchor="middle" font-family="JetBrains Mono, monospace" font-size="10" fill="#e0b563">VSYS</text>
<text x="262" y="382" text-anchor="middle" font-family="JetBrains Mono, monospace" font-size="10" fill="#e0b563">3V3</text>
<text x="292" y="425" text-anchor="start" font-family="JetBrains Mono, monospace" font-size="9.5" fill="#8fd3ff">R_BTN_PU1 100k</text>

<!-- D_INV_ISO1: isolates Q_INV1's base pull-up from PWR_BTN_SENSE so
Expand Down Expand Up @@ -529,10 +529,11 @@ <h2>Why it's arranged this way</h2>
<li><span class="badge">A</span><p><b>Q_PWR1 is the one part on this sheet that isn't low-current.</b> Every downstream milliamp — MCU, XBee, OLED, everything — flows through its source-drain path. DMG2305UX's RDS(on) (~35–50mΩ) keeps drop and heat trivial at this board's current budget, but a SOT-23 has no separate thermal pad — the source/drain copper itself is the heatsink, so give those pads real copper, not just a skinny trace.</p></li>
<li><span class="badge">B</span><p><b>SW_PWR1's position isn't yours to optimize electrically.</b> It has to land under a hole in whatever enclosure this board sits in — mechanical placement drives this part, and the rest of the latch logic has to route to wherever that ends up, not the other way around.</p></li>
<li><span class="badge">C</span><p><b>C_DBNC1 sits right on the switch's own pins.</b> Same principle as the charger's pushbutton guidance in its own datasheet: the debounce cap does its job by being close to the contact bounce it's filtering, not by being close to anything else.</p></li>
<li><span class="badge">D</span><p><b>The diode-OR and its pull-down are one compact decision node — and R_LATCH_G1's value is load-bearing, not incidental.</b> D_OR1, D_OR2, R_LATCH_G1, and Q_LATCH1's gate all meet at PWR_LATCH_G. A press only drives this node through R_INV_PU1 (100kΩ) and a diode drop — with the original 10kΩ pull-down, that divider left barely 0.3V at Q_LATCH1's gate, nowhere near enough to turn it on. Raised to 1MΩ, the same divider delivers ~2.4–3.1V across the battery's usable range, without adding any idle current (that's set by R_INV_PU1, untouched). Keeping this cluster tight matters less for noise than for just keeping the logic legible on the board.</p></li>
<li><span class="badge">D</span><p><b>The diode-OR and its pull-down are one compact decision node — and R_LATCH_G1's value is load-bearing, not incidental.</b> D_OR1, D_OR2, R_LATCH_G1, and Q_LATCH1's gate all meet at PWR_LATCH_G. A press only drives this node through R_INV_PU1 (100kΩ) and a diode drop — with the original 10kΩ pull-down, that divider left barely 0.3V at Q_LATCH1's gate, nowhere near enough to turn it on. Raised to 1MΩ, the same divider delivers ~2.4V at the bottom of the battery range (VSYS≈3.0V, modelling a real ~0.35V diode drop at µA currents) up to ~3.5V at full charge, without adding any idle current (that's set by R_INV_PU1, untouched). A 2N7002's worst-case threshold is 2.5V, which left no margin at the bottom, so Q_LATCH1 is an AO3400A (Vgs(th) ≤1.45V): at least ~1V of overdrive everywhere. Keeping this cluster tight matters less for noise than for just keeping the logic legible on the board.</p></li>
<li><span class="badge">E</span><p><b>PWR_HOLD arriving late is a hard failure, not a glitch.</b> Per this project's own GPIO notes, pin 36 (PWR_HOLD) must go high as the MCU's very first instruction — release the button before that happens and R_LATCH_G1's pull-down turns everything back off. Not a layout fix, but it's the reason D_OR2's path exists at all.</p></li>
<li><span class="badge">F</span><p><b>D_OR1 can't read PWR_BTN_SENSE directly — found the hard way, on real hardware.</b> The original design had R_BTN_PU1 (to VSYS) and a since-removed R_PWR_SENSE1 (to GND) both on that node, dividing it to roughly half VSYS at idle — comfortably above a diode's ~0.6V forward threshold, so D_OR1 was forward-biased (and the board powered on) the instant a battery was plugged in, button or no button. The inverter fixes this properly instead of just retuning the divider: idle, Q_INV1 is on and holds PWR_BTN_TRIGGER at GND; pressed, it turns off and R_INV_PU1 pulls PWR_BTN_TRIGGER to VSYS. Only a real press can forward-bias D_OR1 now. Keep this block close to D_OR1 — it's parts serving one signal, not worth spreading out.<br><br><b>Second-order bug, found the same way:</b> the inverter's own base resistor originally tapped PWR_BTN_SENSE directly, in series with R_BTN_PU1 — stealing enough idle base current to sag the MCU's own button-read node to ~1V instead of near-VSYS. R_INV_BASE1 is now its own dedicated 100kΩ pull-up straight to VSYS (not sharing current with R_BTN_PU1), and D_INV_ISO1 (a Schottky, chosen specifically for its lower forward drop than Q_INV1's own base-emitter junction) lets a press still pull the base low without ever loading PWR_BTN_SENSE.</p></li>
<li><span class="badge">G</span><p><b>PWR_EN had no defined rest state — TI's own datasheet says not to do this.</b> Q_PWR1's drain feeds the buck's EN pin directly, with nothing else on that node. TLV62569's EN pin is a bare comparator input with no internal pull ("Do not leave floating" per its datasheet) — with Q_PWR1 off, EN was held by MOSFET/input leakage alone, no guaranteed logic level. R_PWR_EN_PD1 (100kΩ to GND) fixes it: negligible loading when Q_PWR1's tens-of-mΩ RDS(on) is driving EN high, solid GND the instant it's off.</p></li>
<li><span class="badge">H</span><p><b>PWR_BTN_SENSE must never sit above the MCU's rail.</b> It lands directly on GPIO2 (pin 38), whose ceiling is VDD + 0.3V (~3.6V), but R_BTN_PU1 used to pull it to VSYS — up to ~4.5V on USB, and it back-fed the unpowered 3V3 rail through the pin's clamp diode whenever the MCU was off. Since R_INV_BASE1 gave the inverter its own VSYS pull-up (callout F), R_BTN_PU1 no longer has to supply the inverter, so it now pulls to 3V3: idle reads 3.3V, and with the MCU off the node is simply unpowered instead of leaking into the rail.</p></li>
</ol>
</section>

Expand Down
7 changes: 4 additions & 3 deletions PCB/diagrams/power_control_layout.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@ A press-to-start, software-held latch: the button turns the board on, the MCU ha
|---|---|---|---|
| Q_PWR1 | DMG2305UX | SOT-23 | Main power switch — VSYS to PWR_EN, carries full system current |
| R_PWR_EN_PD1 | 100kΩ | 0402 | `PWR_EN` pull-down to GND — the buck's EN pin has no internal bias and must not be left floating (callout G) |
| Q_LATCH1 | 2N7002 | SOT-23 | Pulls Q_PWR1's gate low to turn it on |
| Q_LATCH1 | AO3400A | SOT-23 | Pulls Q_PWR1's gate low to turn it on; low-Vth (≤1.45V) so a press still turns it on at the bottom of the battery range (callout D) |
| D_OR1 / D_OR2 | 1N4148W | SOD-123 | Diode-OR: `PWR_BTN_TRIGGER` (actual button press, via the inverter) or MCU hold, either one latches power on |
| R_GATE1 | 100kΩ | 0402 | Q_PWR1 gate pull-up — off by default |
| R_LATCH_G1 | 1MΩ | 0402 | Q_LATCH1 gate pull-down — off by default; sized to not starve the diode-OR's drive (callout D) |
| R_BTN_PU1 | 100kΩ | 0402 | Button sense pull-up — MCU reads pressed (0V) vs idle (VSYS) |
| R_BTN_PU1 | 100kΩ | 0402 | Button sense pull-up to **3V3** (not VSYS) — MCU reads pressed (0V) vs idle (3.3V); callout H |
| R_INV_BASE1 | 100kΩ | 0402 | Dedicated VSYS pull-up for Q_INV1's base — decoupled from `PWR_BTN_SENSE` (callout F) |
| D_INV_ISO1 | BAT54W | SOD-123 | Schottky isolator: lets a press pull Q_INV1's base low without loading `PWR_BTN_SENSE` |
| Q_INV1 | MMBT3904 | SOT-23 | Inverter: idle (button up) holds `PWR_BTN_TRIGGER` low; pressed lets `R_INV_PU1` pull it high |
Expand All @@ -45,10 +45,11 @@ Top copper layer. The VSYS→PWR_EN path runs straight across the top as one thi
| **A** | **Q_PWR1 is the one part on this sheet that isn't low-current.** Every downstream milliamp — MCU, XBee, OLED, everything — flows through its source-drain path. DMG2305UX's RDS(on) (~35–50mΩ) keeps drop and heat trivial at this board's current budget, but a SOT-23 has no separate thermal pad — the source/drain copper itself is the heatsink, so give those pads real copper, not just a skinny trace. |
| **B** | **SW_PWR1's position isn't yours to optimize electrically.** It has to land under a hole in whatever enclosure this board sits in — mechanical placement drives this part, and the rest of the latch logic has to route to wherever that ends up, not the other way around. |
| **C** | **C_DBNC1 sits right on the switch's own pins.** Same principle as the charger's pushbutton guidance in its own datasheet: the debounce cap does its job by being close to the contact bounce it's filtering, not by being close to anything else. |
| **D** | **The diode-OR and its pull-down are one compact decision node — and R_LATCH_G1's value is load-bearing, not incidental.** D_OR1, D_OR2, R_LATCH_G1, and Q_LATCH1's gate all meet at PWR_LATCH_G. A press only drives this node through `R_INV_PU1` (100kΩ) and a diode drop — with the original 10kΩ pull-down, that divider left barely 0.3V at Q_LATCH1's gate, nowhere near enough to turn it on. Raised to 1MΩ, the same divider delivers ~2.4–3.1V across the battery's usable range, without adding any idle current (that's set by `R_INV_PU1`, untouched). Keeping this cluster tight matters less for noise than for just keeping the logic legible on the board. |
| **D** | **The diode-OR and its pull-down are one compact decision node — and R_LATCH_G1's value is load-bearing, not incidental.** D_OR1, D_OR2, R_LATCH_G1, and Q_LATCH1's gate all meet at PWR_LATCH_G. A press only drives this node through `R_INV_PU1` (100kΩ) and a diode drop — with the original 10kΩ pull-down, that divider left barely 0.3V at Q_LATCH1's gate, nowhere near enough to turn it on. Raised to 1MΩ, the same divider delivers ~2.4V at the bottom of the battery range (VSYS≈3.0V, modelling a real ~0.35V diode drop at µA currents) up to ~3.5V at full charge, without adding any idle current (that's set by `R_INV_PU1`, untouched). A 2N7002's worst-case threshold is 2.5V, which left no margin at the bottom, so `Q_LATCH1` is an AO3400A (Vgs(th) ≤1.45V): at least ~1V of overdrive everywhere. Keeping this cluster tight matters less for noise than for just keeping the logic legible on the board. |
| **E** | **PWR_HOLD arriving late is a hard failure, not a glitch.** Per this project's own GPIO notes, pin 36 (PWR_HOLD) must go high as the MCU's very first instruction — release the button before that happens and R_LATCH_G1's pull-down turns everything back off. Not a layout fix, but it's the reason D_OR2's path exists at all. |
| **F** | **D_OR1 can't read `PWR_BTN_SENSE` directly — found the hard way, on real hardware.** The original design had `R_BTN_PU1` (to VSYS) and a since-removed `R_PWR_SENSE1` (to GND) both on that node, dividing it to roughly half VSYS at idle — comfortably above a diode's ~0.6V forward threshold, so `D_OR1` was forward-biased (and the board powered on) the instant a battery was plugged in, button or no button. The inverter fixes this properly instead of just retuning the divider: idle, `Q_INV1` is on and holds `PWR_BTN_TRIGGER` at GND; pressed, it turns off and `R_INV_PU1` pulls `PWR_BTN_TRIGGER` to VSYS. Only a real press can forward-bias `D_OR1` now. Keep this block close to `D_OR1` — it's parts serving one signal, not worth spreading out.<br><br>**Second-order bug, found the same way:** the inverter's own base resistor originally tapped `PWR_BTN_SENSE` directly, in series with `R_BTN_PU1` — stealing enough idle base current to sag the MCU's own button-read node to ~1V instead of near-VSYS. `R_INV_BASE1` is now its own dedicated 100kΩ pull-up straight to VSYS (not sharing current with `R_BTN_PU1`), and `D_INV_ISO1` (a Schottky, chosen specifically for its lower forward drop than Q_INV1's own base-emitter junction) lets a press still pull the base low without ever loading `PWR_BTN_SENSE`. |
| **G** | **`PWR_EN` had no defined rest state — TI's own datasheet says not to do this.** `Q_PWR1`'s drain feeds the buck's EN pin directly, with nothing else on that node. TLV62569's EN pin is a bare comparator input with no internal pull ("Do not leave floating" per its datasheet) — with `Q_PWR1` off, EN was held by MOSFET/input leakage alone, no guaranteed logic level. `R_PWR_EN_PD1` (100kΩ to GND) fixes it: negligible loading when `Q_PWR1`'s tens-of-mΩ RDS(on) is driving EN high, solid GND the instant it's off. |
| **H** | **`PWR_BTN_SENSE` must never sit above the MCU's rail.** It lands directly on GPIO2 (pin 38), whose ceiling is VDD + 0.3V (~3.6V), but `R_BTN_PU1` used to pull it to VSYS — up to ~4.5V on USB, and it back-fed the unpowered 3V3 rail through the pin's clamp diode whenever the MCU was off. Since `R_INV_BASE1` gave the inverter its own VSYS pull-up (callout F), `R_BTN_PU1` no longer has to supply the inverter, so it now pulls to 3V3: idle reads 3.3V, and with the MCU off the node is simply unpowered instead of leaking into the rail. |

## Routing priority

Expand Down
4 changes: 2 additions & 2 deletions PCB/diagrams/power_control_layout.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 5 additions & 5 deletions PCB/power_control.kicad_sch
Original file line number Diff line number Diff line change
Expand Up @@ -1741,7 +1741,7 @@
)
)
)
(global_label "VSYS"
(global_label "3V3"
(shape input)
(at 156.21 134.62 90)
(fields_autoplaced yes)
Expand Down Expand Up @@ -3499,7 +3499,7 @@
(justify left)
)
)
(property "Value" "2N7002"
(property "Value" "AO3400A"
(at 194.31 127 0)
(show_name no)
(do_not_autoplace no)
Expand Down Expand Up @@ -3540,7 +3540,7 @@
)
)
)
(property "MF" "Jiangsu Changjing Electronics Technology Co., Ltd."
(property "MF" "Alpha & Omega Semicon"
(at 0 0 0)
(hide yes)
(show_name no)
Expand All @@ -3551,7 +3551,7 @@
)
)
)
(property "Manufacturer_Part_Number" "2N7002"
(property "Manufacturer_Part_Number" "AO3400A"
(at 0 0 0)
(hide yes)
(show_name no)
Expand All @@ -3562,7 +3562,7 @@
)
)
)
(property "LCSC" "C8545"
(property "LCSC" "C20917"
(at 0 0 0)
(hide yes)
(show_name no)
Expand Down
2 changes: 1 addition & 1 deletion PCB/production/bom/snips_controller-bom.csv
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
16;"J_USB1";"USB_C_Receptacle_HRO_TYPE-C-31-M-12";1;"USB-C";;;
17;"L_BUCK1";"L_Bourns-SRN4018";1;"2.2µH";;;
18;"Q_INV1";"SOT-23";1;"MMBT3904";;;
19;"Q_LATCH1";"SOT-23";1;"2N7002";;;
19;"Q_LATCH1";"SOT-23";1;"AO3400A";;;
20;"Q_PWR1";"SOT-23";1;"AO3401A";;;
21;"R_BOOT_PU1, R_EN_PU1, R_STAT1, R_STAT2, R_TS1, R_XBEE_ATTN_PU1, R_XBEE_RST1";"R_0402_1005Metric";7;"10kΩ";;;
22;"R_BTN_PU1, R_GATE1, R_INV_BASE1, R_INV_PU1, R_PWR_EN_PD1, R_VSYS2";"R_0402_1005Metric";6;"100kΩ";;;
Expand Down
Loading
Loading