tinygo.org/x/drivers@v0.27.1-0.20240509133757-7dbca2a54349/easystepper/easystepper.go (about) 1 // Package easystepper provides a simple driver to rotate a 4-wire stepper motor. 2 package easystepper // import "tinygo.org/x/drivers/easystepper" 3 4 import ( 5 "errors" 6 "machine" 7 "time" 8 ) 9 10 // StepMode determines the coil sequence used to perform a single step 11 type StepMode uint8 12 13 // Valid values for StepMode 14 const ( 15 // ModeFour uses a 'four step' coil sequence (12-23-34-41). This is the default (zero-value) mode 16 ModeFour StepMode = iota 17 // ModeEight uses an 'eight step' coil sequence (1-12-2-23-3-34-4-41) 18 ModeEight 19 ) 20 21 // stepCount is a helper function to return the number of steps in a StepMode sequence 22 func (sm StepMode) stepCount() uint { 23 switch sm { 24 default: 25 fallthrough 26 case ModeFour: 27 return 4 28 case ModeEight: 29 return 8 30 } 31 } 32 33 // DeviceConfig contains the configuration data for a single easystepper driver 34 type DeviceConfig struct { 35 // Pin1 ... Pin4 determines the pins to configure and use for the device 36 Pin1, Pin2, Pin3, Pin4 machine.Pin 37 // StepCount is the number of steps required to perform a full revolution of the stepper motor 38 StepCount uint 39 // RPM determines the speed of the stepper motor in 'Revolutions per Minute' 40 RPM uint 41 // Mode determines the coil sequence used to perform a single step 42 Mode StepMode 43 } 44 45 // DualDeviceConfig contains the configuration data for a dual easystepper driver 46 type DualDeviceConfig struct { 47 DeviceConfig 48 // Pin5 ... Pin8 determines the pins to configure and use for the second device 49 Pin5, Pin6, Pin7, Pin8 machine.Pin 50 } 51 52 // Device holds the pins and the delay between steps 53 type Device struct { 54 pins [4]machine.Pin 55 stepDelay time.Duration 56 stepNumber uint8 57 stepMode StepMode 58 } 59 60 // DualDevice holds information for controlling 2 motors 61 type DualDevice struct { 62 devices [2]*Device 63 } 64 65 // New returns a new single easystepper driver given a DeviceConfig 66 func New(config DeviceConfig) (*Device, error) { 67 if config.StepCount == 0 || config.RPM == 0 { 68 return nil, errors.New("config.StepCount and config.RPM must be > 0") 69 } 70 return &Device{ 71 pins: [4]machine.Pin{config.Pin1, config.Pin2, config.Pin3, config.Pin4}, 72 stepDelay: time.Second * 60 / time.Duration((config.StepCount * config.RPM)), 73 stepMode: config.Mode, 74 }, nil 75 } 76 77 // Configure configures the pins of the Device 78 func (d *Device) Configure() { 79 for _, pin := range d.pins { 80 pin.Configure(machine.PinConfig{Mode: machine.PinOutput}) 81 } 82 } 83 84 // NewDual returns a new dual easystepper driver given 8 pins, number of steps and rpm 85 func NewDual(config DualDeviceConfig) (*DualDevice, error) { 86 // Create the first device 87 dev1, err := New(config.DeviceConfig) 88 if err != nil { 89 return nil, err 90 } 91 // Create the second device 92 config.DeviceConfig.Pin1 = config.Pin5 93 config.DeviceConfig.Pin2 = config.Pin6 94 config.DeviceConfig.Pin3 = config.Pin7 95 config.DeviceConfig.Pin4 = config.Pin8 96 dev2, err := New(config.DeviceConfig) 97 if err != nil { 98 return nil, err 99 } 100 // Return composite dual device 101 return &DualDevice{devices: [2]*Device{dev1, dev2}}, nil 102 } 103 104 // Configure configures the pins of the DualDevice 105 func (d *DualDevice) Configure() { 106 d.devices[0].Configure() 107 d.devices[1].Configure() 108 } 109 110 // Move rotates the motor the number of given steps 111 // (negative steps will rotate it the opposite direction) 112 func (d *Device) Move(steps int32) { 113 direction := steps > 0 114 if steps < 0 { 115 steps = -steps 116 } 117 steps += int32(d.stepNumber) 118 var s int32 119 d.stepMotor(d.stepNumber) 120 for s = int32(d.stepNumber); s < steps; s++ { 121 time.Sleep(d.stepDelay) 122 d.moveDirectionSteps(direction, s) 123 } 124 } 125 126 // Off turns off all motor pins 127 func (d *Device) Off() { 128 for _, pin := range d.pins { 129 pin.Low() 130 } 131 } 132 133 // Move rotates the motors the number of given steps 134 // (negative steps will rotate it the opposite direction) 135 func (d *DualDevice) Move(stepsA, stepsB int32) { 136 min := uint8(1) 137 max := uint8(0) 138 var directions [2]bool 139 var minStep int32 140 141 directions[0] = stepsA > 0 142 directions[1] = stepsB > 0 143 if stepsA < 0 { 144 stepsA = -stepsA 145 } 146 if stepsB < 0 { 147 stepsB = -stepsB 148 } 149 if stepsB > stepsA { 150 stepsA, stepsB = stepsB, stepsA 151 max, min = min, max 152 } 153 d.devices[0].stepMotor(d.devices[0].stepNumber) 154 d.devices[1].stepMotor(d.devices[1].stepNumber) 155 stepsA += int32(d.devices[max].stepNumber) 156 minStep = int32(d.devices[min].stepNumber) 157 for s := int32(d.devices[max].stepNumber); s < stepsA; s++ { 158 time.Sleep(d.devices[0].stepDelay) 159 d.devices[max].moveDirectionSteps(directions[max], s) 160 161 if ((s * stepsB) / stepsA) > minStep { 162 minStep++ 163 d.devices[min].moveDirectionSteps(directions[min], minStep) 164 } 165 } 166 } 167 168 // Off turns off all motor pins 169 func (d *DualDevice) Off() { 170 d.devices[0].Off() 171 d.devices[1].Off() 172 } 173 174 // stepMotor changes the pins' state to the correct step 175 func (d *Device) stepMotor(step uint8) { 176 switch d.stepMode { 177 default: 178 fallthrough 179 case ModeFour: 180 d.stepMotor4(step) 181 case ModeEight: 182 d.stepMotor8(step) 183 } 184 } 185 186 // stepMotor4 changes the pins' state to the correct step in 4-step mode 187 func (d *Device) stepMotor4(step uint8) { 188 switch step { 189 case 0: 190 d.pins[0].High() 191 d.pins[1].Low() 192 d.pins[2].High() 193 d.pins[3].Low() 194 break 195 case 1: 196 d.pins[0].Low() 197 d.pins[1].High() 198 d.pins[2].High() 199 d.pins[3].Low() 200 break 201 case 2: 202 d.pins[0].Low() 203 d.pins[1].High() 204 d.pins[2].Low() 205 d.pins[3].High() 206 break 207 case 3: 208 d.pins[0].High() 209 d.pins[1].Low() 210 d.pins[2].Low() 211 d.pins[3].High() 212 break 213 } 214 d.stepNumber = step 215 } 216 217 // stepMotor8 changes the pins' state to the correct step in 8-step mode 218 func (d *Device) stepMotor8(step uint8) { 219 switch step { 220 case 0: 221 d.pins[0].High() 222 d.pins[2].Low() 223 d.pins[1].Low() 224 d.pins[3].Low() 225 case 1: 226 d.pins[0].High() 227 d.pins[2].High() 228 d.pins[1].Low() 229 d.pins[3].Low() 230 case 2: 231 d.pins[0].Low() 232 d.pins[2].High() 233 d.pins[1].Low() 234 d.pins[3].Low() 235 case 3: 236 d.pins[0].Low() 237 d.pins[2].High() 238 d.pins[1].High() 239 d.pins[3].Low() 240 case 4: 241 d.pins[0].Low() 242 d.pins[2].Low() 243 d.pins[1].High() 244 d.pins[3].Low() 245 case 5: 246 d.pins[0].Low() 247 d.pins[2].Low() 248 d.pins[1].High() 249 d.pins[3].High() 250 case 6: 251 d.pins[0].Low() 252 d.pins[2].Low() 253 d.pins[1].Low() 254 d.pins[3].High() 255 case 7: 256 d.pins[0].High() 257 d.pins[2].Low() 258 d.pins[1].Low() 259 d.pins[3].High() 260 } 261 d.stepNumber = step 262 } 263 264 // moveDirectionSteps uses the direction to calculate the correct step and change the motor to it. 265 // Direction true: (4-step mode) 0, 1, 2, 3, 0, 1, 2, ... 266 // Direction false: (4-step mode) 0, 3, 2, 1, 0, 3, 2, ... 267 // Direction true: (8-step mode) 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, ... 268 // Direction false: (8-step mode) 0, 7, 6, 5, 4, 3, 2, 1, 0, 7, 6, ... 269 func (d *Device) moveDirectionSteps(direction bool, step int32) { 270 modulus := int32(d.stepMode.stepCount()) 271 if direction { 272 d.stepMotor(uint8(step % modulus)) 273 } else { 274 d.stepMotor(uint8(((-step % modulus) + modulus) % modulus)) 275 } 276 }