Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- from pathlib import Path
- def path_is_pwm_channel( path ):
- return path.resolve().match('pwmchip[0-9]*/pwm[0-9-]*')
- class Pwm:
- def __init__( self, path, *, frequency=None, period=None, value=None, duty_cycle=None, enabled=None ):
- """path can either be absolute or relative to /dev/pwm/
- Any remaining arguments are passed to configure() as-is. You should typically provide the desired
- frequency (or period) and initial value (or duty_cycle).
- """
- path = Path( '/dev/pwm/', path )
- self.path = path
- if not path.exists():
- raise FileNotFoundError(f'Directory not found: {path}')
- if not path.is_dir():
- raise NotADirectoryError(f'Not a directory: {path}')
- if not path_is_pwm_channel( path ):
- raise RuntimeError(f'Not a sysfs PWM channel: {path}')
- self._enabled = bool( int( (path/'enable').read_text() ) )
- self._period = int( (path/'period').read_text() )
- self._duty_cycle = int( (path/'duty_cycle').read_text() )
- self.configure( frequency=frequency, period=period, value=value, duty_cycle=duty_cycle, enabled=enabled )
- def disable( self ):
- if not self._enabled:
- return
- (self.path/'enable').write_text('0')
- self._enabled = False
- def enable( self ):
- if self._enabled:
- return
- if self._period == 0:
- raise RuntimeError("Cannot enable PWM when frequency is unconfigured (i.e. period is zero)")
- (self.path/'enable').write_text('1')
- self._enabled = True
- def configure( self, *, frequency=None, period=None, value=None, duty_cycle=None, enabled=None ):
- """Configure one or more PWM parameters. You can specify:
- - frequency (in Hz) or period (in ns)
- - value (in range 0.0-1.0) or duty_cycle (in ns)
- - enabled (bool)
- If frequency (or period) is specified then
- - value (or duty_cycle) must also be specified
- - enabled defaults to True
- Otherwise any parameters left unspecified are maintained unchanged.
- """
- if frequency is not None or period is not None:
- if value is None and duty_cycle is None:
- raise RuntimeError("When configuring PWM frequency or period you must also specify value or duty_cycle")
- if enabled is None:
- enabled = True
- else:
- if enabled is None:
- enabled = self._enabled
- if frequency is not None:
- if period is not None:
- raise RuntimeError("Cannot configure both PWM frequency and period")
- if frequency <= 0:
- period = 2**32
- else:
- period = round( 1e9 / frequency )
- if period not in range( 1, 2**32 ):
- raise RuntimeError(f"PWM frequency must be in range {1e9/(2**32-1)} .. {1e9/1} Hz")
- elif period is not None:
- period = round( period )
- if period <= 0 or period >= 2**32:
- raise RuntimeError("PWM period must be in range 1 .. 4294967295 ns")
- else:
- period = self._period
- if value is not None:
- if duty_cycle is not None:
- raise RuntimeError("Cannot configure both PWM value and duty_cycle")
- if period == 0:
- raise RuntimeError("Cannot set PWM value when frequency is unconfigured (i.e. period is zero)")
- if value < 0.0 or value > 1.0:
- raise RuntimeError("PWM value must be in range 0.0 .. 1.0")
- duty_cycle = round( value * period )
- elif duty_cycle is not None:
- duty_cycle = round( duty_cycle )
- if duty_cycle < 0 or duty_cycle > period:
- raise RuntimeError(f"PWM duty_cycle must be in range 0 .. period ({period}) ns")
- else:
- duty_cycle = self._duty_cycle
- if not enabled:
- self.disable()
- if duty_cycle < self._duty_cycle:
- (self.path/'duty_cycle').write_text( str( duty_cycle ) )
- self._duty_cycle = int( (self.path/'duty_cycle').read_text() )
- if period != self._period:
- (self.path/'period').write_text( str( period ) )
- self._period = int( (self.path/'period').read_text() )
- if duty_cycle != self._duty_cycle:
- (self.path/'duty_cycle').write_text( str( duty_cycle ) )
- self._duty_cycle = int( (self.path/'duty_cycle').read_text() )
- if enabled:
- self.enable()
- @property
- def enabled( self ):
- return self._enabled
- @enabled.setter
- def enabled( self, enabled ):
- if enabled:
- self.enable()
- else:
- self.disable()
- @property
- def period( self ):
- return self._period
- @period.setter
- def period( self, period ):
- if self._duty_cycle > 0:
- raise RuntimeError("Cannot set period when PWM value is non-zero (i.e. duty_cycle is non-zero)")
- self.configure( period=period, duty_cycle=0, enabled=self._enabled )
- @property
- def frequency( self ):
- if self._period == 0:
- return None
- return 1e9 / self._period
- @frequency.setter
- def frequency( self, frequency ):
- if self._duty_cycle > 0:
- raise RuntimeError("Cannot set frequency when PWM value is non-zero (i.e. duty_cycle is non-zero)")
- self.configure( frequency=frequency, duty_cycle=0, enabled=self._enabled )
- @property
- def value( self ):
- if self._period == 0:
- return None
- return self._duty_cycle / self._period
- @value.setter
- def value( self, value ):
- self.configure( value=value )
- @property
- def duty_cycle( self ):
- return self._duty_cycle
- @duty_cycle.setter
- def duty_cycle( self, duty_cycle ):
- self.configure( duty_cycle=duty_cycle )
- # support being used as context manager to automatically disable pwm when exiting scope
- def __enter__( self ):
- return self
- def __exit__( self, exc_type, exc_val, exc_tb ):
- self.disable()
Add Comment
Please, Sign In to add comment