silver2row

sysfs_pwm.py from @zmatt

Mar 23rd, 2023
134
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. from pathlib import Path
  2.  
  3. def path_is_pwm_channel( path ):
  4.     return path.resolve().match('pwmchip[0-9]*/pwm[0-9-]*')
  5.  
  6. class Pwm:
  7.     def __init__( self, path, *, frequency=None, period=None, value=None, duty_cycle=None, enabled=None ):
  8.         """path can either be absolute or relative to /dev/pwm/
  9.  
  10.        Any remaining arguments are passed to configure() as-is.  You should typically provide the desired
  11.        frequency (or period) and initial value (or duty_cycle).
  12.        """
  13.         path = Path( '/dev/pwm/', path )
  14.         self.path = path
  15.         if not path.exists():
  16.             raise FileNotFoundError(f'Directory not found: {path}')
  17.         if not path.is_dir():
  18.             raise NotADirectoryError(f'Not a directory: {path}')
  19.         if not path_is_pwm_channel( path ):
  20.             raise RuntimeError(f'Not a sysfs PWM channel: {path}')
  21.  
  22.         self._enabled = bool( int( (path/'enable').read_text() ) )
  23.         self._period = int( (path/'period').read_text() )
  24.         self._duty_cycle = int( (path/'duty_cycle').read_text() )
  25.  
  26.         self.configure( frequency=frequency, period=period, value=value, duty_cycle=duty_cycle, enabled=enabled )
  27.  
  28.     def disable( self ):
  29.         if not self._enabled:
  30.             return
  31.         (self.path/'enable').write_text('0')
  32.         self._enabled = False
  33.  
  34.     def enable( self ):
  35.         if self._enabled:
  36.             return
  37.         if self._period == 0:
  38.             raise RuntimeError("Cannot enable PWM when frequency is unconfigured (i.e. period is zero)")
  39.         (self.path/'enable').write_text('1')
  40.         self._enabled = True
  41.  
  42.     def configure( self, *, frequency=None, period=None, value=None, duty_cycle=None, enabled=None ):
  43.         """Configure one or more PWM parameters.  You can specify:
  44.  
  45.        - frequency (in Hz) or period (in ns)
  46.        - value (in range 0.0-1.0) or duty_cycle (in ns)
  47.        - enabled (bool)
  48.  
  49.        If frequency (or period) is specified then
  50.        - value (or duty_cycle) must also be specified
  51.        - enabled defaults to True
  52.        Otherwise any parameters left unspecified are maintained unchanged.
  53.        """
  54.  
  55.         if frequency is not None or period is not None:
  56.             if value is None and duty_cycle is None:
  57.                 raise RuntimeError("When configuring PWM frequency or period you must also specify value or duty_cycle")
  58.             if enabled is None:
  59.                 enabled = True
  60.         else:
  61.             if enabled is None:
  62.                 enabled = self._enabled
  63.  
  64.         if frequency is not None:
  65.             if period is not None:
  66.                 raise RuntimeError("Cannot configure both PWM frequency and period")
  67.             if frequency <= 0:
  68.                 period = 2**32
  69.             else:
  70.                 period = round( 1e9 / frequency )
  71.             if period not in range( 1, 2**32 ):
  72.                 raise RuntimeError(f"PWM frequency must be in range {1e9/(2**32-1)} .. {1e9/1} Hz")
  73.         elif period is not None:
  74.             period = round( period )
  75.             if period <= 0 or period >= 2**32:
  76.                 raise RuntimeError("PWM period must be in range 1 .. 4294967295 ns")
  77.         else:
  78.             period = self._period
  79.  
  80.         if value is not None:
  81.             if duty_cycle is not None:
  82.                 raise RuntimeError("Cannot configure both PWM value and duty_cycle")
  83.             if period == 0:
  84.                 raise RuntimeError("Cannot set PWM value when frequency is unconfigured (i.e. period is zero)")
  85.             if value < 0.0 or value > 1.0:
  86.                 raise RuntimeError("PWM value must be in range 0.0 .. 1.0")
  87.             duty_cycle = round( value * period )
  88.         elif duty_cycle is not None:
  89.             duty_cycle = round( duty_cycle )
  90.             if duty_cycle < 0 or duty_cycle > period:
  91.                 raise RuntimeError(f"PWM duty_cycle must be in range 0 .. period ({period}) ns")
  92.         else:
  93.             duty_cycle = self._duty_cycle
  94.  
  95.         if not enabled:
  96.             self.disable()
  97.  
  98.         if duty_cycle < self._duty_cycle:
  99.             (self.path/'duty_cycle').write_text( str( duty_cycle ) )
  100.             self._duty_cycle = int( (self.path/'duty_cycle').read_text() )
  101.  
  102.         if period != self._period:
  103.             (self.path/'period').write_text( str( period ) )
  104.             self._period = int( (self.path/'period').read_text() )
  105.  
  106.         if duty_cycle != self._duty_cycle:
  107.             (self.path/'duty_cycle').write_text( str( duty_cycle ) )
  108.             self._duty_cycle = int( (self.path/'duty_cycle').read_text() )
  109.  
  110.         if enabled:
  111.             self.enable()
  112.  
  113.  
  114.     @property
  115.     def enabled( self ):
  116.         return self._enabled
  117.  
  118.     @enabled.setter
  119.     def enabled( self, enabled ):
  120.         if enabled:
  121.             self.enable()
  122.         else:
  123.             self.disable()
  124.  
  125.     @property
  126.     def period( self ):
  127.         return self._period
  128.  
  129.     @period.setter
  130.     def period( self, period ):
  131.         if self._duty_cycle > 0:
  132.             raise RuntimeError("Cannot set period when PWM value is non-zero (i.e. duty_cycle is non-zero)")
  133.         self.configure( period=period, duty_cycle=0, enabled=self._enabled )
  134.  
  135.     @property
  136.     def frequency( self ):
  137.         if self._period == 0:
  138.             return None
  139.         return 1e9 / self._period
  140.  
  141.     @frequency.setter
  142.     def frequency( self, frequency ):
  143.         if self._duty_cycle > 0:
  144.             raise RuntimeError("Cannot set frequency when PWM value is non-zero (i.e. duty_cycle is non-zero)")
  145.         self.configure( frequency=frequency, duty_cycle=0, enabled=self._enabled )
  146.  
  147.     @property
  148.     def value( self ):
  149.         if self._period == 0:
  150.             return None
  151.         return self._duty_cycle / self._period
  152.  
  153.     @value.setter
  154.     def value( self, value ):
  155.         self.configure( value=value )
  156.  
  157.     @property
  158.     def duty_cycle( self ):
  159.         return self._duty_cycle
  160.  
  161.     @duty_cycle.setter
  162.     def duty_cycle( self, duty_cycle ):
  163.         self.configure( duty_cycle=duty_cycle )
  164.  
  165.  
  166.     # support being used as context manager to automatically disable pwm when exiting scope
  167.  
  168.     def __enter__( self ):
  169.         return self
  170.  
  171.     def __exit__( self, exc_type, exc_val, exc_tb ):
  172.         self.disable()
Add Comment
Please, Sign In to add comment