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 }