ShiboSoftwareDev/nema8-20mm-usbc-pd-controller
A 20×20 mm four-layer USB‑C PD stepper motor controller PCB using a CH32X035 MCU, TMC2209 driver, protected power inputs, USB data/boot interface, 3.3 V regulation, ESD/TVS protection, current-sense circuitry, and NEMA‑8 motor outputs.
- Version
- 1.0.13
- License
- unset
- Stars
- 0
four-layer.circuit.tsx
PCB
Schematic
import { Fragment } from "react"
import { AutoroutingPipelineSolver9_PreloadedTraceGraph } from "@tscircuit/capacity-autorouter"
import { BAT54KFILM } from "./imports/BAT54KFILM"
import { BAT54WS } from "./imports/BAT54WS"
import { BM04B_SRSS_TB_LF__SN_ } from "./imports/BM04B_SRSS_TB_LF__SN_"
import { BSMD1206_110_16V } from "./imports/BSMD1206_110_16V"
import { CH32X035F8U6 } from "./imports/CH32X035F8U6"
import { HT7533_1 } from "./imports/HT7533_1"
import { PMEG6020ELRX } from "./imports/PMEG6020ELRX"
import { PT1206FR_7W0R33L } from "./imports/PT1206FR_7W0R33L"
import { TMC2209_LA_T } from "./imports/TMC2209_LA_T"
import { TL3780AF100QG } from "./imports/TL3780AF100QG"
import { TYPE_C_31_M_12 } from "./imports/TYPE_C_31_M_12"
import { TYPE_C_6P_LTH5_5_108 } from "./imports/TYPE_C_6P_LTH5_5_108"
import { USBLC6_2SC6 } from "./imports/USBLC6_2SC6"
const resistorParts: Record<string, string> = {
"27": "C25100",
"1k": "C11702",
"4.7k": "C25900",
"5.1k": "C25905",
"10k": "C25744",
"12k": "C25752",
"47k": "C25792",
"100k": "C25741",
"270k": "C25770",
}
const capacitorParts: Record<string, { part: string; footprint: string; voltage: string }> = {
"22nF_50V": { part: "C1532", footprint: "0402", voltage: "50V" },
"100nF": { part: "C1525", footprint: "0402", voltage: "16V" },
"100nF_50V": { part: "C14663", footprint: "0603", voltage: "50V" },
"2.2uF_10V": { part: "C107370", footprint: "0402", voltage: "10V" },
"2.2uF": { part: "C23630", footprint: "0603", voltage: "16V" },
"10uF_25V": { part: "C96446", footprint: "0603", voltage: "25V" },
}
const R = ({ name, resistance, connections: _connections, ...props }: any) => (
<resistor
name={name}
resistance={resistance}
footprint="0402"
supplierPartNumbers={{ jlcpcb: [resistorParts[resistance]] }}
{...props}
/>
)
const C = ({ name, value, connections: _connections, ...props }: any) => {
const item = capacitorParts[value]
return (
<capacitor
name={name}
capacitance={value.replace(/_\d+V$/, "")}
footprint={item.footprint}
maxVoltageRating={item.voltage}
maxDecouplingTraceLength="50mm"
supplierPartNumbers={{ jlcpcb: [item.part] }}
{...props}
/>
)
}
const nets = [
"GND",
"PD_VBUS_RAW",
"PD_VBUS_FUSED",
"VM",
"USB_5V",
"LOGIC_IN",
"VIO",
"PD_CC1",
"PD_CC2",
"DATA_CC1",
"DATA_CC2",
"USB_DM_CONN",
"USB_DP_CONN",
"USB_DM",
"USB_DP",
"BOOT_BIAS",
"VBUS_ADC",
"DIAG",
"STEP",
"DIR",
"ENN",
"STDBY",
"UART_MCU",
"UART_DRV",
"V5_DRV",
"VREF",
"CP_HI",
"CP_LO",
"VCP",
"SENSE_A",
"SENSE_B",
"MOTOR_A_NEG",
"MOTOR_A_POS",
"MOTOR_B_POS",
"MOTOR_B_NEG",
] as const
const sheets = {
power: "power",
control: "control",
driver: "driver",
} as const
const sections = {
pdInput: "pd_input",
logicPower: "logic_power",
usbData: "usb_data",
controller: "controller",
driverCore: "driver_core",
} as const
const driverX = 0
const driverY = -0.5
const mcuX = 5.6
const mcuY = 0
const thermalViaOffsets = [
[-0.7, -0.7], [-0.7, 0.7], [0.7, -0.7], [0.7, 0.7],
] as const
const traceWidthForNet = (name: (typeof nets)[number]) => {
if (name.startsWith("MOTOR_")) return "0.3mm"
if (name.startsWith("SENSE_")) return "0.3mm"
if (name === "PD_VBUS_RAW" || name === "PD_VBUS_FUSED") return "0.5mm"
if (name === "VM") return "0.4mm"
if (["USB_5V", "LOGIC_IN", "VIO", "V5_DRV", "VCP"].includes(name)) return "0.25mm"
return "0.15mm"
}
// Ground is carried by a dedicated inner-1 plane. Every outer-layer ground
// land gets a full-stack 0.30/0.45 mm through-via fanout into that plane.
const usbGroundVias = [
{ from: ".J_USB_PD > .GND1", x: -4.0, y: 0.06254755 },
{ from: ".J_USB_PD > .GND2", x: 2.749931, y: 0.06254755 },
{ from: ".J_USB_DATA > .GND1", x: 1.25, y: 2.174 },
{ from: ".J_USB_DATA > .GND2", x: 1.75, y: 2.174 },
] as const
const connectorShellGrounds = [
{ from: ".J_USB_PD > .EP1", x: 4.9, y: 0.7 },
{ from: ".J_USB_PD > .EP2", x: -4.9, y: 0.7 },
{ from: ".J_USB_DATA > .EH1", x: 5.2, y: 0.75 },
{ from: ".J_USB_DATA > .EH2", x: -5.2, y: 0.75 },
{ from: ".J_USB_DATA > .EH3", x: 5.2, y: -2.05 },
{ from: ".J_USB_DATA > .EH4", x: -5.2, y: -2.05 },
] as const
const driverEpGroundPaths = [
{ from: ".U_DRV > .GND2", path: [
{ x: -0.5, y: -1.3 },
{ x: 0, y: 0 },
] },
{ from: ".U_DRV > .GND1", path: [
{ x: 0, y: 1.3 },
{ x: 0, y: 0 },
] },
{ from: ".U_DRV > .SPREAD", path: [
{ x: 1.6, y: -2.5 }, { x: 1.6, y: -1.4 }, { x: 0, y: 0 },
] },
{ from: ".U_DRV > .MS1_AD0", path: [
{ x: 1.4, y: -1.0 }, { x: 0, y: 0 },
] },
{ from: ".U_DRV > .MS2_AD1", path: [
{ x: 1.4, y: -0.5 }, { x: 0, y: 0 },
] },
{ from: ".U_DRV > .CLK", path: [
{ x: 1.4, y: 1.0 }, { x: 0, y: 0 },
] },
{ from: ".U_DRV > .UNUSED", path: [
{ x: -1.4, y: 0 }, { x: 0, y: 0 },
] },
] as const
const groundFanouts = [
// Coordinates are local to each source component (the pcbPath convention).
{ from: ".C_VM_HF1 > .pin2", x: 1.4, y: 0, fromLayer: "top" },
{ from: ".D_VM_TVS > .anode", x: 2.5, y: 0, fromLayer: "bottom" },
{ from: ".C_VM_BULK1 > .pin2", x: -0.83, y: -0.8, fromLayer: "bottom" },
{ from: ".C_VM_BULK2 > .pin2", x: -0.83, y: -0.8, fromLayer: "bottom" },
{ from: ".R_PD_CC1 > .pin2", x: -1.1, y: 0, fromLayer: "bottom" },
{ from: ".R_PD_CC2 > .pin2", x: -1.1, y: 0, fromLayer: "bottom" },
{ from: ".R_DATA_CC1 > .pin2", x: -0.51, y: 0.65, fromLayer: "bottom" },
{ from: ".R_DATA_CC2 > .pin2", x: -0.51, y: 0.65, fromLayer: "bottom" },
{ from: ".C_LDO_IN > .pin2", x: -1.4, y: 0, fromLayer: "bottom" },
{ from: ".U_3V3 > .GND", x: -1.9, y: -0.95, fromLayer: "bottom" },
{ from: ".C_LDO_OUT > .pin2", x: -1.0, y: 0, fromLayer: "bottom" },
{ from: ".C_MCU_HF > .pin2", x: -0.6, y: 0.7, fromLayer: "bottom" },
{ from: ".C_MCU_BULK > .pin2", x: -0.2, y: 1.0, fromLayer: "bottom" },
{ from: ".R_STDBY_PD > .pin2", x: 1.1, y: 0, fromLayer: "top" },
{ from: ".C_VIO > .pin2", x: 3.3, y: 0, fromLayer: "top" },
{ from: ".R_VREF_BOTTOM > .pin2", x: -0.7, y: 0.8, fromLayer: "bottom" },
{ from: ".R_VBUS_BOTTOM > .pin2", x: 0.8, y: 0, fromLayer: "top" },
{ from: ".R_SENSE_A > .pin2", x: -2.2, y: 0, fromLayer: "bottom" },
{ from: ".R_SENSE_B > .pin2", x: -2.0, y: 1.0, fromLayer: "bottom" },
] as const
// Explicitly enumerate the signal/power routes so the default autorouter does
// not waste channels trying to duplicate the copper-pour ground network.
const routedConnections = [
[".J_USB_PD > .VBUS1", ".J_USB_PD > .VBUS2"],
[".J_USB_PD > .VBUS2", ".F1 > .pin1"],
[".F1 > .pin2", ".D_BLOCK > .anode"],
[".D_BLOCK > .cathode", ".D_VM_TVS > .cathode"],
[".D_VM_TVS > .cathode", ".C_VM_BULK1 > .pin1"],
[".C_VM_BULK1 > .pin1", ".C_VM_BULK2 > .pin1"],
[".C_VM_BULK2 > .pin1", ".D_LOGIC_PD > .anode"],
[".D_LOGIC_PD > .anode", ".R_VBUS_TOP > .pin1"],
[".D_LOGIC_PD > .anode", ".C_VM_HF1 > .pin1"],
[".C_VM_HF1 > .pin1", ".U_DRV > .VS1"],
[".U_DRV > .VS1", ".U_DRV > .VS2"],
[".C_VM_HF1 > .pin1", ".C_VCP > .pin2"],
[".J_USB_DATA > .VBUS1", ".J_USB_DATA > .VBUS2"],
[".J_USB_DATA > .VBUS2", ".D_LOGIC_USB > .anode"],
[".D_LOGIC_PD > .cathode", ".D_LOGIC_USB > .cathode"],
[".D_LOGIC_USB > .cathode", ".U_3V3 > .VIN"],
[".U_3V3 > .VIN", ".C_LDO_IN > .pin1"],
[".U_3V3 > .VOUT", ".C_LDO_OUT > .pin1"],
[".C_LDO_OUT > .pin1", ".U_MCU > .VDD"],
[".U_MCU > .VDD", ".C_MCU_HF > .pin1"],
[".C_MCU_HF > .pin1", ".C_MCU_BULK > .pin1"],
[".C_MCU_HF > .pin1", ".R_BOOT > .pin1"],
[".C_MCU_BULK > .pin1", ".R_EN_PU > .pin1"],
[".R_EN_PU > .pin1", ".U_DRV > .VCC_IO"],
[".U_DRV > .VCC_IO", ".C_VIO > .pin1"],
[".U_DRV > .VCC_IO", ".U_USB_ESD > .VBUS"],
[".J_USB_PD > .CC1", ".U_MCU > .CC1"],
[".J_USB_PD > .CC2", ".U_MCU > .CC2"],
[".J_USB_DATA > .CC1", ".R_DATA_CC1 > .pin1"],
[".J_USB_DATA > .CC2", ".R_DATA_CC2 > .pin1"],
[".J_USB_DATA > .DN1", ".U_USB_ESD > .DM_IN"],
[".J_USB_DATA > .DN2", ".U_USB_ESD > .DM_IN"],
[".J_USB_DATA > .DP1", ".U_USB_ESD > .DP_IN"],
[".J_USB_DATA > .DP2", ".U_USB_ESD > .DP_IN"],
[".U_USB_ESD > .DM_OUT", ".U_MCU > .USB_DM"],
[".U_USB_ESD > .DP_OUT", ".U_MCU > .USB_DP"],
[".U_MCU > .USB_DP", ".SW_BOOT > .pin102"],
[".R_BOOT > .pin2", ".SW_BOOT > .pin101"],
[".R_VBUS_TOP > .pin2", ".R_VBUS_BOTTOM > .pin1"],
[".R_VBUS_BOTTOM > .pin1", ".U_MCU > .VBUS_ADC"],
[".U_DRV > .DIAG", ".U_MCU > .DIAG_IN"],
[".U_DRV > .STEP", ".U_MCU > .STEP_OUT"],
[".U_DRV > .DIR", ".U_MCU > .DIR_OUT"],
[".U_DRV > .ENN", ".U_MCU > .ENABLE_OUT"],
[".U_DRV > .ENN", ".R_EN_PU > .pin2"],
[".U_DRV > .STDBY", ".U_MCU > .STDBY_OUT"],
[".U_DRV > .STDBY", ".R_STDBY_PD > .pin1"],
[".U_MCU > .UART1", ".U_DRV > .PDN_UART"],
[".U_MCU > .UART1", ".U_DRV > .PDN_UART"], // reserved duplicate; not rendered
[".U_DRV > .5VOUT", ".C_5V > .pin1"],
[".C_5V > .pin1", ".R_VREF_TOP > .pin1"],
[".R_VREF_TOP > .pin2", ".R_VREF_BOTTOM > .pin1"],
[".R_VREF_BOTTOM > .pin1", ".C_VREF > .pin1"],
[".C_VREF > .pin1", ".U_DRV > .VREF"],
[".U_DRV > .CPO", ".C_CP > .pin1"],
[".U_DRV > .CPI", ".C_CP > .pin2"],
[".U_DRV > .VCP", ".C_VCP > .pin1"],
[".U_DRV > .BRA", ".R_SENSE_A > .pin1"],
[".U_DRV > .BRB", ".R_SENSE_B > .pin1"],
[".U_DRV > .OA2", ".J_MOTOR > .pin1"],
[".U_DRV > .OA1", ".J_MOTOR > .pin2"],
[".U_DRV > .OB1", ".J_MOTOR > .pin3"],
[".U_DRV > .OB2", ".J_MOTOR > .pin4"],
[".C_VM_HF1 > .pin2", ".U_DRV > .GND1"],
[".D_VM_TVS > .anode", ".C_VM_BULK1 > .pin2"],
[".D_VM_TVS > .anode", ".C_VM_BULK2 > .pin2"],
[".U_DRV > .SPREAD", ".U_DRV > .EP"],
[".U_DRV > .MS1_AD0", ".U_DRV > .EP"],
[".U_DRV > .MS2_AD1", ".U_DRV > .EP"],
[".U_DRV > .CLK", ".U_DRV > .EP"],
[".U_DRV > .UNUSED", ".U_DRV > .EP"],
[".R_DATA_CC1 > .pin2", ".R_DATA_CC2 > .pin2"],
[".R_DATA_CC2 > .pin2", ".U_MCU > .GND"],
[".U_USB_ESD > .GND", ".J_USB_DATA > .GND1"],
[".C_LDO_IN > .pin2", ".U_3V3 > .GND"],
[".C_LDO_OUT > .pin2", ".C_LDO_IN > .pin2"],
[".U_3V3 > .GND", ".J_USB_DATA > .EH3"],
[".C_MCU_HF > .pin2", ".U_MCU > .GND"],
[".C_MCU_BULK > .pin2", ".U_MCU > .GND"],
[".R_STDBY_PD > .pin2", ".U_DRV > .EP"],
[".C_5V > .pin2", ".U_DRV > .EP"],
[".C_VIO > .pin2", ".U_DRV > .EP"],
[".R_VREF_BOTTOM > .pin2", ".U_DRV > .EP"],
[".C_VREF > .pin2", ".U_DRV > .EP"],
[".R_VBUS_BOTTOM > .pin2", ".U_MCU > .GND"],
[".C_VM_BULK1 > .pin2", ".J_USB_PD > .GND1"],
[".C_VM_BULK2 > .pin2", ".J_USB_PD > .GND2"],
[".R_SENSE_A > .pin2", ".U_DRV > .EP"],
[".R_SENSE_B > .pin2", ".U_DRV > .EP"],
] as const
const traceWidthForRouteIndex = (index: number) => {
if (index <= 2) return "0.5mm"
if (index <= 11) return "0.4mm"
if (index >= 57 && index <= 62) return "0.3mm"
if (index === 63) return "0.3mm"
if (index === 64 || index === 65) return "0.5mm"
if (index >= 71 && index <= 73) return "0.2mm"
if (index >= 74 && index <= 76) return "0.25mm"
if (index >= 77 && index <= 83) return "0.2mm"
if (index === 84) return "0.2mm"
if (index === 85 || index === 86) return "0.5mm"
if (index === 87 || index === 88) return "0.3mm"
if ((index >= 12 && index <= 25) || index === 49 || index === 50 || index === 56) return "0.25mm"
if (index === 54 || index === 55) return "0.2mm"
return "0.15mm"
}
const routeNames = (start: number, end: number) =>
Array.from({ length: end - start + 1 }, (_, offset) => `ROUTE_${start + offset}`)
// Keep signal and power nets under the default autorouter. This avoids the
// partial-layer via definitions that hand routing can accidentally introduce;
// every layer transition is generated as a normal plated through-via.
const manualRouteIndexes = new Set<number>([51])
const manualPcbPathForRouteIndex = (index: number): any[] | undefined => {
if (index === 14) return [
{ x: 0.8, y: 1.6 },
{ x: 4.6, y: 1.6 },
{ x: 4.6, y: 0.6 },
{ x: 4.34, y: 0.6 },
]
if (index === 15) return [
{ x: 1.14, y: 0 },
{ x: 0.2, y: 0.5 },
{ x: 0.2, y: 10.5 },
{ x: 1.1, y: 10.5 },
{ x: 1.1, y: 12.6 },
{ x: 0.63, y: 12.6 },
]
if (index === 16) return [
{ x: 1.23, y: 0 },
{ x: 1.6, y: -1.0 },
{ x: 1.6, y: -2.975 },
]
if (index === 24) return [
{ x: 1.8, y: 2.5 },
{ x: 3.2, y: 2.3 },
]
if (index === 26) return [
{ x: -0.55, y: 1.2 },
{ x: -0.55, y: 1.2, via: true, fromLayer: "top", toLayer: "inner2" },
{ x: -0.55, y: 1.2 },
{ x: 5.4, y: 1.2 },
{ x: 4.7, y: 4.2 },
{ x: 4.7, y: 4.2, via: true, fromLayer: "inner2", toLayer: "bottom" },
{ x: 4.7, y: 4.2 },
{ x: 4.9, y: 4.6 },
]
if (index === 27) return [
{ x: 0.55, y: 1.2 },
{ x: 0.55, y: 1.2, via: true, fromLayer: "top", toLayer: "inner1" },
{ x: 0.55, y: 1.2 },
{ x: 0.55, y: 4.7 },
{ x: 4.0, y: 4.7 },
{ x: 4.0, y: 4.7, via: true, fromLayer: "inner1", toLayer: "bottom" },
{ x: 4.0, y: 4.7 },
{ x: 4.5, y: 5.0 },
]
if (index === 28) return [
{ x: -2.4, y: 3.25 },
{ x: -3.0, y: 3.45 },
{ x: -3.0, y: 3.45, via: true, fromLayer: "top", toLayer: "bottom" },
{ x: -3.0, y: 3.45 },
{ x: -3.5, y: 4.05 },
{ x: -8.71, y: 4.05 },
]
if (index === 29) return [
{ x: 0.75, y: 1.55 },
{ x: 0.75, y: -1.45 },
{ x: 0, y: -1.45 },
{ x: 0, y: -1.45, via: true, fromLayer: "top", toLayer: "bottom" },
{ x: 0, y: -1.45 },
{ x: -1.31, y: -2.55 },
]
if (index === 34) return [
{ x: 1.0, y: -2.4 },
{ x: 1.0, y: -2.4, via: true, fromLayer: "bottom", toLayer: "inner2" },
{ x: 1.0, y: -2.4 },
{ x: 1.0, y: -5.6 },
{ x: -1.2, y: -7.8 },
{ x: -7.3, y: -7.8 },
{ x: -7.3, y: -7.8, via: true, fromLayer: "inner2", toLayer: "bottom" },
{ x: -7.3, y: -7.8 },
{ x: -7.3, y: -5.0 },
{ x: -7.0, y: -4.5 },
]
if (index === 36) return [
{ x: 0, y: 2.3 },
{ x: 0, y: 2.3, via: true, fromLayer: "bottom", toLayer: "inner1" },
{ x: 0, y: 2.3 },
{ x: -2.7, y: 2.3 },
{ x: -2.7, y: -5.5 },
{ x: -3.2, y: -5.5 },
{ x: -3.2, y: -5.5, via: true, fromLayer: "inner1", toLayer: "top" },
{ x: -3.2, y: -5.5 },
]
if (index === 39) return [
{ x: 0.8, y: 1.0 },
{ x: 0.8, y: 1.0, via: true, fromLayer: "top", toLayer: "inner2" },
{ x: 0.8, y: 1.0 },
{ x: -2.6, y: 6.7 },
{ x: -2.6, y: 6.7, via: true, fromLayer: "inner2", toLayer: "bottom" },
{ x: -2.6, y: 6.7 },
]
if (index === 41) return [
{ x: 1.0, y: 3.3 },
{ x: 1.0, y: 3.3, via: true, fromLayer: "top", toLayer: "inner2" },
{ x: 1.0, y: 3.3 },
{ x: 1.5, y: 2.3 },
{ x: 1.5, y: -0.5 },
{ x: 3.2, y: -0.7 },
{ x: 3.2, y: -0.7, via: true, fromLayer: "inner2", toLayer: "bottom" },
{ x: 3.2, y: -0.7 },
{ x: 3.2, y: 0.1 },
]
if (index === 42) return [
{ x: -0.5, y: 2.5 },
{ x: -0.5, y: 3.3 },
{ x: 3.2, y: 3.3 },
{ x: 3.2, y: 3.3, via: true, fromLayer: "top", toLayer: "bottom" },
{ x: 3.2, y: 0.5 },
{ x: 3.8, y: 0.5 },
]
if (index === 43) return [
{ x: -1.0, y: -3.2 },
{ x: -1.0, y: -3.2, via: true, fromLayer: "top", toLayer: "inner2" },
{ x: -1.0, y: -3.2 },
{ x: 3.5, y: -3.2 },
{ x: 3.5, y: 0.7 },
{ x: 3.7, y: 0.7 },
{ x: 3.7, y: 0.7, via: true, fromLayer: "inner2", toLayer: "bottom" },
{ x: 3.7, y: 0.7 },
]
if (index === 44) return [
{ x: -0.8, y: -3.2 },
{ x: -3.6, y: -3.2 },
]
if (index === 45) return [
{ x: -1.0, y: 3.3 },
{ x: -2.2, y: 3.3 },
{ x: -2.2, y: 3.3, via: true, fromLayer: "top", toLayer: "inner2" },
{ x: -2.2, y: 3.3 },
{ x: -2.2, y: 3.7 },
{ x: 3.1, y: 3.7 },
{ x: 3.1, y: 3.2 },
{ x: 3.1, y: 3.2, via: true, fromLayer: "inner2", toLayer: "bottom" },
{ x: 3.1, y: 3.2 },
{ x: 3.1, y: 2.5 },
{ x: 4.5, y: 2.5 },
{ x: 4.5, y: 2.0 },
]
if (index === 46) return [
{ x: -1.0, y: 3.0 },
{ x: -4.7, y: 2.7 },
]
if (index === 47) return [
{ x: 2.1, y: -0.8 },
{ x: 2.1, y: -1.2 },
{ x: 2.1, y: -1.2, via: true, fromLayer: "bottom", toLayer: "top" },
{ x: 2.1, y: -1.2 },
]
if (index === 49) return [
{ x: 3.0, y: -1.5 },
{ x: 3.5, y: -2.5 },
]
if (index === 50) return [
{ x: 0, y: -0.9 },
{ x: 0, y: -0.9, via: true, fromLayer: "top", toLayer: "inner1" },
{ x: 0, y: -0.9 },
{ x: 0, y: -0.6 },
{ x: -4.1, y: -0.6 },
{ x: -4.1, y: 0.6 },
{ x: -4.1, y: 0.6, via: true, fromLayer: "inner1", toLayer: "bottom" },
{ x: -4.1, y: 0.6 },
{ x: -4.1, y: 1.61 },
]
if (index === 51) return [
{ x: -0.51, y: 0 },
{ x: -0.51, y: -0.8 },
{ x: 2.81, y: -0.8 },
{ x: 2.81, y: 0 },
]
if (index === 52) return [
{ x: 0.51, y: -0.7 },
{ x: 2.81, y: -0.7 },
]
if (index === 53) return [
{ x: 0.2, y: 0 },
{ x: 0.2, y: 1.3 },
{ x: 0.2, y: 1.3, via: true, fromLayer: "bottom", toLayer: "top" },
{ x: 0.2, y: 1.3 },
]
if (index === 54) return [{ x: 0, y: -3.2 }]
if (index === 55) return [{ x: 0.5, y: -3.2 }]
if (index === 56) return [
{ x: 1.0, y: -3.1 },
{ x: 1.5, y: -3.3 },
{ x: 2.19, y: -3.3 },
]
if (index === 59) return [
{ x: -2.0, y: 2.0 },
{ x: -8.3, y: 2.0 },
]
if (index === 60) return [
{ x: -3.0, y: 0.5 },
{ x: -3.5, y: 1.0 },
{ x: -8.3, y: 1.0 },
]
if (index === 61) return [
{ x: -3.0, y: -0.5 },
{ x: -3.5, y: 0 },
{ x: -8.3, y: 0 },
]
if (index === 62) return [
{ x: -2.5, y: -1.5 },
{ x: -6.8, y: -1.5 },
{ x: -8.3, y: -1.0 },
]
if (index === 57) return [
{ x: -2.2, y: 1.0 },
{ x: -2.2, y: 1.0, via: true, fromLayer: "top", toLayer: "bottom" },
{ x: -2.2, y: 1.0 },
]
if (index === 58) return [
{ x: -2.2, y: -1.0 },
{ x: -2.2, y: -1.0, via: true, fromLayer: "top", toLayer: "bottom" },
{ x: -2.2, y: -1.0 },
]
return undefined
}
const routePhaseForIndex = (index: number) => {
return index === 40 ? null : 1
}
const replaceAutoroutedRoute = (trace: any, route: any[]) => {
const original = trace.route ?? []
const firstPort = original.find((point: any) => point.start_pcb_port_id)?.start_pcb_port_id
const lastPort = [...original].reverse().find((point: any) => point.end_pcb_port_id)?.end_pcb_port_id
return {
...trace,
route: route.map((point, index) => ({
...point,
...(index === 0 && firstPort ? { start_pcb_port_id: firstPort } : {}),
...(index === route.length - 1 && lastPort ? { end_pcb_port_id: lastPort } : {}),
})),
}
}
const postprocessDenseUsbRoutes = (traces: any[]) => {
return traces.map((trace: any) => {
const traceIdentity = `${trace.connection_name ?? ""} ${trace.pcb_trace_id ?? ""}`
const route = trace.route ?? []
const touches = (x: number, y: number) => route.some((point: any) =>
Math.abs((point.x ?? Infinity) - x) < 0.01 && Math.abs((point.y ?? Infinity) - y) < 0.01)
if (traceIdentity.includes("ROUTE_28") || touches(-0.69, 8.6)) {
return replaceAutoroutedRoute(trace, [
{ route_type: "wire", x: 2.4, y: 3.876, width: 0.15, layer: "top" },
{ route_type: "wire", x: 2.4, y: 3.0, width: 0.15, layer: "top" },
{ route_type: "via", x: 2.4, y: 3.0, from_layer: "top", to_layer: "inner2", via_diameter: 0.45, via_hole_diameter: 0.3 },
{ route_type: "wire", x: 2.4, y: 3.0, width: 0.15, layer: "inner2" },
{ route_type: "wire", x: 1.9, y: 4.2, width: 0.15, layer: "inner2" },
{ route_type: "wire", x: 1.9, y: 8.0, width: 0.15, layer: "inner2" },
{ route_type: "wire", x: -0.69, y: 8.0, width: 0.15, layer: "inner2" },
{ route_type: "via", x: -0.69, y: 8.0, from_layer: "inner2", to_layer: "bottom", via_diameter: 0.45, via_hole_diameter: 0.3 },
{ route_type: "wire", x: -0.69, y: 8.0, width: 0.15, layer: "bottom" },
{ route_type: "wire", x: -0.69, y: 8.6, width: 0.15, layer: "bottom" },
])
}
if (traceIdentity.includes("ROUTE_29") || touches(1.31, 8.6)) {
return replaceAutoroutedRoute(trace, [
{ route_type: "wire", x: -0.75, y: 3.876, width: 0.15, layer: "top" },
{ route_type: "wire", x: -0.75, y: 3.1, width: 0.15, layer: "top" },
{ route_type: "via", x: -0.75, y: 3.1, from_layer: "top", to_layer: "inner2", via_diameter: 0.45, via_hole_diameter: 0.3 },
{ route_type: "wire", x: -0.75, y: 3.1, width: 0.15, layer: "inner2" },
{ route_type: "wire", x: -0.75, y: 7.4, width: 0.15, layer: "inner2" },
{ route_type: "wire", x: 1.31, y: 7.4, width: 0.15, layer: "inner2" },
{ route_type: "via", x: 1.31, y: 7.4, from_layer: "inner2", to_layer: "bottom", via_diameter: 0.45, via_hole_diameter: 0.3 },
{ route_type: "wire", x: 1.31, y: 7.4, width: 0.15, layer: "bottom" },
{ route_type: "wire", x: 1.31, y: 8.6, width: 0.15, layer: "bottom" },
])
}
if (traceIdentity.includes("ROUTE_34") || traceIdentity.includes("source_trace_34") || touches(2.15, 6.1491)) {
return replaceAutoroutedRoute(trace, [
{ route_type: "wire", x: 2.15, y: 6.1491, width: 0.15, layer: "bottom" },
{ route_type: "wire", x: 3.6, y: 6.0, width: 0.15, layer: "bottom" },
{ route_type: "via", x: 3.6, y: 6.0, from_layer: "bottom", to_layer: "inner2", via_diameter: 0.45, via_hole_diameter: 0.3 },
{ route_type: "wire", x: 3.6, y: 6.0, width: 0.15, layer: "inner2" },
{ route_type: "wire", x: 6.8, y: 6.0, width: 0.15, layer: "inner2" },
{ route_type: "wire", x: 9.0, y: 3.8, width: 0.15, layer: "inner2" },
{ route_type: "wire", x: 9.0, y: -2.3, width: 0.15, layer: "inner2" },
{ route_type: "via", x: 9.0, y: -2.3, from_layer: "inner2", to_layer: "bottom", via_diameter: 0.45, via_hole_diameter: 0.3 },
{ route_type: "wire", x: 9.0, y: -2.3, width: 0.15, layer: "bottom" },
{ route_type: "wire", x: 6.2, y: -2.3, width: 0.15, layer: "bottom" },
{ route_type: "wire", x: 5.7, y: -2.0, width: 0.15, layer: "bottom" },
{ route_type: "wire", x: 5.7, y: -1.5, width: 0.15, layer: "bottom" },
])
}
if (traceIdentity.includes("ROUTE_36") || traceIdentity.includes("source_trace_36") || (touches(5.3, -1.5) && touches(7.6, 6.150492))) {
return replaceAutoroutedRoute(trace, [
{ route_type: "wire", x: 5.3, y: -1.5, width: 0.15, layer: "bottom" },
{ route_type: "wire", x: 5.3, y: -1.9, width: 0.15, layer: "bottom" },
{ route_type: "via", x: 5.3, y: -1.9, from_layer: "bottom", to_layer: "inner1", via_diameter: 0.45, via_hole_diameter: 0.3 },
{ route_type: "wire", x: 5.3, y: -1.9, width: 0.15, layer: "inner1" },
{ route_type: "wire", x: 8.0, y: -1.9, width: 0.15, layer: "inner1" },
{ route_type: "wire", x: 8.0, y: 5.5, width: 0.15, layer: "inner1" },
{ route_type: "wire", x: 8.5, y: 5.5, width: 0.15, layer: "inner1" },
{ route_type: "via", x: 8.5, y: 5.5, from_layer: "inner1", to_layer: "top", via_diameter: 0.45, via_hole_diameter: 0.3 },
{ route_type: "wire", x: 8.5, y: 5.5, width: 0.15, layer: "top" },
{ route_type: "wire", x: 7.6, y: 6.150492, width: 0.15, layer: "top" },
])
}
if (traceIdentity.includes("ROUTE_43") || (touches(-1, -3) && touches(4.1, 0.4))) {
return replaceAutoroutedRoute(trace, [
{ route_type: "wire", x: -1, y: -3, width: 0.15, layer: "top" },
{ route_type: "wire", x: -1, y: -3.2, width: 0.15, layer: "top" },
{ route_type: "via", x: -1, y: -3.2, from_layer: "top", to_layer: "inner2", via_diameter: 0.45, via_hole_diameter: 0.3 },
{ route_type: "wire", x: -1, y: -3.2, width: 0.15, layer: "inner2" },
{ route_type: "wire", x: 3.5, y: -3.2, width: 0.15, layer: "inner2" },
{ route_type: "wire", x: 3.5, y: 0.7, width: 0.15, layer: "inner2" },
{ route_type: "wire", x: 3.7, y: 0.7, width: 0.15, layer: "inner2" },
{ route_type: "via", x: 3.7, y: 0.7, from_layer: "inner2", to_layer: "bottom", via_diameter: 0.45, via_hole_diameter: 0.3 },
{ route_type: "wire", x: 3.7, y: 0.7, width: 0.15, layer: "bottom" },
{ route_type: "wire", x: 4.1, y: 0.4, width: 0.15, layer: "bottom" },
])
}
return trace
})
}
// Keep inner1 dedicated to the ground plane and let low-current signals use
// inner2. Power, sense, and motor buses remain restricted to the outer layers.
// With allowBlindAndBuriedVias=false, core expands every routed transition to
// a plated hole spanning top, inner1, inner2, and bottom.
const outerLayerDefaultAutorouter = async (simpleRouteJson: any) => {
const guardedInput = {
...simpleRouteJson,
layerCount: 4,
allowBlindAndBuriedVias: false,
}
const solver = new AutoroutingPipelineSolver9_PreloadedTraceGraph(guardedInput, {
effort: 4,
cacheProvider: null,
})
const listeners: Record<string, Array<(event: any) => void>> = {
complete: [], error: [], progress: [],
}
return {
on(eventName: string, callback: (event: any) => void) {
listeners[eventName]?.push(callback)
},
start() {
setTimeout(() => {
const originalRandom = Math.random
const configuredSeed = (globalThis as any).process?.env?.TSCI_ROUTER_SEED
let randomState = Number(configuredSeed ?? "2209") >>> 0
Math.random = () => {
randomState = (1664525 * randomState + 1013904223) >>> 0
return randomState / 0x1_0000_0000
}
try {
solver.solve()
if (solver.failed) {
const error = new Error(solver.error ?? "Outer-layer default autorouting failed")
listeners.error.forEach((callback) => callback({ type: "error", error }))
return
}
const traces = postprocessDenseUsbRoutes(solver.getOutputSimplifiedPcbTraces())
listeners.complete.forEach((callback) => callback({ type: "complete", traces }))
} catch (cause) {
const error = cause instanceof Error ? cause : new Error(String(cause))
listeners.error.forEach((callback) => callback({ type: "error", error }))
} finally {
Math.random = originalRandom
}
}, 0)
},
stop() {
// The synchronous local solver has already completed when core calls stop.
},
getOutputSimpleRouteJson() {
const output = solver.getOutputSimpleRouteJson()
return {
...output,
traces: (output.traces ?? []).map((trace: any) => {
const pcbTraceId = String(trace.pcb_trace_id ?? "")
const isBootSupplyTree = pcbTraceId.startsWith("source_trace_14__source_trace_15__source_trace_16")
const isSenseNeck = pcbTraceId === "source_trace_55_0"
return {
...trace,
route: (trace.route ?? []).map((point: any) => {
if (
isBootSupplyTree &&
Math.abs((point.x ?? Infinity) - 8.03) < 0.01 &&
Math.abs((point.y ?? Infinity) - 5.434) < 0.01
) {
return { ...point, y: 5.3 }
}
if (point.route_type !== "wire") return point
if (isSenseNeck) return { ...point, width: Math.max(point.width ?? 0.15, 0.28) }
return point
}),
}
}),
}
},
}
}
export default function Nema8FourLayerController() {
return (
<board
width="20mm"
height="20mm"
layers={4}
thickness="1.6mm"
fabricatorPreset="jlcpcb_standard_20260912"
autorouterEffortLevel="2x"
minTraceWidth="0.15mm"
minTraceToPadEdgeClearance="0.1mm"
minTraceToHoleEdgeClearance="0.2mm"
minViaEdgeToPadEdgeClearance="0.1mm"
minViaHoleEdgeToViaHoleEdgeClearance="0.2mm"
minPlatedHoleDrillEdgeToDrillEdgeClearance="0.3mm"
minPadEdgeToPadEdgeClearance="0.1mm"
minBoardEdgeClearance="0.2mm"
minViaHoleDiameter="0.3mm"
minViaPadDiameter="0.45mm"
allowBlindAndBuriedVias={false}
isViaInPadAllowed
routeRemaining={false}
autorouterVersion="latest"
autorouter={{
algorithmFn: outerLayerDefaultAutorouter,
serverCacheEnabled: false,
allowViaInPad: false,
traceClearance: "0.1mm",
}}
pcbStyle={{ viaHoleDiameter: "0.3mm", viaPadDiameter: "0.45mm" }}
>
<schematicsheet name={sheets.power} displayName="1. USB-C PD and power" sheetSize="A4" sheetIndex={1}>
<schematicsection name={sections.pdInput} displayName="USB-C PD input and motor supply" />
<schematicsection name={sections.logicPower} displayName="3.3 V logic power" />
<schematictext schX={-13} schY={8.5} anchor="top_left" fontSize={0.22}
text="PWR / PD — CH32X035 negotiates the motor voltage on the dedicated USB-C input." />
<schematictext schX={-13} schY={8} anchor="top_left" fontSize={0.19}
text="F1 protects the input; D_BLOCK limits reverse current; D_VM_TVS clamps VM transients." />
<schematictext schX={1} schY={8.5} anchor="top_left" fontSize={0.22}
text="LOGIC POWER — diode-ORed VM or DATA USB 5 V is regulated to 3.3 V." />
</schematicsheet>
<schematicsheet name={sheets.control} displayName="2. USB programming and controller" sheetSize="A4" sheetIndex={2}>
<schematicsection name={sections.usbData} displayName="USB-C data/programming" />
<schematicsection name={sections.controller} displayName="CH32X035 controller" />
<schematictext schX={-13} schY={8.5} anchor="top_left" fontSize={0.22}
text="DATA / PROG — USB 2.0 programming/data and optional 5 V logic power." />
<schematictext schX={-13} schY={8} anchor="top_left" fontSize={0.19}
text="Hold BOOT while connecting DATA / PROG to enter the ROM USB bootloader." />
<schematictext schX={1} schY={8.5} anchor="top_left" fontSize={0.22}
text="U_MCU negotiates PD and controls STEP, DIR, ENN, UART, and STDBY." />
</schematicsheet>
<schematicsheet name={sheets.driver} displayName="3. TMC2209 motor stage" sheetSize="A4" sheetIndex={3}>
<schematicsection name={sections.driverCore} displayName="TMC2209 driver and current regulation" />
<schematictext schX={-13} schY={8.5} anchor="top_left" fontSize={0.22}
text="U_DRV — top-side TMC2209 with a plated-through thermal-via field." />
<schematictext schX={-13} schY={8} anchor="top_left" fontSize={0.19}
text="UART changes run/hold current; the VREF network provides a hardware current ceiling." />
<schematictext schX={1} schY={8.5} anchor="top_left" fontSize={0.22}
text="MOTOR — 8HS15-0604S; edge-facing JST order A-, A+, B+, B-." />
</schematicsheet>
{nets.map((name) => (
<Fragment key={name}>
<net
name={name}
nominalTraceWidth={traceWidthForNet(name)}
isGroundNet={name === "GND"}
routingPhaseIndex={name === "GND" ? null : undefined}
/>
</Fragment>
))}
<copperpour name="GND_PLANE" layer="inner1" connectsTo="net.GND"
padMargin="0.3mm" traceMargin="0.3mm" boardEdgeMargin="0.25mm" useThermalReliefs />
{routedConnections.map(([from, to], index) => index === 40 || index === 48 || index >= 63 ? null : (
<Fragment key={`route-${index}`}>
<trace name={`ROUTE_${index}`} from={from} to={to}
thickness={traceWidthForRouteIndex(index)}
pcbPath={manualRouteIndexes.has(index) ? manualPcbPathForRouteIndex(index) : undefined}
routingPhaseIndex={routePhaseForIndex(index)} />
</Fragment>
))}
<bus name="PD_INPUT_BUS" connections={routeNames(0, 2)} routingPhaseIndex={1}
pcbTraceWidth="0.5mm" pcbAllowedLayers={["top", "bottom"]} />
<bus name="VM_POWER_BUS" connections={routeNames(3, 11)} routingPhaseIndex={1}
pcbTraceWidth="0.4mm" pcbAllowedLayers={["top", "bottom"]} />
<bus name="LOGIC_POWER_POLICY" connections={routeNames(12, 25)} routingPhaseIndex={1}
pcbAllowedLayers={["top", "inner2", "bottom"]} />
<bus name="PD_CC_SIGNAL_POLICY" connections={routeNames(26, 27)} routingPhaseIndex={1}
pcbAllowedLayers={["top", "inner2", "bottom"]} />
<bus name="PD_RD_SIGNAL_POLICY" connections={["PD_CC1_RD", "PD_CC2_RD"]} routingPhaseIndex={1}
pcbAllowedLayers={["top", "inner2", "bottom"]} />
<bus name="USB_SIGNAL_POLICY" connections={routeNames(28, 39)} routingPhaseIndex={1}
pcbAllowedLayers={["top", "inner2", "bottom"]} />
<bus name="STEP_SIGNAL_POLICY" connections={["ROUTE_41"]} routingPhaseIndex={1}
pcbAllowedLayers={["top", "inner2", "bottom"]} />
{routeNames(42, 47).map((routeName, offset) => (
<Fragment key={`control-signal-policy-${routeName}`}>
<bus name={`CONTROL_SIGNAL_POLICY_${offset + 1}`} connections={[routeName]}
routingPhaseIndex={1} pcbAllowedLayers={["top", "inner2", "bottom"]} />
</Fragment>
))}
{routeNames(49, 56).map((routeName, offset) => (
<Fragment key={`driver-local-policy-${routeName}`}>
<bus name={`DRIVER_LOCAL_POLICY_${offset + 1}`} connections={[routeName]}
routingPhaseIndex={1} pcbAllowedLayers={["top", "inner2", "bottom"]} />
</Fragment>
))}
<bus name="SENSE_BUS" connections={routeNames(57, 58)} routingPhaseIndex={1}
pcbTraceWidth="0.3mm" pcbAllowedLayers={["top", "bottom"]} />
<bus name="MOTOR_BUS" connections={routeNames(59, 62)} routingPhaseIndex={1}
pcbTraceWidth="0.3mm" pcbAllowedLayers={["top", "bottom"]} preferredLayer="top" />
<autoroutingphase name="GLOBAL_DEFAULT_ROUTING" phaseIndex={1}
connections={[
...routeNames(0, 39),
...routeNames(41, 47),
...routeNames(49, 62),
"PD_CC1_RD",
"PD_CC2_RD",
].filter((routeName) => !manualRouteIndexes.has(Number(routeName.slice(6))))} />
<TYPE_C_6P_LTH5_5_108
name="J_USB_PD"
layer="top"
pcbX={0}
pcbY={-6.9}
pcbRotation={0}
schSectionName={sections.pdInput}
schSheetName={sheets.power}
schX={-11}
schY={3}
connections={{ EP1: "net.GND", EP2: "net.GND", GND1: "net.GND", GND2: "net.GND" }}
/>
<TYPE_C_31_M_12
name="J_USB_DATA"
layer="top"
pcbX={0}
pcbY={6.05}
pcbRotation={180}
schSectionName={sections.usbData}
schSheetName={sheets.control}
schX={-11}
schY={3}
noConnect={["SBU1", "SBU2"]}
connections={{ EH1: "net.GND", EH2: "net.GND", EH3: "net.GND", EH4: "net.GND", GND1: "net.GND", GND2: "net.GND" }}
/>
<BM04B_SRSS_TB_LF__SN_
name="J_MOTOR"
layer="top"
pcbX={-7.6}
pcbY={0}
pcbRotation={270}
schSectionName={sections.driverCore}
schSheetName={sheets.driver}
schX={10}
schY={0}
noConnect={["pin5"]}
pcbPinLabels={{ pin1: "A-", pin2: "A+", pin3: "B+", pin4: "B-" }}
/>
<TMC2209_LA_T
name="U_DRV"
layer="top"
pcbX={driverX}
pcbY={driverY}
pcbRotation={0}
schSectionName={sections.driverCore}
schSheetName={sheets.driver}
schX={0}
schY={1}
schWidth={3.5}
schHeight={5}
noConnect={["INDEX", "DIAG"]}
/>
{thermalViaOffsets.map(([x, y]) => (
<Fragment key={`thermal-${x}-${y}`}>
<via
name={`U_DRV_THERMAL_${x}_${y}`}
pcbX={driverX + x}
pcbY={driverY + y}
fromLayer="top"
toLayer="bottom"
holeDiameter="0.3mm"
outerDiameter="0.45mm"
connectsTo="net.GND"
tented="bottom_tented"
/>
</Fragment>
))}
<CH32X035F8U6
name="U_MCU"
layer="bottom"
pcbX={mcuX}
pcbY={mcuY}
pcbRotation={180}
schSectionName={sections.controller}
schSheetName={sheets.control}
schX={4.5}
schY={2}
schWidth={4}
schHeight={5}
noConnect={["PA5", "PA6", "PB0", "PB1", "PB3", "PB11", "PB12", "DIO", "DCK"]}
/>
<R name="R_PD_CC1" resistance="5.1k" layer="bottom" pcbX={-3.5} pcbY={-9.2} pcbRotation={0}
schSectionName={sections.pdInput} schSheetName={sheets.power} schX={-9} schY={-1}
connections={{ pin1: "net.PD_CC1", pin2: "net.GND" }} />
<R name="R_PD_CC2" resistance="5.1k" layer="bottom" pcbX={3.5} pcbY={-9.2} pcbRotation={180}
schSectionName={sections.pdInput} schSheetName={sheets.power} schX={-6} schY={-1}
connections={{ pin1: "net.PD_CC2", pin2: "net.GND" }} />
<trace name="PD_CC1_RD" from=".J_USB_PD > .CC1" to=".R_PD_CC1 > .pin1"
thickness="0.15mm" routingPhaseIndex={1} />
<trace name="PD_CC2_RD" from=".J_USB_PD > .CC2" to=".R_PD_CC2 > .pin1"
thickness="0.15mm" routingPhaseIndex={1} />
<R name="R_DATA_CC1" resistance="5.1k" layer="bottom" pcbX={-1.2} pcbY={8.6} pcbRotation={0}
schSectionName={sections.usbData} schSheetName={sheets.control} schX={-10} schY={-2}
connections={{ pin1: "net.DATA_CC1", pin2: "net.GND" }} />
<R name="R_DATA_CC2" resistance="5.1k" layer="bottom" pcbX={0.8} pcbY={8.6} pcbRotation={0}
schSectionName={sections.usbData} schSheetName={sheets.control} schX={-7} schY={-2}
connections={{ pin1: "net.DATA_CC2", pin2: "net.GND" }} />
<USBLC6_2SC6 name="U_USB_ESD" layer="bottom" pcbX={1.2} pcbY={5.0} pcbRotation={90}
schSectionName={sections.usbData} schSheetName={sheets.control} schX={-5} schY={3} />
<BAT54KFILM name="D_LOGIC_PD" layer="bottom" pcbX={4.8} pcbY={-5.6} pcbRotation={0}
schSectionName={sections.logicPower} schSheetName={sheets.power} schX={-4} schY={-3} />
<BAT54WS name="D_LOGIC_USB" layer="bottom" pcbX={-7.5} pcbY={4.5} pcbRotation={90}
schSectionName={sections.logicPower} schSheetName={sheets.power} schX={0} schY={-3} />
<HT7533_1 name="U_3V3" layer="bottom" pcbX={7.4} pcbY={7.6} pcbRotation={0}
schSectionName={sections.logicPower} schSheetName={sheets.power} schX={5} schY={-3}
schWidth={3} schHeight={3}
schPinArrangement={{
leftSide: { pins: [3], direction: "top-to-bottom" },
rightSide: { pins: [2], direction: "top-to-bottom" },
bottomSide: { pins: [1], direction: "left-to-right" },
}}
/>
<C name="C_LDO_IN" value="2.2uF" layer="bottom" pcbX={9.0} pcbY={3.8} pcbRotation={90}
schSectionName={sections.logicPower} schSheetName={sheets.power} schX={3} schY={-5.5}
connections={{ pin1: "net.LOGIC_IN", pin2: "net.GND" }} />
<C name="C_LDO_OUT" value="2.2uF_10V" layer="bottom" pcbX={3.4} pcbY={7.1} pcbRotation={0}
schSectionName={sections.logicPower} schSheetName={sheets.power} schX={8} schY={-5.5}
connections={{ pin1: "net.VIO", pin2: "net.GND" }} />
<C name="C_MCU_HF" value="100nF" layer="bottom" pcbX={4.0} pcbY={-3.6} pcbRotation={180}
schSectionName={sections.controller} schSheetName={sheets.control} schX={10} schY={4.5}
connections={{ pin1: "net.VIO", pin2: "net.GND" }} />
<C name="C_MCU_BULK" value="2.2uF_10V" layer="bottom" pcbX={6.6} pcbY={-3.2} pcbRotation={180}
schSectionName={sections.controller} schSheetName={sheets.control} schX={10} schY={2.5}
connections={{ pin1: "net.VIO", pin2: "net.GND" }} />
<R name="R_VBUS_TOP" resistance="270k" layer="top" pcbX={8.4} pcbY={-2.0} pcbRotation={0}
schSectionName={sections.controller} schSheetName={sheets.control} schX={9} schY={-1}
connections={{ pin1: "net.VM", pin2: "net.VBUS_ADC" }} />
<R name="R_VBUS_BOTTOM" resistance="47k" layer="top" pcbX={8.4} pcbY={-4.2} pcbRotation={0}
schSectionName={sections.controller} schSheetName={sheets.control} schX={12} schY={-1}
connections={{ pin1: "net.VBUS_ADC", pin2: "net.GND" }} />
<R name="R_BOOT" resistance="4.7k" layer="bottom" pcbX={6.5} pcbY={5.2} pcbRotation={0}
schSectionName={sections.controller} schSheetName={sheets.control} schX={7} schY={-4}
connections={{ pin1: "net.VIO", pin2: "net.BOOT_BIAS" }} />
<TL3780AF100QG name="SW_BOOT" layer="top" pcbX={7.6} pcbY={4.5} pcbRotation={90}
schSectionName={sections.controller} schSheetName={sheets.control} schX={11} schY={-4} />
<R name="R_EN_PU" resistance="100k" layer="top" pcbX={-4.2} pcbY={-3.0} pcbRotation={0}
schSectionName={sections.controller} schSheetName={sheets.control} schX={-1} schY={-4}
connections={{ pin1: "net.VIO", pin2: "net.ENN" }} />
<R name="R_STDBY_PD" resistance="100k" layer="top" pcbX={-4.2} pcbY={2.2} pcbRotation={0}
schSectionName={sections.controller} schSheetName={sheets.control} schX={1.5} schY={-4}
connections={{ pin1: "net.STDBY", pin2: "net.GND" }} />
<BSMD1206_110_16V name="F1" layer="top" pcbX={-7.5} pcbY={-8.0} pcbRotation={180}
schSectionName={sections.pdInput} schSheetName={sheets.power} schX={-6} schY={4} />
<PMEG6020ELRX name="D_BLOCK" layer="bottom" pcbX={0} pcbY={-8.6} pcbRotation={0}
schSectionName={sections.pdInput} schSheetName={sheets.power} schX={-2} schY={4} />
<diode name="D_VM_TVS" layer="bottom" pcbX={-0.7} pcbY={-5.5} pcbRotation={0}
manufacturerPartNumber="SMAJ16A" supplierPartNumbers={{ jlcpcb: ["C20419815"] }}
footprint="smdpads2_p3.6mm_pw2mm_ph2.1mm"
schSectionName={sections.pdInput} schSheetName={sheets.power} schX={1} schY={1} schRotation={90} />
{[{ x: -8.2, y: -6.8 }, { x: -8.2, y: -3.2 }].map(({ x, y }, index) => (
<C key={`bulk-${index}`} name={`C_VM_BULK${index + 1}`} value="10uF_25V"
layer="bottom" pcbX={x} pcbY={y} pcbRotation={90}
schSectionName={sections.pdInput} schSheetName={sheets.power}
schX={4 + index * 3} schY={1}
connections={{ pin1: "net.VM", pin2: "net.GND" }} />
))}
<C name="C_VM_HF1" value="100nF_50V" layer="top" pcbX={-3.8} pcbY={-4.5} pcbRotation={180}
schSectionName={sections.driverCore} schSheetName={sheets.driver} schX={-10} schY={4}
connections={{ pin1: "net.VM", pin2: "net.GND" }} />
<C name="C_CP" value="22nF_50V" layer="top" pcbX={0.25} pcbY={-4.3} pcbRotation={0}
schSectionName={sections.driverCore} schSheetName={sheets.driver} schX={-6} schY={4}
connections={{ pin1: "net.CP_HI", pin2: "net.CP_LO" }} />
<capacitor name="C_VCP" capacitance="100nF" footprint="0402" maxVoltageRating="50V"
supplierPartNumbers={{ jlcpcb: ["C525226"] }} layer="top" pcbX={2.7} pcbY={-4.3} pcbRotation={0}
schSectionName={sections.driverCore} schSheetName={sheets.driver} schX={-6} schY={2} />
<C name="C_5V" value="2.2uF_10V" layer="top" pcbX={5.2} pcbY={-3.4} pcbRotation={0}
schSectionName={sections.driverCore} schSheetName={sheets.driver} schX={-6} schY={0}
connections={{ pin1: "net.V5_DRV", pin2: "net.GND" }} />
<C name="C_VIO" value="100nF" layer="top" pcbX={5.4} pcbY={1.8} pcbRotation={0}
schSectionName={sections.driverCore} schSheetName={sheets.driver} schX={-6} schY={-2}
connections={{ pin1: "net.VIO", pin2: "net.GND" }} />
<PT1206FR_7W0R33L name="R_SENSE_A" layer="bottom" pcbX={-4.5} pcbY={0.5} pcbRotation={0}
schSectionName={sections.driverCore} schSheetName={sheets.driver} schX={4} schY={-3} />
<PT1206FR_7W0R33L name="R_SENSE_B" layer="bottom" pcbX={-4.8} pcbY={-1.8} pcbRotation={0}
schSectionName={sections.driverCore} schSheetName={sheets.driver} schX={7} schY={-3} />
<R name="R_VREF_TOP" resistance="12k" layer="bottom" pcbX={1.8} pcbY={-2.3} pcbRotation={90}
schSectionName={sections.driverCore} schSheetName={sheets.driver} schX={4} schY={4}
connections={{ pin1: "net.V5_DRV", pin2: "net.VREF" }} />
<R name="R_VREF_BOTTOM" resistance="10k" layer="bottom" pcbX={1.8} pcbY={0} pcbRotation={90}
schSectionName={sections.driverCore} schSheetName={sheets.driver} schX={7} schY={4}
connections={{ pin1: "net.VREF", pin2: "net.GND" }} />
<C name="C_VREF" value="100nF" layer="bottom" pcbX={0.2} pcbY={-2.7} pcbRotation={0}
schSectionName={sections.driverCore} schSheetName={sheets.driver} schX={10} schY={4}
connections={{ pin1: "net.VREF", pin2: "net.GND" }} />
{connectorShellGrounds.map(({ from, x, y }, index) => (
<Fragment key={`connector-shell-ground-${index}`}>
<trace name={`CONNECTOR_SHELL_GND_${index + 1}`} from={from} to="net.GND" thickness="0.2mm"
pcbPath={[{ x, y }, { x, y, via: true, fromLayer: "top", toLayer: "bottom" }]}
routingPhaseIndex={null} />
</Fragment>
))}
{groundFanouts.map(({ from, x, y, fromLayer }, index) => (
<Fragment key={`ground-fanout-${index}`}>
<trace name={`GROUND_FANOUT_${index + 1}`} from={from} to="net.GND"
thickness={from.includes("R_DATA_CC") ? "0.15mm" : "0.2mm"}
pcbPath={[
{ x, y },
{
x, y, via: true,
fromLayer,
toLayer: fromLayer === "top" ? "bottom" : "top",
},
]}
routingPhaseIndex={null} />
</Fragment>
))}
<trace name="USB_ESD_GND_FANOUT" from=".U_USB_ESD > .GND" to="net.GND" thickness="0.2mm"
pcbPathRelativeTo=".U_USB_ESD > .GND"
pcbPath={[{ x: 0, y: 0, via: true, fromLayer: "bottom", toLayer: "top" }]}
routingPhaseIndex={null} />
<trace name="C_VREF_GND_FANOUT" from=".C_VREF > .pin2" to="net.GND" thickness="0.2mm"
pcbPath={[
{ x: -0.51, y: -1.2 },
{ x: 0.9, y: -2.8 },
{ x: 0.9, y: -2.8, via: true, fromLayer: "bottom", toLayer: "top" },
]}
routingPhaseIndex={null} />
<trace name="C_5V_GND_FANOUT" from=".C_5V > .pin2" to="net.GND" thickness="0.2mm"
pcbPath={[
{ x: 1.1, y: -0.9 },
{ x: 1.1, y: -0.9, via: true, fromLayer: "top", toLayer: "bottom" },
]}
routingPhaseIndex={null} />
{usbGroundVias.map(({ from, x, y }, index) => (
<Fragment key={`usb-ground-via-${index}`}>
<trace name={`USB_GND_VIA_${index + 1}`} from={from} to="net.GND" thickness="0.15mm"
pcbPath={[
{ x, y },
{ x, y, via: true, fromLayer: "top", toLayer: "bottom" },
]}
routingPhaseIndex={null} />
</Fragment>
))}
{driverEpGroundPaths.map(({ from, path }, index) => (
<Fragment key={`driver-ground-via-${index}`}>
<trace name={`U_DRV_GND_VIA_${index + 1}`} from={from} to="net.GND" thickness="0.15mm"
pcbPath={[...path]}
routingPhaseIndex={null} />
</Fragment>
))}
<trace name="U_DRV_EP_GND" from=".U_DRV > .EP" to="net.GND" thickness="0.15mm"
pcbPathRelativeTo=".U_DRV > .EP"
pcbPath={[{ x: 0, y: 0, via: true, fromLayer: "top", toLayer: "bottom" }]}
routingPhaseIndex={null} />
<trace name="U_MCU_EP_GND" from=".U_MCU > .GND" to="net.GND" thickness="0.15mm"
pcbPathRelativeTo=".U_MCU > .GND"
pcbPath={[{ x: 0, y: 0, via: true, fromLayer: "bottom", toLayer: "top" }]}
routingPhaseIndex={null} />
<hole name="MOTOR_M2_UPPER_LEFT" pcbX={-7.7} pcbY={7.7} diameter="2.2mm" />
<hole name="MOTOR_M2_LOWER_RIGHT" pcbX={7.7} pcbY={-7.7} diameter="2.2mm" />
<silkscreentext text="PWR" layer="top" pcbX={-5.7} pcbY={-7.0} pcbRotation={90} fontSize="0.7mm" />
<silkscreentext text="PD IN" layer="top" pcbX={-6.5} pcbY={-7.0} pcbRotation={90} fontSize="0.5mm" />
<silkscreentext text="DATA" layer="top" pcbX={5.7} pcbY={6.2} pcbRotation={270} fontSize="0.7mm" />
<silkscreentext text="PROG" layer="top" pcbX={6.5} pcbY={6.2} pcbRotation={270} fontSize="0.5mm" />
<silkscreentext text="MOTOR" layer="top" pcbX={-8.8} pcbY={4.0} pcbRotation={270} fontSize="0.5mm" />
<silkscreentext text="A- A+ B+ B-" layer="top" pcbX={-5.8} pcbY={0} pcbRotation={270} fontSize="0.4mm" />
<silkscreentext text="BOOT" layer="top" pcbX={7.6} pcbY={6.4} fontSize="0.45mm" />
<silkscreentext text="NEMA8 20x20 4L" layer="bottom" pcbX={0} pcbY={-8.8} pcbRotation={180} fontSize="0.5mm" />
<silkscreentext text="0.60A MAX" layer="bottom" pcbX={0} pcbY={8.8} fontSize="0.5mm" />
</board>
)
}