1
0
mirror of https://github.com/hybridgroup/gobot.git synced 2025-05-01 13:48:57 +08:00
hybridgroup.gobot/drivers/gpio/easy_driver.go

215 lines
5.5 KiB
Go
Raw Normal View History

package gpio
import (
"fmt"
"strings"
"time"
"gobot.io/x/gobot/v2"
)
const easyDriverDebug = false
// EasyDriver is an driver for stepper hardware board from SparkFun (https://www.sparkfun.com/products/12779)
// This should also work for the BigEasyDriver (untested). It is basically a wrapper for the common StepperDriver{}
// with the specific additions for the board, e.g. direction, enable and sleep outputs.
type EasyDriver struct {
*StepperDriver
stepPin string
dirPin string
enPin string
sleepPin string
anglePerStep float32
sleeping bool
}
// NewEasyDriver returns a new driver
// TODO: Support selecting phase input instead of hard-wiring MS1 and MS2 to board truth table
// A - DigitalWriter
// anglePerStep - Step angle of motor
// stepPin - Pin corresponding to step input on EasyDriver
// dirPin - Pin corresponding to dir input on EasyDriver. Optional
// enPin - Pin corresponding to enabled input on EasyDriver. Optional
// sleepPin - Pin corresponding to sleep input on EasyDriver. Optional
func NewEasyDriver(
a DigitalWriter,
anglePerStep float32,
stepPin string,
dirPin string,
enPin string,
sleepPin string,
) *EasyDriver {
if anglePerStep <= 0 {
panic("angle per step needs to be greater than zero")
}
// panic if step pin isn't set
if stepPin == "" {
panic("Step pin is not set")
}
stepper := NewStepperDriver(a, [4]string{}, nil, 1)
stepper.name = gobot.DefaultName("EasyDriver")
stepper.stepperDebug = easyDriverDebug
stepper.haltIfRunning = false
stepper.stepsPerRev = 360.0 / anglePerStep
d := &EasyDriver{
StepperDriver: stepper,
stepPin: stepPin,
dirPin: dirPin,
enPin: enPin,
sleepPin: sleepPin,
anglePerStep: anglePerStep,
sleeping: false,
}
d.stepFunc = d.onePinStepping
d.sleepFunc = d.sleepWithSleepPin
d.beforeHalt = d.shutdown
// 1/4 of max speed. Not too fast, not too slow
d.speedRpm = d.MaxSpeed() / 4
return d
}
// SetDirection sets the direction to be moving.
func (d *EasyDriver) SetDirection(direction string) error {
direction = strings.ToLower(direction)
if direction != StepperDriverForward && direction != StepperDriverBackward {
return fmt.Errorf("Invalid direction '%s'. Value should be '%s' or '%s'",
direction, StepperDriverForward, StepperDriverBackward)
}
if d.dirPin == "" {
return fmt.Errorf("dirPin is not set for '%s'", d.name)
}
writeVal := byte(0) // low is forward
if direction == StepperDriverBackward {
writeVal = 1 // high is backward
}
//nolint:forcetypeassert // type safe by constructor
if err := d.connection.(DigitalWriter).DigitalWrite(d.dirPin, writeVal); err != nil {
return err
}
// ensure that write of variable can not interfere with read in step()
d.valueMutex.Lock()
defer d.valueMutex.Unlock()
d.direction = direction
return nil
}
// Enable enables all motor output
func (d *EasyDriver) Enable() error {
if d.enPin == "" {
d.disabled = false
return fmt.Errorf("enPin is not set - board '%s' is enabled by default", d.name)
}
// enPin is active low
//nolint:forcetypeassert // type safe by constructor
if err := d.connection.(DigitalWriter).DigitalWrite(d.enPin, 0); err != nil {
return err
}
d.disabled = false
return nil
}
// Disable disables all motor output
func (d *EasyDriver) Disable() error {
if d.enPin == "" {
return fmt.Errorf("enPin is not set for '%s'", d.name)
}
_ = d.stopIfRunning() // drop step errors
// enPin is active low
//nolint:forcetypeassert // type safe by constructor
if err := d.connection.(DigitalWriter).DigitalWrite(d.enPin, 1); err != nil {
return err
}
d.disabled = true
return nil
}
// IsEnabled returns a bool stating whether motor is enabled
func (d *EasyDriver) IsEnabled() bool {
return !d.disabled
}
// Wake wakes up the driver
func (d *EasyDriver) Wake() error {
if d.sleepPin == "" {
return fmt.Errorf("sleepPin is not set for '%s'", d.name)
}
// sleepPin is active low
//nolint:forcetypeassert // type safe by constructor
if err := d.connection.(DigitalWriter).DigitalWrite(d.sleepPin, 1); err != nil {
return err
}
d.sleeping = false
// we need to wait 1ms after sleeping before doing a step to charge the step pump (according to data sheet)
time.Sleep(1 * time.Millisecond)
return nil
}
// IsSleeping returns a bool stating whether motor is sleeping
func (d *EasyDriver) IsSleeping() bool {
return d.sleeping
}
func (d *EasyDriver) onePinStepping() error {
// ensure that read and write of variables (direction, stepNum) can not interfere
d.valueMutex.Lock()
defer d.valueMutex.Unlock()
// a valid steps occurs for a low to high transition
//nolint:forcetypeassert // type safe by constructor
if err := d.connection.(DigitalWriter).DigitalWrite(d.stepPin, 0); err != nil {
return err
}
time.Sleep(d.getDelayPerStep())
//nolint:forcetypeassert // type safe by constructor
if err := d.connection.(DigitalWriter).DigitalWrite(d.stepPin, 1); err != nil {
return err
}
if d.direction == StepperDriverForward {
d.stepNum++
} else {
d.stepNum--
}
return nil
}
// sleepWithSleepPin puts the driver to sleep and disables all motor output. Low power mode.
func (d *EasyDriver) sleepWithSleepPin() error {
if d.sleepPin == "" {
return fmt.Errorf("sleepPin is not set for '%s'", d.name)
}
_ = d.stopIfRunning() // drop step errors
// sleepPin is active low
//nolint:forcetypeassert // type safe by constructor
if err := d.connection.(DigitalWriter).DigitalWrite(d.sleepPin, 0); err != nil {
return err
}
d.sleeping = true
return nil
}