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