]> Repositories - Adafruit_Blinka-hackapet.git/blob - src/adafruit_blinka/microcontroller/generic_linux/lgpio_pwmout.py
Merge branch 'main' of https://github.com/ErikApption/Adafruit_Blinka into erikapption
[Adafruit_Blinka-hackapet.git] / src / adafruit_blinka / microcontroller / generic_linux / lgpio_pwmout.py
1 # SPDX-FileCopyrightText: 2021 Melissa LeBlanc-Williams for Adafruit Industries
2 #
3 # SPDX-License-Identifier: MIT
4
5 """ PWMOut Class for lgpio lg library tx_pwm library """
6
7 import lgpio
8 from adafruit_blinka.microcontroller.generic_linux.lgpio_pin import CHIP
9
10
11 class PWMError(IOError):
12     """Base class for PWM errors."""
13
14
15 class PWMOut:
16     """Pulse Width Modulation Output Class"""
17
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...")
21         self._pin = pin
22         result = lgpio.gpio_claim_output(CHIP, self._pin.id, lFlags=lgpio.SET_PULL_NONE)
23         if result < 0:
24             raise RuntimeError(lgpio.error_text(result))
25         self._enabled = False
26         self._deinited = False
27         self._period = 0
28         # set frequency
29         self._frequency = frequency
30         # set duty
31         self.duty_cycle = duty_cycle
32         self.enabled = True
33
34     def __del__(self):
35         self.deinit()
36
37     def __enter__(self):
38         return self
39
40     def __exit__(self, _exc_type, _exc_val, _exc_tb):
41         self.deinit()
42
43     def deinit(self):
44         """Deinit the PWM."""
45         if not self._deinited:
46             if self.enabled:
47                 self._enabled = False  # turn off the pwm
48             self._deinited = True
49
50     def _is_deinited(self):
51         """raise Value error if the object has been de-inited"""
52         if self._deinited:
53             raise ValueError(
54                 "Object has been deinitialize and can no longer "
55                 "be used. Create a new object."
56             )
57
58     @property
59     def period(self):
60         """Get or set the PWM's output period in seconds.
61
62         Raises:
63             PWMError: if an I/O or OS error occurs.
64             TypeError: if value type is not int or float.
65
66         :type: int, float
67         """
68         return 1.0 / self.frequency
69
70     @period.setter
71     def period(self, period):
72         if not isinstance(period, (int, float)):
73             raise TypeError("Invalid period type, should be int or float.")
74
75         self.frequency = 1.0 / period
76
77     @property
78     def duty_cycle(self):
79         """Get or set the PWM's output duty cycle which is the fraction of
80         each pulse which is high. 16-bit
81
82         Raises:
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.
86
87         :type: int, float
88         """
89         return int(self._duty_cycle * 65535)
90
91     @duty_cycle.setter
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.")
95
96         if not 0 <= duty_cycle <= 65535:
97             raise ValueError("Invalid duty cycle value, should be between 0 and 65535")
98
99         # convert from 16-bit
100         duty_cycle /= 65535.0
101
102         self._duty_cycle = duty_cycle
103         if self._enabled:
104             self.enabled = True  # turn on with new values
105
106     @property
107     def frequency(self):
108         """Get or set the PWM's output frequency in Hertz.
109
110         Raises:
111             PWMError: if an I/O or OS error occurs.
112             TypeError: if value type is not int or float.
113
114         :type: int, float
115         """
116
117         return self._frequency
118
119     @frequency.setter
120     def frequency(self, frequency):
121         if not isinstance(frequency, (int, float)):
122             raise TypeError("Invalid frequency type, should be int or float.")
123
124         self._frequency = frequency
125         if self.enabled:
126             self.enabled = True  # turn on with new values
127
128     @property
129     def enabled(self):
130         """Get or set the PWM's output enabled state.
131
132         Raises:
133             PWMError: if an I/O or OS error occurs.
134             TypeError: if value type is not bool.
135
136         :type: bool
137         """
138         return self._enabled
139
140     @enabled.setter
141     def enabled(self, value):
142         if not isinstance(value, bool):
143             raise TypeError("Invalid enabled type, should be bool.")
144
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)
149         if result < 0:
150             raise RuntimeError(lgpio.error_text(result))
151         return result
152
153     # String representation
154     def __str__(self):
155         return (
156             f"pin {self._pin} (freq={self.frequency:f} Hz, duty_cycle="
157             f"{self.duty_cycle}({round(self.duty_cycle / 655.35)}%)"
158         )