shibosoftwaredev/f1c100s-linux-nema17-stepper-controller
A four-layer NEMA 17 motor-controller PCB combining an Allwinner F1C100S Linux computer, USB‑PD/buck power system, W5500 Ethernet, STM32 real-time motion control, DRV8825 stepper driver, protection/temperature/current telemetry, USB interfaces, and local LED/buzzer diagnostics.
- Version
- 0.4.56
- License
- unset
- Stars
- 0
scripts/author-fab-buck-routes.py
"""Local feed-forward branch relocation, candidate only.
The 5 V pickup lands on the existing inductor output pad. The independent
emitted-copper graph must still prove the original R_TOP-to-R_FF source net.
The existing capacitor-to-FB return and all other routing are retained.
"""
from route_geometry import *
import copy, hashlib
c = load_circuit("routing/fab-buck-pocket.circuit.json")
traces = {e["name"]: e for e in c if e["type"] == "source_trace" and e.get("name")}
root = connectivity(c)
mapping = json.loads(Path("routing/net-map.json").read_text())["roots"]
by_id = {t["source_trace_id"]: t for t in traces.values()}
parts = {e["name"]: e for e in c if e["type"] == "source_component"}
sp = {(e["source_component_id"], e["pin_number"]): e for e in c if e["type"] == "source_port" and "pin_number" in e}
ports = {e["source_port_id"]: e for e in c if e["type"] == "pcb_port"}
def terminal(name, pin):
return ports[sp[parts[name]["source_component_id"], pin]["source_port_id"]]
plans = {
"BUCK_FB_TOP": (("L_BUCK", 2), ("R_BUCK_FF", 1), [(-16.1, -6.05), (-16.1, -4.95), (-12.9, -4.95)]),
"BUCK_FF_CAP": (("R_BUCK_FF", 2), ("C_BUCK_FF", 1), [(-12.3, -6.41), (-12.01, -6.7)]),
}
affected = {traces[name]["source_trace_id"] for name in plans}
original = json.loads(Path("routing/complete-routes.json").read_text())
routes = [t for t in original if t["source_trace_id"] not in affected]
for name, (a, b, points) in plans.items():
start, end = terminal(*a), terminal(*b)
path = [(start["x"], start["y"])] + points + [(end["x"], end["y"])]
route = [dict(route_type="wire", layer="bottom", width=.15, x=x, y=y) for x, y in path]
route[0]["start_pcb_port_id"] = start["pcb_port_id"]
route[-1]["end_pcb_port_id"] = end["pcb_port_id"]
source = traces[name]
routes.append(dict(type="pcb_trace", pcb_trace_id="fab_"+name.lower(),
source_trace_id=source["source_trace_id"], subcircuit_id=source["subcircuit_id"],
subcircuit_connectivity_map_key=source.get("subcircuit_connectivity_map_key"), route=route))
result = [e for e in c if e["type"] not in ["pcb_trace", "pcb_via", "pcb_copper_pour"]]
result += copy.deepcopy(routes)
result += [copy.deepcopy(e) for e in c if e["type"] == "pcb_trace" and e.get("pcb_component_id")]
physical = dict(wires=[], vias=[])
vias = {}
for t in routes:
s = by_id.get(t["source_trace_id"])
if not s: s = next(e for e in c if e["type"] == "source_trace" and e["source_trace_id"] == t["source_trace_id"])
net = mapping[root((s["connected_source_port_ids"] + s["connected_source_net_ids"])[0])]
for a, b in zip(t["route"], t["route"][1:]):
if a["route_type"] == b["route_type"] == "wire" and a["layer"] == b["layer"]:
if math.dist((a["x"], a["y"]), (b["x"], b["y"])) > 1e-8:
physical["wires"].append(dict(net=net, layer=a["layer"], width=max(a["width"], b["width"]), points=[[a["x"], a["y"]], [b["x"], b["y"]]]))
for p in t["route"]:
if p["route_type"] != "via": continue
key = round(p["x"], 6), round(p["y"], 6)
if key in vias:
assert vias[key]["net"] == net
continue
vias[key] = dict(net=net, x=p["x"], y=p["y"])
result.append(dict(type="pcb_via", pcb_via_id="fab_candidate_"+str(len(vias)),
pcb_trace_id=t["pcb_trace_id"], source_trace_id=t["source_trace_id"], subcircuit_id=t["subcircuit_id"],
x=p["x"], y=p["y"], hole_diameter=.3, outer_diameter=.45, layers=LAYERS,
tented_on_top=True, tented_on_bottom=True))
native = json.loads(Path("routing/native-features.json").read_text())
for v in native["thermal_vias"] + native.get("auxiliary_vias", []):
key = round(v["x"], 6), round(v["y"], 6)
if key in vias: continue
vias[key] = dict(net=v["net"], x=v["x"], y=v["y"])
existing = next(e for e in c if e["type"] == "pcb_via" and math.dist((e["x"], e["y"]), key) < 1e-6)
result.append(copy.deepcopy(existing))
physical["vias"] = list(vias.values())
write = lambda p, value: Path(p).write_text(json.dumps(value, indent=2)+"\n")
write("routing/fab-buck-routes.json", routes)
write("routing/fab-buck-physical.json", physical)
write("reports/fab-buck-candidate.circuit.json", result)
def length(ts, sid):
return sum(math.dist((a["x"], a["y"]), (b["x"], b["y"])) for t in ts if t["source_trace_id"] == sid
for a, b in zip(t["route"], t["route"][1:]) if a["route_type"] == b["route_type"] == "wire" and a["layer"] == b["layer"])
report = dict(candidateOnly=True, installed=False, changes=[dict(name=name, beforeMm=length(original, traces[name]["source_trace_id"]),
afterMm=length(routes, traces[name]["source_trace_id"])) for name in plans])
write("reports/fab-buck-route-delta.json", report)
print(json.dumps(report))