diff --git a/easystepper/easystepper.go b/easystepper/easystepper.go index 653a400..b6b93f8 100644 --- a/easystepper/easystepper.go +++ b/easystepper/easystepper.go @@ -2,28 +2,76 @@ package easystepper // import "tinygo.org/x/drivers/easystepper" import ( + "errors" "machine" "time" ) +// StepMode determines the coil sequence used to perform a single step +type StepMode uint8 + +// Valid values for StepMode +const ( + // ModeFour uses a 'four step' coil sequence (12-23-34-41). This is the default (zero-value) mode + ModeFour StepMode = iota + // ModeEight uses an 'eight step' coil sequence (1-12-2-23-3-34-4-41) + ModeEight +) + +// stepCount is a helper function to return the number of steps in a StepMode sequence +func (sm StepMode) stepCount() uint { + switch sm { + default: + fallthrough + case ModeFour: + return 4 + case ModeEight: + return 8 + } +} + +// DeviceConfig contains the configuration data for a single easystepper driver +type DeviceConfig struct { + // Pin1 ... Pin4 determines the pins to configure and use for the device + Pin1, Pin2, Pin3, Pin4 machine.Pin + // StepCount is the number of steps required to perform a full revolution of the stepper motor + StepCount uint + // RPM determines the speed of the stepper motor in 'Revolutions per Minute' + RPM uint + // Mode determines the coil sequence used to perform a single step + Mode StepMode +} + +// DualDeviceConfig contains the configuration data for a dual easystepper driver +type DualDeviceConfig struct { + DeviceConfig + // Pin5 ... Pin8 determines the pins to configure and use for the second device + Pin5, Pin6, Pin7, Pin8 machine.Pin +} + // Device holds the pins and the delay between steps type Device struct { pins [4]machine.Pin - stepDelay int32 + stepDelay time.Duration stepNumber uint8 + stepMode StepMode } // DualDevice holds information for controlling 2 motors type DualDevice struct { - devices [2]Device + devices [2]*Device } -// New returns a new easystepper driver given 4 pins, number of steps and rpm -func New(pin1, pin2, pin3, pin4 machine.Pin, steps int32, rpm int32) Device { - return Device{ - pins: [4]machine.Pin{pin1, pin2, pin3, pin4}, - stepDelay: 60000000 / (steps * rpm), +// New returns a new single easystepper driver given a DeviceConfig +func New(config DeviceConfig) (*Device, error) { + if config.StepCount == 0 || config.RPM == 0 { + return nil, errors.New("config.StepCount and config.RPM must be > 0") } + return &Device{ + pins: [4]machine.Pin{config.Pin1, config.Pin2, config.Pin3, config.Pin4}, + stepDelay: time.Second * 60 / time.Duration((config.StepCount * config.RPM)), + stepMode: config.Mode, + }, nil } // Configure configures the pins of the Device @@ -34,17 +82,23 @@ func (d *Device) Configure() { } // NewDual returns a new dual easystepper driver given 8 pins, number of steps and rpm -func NewDual(pin1, pin2, pin3, pin4, pin5, pin6, pin7, pin8 machine.Pin, steps int32, rpm int32) DualDevice { - var dual DualDevice - dual.devices[0] = Device{ - pins: [4]machine.Pin{pin1, pin2, pin3, pin4}, - stepDelay: 60000000 / (steps * rpm), +func NewDual(config DualDeviceConfig) (*DualDevice, error) { + // Create the first device + dev1, err := New(config.DeviceConfig) + if err != nil { + return nil, err } - dual.devices[1] = Device{ - pins: [4]machine.Pin{pin5, pin6, pin7, pin8}, - stepDelay: 60000000 / (steps * rpm), + // Create the second device + config.DeviceConfig.Pin1 = config.Pin5 + config.DeviceConfig.Pin2 = config.Pin6 + config.DeviceConfig.Pin3 = config.Pin7 + config.DeviceConfig.Pin4 = config.Pin8 + dev2, err := New(config.DeviceConfig) + if err != nil { + return nil, err } - return dual + // Return composite dual device + return &DualDevice{devices: [2]*Device{dev1, dev2}}, nil } // Configure configures the pins of the DualDevice @@ -64,7 +118,7 @@ func (d *Device) Move(steps int32) { var s int32 d.stepMotor(d.stepNumber) for s = int32(d.stepNumber); s < steps; s++ { - time.Sleep(time.Duration(d.stepDelay) * time.Microsecond) + time.Sleep(d.stepDelay) d.moveDirectionSteps(direction, s) } } @@ -101,7 +155,7 @@ func (d *DualDevice) Move(stepsA, stepsB int32) { stepsA += int32(d.devices[max].stepNumber) minStep = int32(d.devices[min].stepNumber) for s := int32(d.devices[max].stepNumber); s < stepsA; s++ { - time.Sleep(time.Duration(d.devices[0].stepDelay) * time.Microsecond) + time.Sleep(d.devices[0].stepDelay) d.devices[max].moveDirectionSteps(directions[max], s) if ((s * stepsB) / stepsA) > minStep { @@ -119,6 +173,18 @@ func (d *DualDevice) Off() { // stepMotor changes the pins' state to the correct step func (d *Device) stepMotor(step uint8) { + switch d.stepMode { + default: + fallthrough + case ModeFour: + d.stepMotor4(step) + case ModeEight: + d.stepMotor8(step) + } +} + +// stepMotor4 changes the pins' state to the correct step in 4-step mode +func (d *Device) stepMotor4(step uint8) { switch step { case 0: d.pins[0].High() @@ -148,13 +214,63 @@ func (d *Device) stepMotor(step uint8) { d.stepNumber = step } +// stepMotor8 changes the pins' state to the correct step in 8-step mode +func (d *Device) stepMotor8(step uint8) { + switch step { + case 0: + d.pins[0].High() + d.pins[2].Low() + d.pins[1].Low() + d.pins[3].Low() + case 1: + d.pins[0].High() + d.pins[2].High() + d.pins[1].Low() + d.pins[3].Low() + case 2: + d.pins[0].Low() + d.pins[2].High() + d.pins[1].Low() + d.pins[3].Low() + case 3: + d.pins[0].Low() + d.pins[2].High() + d.pins[1].High() + d.pins[3].Low() + case 4: + d.pins[0].Low() + d.pins[2].Low() + d.pins[1].High() + d.pins[3].Low() + case 5: + d.pins[0].Low() + d.pins[2].Low() + d.pins[1].High() + d.pins[3].High() + case 6: + d.pins[0].Low() + d.pins[2].Low() + d.pins[1].Low() + d.pins[3].High() + case 7: + d.pins[0].High() + d.pins[2].Low() + d.pins[1].Low() + d.pins[3].High() + } + d.stepNumber = step +} + // moveDirectionSteps uses the direction to calculate the correct step and change the motor to it. -// Direction true: 0, 1, 2, 3, 0, 1, 2, ... -// Direction false: 0, 3, 2, 1, 0, 3, 2, ... +// Direction true: (4-step mode) 0, 1, 2, 3, 0, 1, 2, ... +// Direction false: (4-step mode) 0, 3, 2, 1, 0, 3, 2, ... +// Direction true: (8-step mode) 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, ... +// Direction false: (8-step mode) 0, 7, 6, 5, 4, 3, 2, 1, 0, 7, 6, ... func (d *Device) moveDirectionSteps(direction bool, step int32) { + modulus := int32(d.stepMode.stepCount()) if direction { - d.stepMotor(uint8(step % 4)) + d.stepMotor(uint8(step % modulus)) } else { - d.stepMotor(uint8((step + 2*(step%2)) % 4)) + d.stepMotor(uint8(((-step % modulus) + modulus) % modulus)) } } diff --git a/examples/easystepper/main.go b/examples/easystepper/main.go index c32de91..9a60a6e 100644 --- a/examples/easystepper/main.go +++ b/examples/easystepper/main.go @@ -8,7 +8,11 @@ import ( ) func main() { - motor := easystepper.New(machine.P13, machine.P15, machine.P14, machine.P16, 200, 75) + config := easystepper.DeviceConfig{ + Pin1: machine.P13, Pin2: machine.P15, Pin3: machine.P14, Pin4: machine.P16, + StepCount: 200, RPM: 75, Mode: easystepper.ModeFour, + } + motor, _ := easystepper.New(config) motor.Configure() for {