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