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
firmware/test_tmc2209.py
import importlib.util
from pathlib import Path
import sys, types, unittest
from unittest.mock import patch
class ProtocolTests(unittest.TestCase):
def setUp(self):
self.now=0
def ticks():
self.now+=1
return self.now
with patch.dict(sys.modules, {'machine':types.SimpleNamespace(UART=None,Pin=None),
'time':types.SimpleNamespace(sleep_ms=lambda _:None,ticks_ms=ticks,ticks_diff=lambda a,b:a-b)}):
spec=importlib.util.spec_from_file_location('tmc_protocol',Path(__file__).with_name('tmc2209.py'))
self.mod=importlib.util.module_from_spec(spec);spec.loader.exec_module(self.mod)
mod=self.mod
class Bus:
def __init__(self):self.rx=b'';self.regs={6:0x21000000,2:0,1:0,0x6F:0};self.reject=False;self.corrupt=False
def read(self):r,self.rx=self.rx,b'';return r
def write(self,b):
assert mod.crc8(b[:-1])==b[-1]
if len(b)==8:
if not self.reject:
register=b[2]&127;value=int.from_bytes(b[3:7],'big')
self.regs[register]=self.regs.get(register,0)&~value if register==1 else value
self.regs[2]=(self.regs[2]+1)&255
self.rx=b
else:
r=bytes((5,255,b[2]))+self.regs.get(b[2],0).to_bytes(4,'big')
self.rx=b+r+bytes((mod.crc8(r)^(1 if self.corrupt else 0),))
self.bus=Bus();self.driver=mod.TMC2209(self.bus)
def test_known_crc_vector(self):self.assertEqual(self.mod.crc8(bytes((5,0,0))),0x48)
def test_echo_and_configuration(self):
self.driver.configure();self.assertEqual(self.bus.regs[0],0x1C0)
self.assertEqual((self.bus.regs[0x10]>>8)&31,14)
self.assertEqual((self.bus.regs[0x6C]>>24)&15,4)
self.assertEqual(self.bus.regs[0x22],0)
def test_rejected_write_and_wrap(self):
self.bus.regs[2]=255;self.driver.write(0,123);self.assertEqual(self.bus.regs[2],0)
self.bus.reject=True
with self.assertRaises(OSError):self.driver.write(0,0)
def test_bad_crc(self):
self.bus.corrupt=True
with self.assertRaises(OSError):self.driver.read(6)
def test_reset_thermal_short_and_wrong_part(self):
self.driver.configure();self.bus.regs[1]=1
with self.assertRaises(OSError):self.driver.check()
self.driver.configure();self.bus.regs[0x6F]=1
with self.assertRaises(OSError):self.driver.check()
self.bus.regs[6]=0
with self.assertRaises(OSError):self.driver.configure()
if __name__=='__main__':unittest.main()