astra/nema17-stepper-motor-controller-spring-clamp-rp2040

A 4-layer RP2040 controller board that negotiates USB‑C PD motor power and drives a NEMA17 stepper via a DRV8825, with current sensing, thermal shutdown, RGB status LED, buzzer alarm, optional AS5600 encoder, protection/filtering, and test points.

Version
1.4.0
License
unset
Stars
0

firmware/test_stepper_driver.py

import unittest
from stepper_driver import StepperDriver


class FakePin:
    OUT = 1
    events = []

    def __init__(self, number, mode, value):
        self.number, self.level = number, value
        self.events.append((number, value))

    def value(self, value=None):
        if value is not None:
            self.level = value
            self.events.append((self.number, value))
        return self.level


class StepperTests(unittest.TestCase):
    def setUp(self):
        FakePin.events = []
        self.delays = []
        self.driver = StepperDriver(FakePin, self.delays.append)

    def test_boot_and_stop_disable_active_low_outputs(self):
        self.assertEqual(FakePin.events[0], (21, 1))
        self.assertEqual(self.driver.reset_n.value(), 0)
        self.assertFalse(self.driver.pulse(True, lambda: True))
        self.driver.enable()
        self.driver.disable()
        self.assertEqual(self.driver.enable_n.value(), 1)
        self.assertEqual(self.driver.reset_n.value(), 0)
        self.assertEqual(self.driver.step_pin.value(), 0)

    def test_step_polarity_and_minimum_timing(self):
        self.driver.enable()
        FakePin.events = []
        self.assertTrue(self.driver.pulse(False, lambda: True))
        self.assertEqual(FakePin.events, [(19, 0), (18, 1), (18, 0)])
        self.assertGreaterEqual(self.delays[0], 1)
        self.assertTrue(all(t >= 2 for t in self.delays[1:]))

    def test_trip_during_direction_setup_prevents_pulse(self):
        self.driver.enable()
        calls = iter([True, False])
        FakePin.events = []
        self.assertFalse(self.driver.pulse(True, lambda: next(calls)))
        self.assertNotIn((18, 1), FakePin.events)
        self.assertEqual(self.driver.enable_n.value(), 1)
        self.assertEqual(self.driver.reset_n.value(), 0)

    def test_disallowed_motion_never_raises_step(self):
        self.driver.enable()
        FakePin.events = []
        self.assertFalse(self.driver.pulse(True, lambda: False))
        self.assertNotIn((18, 1), FakePin.events)