mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-07-26 10:38:41 +00:00
servo: add driver using PWM
This PR adds support for controlling servos. For example, on the Arduino Uno it should be able to control up to 6 servos jitter-free when using all available PWM pins. I haven't added support for setting a position in degrees, mainly because this varies by servo and it's probably necessary to configure the bounds in some way. Therefore, I added just SetMicroseconds. This makes the API possible to use and leaves the possibility of adding a SetPosition in the future.
This commit is contained in:
committed by
Ron Evans
parent
428db3cd12
commit
c765ef3970
@@ -103,6 +103,8 @@ smoke-test:
|
||||
@md5sum ./build/test.hex
|
||||
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/pcd8544/setpixel/main.go
|
||||
@md5sum ./build/test.hex
|
||||
tinygo build -size short -o ./build/test.hex -target=arduino ./examples/servo
|
||||
@md5sum ./build/test.hex
|
||||
tinygo build -size short -o ./build/test.hex -target=pybadge ./examples/shifter/main.go
|
||||
@md5sum ./build/test.hex
|
||||
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/sht3x/main.go
|
||||
@@ -196,7 +198,7 @@ DRIVERS = $(wildcard */)
|
||||
NOTESTS = build examples flash semihosting pcd8544 shiftregister st7789 microphone mcp3008 gps microbitmatrix \
|
||||
hcsr04 ssd1331 ws2812 thermistor apa102 easystepper ssd1351 ili9341 wifinina shifter hub75 \
|
||||
hd44780 buzzer ssd1306 espat l9110x st7735 bmi160 l293x dht keypad4x4 max72xx p1am tone tm1637 \
|
||||
pcf8563 mcp2515
|
||||
pcf8563 mcp2515 servo
|
||||
TESTS = $(filter-out $(addsuffix /%,$(NOTESTS)),$(DRIVERS))
|
||||
|
||||
unit-test:
|
||||
|
||||
@@ -101,6 +101,7 @@ The following 64 devices are supported.
|
||||
| [PCF8563 real time clock](https://www.nxp.com/docs/en/data-sheet/PCF8563.pdf) | I2C |
|
||||
| [Resistive Touchscreen (4-wire)](http://ww1.microchip.com/downloads/en/Appnotes/doc8091.pdf) | GPIO |
|
||||
| [Semihosting](https://wiki.segger.com/Semihosting) | Debug |
|
||||
| [Servo](https://learn.sparkfun.com/tutorials/hobby-servo-tutorial/all) | PWM |
|
||||
| [Shift register (PISO)](https://en.wikipedia.org/wiki/Shift_register#Parallel-in_serial-out_\(PISO\)) | GPIO |
|
||||
| [Shift registers (SIPO)](https://en.wikipedia.org/wiki/Shift_register#Serial-in_parallel-out_(SIPO)) | GPIO |
|
||||
| [SHT3x Digital Humidity Sensor](https://www.sensirion.com/fileadmin/user_upload/customers/sensirion/Dokumente/0_Datasheets/Humidity/Sensirion_Humidity_Sensors_SHT3x_Datasheet_digital.pdf) | I2C |
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/servo"
|
||||
)
|
||||
|
||||
// Configuration for the Arduino Uno.
|
||||
// Please change the PWM and pin if you want to try this example on a different
|
||||
// board.
|
||||
var (
|
||||
pwm = machine.Timer1
|
||||
pin = machine.D9
|
||||
)
|
||||
|
||||
func main() {
|
||||
s, err := servo.New(pwm, pin)
|
||||
if err != nil {
|
||||
for {
|
||||
println("could not configure servo")
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
println("setting to 0°")
|
||||
s.SetMicroseconds(1000)
|
||||
time.Sleep(3 * time.Second)
|
||||
|
||||
println("setting to 45°")
|
||||
s.SetMicroseconds(1500)
|
||||
time.Sleep(3 * time.Second)
|
||||
|
||||
println("setting to 90°")
|
||||
s.SetMicroseconds(2000)
|
||||
time.Sleep(3 * time.Second)
|
||||
|
||||
for {
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package servo
|
||||
|
||||
import "machine"
|
||||
|
||||
// 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))
|
||||
}
|
||||
Reference in New Issue
Block a user