seveibar/nema17-stepper-motor-driver-tmc2209-usb-pd

RP2040-based USB-C PD stepper-motor controller with TMC2209 drive, AS5600 position feedback, TMP102 thermal shutdown, current/voltage telemetry, RGB status LED, and protected buzzer alarm circuitry.

Version
0.2.0
License
unset
Stars
0

scripts/verify-connectivity.py

"""Validate physical TMC2209 pins against rendered source connectivity."""
import json, sys
from collections import Counter
j=json.load(open(sys.argv[1] if len(sys.argv)>1 else 'dist/index/circuit.json'))
components={e['name']:e['source_component_id'] for e in j if e['type']=='source_component'}
ports=[e for e in j if e['type']=='source_port']
parent={}
def root(x):
 parent.setdefault(x,x)
 if parent[x]!=x: parent[x]=root(parent[x])
 return parent[x]
for t in j:
 if t['type']=='source_trace':
  nodes=t.get('connected_source_port_ids',[])+t.get('connected_source_net_ids',[])
  for n in nodes[1:]:parent[root(n)]=root(nodes[0])
def pin(c,n):
 return next(e['source_port_id'] for e in ports if e['source_component_id']==components[c] and e.get('pin_number')==n)
def same(*ps):
 assert len({root(pin(*p)) for p in ps})==1, ('Disconnected',ps)
def separate(*ps):
 assert len({root(pin(*p)) for p in ps})==len(ps),('Short',ps)
# Datasheet physical pin numbers, not imported aliases.
same(('DRIVER',3),('DRIVER',18),('DRIVER',29),('DRIVER',25),('U1',57),('R_ISEN_A',2),('R_ISEN_B',2))
same(('DRIVER',22),('DRIVER',28),('R_BUS_SHUNT',2),('C_VCP',2),('C_VM_HF',1),('C_VM_HF2',1))
same(('DRIVER',15),('L_AVDD',1),('R_ENN_PU',2),('C_VCCIO',1))
separate(('DRIVER',29),('DRIVER',28),('DRIVER',15),('DRIVER',8),('DRIVER',6),('DRIVER',4),('DRIVER',5))
for dp,jp in [(24,1),(21,2),(26,3),(1,4)]:same(('DRIVER',dp),('J1',jp))
separate(('J1',1),('J1',2),('J1',3),('J1',4),('DRIVER',29),('DRIVER',28))
same(('DRIVER',23),('R_ISEN_A',1));same(('DRIVER',27),('R_ISEN_B',1))
separate(('DRIVER',23),('DRIVER',27),('DRIVER',29))
for dp,c,cp in [(4,'C_CP',1),(5,'C_CP',2),(6,'C_VCP',1),(8,'C_5VOUT',1),(16,'U1',29),(19,'U1',30),(11,'U1',35),(14,'R_UART_TX',2),(14,'U1',32),(2,'U_THERM_GATE',4),(2,'R_ENN_PU',1)]:same(('DRIVER',dp),(c,cp))
same(('R_UART_TX',1),('U1',31));separate(('R_UART_TX',1),('R_UART_TX',2))
for n in [7,9,10,13,20]:same(('DRIVER',n),('DRIVER',29))
separate(('DRIVER',2),('U_THERM_GATE',1),('U_THERM_GATE',2),('DRIVER',29),('DRIVER',15))
same(('U_TEMP',3),('U_THERM_GATE',2),('U1',36))
same(('Q_PD_ENABLE',3),('U_THERM_GATE',1))
# Required unused inputs must remain open, never accidentally grounded.
separate(('DRIVER',17),('DRIVER',12),('DRIVER',29),('DRIVER',15))
assert next(e for e in j if e['type']=='source_component' and e['name']=='U_THERM_GATE')['manufacturer_part_number']=='SN74LVC1G00DBVR'
# New telemetry/LED physical pins cross-checked against manufacturer drawings.
same(('U1',40),('R_VSENSE_TOP',2),('R_VSENSE_BOTTOM',1),('C_VSENSE',1))
same(('U1',41),('R_CURRENT_FILTER',2),('C_CURRENT_FILTER',1))
same(('U_BUS_CURRENT',1),('R_CURRENT_FILTER',1))
same(('U_BUS_CURRENT',3),('R_BUS_SHUNT',1),('F_MOTOR_VBUS',2))
same(('U_BUS_CURRENT',4),('R_BUS_SHUNT',2),('DRIVER',28))
same(('U_BUS_CURRENT',5),('L_AVDD',1),('C_BUS_CURRENT',1))
same(('U_BUS_CURRENT',2),('DRIVER',29),('R_VSENSE_BOTTOM',2))
separate(('R_BUS_SHUNT',1),('R_BUS_SHUNT',2),('U1',40),('U1',41),('DRIVER',29))
same(('Q_VSENSE',3),('R_VSENSE_TOP',1))
same(('Q_VSENSE',2),('J_MOTOR_USB',6),('R_VSENSE_PU',1))
same(('Q_VSENSE',1),('R_VSENSE_PU',2),('R_VSENSE_GATE',1))
same(('Q_VSENSE_ENABLE',3),('R_VSENSE_GATE',2))
same(('Q_VSENSE_ENABLE',1),('L_AVDD',1))
same(('Q_VSENSE_ENABLE',2),('DRIVER',29))
same(('U1',37),('U_RGB_BUFFER',2),('R_RGB_PD',1))
same(('U_RGB_BUFFER',4),('R_RGB_DATA',1))
same(('R_RGB_DATA',2),('D_RGB',3))
same(('D_RGB',4),('U_RGB_BUFFER',5),('J_USB',18),('C_RGB',1))
same(('D_RGB',2),('U_RGB_BUFFER',3),('U_RGB_BUFFER',1),('DRIVER',29))
separate(('D_RGB',1),('D_RGB',2),('D_RGB',3),('D_RGB',4),('J_MOTOR_USB',6))
same(('U_PD',9),('R_PD_CFG1',1));same(('R_PD_CFG1',2),('DRIVER',29))
separate(('U_PD',2),('U_PD',3),('U_PD',9),('DRIVER',29))
assert 'D1' not in components and 'R_LED' not in components
values={e['name']:e for e in j if e['type']=='source_component'}
assert values['R_PD_CFG1']['resistance']==24000
assert values['R_BUS_SHUNT']['resistance']==0.05
assert values['R_VSENSE_TOP']['resistance']/values['R_VSENSE_BOTTOM']['resistance']==10
assert values['D_MOTOR_TVS']['manufacturer_part_number']=='SMBJ13CA'
print('PASS: 12 V selection, ADC2 voltage, ADC3 amplified current, switched divider and 5 V RGB level shifter')
source_errors=[e for e in j if e['type'].startswith('source_') and e['type'].endswith('_error')]
assert not source_errors,source_errors
print('PASS: physical pinout, 4 isolated motor outputs, UART, sense resistors, supplies, charge pump, NAND interlock and unused pins')
errors=Counter(e['type'] for e in j if e['type'].endswith('_error'))
print('PCB/build errors (not suppressed):',dict(errors))