/ tests / test_stepper.py
test_stepper.py
  1  import os
  2  import sys
  3  from unittest.mock import MagicMock
  4  
  5  # Fix up the path to include our neighboring module.
  6  sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
  7  micropython = MagicMock()  # pylint: disable-msg=invalid-name
  8  micropython.const = lambda x: x
  9  sys.modules["micropython"] = micropython
 10  
 11  from adafruit_motor import stepper  # pylint: disable-msg=wrong-import-position
 12  
 13  
 14  class Coil:
 15      def __init__(self):
 16          self._duty_cycle = 0
 17  
 18      @property
 19      def frequency(self):
 20          return 1500
 21  
 22      @property
 23      def duty_cycle(self):
 24          return self._duty_cycle
 25  
 26      @duty_cycle.setter
 27      def duty_cycle(self, value):
 28          assert 0 <= value <= 0xFFFF
 29          self._duty_cycle = value
 30  
 31  
 32  def test_single_coil():
 33      coil = (Coil(), Coil(), Coil(), Coil())
 34      # We undo the coil order so our tests make more sense.
 35      motor = stepper.StepperMotor(coil[2], coil[0], coil[1], coil[3])
 36      # Always start with a single step.
 37      for j in range(4):
 38          assert coil[j].duty_cycle == (0xFFFF if j == 0 else 0)
 39      for i in range(1, 7):  # Test 6 steps so we wrap around
 40          motor.onestep()
 41          for j in range(4):
 42              assert coil[j].duty_cycle == (0xFFFF if i % 4 == j else 0)
 43  
 44  
 45  def test_double_coil():
 46      coil = (Coil(), Coil(), Coil(), Coil())
 47      # We undo the coil order so our tests make more sense.
 48      motor = stepper.StepperMotor(coil[2], coil[0], coil[1], coil[3])
 49      # Despite double stepping we always start with a single step.
 50      for j in range(4):
 51          assert coil[j].duty_cycle == (0xFFFF if j == 0 else 0)
 52      for i in range(6):  # Test 6 steps so we wrap around
 53          motor.onestep(style=stepper.DOUBLE)
 54          for j in range(4):
 55              assert coil[j].duty_cycle == (
 56                  0xFFFF if i % 4 == j or (i + 1) % 4 == j else 0
 57              )
 58  
 59  
 60  def test_interleave_steps():
 61      coil = (Coil(), Coil(), Coil(), Coil())
 62      # We undo the coil order so our tests make more sense.
 63      motor = stepper.StepperMotor(coil[2], coil[0], coil[1], coil[3])
 64      # We always start with a single step.
 65      for j in range(4):
 66          assert coil[j].duty_cycle == (0xFFFF if j == 0 else 0)
 67      for i in range(15):  # Test 15 half steps so we wrap around
 68          motor.onestep(style=stepper.INTERLEAVE)
 69          for j in range(4):
 70              expected_value = 0
 71              # Even half steps should be DOUBLE coil active steps.
 72              if i % 2 == 0:
 73                  if j == i // 2 % 4 or j == (i // 2 + 1) % 4:
 74                      expected_value = 0xFFFF
 75              # Odd half steps should be SINGLE coil active steps
 76              elif j == (i // 2 + 1) % 4:
 77                  expected_value = 0xFFFF
 78              assert coil[j].duty_cycle == expected_value
 79  
 80      motor.onestep(direction=stepper.BACKWARD, style=stepper.INTERLEAVE)
 81      assert coil[0].duty_cycle == 0
 82      assert coil[1].duty_cycle == 0
 83      assert coil[2].duty_cycle == 0
 84      assert coil[3].duty_cycle == 0xFFFF
 85  
 86  
 87  def test_microstep_steps():
 88      coil = (Coil(), Coil(), Coil(), Coil())
 89      # We undo the coil order so our tests make more sense.
 90      motor = stepper.StepperMotor(coil[2], coil[0], coil[1], coil[3], microsteps=2)
 91      # We always start with a single step.
 92      for j in range(4):
 93          assert coil[j].duty_cycle == (0xFFFF if j == 0 else 0)
 94      motor.onestep(style=stepper.MICROSTEP)
 95      assert coil[0].duty_cycle == 0xB504
 96      assert coil[1].duty_cycle == 0xB504
 97      assert coil[2].duty_cycle == 0
 98      assert coil[3].duty_cycle == 0
 99  
100      motor.onestep(style=stepper.MICROSTEP)
101      assert coil[0].duty_cycle == 0x0
102      assert coil[1].duty_cycle == 0xFFFF
103      assert coil[2].duty_cycle == 0
104      assert coil[3].duty_cycle == 0
105  
106      motor.onestep(style=stepper.MICROSTEP)
107      assert coil[0].duty_cycle == 0
108      assert coil[1].duty_cycle == 0xB504
109      assert coil[2].duty_cycle == 0xB504
110      assert coil[3].duty_cycle == 0
111  
112      motor.onestep(direction=stepper.BACKWARD, style=stepper.MICROSTEP)
113      assert coil[0].duty_cycle == 0x0
114      assert coil[1].duty_cycle == 0xFFFF
115      assert coil[2].duty_cycle == 0
116      assert coil[3].duty_cycle == 0
117  
118  
119  def test_double_to_single():
120      coil = (Coil(), Coil(), Coil(), Coil())
121      # We undo the coil order so our tests make more sense.
122      motor = stepper.StepperMotor(coil[2], coil[0], coil[1], coil[3])
123      # We always start with a single step.
124      for j in range(4):
125          assert coil[j].duty_cycle == (0xFFFF if j == 0 else 0)
126  
127      motor.onestep(direction=stepper.BACKWARD, style=stepper.DOUBLE)
128      assert coil[0].duty_cycle == 0xFFFF
129      assert coil[1].duty_cycle == 0
130      assert coil[2].duty_cycle == 0
131      assert coil[3].duty_cycle == 0xFFFF
132  
133      motor.onestep(direction=stepper.BACKWARD, style=stepper.SINGLE)
134      assert coil[0].duty_cycle == 0
135      assert coil[1].duty_cycle == 0
136      assert coil[2].duty_cycle == 0
137      assert coil[3].duty_cycle == 0xFFFF
138  
139      motor.onestep(direction=stepper.FORWARD, style=stepper.DOUBLE)
140      assert coil[0].duty_cycle == 0xFFFF
141      assert coil[1].duty_cycle == 0
142      assert coil[2].duty_cycle == 0
143      assert coil[3].duty_cycle == 0xFFFF
144  
145      motor.onestep(direction=stepper.FORWARD, style=stepper.SINGLE)
146      assert coil[0].duty_cycle == 0xFFFF
147      assert coil[1].duty_cycle == 0
148      assert coil[2].duty_cycle == 0
149      assert coil[3].duty_cycle == 0
150  
151  
152  def test_microstep_to_single():
153      coil = (Coil(), Coil(), Coil(), Coil())
154      # We undo the coil order so our tests make more sense.
155      motor = stepper.StepperMotor(coil[2], coil[0], coil[1], coil[3])
156      # We always start with a single step.
157      for j in range(4):
158          assert coil[j].duty_cycle == (0xFFFF if j == 0 else 0)
159  
160      motor.onestep(direction=stepper.BACKWARD, style=stepper.MICROSTEP)
161      assert coil[0].duty_cycle == 0xFEC3
162      assert coil[1].duty_cycle == 0
163      assert coil[2].duty_cycle == 0
164      assert coil[3].duty_cycle == 0x1918
165  
166      motor.onestep(direction=stepper.BACKWARD, style=stepper.SINGLE)
167      assert coil[0].duty_cycle == 0
168      assert coil[1].duty_cycle == 0
169      assert coil[2].duty_cycle == 0
170      assert coil[3].duty_cycle == 0xFFFF
171  
172      motor.onestep(direction=stepper.FORWARD, style=stepper.MICROSTEP)
173      assert coil[0].duty_cycle == 0x1918
174      assert coil[1].duty_cycle == 0
175      assert coil[2].duty_cycle == 0
176      assert coil[3].duty_cycle == 0xFEC3
177  
178      motor.onestep(direction=stepper.FORWARD, style=stepper.SINGLE)
179      assert coil[0].duty_cycle == 0xFFFF
180      assert coil[1].duty_cycle == 0
181      assert coil[2].duty_cycle == 0
182      assert coil[3].duty_cycle == 0