1 # SPDX-FileCopyrightText: 2021 Melissa LeBlanc-Williams for Adafruit Industries
 
   3 # SPDX-License-Identifier: MIT
 
   5 """ PWMOut Class for lgpio lg library tx_pwm library """
 
   8 from adafruit_blinka.microcontroller.generic_linux.lgpio_pin import CHIP
 
  11 class PWMError(IOError):
 
  12     """Base class for PWM errors."""
 
  16     """Pulse Width Modulation Output Class"""
 
  18     def __init__(self, pin, *, frequency=500, duty_cycle=0, variable_frequency=False):
 
  19         if variable_frequency:
 
  20             print("Variable Frequency is not supported, ignoring...")
 
  22         result = lgpio.gpio_claim_output(CHIP, self._pin.id, lFlags=lgpio.SET_PULL_NONE)
 
  24             raise RuntimeError(lgpio.error_text(result))
 
  26         self._deinited = False
 
  29         self._frequency = frequency
 
  31         self.duty_cycle = duty_cycle
 
  40     def __exit__(self, _exc_type, _exc_val, _exc_tb):
 
  45         if not self._deinited:
 
  47                 self._enabled = False  # turn off the pwm
 
  50     def _is_deinited(self):
 
  51         """raise Value error if the object has been de-inited"""
 
  54                 "Object has been deinitialize and can no longer "
 
  55                 "be used. Create a new object."
 
  60         """Get or set the PWM's output period in seconds.
 
  63             PWMError: if an I/O or OS error occurs.
 
  64             TypeError: if value type is not int or float.
 
  68         return 1.0 / self.frequency
 
  71     def period(self, period):
 
  72         if not isinstance(period, (int, float)):
 
  73             raise TypeError("Invalid period type, should be int or float.")
 
  75         self.frequency = 1.0 / period
 
  79         """Get or set the PWM's output duty cycle which is the fraction of
 
  80         each pulse which is high. 16-bit
 
  83             PWMError: if an I/O or OS error occurs.
 
  84             TypeError: if value type is not int or float.
 
  85             ValueError: if value is out of bounds of 0.0 to 1.0.
 
  89         return int(self._duty_cycle * 65535)
 
  92     def duty_cycle(self, duty_cycle):
 
  93         if not isinstance(duty_cycle, (int, float)):
 
  94             raise TypeError("Invalid duty cycle type, should be int or float.")
 
  96         if not 0 <= duty_cycle <= 65535:
 
  97             raise ValueError("Invalid duty cycle value, should be between 0 and 65535")
 
 100         duty_cycle /= 65535.0
 
 102         self._duty_cycle = duty_cycle
 
 104             self.enabled = True  # turn on with new values
 
 108         """Get or set the PWM's output frequency in Hertz.
 
 111             PWMError: if an I/O or OS error occurs.
 
 112             TypeError: if value type is not int or float.
 
 117         return self._frequency
 
 120     def frequency(self, frequency):
 
 121         if not isinstance(frequency, (int, float)):
 
 122             raise TypeError("Invalid frequency type, should be int or float.")
 
 124         self._frequency = frequency
 
 126             self.enabled = True  # turn on with new values
 
 130         """Get or set the PWM's output enabled state.
 
 133             PWMError: if an I/O or OS error occurs.
 
 134             TypeError: if value type is not bool.
 
 141     def enabled(self, value):
 
 142         if not isinstance(value, bool):
 
 143             raise TypeError("Invalid enabled type, should be bool.")
 
 145         frequency = self._frequency if value else 0
 
 146         duty_cycle = round(self._duty_cycle * 100)
 
 147         self._enabled = value
 
 148         result = lgpio.tx_pwm(CHIP, self._pin.id, frequency, duty_cycle)
 
 150             raise RuntimeError(lgpio.error_text(result))
 
 153     # String representation
 
 156             f"pin {self._pin} (freq={self.frequency:f} Hz, duty_cycle="
 
 157             f"{self.duty_cycle}({round(self.duty_cycle / 655.35)}%)"