2019-12-27 17:23:08 +00:00
|
|
|
package actuator
|
|
|
|
|
|
|
|
import (
|
2022-05-24 18:02:26 +00:00
|
|
|
"fmt"
|
|
|
|
"github.com/cyrilix/robocar-pca9685/pkg/util"
|
2021-10-12 17:28:56 +00:00
|
|
|
"go.uber.org/zap"
|
2022-05-24 18:02:26 +00:00
|
|
|
"periph.io/x/conn/v3/gpio"
|
2021-09-01 19:34:31 +00:00
|
|
|
"periph.io/x/conn/v3/i2c/i2creg"
|
|
|
|
"periph.io/x/conn/v3/physic"
|
|
|
|
"periph.io/x/devices/v3/pca9685"
|
|
|
|
"periph.io/x/host/v3"
|
2019-12-27 17:23:08 +00:00
|
|
|
)
|
|
|
|
|
2022-05-24 18:02:26 +00:00
|
|
|
const (
|
|
|
|
MinPercent = -1.
|
|
|
|
MaxPercent = 1.
|
2019-12-27 17:23:08 +00:00
|
|
|
)
|
|
|
|
|
2022-05-24 18:02:26 +00:00
|
|
|
type PWM int
|
|
|
|
|
|
|
|
func NewDevice(freq physic.Frequency) *pca9685.Dev {
|
|
|
|
zap.S().Info("NewDevice pca9685 controller")
|
2019-12-27 17:23:08 +00:00
|
|
|
_, err := host.Init()
|
|
|
|
if err != nil {
|
2022-05-24 18:02:26 +00:00
|
|
|
zap.S().Fatalf("unable to NewDevice host: %v", err)
|
2019-12-27 17:23:08 +00:00
|
|
|
}
|
|
|
|
|
2021-10-12 17:28:56 +00:00
|
|
|
zap.S().Info("open i2c bus")
|
2019-12-27 17:23:08 +00:00
|
|
|
bus, err := i2creg.Open("")
|
|
|
|
if err != nil {
|
2022-05-24 18:02:26 +00:00
|
|
|
zap.S().Fatalf("unable to NewDevice i2c bus: %v", err)
|
2019-12-27 17:23:08 +00:00
|
|
|
}
|
2022-05-24 18:02:26 +00:00
|
|
|
zap.S().Infof("i2c bus opened: %v", bus)
|
2019-12-27 17:23:08 +00:00
|
|
|
|
2022-05-24 18:02:26 +00:00
|
|
|
device, err := pca9685.NewI2C(bus, pca9685.I2CAddr)
|
2019-12-27 17:23:08 +00:00
|
|
|
if err != nil {
|
2022-05-24 18:02:26 +00:00
|
|
|
zap.S().Fatalf("unable to NewDevice pca9685 bus: %v", err)
|
2019-12-27 17:23:08 +00:00
|
|
|
}
|
2022-06-15 17:53:02 +00:00
|
|
|
zap.S().Infof("set pwm frequency to %v", freq)
|
2022-05-24 18:02:26 +00:00
|
|
|
err = device.SetPwmFreq(freq)
|
2019-12-27 17:23:08 +00:00
|
|
|
if err != nil {
|
2021-10-12 17:28:56 +00:00
|
|
|
zap.S().Panicf("unable to set pwm frequency: %v", err)
|
2019-12-27 17:23:08 +00:00
|
|
|
}
|
2022-05-24 18:02:26 +00:00
|
|
|
zap.S().Info("NewDevice done")
|
|
|
|
return device
|
|
|
|
}
|
|
|
|
|
2022-06-15 17:53:02 +00:00
|
|
|
func (c *Pca9685Controller) convertToDuty(percent float32) (gpio.Duty, error) {
|
2022-05-24 18:02:26 +00:00
|
|
|
// map absolute angle to angle that vehicle can implement.
|
2022-06-15 17:53:02 +00:00
|
|
|
pw := int(c.neutralPWM)
|
2022-05-24 18:02:26 +00:00
|
|
|
if percent > 0 {
|
2022-06-16 13:24:56 +00:00
|
|
|
pw = util.MapRange(float64(percent), 0, c.maxPercent, float64(c.neutralPWM), float64(c.maxPWM))
|
2022-05-24 18:02:26 +00:00
|
|
|
} else if percent < 0 {
|
2022-06-16 13:24:56 +00:00
|
|
|
pw = util.MapRange(float64(percent), c.minPercent, 0, float64(c.minPWM), float64(c.neutralPWM))
|
2022-05-24 18:02:26 +00:00
|
|
|
}
|
2022-06-15 17:53:02 +00:00
|
|
|
c.logr.Debugf("convert value %v to pw: %v", percent, pw)
|
2022-05-24 18:02:26 +00:00
|
|
|
|
2022-06-15 17:53:02 +00:00
|
|
|
per := c.freq.Period().Microseconds()
|
2022-05-24 18:02:26 +00:00
|
|
|
|
|
|
|
draw := float64(pw) / float64(per)
|
2022-06-16 07:28:01 +00:00
|
|
|
return gpio.Duty(int32(draw * float64(gpio.DutyMax))), nil
|
2022-05-24 18:02:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type Pca9685Controller struct {
|
2022-06-15 17:53:02 +00:00
|
|
|
logr *zap.SugaredLogger
|
2022-05-24 18:02:26 +00:00
|
|
|
pin gpio.PinIO
|
|
|
|
minPWM, maxPWM, neutralPWM PWM
|
2022-06-16 13:24:56 +00:00
|
|
|
minPercent, maxPercent float64
|
2022-05-24 18:02:26 +00:00
|
|
|
freq physic.Frequency
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Pca9685Controller) Close() error {
|
|
|
|
return c.pin.Halt()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Pca9685Controller) SetDuty(d gpio.Duty) error {
|
2022-06-16 07:28:01 +00:00
|
|
|
c.logr.Debugf("set duty %v -> %d (12bits value: %d)", d.String(), d, d>>12)
|
|
|
|
|
2022-05-24 18:02:26 +00:00
|
|
|
err := c.pin.PWM(d, c.freq)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("unable to set pwm value: %v", err)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetPercentValue Set percent value
|
|
|
|
func (c *Pca9685Controller) SetPercentValue(p float32) error {
|
2022-06-15 17:53:02 +00:00
|
|
|
d, err := c.convertToDuty(p)
|
2022-05-24 18:02:26 +00:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("unable to compute Duty value for steering: %w", err)
|
|
|
|
}
|
|
|
|
err = c.SetDuty(d)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("unable to apply duty value '%v' for pin '%v': '%w' ", d, c.pin.Name(), err)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-06-16 13:24:56 +00:00
|
|
|
func NewPca9685Controller(device *pca9685.Dev, channel int, minPWM, maxPWM, neutralPWM PWM, minPercent, maxPercent float64,
|
|
|
|
freq physic.Frequency, logr *zap.SugaredLogger) (*Pca9685Controller, error) {
|
2022-05-24 18:02:26 +00:00
|
|
|
p, err := device.CreatePin(channel)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("unable to create pin for channel %v: %w", channel, err)
|
|
|
|
}
|
|
|
|
s := Pca9685Controller{
|
|
|
|
pin: p,
|
|
|
|
minPWM: minPWM,
|
|
|
|
maxPWM: maxPWM,
|
|
|
|
neutralPWM: neutralPWM,
|
2022-06-16 13:24:56 +00:00
|
|
|
minPercent: minPercent,
|
|
|
|
maxPercent: maxPercent,
|
2022-05-24 18:02:26 +00:00
|
|
|
freq: freq,
|
2022-06-15 17:53:02 +00:00
|
|
|
logr: logr,
|
2022-05-24 18:02:26 +00:00
|
|
|
}
|
|
|
|
return &s, nil
|
2019-12-27 17:23:08 +00:00
|
|
|
}
|