1
0
mirror of https://github.com/hybridgroup/gobot.git synced 2025-04-27 13:48:56 +08:00
2022-09-24 13:52:26 +02:00

297 lines
7.7 KiB
Go

package sysfs
import (
"bytes"
"fmt"
"os"
"path"
"strconv"
"syscall"
"time"
)
const (
pwmPinErrorPattern = "%s() failed for pin %s with %v"
pwmPinSetErrorPattern = "%s(%v) failed for pin %s with %v"
)
const (
polarityNormal = "normal"
polarityInverted = "inverted"
)
const pwmDebug = false
// PWMPin is the interface for sysfs PWM interactions
type PWMPinner interface {
// Export exports the pin for use by the operating system
Export() error
// Unexport unexports the pin and releases the pin from the operating system
Unexport() error
// Enable enables/disables the PWM pin
// TODO: rename to "SetEnable(bool)" according to golang style and allow "Enable()" to be the getter function
Enable(bool) (err error)
// Polarity returns the polarity either normal or inverted
Polarity() (polarity string, err error)
// SetPolarity writes value to pwm polarity path
SetPolarity(value string) (err error)
// InvertPolarity sets the polarity to inverted if called with true
InvertPolarity(invert bool) (err error)
// Period returns the current PWM period for pin
Period() (period uint32, err error)
// SetPeriod sets the current PWM period for pin
SetPeriod(period uint32) (err error)
// DutyCycle returns the duty cycle for the pin
DutyCycle() (duty uint32, err error)
// SetDutyCycle writes the duty cycle to the pin
SetDutyCycle(duty uint32) (err error)
}
// PWMPinnerProvider is the interface that an Adaptor should implement to allow
// clients to obtain access to any PWMPin's available on that board.
type PWMPinnerProvider interface {
PWMPin(string) (PWMPinner, error)
}
type PWMPin struct {
pin string
Path string
enabled bool
write func(path string, data []byte) (i int, err error)
read func(path string) ([]byte, error)
}
// NewPwmPin returns a new pwmPin
func NewPWMPin(pin int) *PWMPin {
return &PWMPin{
pin: strconv.Itoa(pin),
enabled: false,
Path: "/sys/class/pwm/pwmchip0",
read: readPwmFile,
write: writePwmFile}
}
// Export writes pin to pwm export path
func (p *PWMPin) Export() error {
_, err := p.write(p.pwmExportPath(), []byte(p.pin))
if err != nil {
// If EBUSY then the pin has already been exported
e, ok := err.(*os.PathError)
if !ok || e.Err != syscall.EBUSY {
return fmt.Errorf(pwmPinErrorPattern, "Export", p.pin, err)
}
}
// Pause to avoid race condition in case there is any udev rule
// that changes file permissions on newly exported PWMPin. This
// is a common circumstance when running as a non-root user.
time.Sleep(100 * time.Millisecond)
return nil
}
// Unexport writes pin to pwm unexport path
func (p *PWMPin) Unexport() (err error) {
if _, err = p.write(p.pwmUnexportPath(), []byte(p.pin)); err != nil {
err = fmt.Errorf(pwmPinErrorPattern, "Unexport", p.pin, err)
}
return
}
// enable returns current enable value
func (p *PWMPin) enable() (enabled bool, err error) {
buf, err := p.read(p.pwmEnablePath())
if err != nil {
return enabled, fmt.Errorf(pwmPinErrorPattern, "enable", p.pin, err)
}
if len(buf) == 0 {
return enabled, nil
}
v := bytes.TrimRight(buf, "\n")
val, e := strconv.Atoi(string(v))
return val > 0, e
}
// Enable writes value to pwm enable path
func (p *PWMPin) Enable(enable bool) (err error) {
if p.enabled != enable {
p.enabled = enable
enableVal := 0
if enable {
enableVal = 1
}
if _, err = p.write(p.pwmEnablePath(), []byte(fmt.Sprintf("%v", enableVal))); err != nil {
err = fmt.Errorf(pwmPinSetErrorPattern, "Enable", enable, p.pin, err)
if pwmDebug {
p.printState()
}
}
}
return
}
// Polarity returns current polarity value
func (p *PWMPin) Polarity() (polarity string, err error) {
buf, err := p.read(p.pwmPolarityPath())
if err != nil {
return polarity, fmt.Errorf(pwmPinErrorPattern, "Polarity", p.pin, err)
}
if len(buf) == 0 {
return "", nil
}
return string(bytes.TrimRight(buf, "\n")), nil
}
// SetPolarity writes value to pwm polarity path
func (p *PWMPin) SetPolarity(value string) (err error) {
if p.enabled {
return fmt.Errorf("Cannot set PWM polarity when enabled")
}
if _, err = p.write(p.pwmPolarityPath(), []byte(value)); err != nil {
err = fmt.Errorf(pwmPinSetErrorPattern, "SetPolarity", value, p.pin, err)
if pwmDebug {
p.printState()
}
}
return
}
// InvertPolarity sets the polarity to "inverted" when 'true' is given, otherwise to "normal"
func (p *PWMPin) InvertPolarity(invert bool) (err error) {
polarity := polarityNormal
if invert {
polarity = polarityInverted
}
return p.SetPolarity(polarity)
}
// Period reads from pwm period path and returns value in nanoseconds
func (p *PWMPin) Period() (period uint32, err error) {
buf, err := p.read(p.pwmPeriodPath())
if err != nil {
return period, fmt.Errorf(pwmPinErrorPattern, "Period", p.pin, err)
}
if len(buf) == 0 {
return 0, nil
}
v := bytes.TrimRight(buf, "\n")
val, e := strconv.Atoi(string(v))
return uint32(val), e
}
// SetPeriod sets pwm period in nanoseconds
func (p *PWMPin) SetPeriod(period uint32) (err error) {
if _, err = p.write(p.pwmPeriodPath(), []byte(fmt.Sprintf("%v", period))); err != nil {
err = fmt.Errorf(pwmPinSetErrorPattern, "SetPeriod", period, p.pin, err)
if pwmDebug {
p.printState()
}
}
return
}
// DutyCycle reads from pwm duty cycle path and returns value in nanoseconds
func (p *PWMPin) DutyCycle() (duty uint32, err error) {
buf, err := p.read(p.pwmDutyCyclePath())
if err != nil {
return duty, fmt.Errorf(pwmPinErrorPattern, "DutyCycle", p.pin, err)
}
v := bytes.TrimRight(buf, "\n")
val, e := strconv.Atoi(string(v))
return uint32(val), e
}
// SetDutyCycle writes value to pwm duty cycle path
// duty is in nanoseconds
func (p *PWMPin) SetDutyCycle(duty uint32) (err error) {
if _, err = p.write(p.pwmDutyCyclePath(), []byte(fmt.Sprintf("%v", duty))); err != nil {
err = fmt.Errorf(pwmPinSetErrorPattern, "SetDutyCycle", duty, p.pin, err)
if pwmDebug {
p.printState()
}
}
return
}
// pwmExportPath returns export path
func (p *PWMPin) pwmExportPath() string {
return path.Join(p.Path, "export")
}
// pwmUnexportPath returns unexport path
func (p *PWMPin) pwmUnexportPath() string {
return path.Join(p.Path, "unexport")
}
// pwmDutyCyclePath returns duty_cycle path for specified pin
func (p *PWMPin) pwmDutyCyclePath() string {
return path.Join(p.Path, "pwm"+p.pin, "duty_cycle")
}
// pwmPeriodPath returns period path for specified pin
func (p *PWMPin) pwmPeriodPath() string {
return path.Join(p.Path, "pwm"+p.pin, "period")
}
// pwmEnablePath returns enable path for specified pin
func (p *PWMPin) pwmEnablePath() string {
return path.Join(p.Path, "pwm"+p.pin, "enable")
}
// pwmPolarityPath returns polarity path for specified pin
func (p *PWMPin) pwmPolarityPath() string {
return path.Join(p.Path, "pwm"+p.pin, "polarity")
}
func writePwmFile(path string, data []byte) (i int, err error) {
file, err := OpenFile(path, os.O_WRONLY, 0644)
defer file.Close()
if err != nil {
return
}
return file.Write(data)
}
func readPwmFile(path string) ([]byte, error) {
file, err := OpenFile(path, os.O_RDONLY, 0644)
defer file.Close()
if err != nil {
return make([]byte, 0), err
}
buf := make([]byte, 200)
var i int
i, err = file.Read(buf)
if i == 0 {
return []byte{}, err
}
return buf[:i], err
}
func (p *PWMPin) printState() {
enabled, _ := p.enable()
polarity, _ := p.Polarity()
period, _ := p.Period()
duty, _ := p.DutyCycle()
fmt.Println("Print state of all PWM variables...")
fmt.Printf("Enable: %v, ", enabled)
fmt.Printf("Polarity: %v, ", polarity)
fmt.Printf("Period: %v, ", period)
fmt.Printf("DutyCycle: %v, ", duty)
var powerPercent float64
if enabled {
if polarity == polarityNormal {
powerPercent = float64(duty) / float64(period) * 100
} else {
powerPercent = float64(period) / float64(duty) * 100
}
}
fmt.Printf("Power: %.1f\n", powerPercent)
}