mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-07-26 10:38:41 +00:00
1c2b802f47
servo: Add function `SetAngleWithMicroseconds`,Adjust the angle by customizing the control pulse width
118 lines
3.8 KiB
Go
118 lines
3.8 KiB
Go
package servo
|
|
|
|
import (
|
|
"machine"
|
|
|
|
"errors"
|
|
)
|
|
|
|
var ErrInvalidAngle = errors.New("servo: invalid angle")
|
|
|
|
// PWM is the interface necessary for controlling typical servo motors.
|
|
type PWM interface {
|
|
Configure(config machine.PWMConfig) error
|
|
Channel(pin machine.Pin) (channel uint8, err error)
|
|
Top() uint32
|
|
Set(channel uint8, value uint32)
|
|
}
|
|
|
|
// Array is an array of servos controlled by a single PWM peripheral. On most
|
|
// chips, one PWM peripheral can control multiple servos (usually two or four).
|
|
type Array struct {
|
|
pwm PWM
|
|
}
|
|
|
|
// Servo is a single servo (connected to one PWM output) that's part of a servo
|
|
// array.
|
|
type Servo struct {
|
|
pwm PWM
|
|
channel uint8
|
|
}
|
|
|
|
const pwmPeriod = 20e6 // 20ms
|
|
|
|
// NewArray returns a new servo array based on the given PWM, for if you want to
|
|
// control multiple servos from a single PWM peripheral. Using a single PWM for
|
|
// multiple servos saves PWM peripherals for other uses and might use less power
|
|
// depending on the chip.
|
|
//
|
|
// If you only want to control a single servo, you could use the New shorthand
|
|
// instead.
|
|
func NewArray(pwm PWM) (Array, error) {
|
|
err := pwm.Configure(machine.PWMConfig{
|
|
Period: pwmPeriod,
|
|
})
|
|
if err != nil {
|
|
return Array{}, err
|
|
}
|
|
return Array{pwm}, nil
|
|
}
|
|
|
|
// Add adds a new servo to the servo array. Please check the chip documentation
|
|
// which pins can be controlled by the given PWM: depending on the chip this
|
|
// might be rigid (only a single pin) or very flexible (you can pick any pin).
|
|
func (array Array) Add(pin machine.Pin) (Servo, error) {
|
|
channel, err := array.pwm.Channel(pin)
|
|
if err != nil {
|
|
return Servo{}, err
|
|
}
|
|
return Servo{
|
|
pwm: array.pwm,
|
|
channel: channel,
|
|
}, nil
|
|
}
|
|
|
|
// New is a shorthand for NewArray and array.Add. This is useful if you only
|
|
// want to control just a single servo.
|
|
func New(pwm PWM, pin machine.Pin) (Servo, error) {
|
|
array, err := NewArray(pwm)
|
|
if err != nil {
|
|
return Servo{}, err
|
|
}
|
|
return array.Add(pin)
|
|
}
|
|
|
|
// SetMicroseconds sets the output signal to be high for the given number of
|
|
// microseconds. For many servos the range is normally between 1000µs and 2000µs
|
|
// for 90° of rotation (with 1500µs being the 'neutral' middle position).
|
|
//
|
|
// In many cases they can actually go a bit further, with a wider range of
|
|
// supported pulse ranges. For example, they might allow pulse widths from 500µs
|
|
// to 2500µs, but be warned that going outside of the 1000µs-2000µs range might
|
|
// break the servo as it might destroy the gears if it doesn't support this
|
|
// range. Therefore, to be sure check the datasheet before you try values
|
|
// outside of the 1000µs-2000µs range.
|
|
func (s Servo) SetMicroseconds(microseconds int16) {
|
|
value := uint64(s.pwm.Top()) * uint64(microseconds) / (pwmPeriod / 1000)
|
|
s.pwm.Set(s.channel, uint32(value))
|
|
}
|
|
|
|
// SetAngle sets the angle of the servo in degrees. The angle should be between
|
|
// 0 and 180, where 0 is the minimum angle and 180 is the maximum angle.
|
|
// This function should work for most servos, but if it doesn't work for yours
|
|
// you can use SetMicroseconds directly instead.
|
|
func (s Servo) SetAngle(angle int) error {
|
|
if angle < 0 || angle > 180 {
|
|
return ErrInvalidAngle
|
|
}
|
|
|
|
// 0° is 1000µs, 180° is 2000µs. See explanation in SetMicroseconds.
|
|
microseconds := angle*1000/180 + 1000
|
|
s.SetMicroseconds(int16(microseconds))
|
|
|
|
return nil
|
|
}
|
|
|
|
// SetAngleWithMicroseconds sets the angle of the servo in degrees. The angle should be between
|
|
// 0 and 180, where 0 is the minimum angle and 180 is the maximum angle.
|
|
// The high duration can be customized
|
|
// 0° is lowMicroseconds(us), 180° is highMicroseconds(us)
|
|
func (s Servo) SetAngleWithMicroseconds(angle int, lowMicroseconds, highMicroseconds int) error {
|
|
if angle < 0 || angle > 180 {
|
|
return ErrInvalidAngle
|
|
}
|
|
microseconds := lowMicroseconds + (highMicroseconds-lowMicroseconds)*angle/180
|
|
s.SetMicroseconds(int16(microseconds))
|
|
return nil
|
|
}
|