gobot.io/x/gobot@v1.16.0/sysfs/pwm_pin.go (about)

     1  package sysfs
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"os"
     7  	"strconv"
     8  	"syscall"
     9  	"time"
    10  )
    11  
    12  // PWMPin is the interface for sysfs PWM interactions
    13  type PWMPinner interface {
    14  	// Export exports the pin for use by the operating system
    15  	Export() error
    16  	// Unexport unexports the pin and releases the pin from the operating system
    17  	Unexport() error
    18  	// Enable enables/disables the PWM pin
    19  	Enable(bool) (err error)
    20  	// Polarity returns the polarity either normal or inverted
    21  	Polarity() (polarity string, err error)
    22  	// InvertPolarity sets the polarity to inverted if called with true
    23  	InvertPolarity(invert bool) (err error)
    24  	// Period returns the current PWM period for pin
    25  	Period() (period uint32, err error)
    26  	// SetPeriod sets the current PWM period for pin
    27  	SetPeriod(period uint32) (err error)
    28  	// DutyCycle returns the duty cycle for the pin
    29  	DutyCycle() (duty uint32, err error)
    30  	// SetDutyCycle writes the duty cycle to the pin
    31  	SetDutyCycle(duty uint32) (err error)
    32  }
    33  
    34  // PWMPinnerProvider is the interface that an Adaptor should implement to allow
    35  // clients to obtain access to any PWMPin's available on that board.
    36  type PWMPinnerProvider interface {
    37  	PWMPin(string) (PWMPinner, error)
    38  }
    39  
    40  type PWMPin struct {
    41  	pin     string
    42  	Path    string
    43  	enabled bool
    44  	write   func(path string, data []byte) (i int, err error)
    45  	read    func(path string) ([]byte, error)
    46  }
    47  
    48  // NewPwmPin returns a new pwmPin
    49  func NewPWMPin(pin int) *PWMPin {
    50  	return &PWMPin{
    51  		pin:     strconv.Itoa(pin),
    52  		enabled: false,
    53  		Path:    "/sys/class/pwm/pwmchip0",
    54  		read:    readPwmFile,
    55  		write:   writePwmFile}
    56  }
    57  
    58  // Export writes pin to pwm export path
    59  func (p *PWMPin) Export() error {
    60  	_, err := p.write(p.pwmExportPath(), []byte(p.pin))
    61  	if err != nil {
    62  		// If EBUSY then the pin has already been exported
    63  		e, ok := err.(*os.PathError)
    64  		if !ok || e.Err != syscall.EBUSY {
    65  			return err
    66  		}
    67  	}
    68  
    69  	// Pause to avoid race condition in case there is any udev rule
    70  	// that changes file permissions on newly exported PWMPin. This
    71  	// is a common circumstance when running as a non-root user.
    72  	time.Sleep(100 * time.Millisecond)
    73  
    74  	return nil
    75  }
    76  
    77  // Unexport writes pin to pwm unexport path
    78  func (p *PWMPin) Unexport() (err error) {
    79  	_, err = p.write(p.pwmUnexportPath(), []byte(p.pin))
    80  	return
    81  }
    82  
    83  // Enable writes value to pwm enable path
    84  func (p *PWMPin) Enable(enable bool) (err error) {
    85  	if p.enabled != enable {
    86  		p.enabled = enable
    87  		enableVal := 0
    88  		if enable {
    89  			enableVal = 1
    90  		}
    91  		_, err = p.write(p.pwmEnablePath(), []byte(fmt.Sprintf("%v", enableVal)))
    92  	}
    93  	return
    94  }
    95  
    96  // Polarity returns current polarity value
    97  func (p *PWMPin) Polarity() (polarity string, err error) {
    98  	buf, err := p.read(p.pwmPolarityPath())
    99  	if err != nil {
   100  		return
   101  	}
   102  	if len(buf) == 0 {
   103  		return "", nil
   104  	}
   105  
   106  	return string(buf), nil
   107  }
   108  
   109  // InvertPolarity writes value to pwm polarity path
   110  func (p *PWMPin) InvertPolarity(invert bool) (err error) {
   111  	if !p.enabled {
   112  		polarity := "normal"
   113  		if invert {
   114  			polarity = "inverted"
   115  		}
   116  		_, err = p.write(p.pwmPolarityPath(), []byte(polarity))
   117  	} else {
   118  		err = fmt.Errorf("Cannot set PWM polarity when enabled")
   119  	}
   120  	return
   121  }
   122  
   123  // Period reads from pwm period path and returns value in nanoseconds
   124  func (p *PWMPin) Period() (period uint32, err error) {
   125  	buf, err := p.read(p.pwmPeriodPath())
   126  	if err != nil {
   127  		return
   128  	}
   129  	if len(buf) == 0 {
   130  		return 0, nil
   131  	}
   132  
   133  	v := bytes.TrimRight(buf, "\n")
   134  	val, e := strconv.Atoi(string(v))
   135  	return uint32(val), e
   136  }
   137  
   138  // SetPeriod sets pwm period in nanoseconds
   139  func (p *PWMPin) SetPeriod(period uint32) (err error) {
   140  	_, err = p.write(p.pwmPeriodPath(), []byte(fmt.Sprintf("%v", period)))
   141  	return
   142  }
   143  
   144  // DutyCycle reads from pwm duty cycle path and returns value in nanoseconds
   145  func (p *PWMPin) DutyCycle() (duty uint32, err error) {
   146  	buf, err := p.read(p.pwmDutyCyclePath())
   147  	if err != nil {
   148  		return
   149  	}
   150  
   151  	v := bytes.TrimRight(buf, "\n")
   152  	val, e := strconv.Atoi(string(v))
   153  	return uint32(val), e
   154  }
   155  
   156  // SetDutyCycle writes value to pwm duty cycle path
   157  // duty is in nanoseconds
   158  func (p *PWMPin) SetDutyCycle(duty uint32) (err error) {
   159  	_, err = p.write(p.pwmDutyCyclePath(), []byte(fmt.Sprintf("%v", duty)))
   160  	return
   161  }
   162  
   163  // pwmExportPath returns export path
   164  func (p *PWMPin) pwmExportPath() string {
   165  	return p.Path + "/export"
   166  }
   167  
   168  // pwmUnexportPath returns unexport path
   169  func (p *PWMPin) pwmUnexportPath() string {
   170  	return p.Path + "/unexport"
   171  }
   172  
   173  // pwmDutyCyclePath returns duty_cycle path for specified pin
   174  func (p *PWMPin) pwmDutyCyclePath() string {
   175  	return p.Path + "/pwm" + p.pin + "/duty_cycle"
   176  }
   177  
   178  // pwmPeriodPath returns period path for specified pin
   179  func (p *PWMPin) pwmPeriodPath() string {
   180  	return p.Path + "/pwm" + p.pin + "/period"
   181  }
   182  
   183  // pwmEnablePath returns enable path for specified pin
   184  func (p *PWMPin) pwmEnablePath() string {
   185  	return p.Path + "/pwm" + p.pin + "/enable"
   186  }
   187  
   188  // pwmPolarityPath returns polarity path for specified pin
   189  func (p *PWMPin) pwmPolarityPath() string {
   190  	return p.Path + "/pwm" + p.pin + "/polarity"
   191  }
   192  
   193  func writePwmFile(path string, data []byte) (i int, err error) {
   194  	file, err := OpenFile(path, os.O_WRONLY, 0644)
   195  	defer file.Close()
   196  	if err != nil {
   197  		return
   198  	}
   199  
   200  	return file.Write(data)
   201  }
   202  
   203  func readPwmFile(path string) ([]byte, error) {
   204  	file, err := OpenFile(path, os.O_RDONLY, 0644)
   205  	defer file.Close()
   206  	if err != nil {
   207  		return make([]byte, 0), err
   208  	}
   209  
   210  	buf := make([]byte, 200)
   211  	var i int
   212  	i, err = file.Read(buf)
   213  	if i == 0 {
   214  		return []byte{}, err
   215  	}
   216  	return buf[:i], err
   217  }