]> Repositories - Adafruit_Blinka-hackapet.git/blob - src/adafruit_blinka/microcontroller/bcm283x/pwmio/PWMOut.py
c1494064be57c55a0551c059c2a71a9a87488c42
[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 # pylint: disable=unnecessary-pass
12 class PWMError(IOError):
13     """Base class for PWM errors."""
14
15     pass
16
17
18 # pylint: enable=unnecessary-pass
19
20
21 class PWMOut:            # pylint: disable=invalid-name
22     """Pulse Width Modulation Output Class"""
23
24     def __init__(self, pin, *, frequency=500, duty_cycle=0,
25                  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(board.pin.CHIP, self._pin.id,
30                                          lFlags=lgpio.SET_PULL_NONE)
31         if result < 0:
32             raise RuntimeError(lgpio.error_text(result))
33         self._enabled = False
34         self._deinited = False
35         self._period = 0
36         # set frequency
37         self._frequency = frequency
38         # set duty
39         self.duty_cycle = duty_cycle
40         self.enabled = True
41
42     def __del__(self):
43         self.deinit()
44
45     def __enter__(self):
46         return self
47
48     def __exit__(self, exc_type, exc_val, exc_tb):
49         self.deinit()
50
51     def deinit(self):
52         """Deinit the PWM."""
53         if not self._deinited:
54             if self.enabled:
55                 self._enabled = False       # turn off the pwm
56             self._deinited = True
57
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(
107                 "Invalid duty cycle value, should be between 0 and 65535")
108
109         # convert from 16-bit
110         duty_cycle /= 65535.0
111
112         self._duty_cycle = duty_cycle
113         if self._enabled:
114             self.enabled = True        # turn on with new values
115
116     @property
117     def frequency(self):
118         """Get or set the PWM's output frequency in Hertz.
119
120         Raises:
121             PWMError: if an I/O or OS error occurs.
122             TypeError: if value type is not int or float.
123
124         :type: int, float
125         """
126
127         return self._frequency
128
129     @frequency.setter
130     def frequency(self, frequency):
131         if not isinstance(frequency, (int, float)):
132             raise TypeError("Invalid frequency type, should be int or float.")
133
134         self._frequency = frequency
135         if self.enabled:
136             self.enabled = True        # turn on with new values
137
138     @property
139     def enabled(self):
140         """Get or set the PWM's output enabled state.
141
142         Raises:
143             PWMError: if an I/O or OS error occurs.
144             TypeError: if value type is not bool.
145
146         :type: bool
147         """
148         return self._enabled
149
150     @enabled.setter
151     def enabled(self, value):
152         if not isinstance(value, bool):
153             raise TypeError("Invalid enabled type, should be bool.")
154
155         frequency = self._frequency if value else 0
156         duty_cycle = round(self._duty_cycle * 100)
157         self._enabled = value
158         result = lgpio.tx_pwm(board.pin.CHIP, self._pin.id, frequency, duty_cycle)
159         if result < 0:
160             raise RuntimeError(lgpio.error_text(result))
161         return result
162
163
164     # String representation
165     def __str__(self):
166         return (f"pin {self._pin} (freq={self.frequency:f} Hz, duty_cycle="
167                 f"{self.duty_cycle}({round(self.duty_cycle / 655.35)}%)")