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  }