1 """PWMOut Class for NXP LPC4330"""
3 from greatfet import GreatFET
4 from greatfet.interfaces.pattern_generator import PatternGenerator
7 from microcontroller.pin import pwmOuts
9 raise RuntimeError("No PWM outputs defined for this board")
11 from microcontroller.pin import Pin
14 # pylint: disable=unnecessary-pass
15 class PWMError(IOError):
16 """Base class for PWM errors."""
21 # pylint: enable=unnecessary-pass
23 """Pulse Width Modulation Output Class"""
25 MAX_CYCLE_LEVEL = 1024
27 def __init__(self, pin, *, frequency=750, duty_cycle=0, variable_frequency=False):
28 """This class makes use of the GreatFET One's Pattern Generator to create a
29 Simulated Pulse width modulation. The way that the Pattern Generator works is that
30 takes a pattern in the form of bytes and will repeat the output. The trick to simulate
31 PWM is to generate the correct byte pattern for the correct channel.
34 pin (Pin): CircuitPython Pin object to output to
35 duty_cycle (int) : The fraction of each pulse which is high. 16-bit
36 frequency (int) : target frequency in Hertz (32-bit)
39 PWMOut: PWMOut object.
42 PWMError: if an I/O or OS error occurs.
43 TypeError: if `channel` or `pin` types are invalid.
44 ValueError: if PWM channel does not exist.
48 if variable_frequency:
49 raise NotImplemented("Variable Frequency is not currently supported.")
54 self._open(pin, duty_cycle, frequency)
59 def __exit__(self, t, value, traceback):
62 def _open(self, pin, duty=0, freq=500):
64 for pwmpair in pwmOuts:
66 self._channel = pwmpair[0]
69 if self._channel is None:
70 raise RuntimeError("No PWM channel found for this Pin")
73 self.duty_cycle = duty
78 self._set_enabled(True)
81 """Deinit the GreatFET One PWM."""
82 # pylint: disable=broad-except
84 if self._channel is not None:
86 self._set_enabled(False)
87 except Exception as e:
88 # due to a race condition for which I have not yet been
89 # able to find the root cause, deinit() often fails
90 # but it does not effect future usage of the pwm pin
92 "warning: failed to deinitialize pwm pin {0} due to: {1}\n".format(
93 self._channel, type(e).__name__
99 # pylint: enable=broad-except
101 def _is_deinited(self):
102 if self._pattern is None:
104 "Object has been deinitialize and can no longer "
105 "be used. Create a new object."
110 def _get_period(self):
111 return 1.0 / self._get_frequency()
113 def _set_period(self, period):
114 """Get or set the PWM's output period in seconds.
117 PWMError: if an I/O or OS error occurs.
118 TypeError: if value type is not int or float.
122 if not isinstance(period, (int, float)):
123 raise TypeError("Invalid period type, should be int or float.")
125 self._set_frequency(1.0 / period)
127 period = property(_get_period, _set_period)
129 def _get_duty_cycle(self):
130 """Get or set the PWM's output duty cycle as a ratio from 0.0 to 1.0.
133 PWMError: if an I/O or OS error occurs.
134 TypeError: if value type is not int or float.
135 ValueError: if value is out of bounds of 0.0 to 1.0.
139 return self._duty_cycle
141 def _set_duty_cycle(self, duty_cycle):
142 if not isinstance(duty_cycle, (int, float)):
143 raise TypeError("Invalid duty cycle type, should be int or float.")
145 # convert from 16-bit
146 if isinstance(duty_cycle, int):
147 duty_cycle /= 65535.0
148 if not 0.0 <= duty_cycle <= 1.0:
149 raise ValueError("Invalid duty cycle value, should be between 0.0 and 1.0.")
151 # Generate a pattern for 1024 samples of the duty cycle
152 pattern = [(1 << self._channel)] * round(PWMOut.MAX_CYCLE_LEVEL * duty_cycle)
153 pattern += [(0 << self._channel)] * round(
154 PWMOut.MAX_CYCLE_LEVEL * (1.0 - duty_cycle)
157 self._pattern = pattern
158 self._duty_cycle = duty_cycle
160 self._set_enabled(True)
162 duty_cycle = property(_get_duty_cycle, _set_duty_cycle)
164 def _get_frequency(self):
165 return int(PWMOut._nova.getIOpinPWMFreq(self._pwmpin).split("PWMFREQ ")[1])
167 def _set_frequency(self, frequency):
168 """Get or set the PWM's output frequency in Hertz.
171 PWMError: if an I/O or OS error occurs.
172 TypeError: if value type is not int or float.
176 if not isinstance(frequency, (int, float)):
177 raise TypeError("Invalid frequency type, should be int or float.")
179 # We are sending 1024 samples per second already
180 self._gf.pattern_generator.set_sample_rate(frequency * len(self._pattern))
182 frequency = property(_get_frequency, _set_frequency)
184 def _get_enabled(self):
185 enabled = self._enable
192 raise PWMError(None, 'Unknown enabled value: "%s"' % enabled)
194 def _set_enabled(self, value):
195 """Get or set the PWM's output enabled state.
198 PWMError: if an I/O or OS error occurs.
199 TypeError: if value type is not bool.
203 if not isinstance(value, bool):
204 raise TypeError("Invalid enabled type, should be string.")
209 self._gf.pattern_generator.scan_out_pattern(self._pattern)
211 self._gf.pattern_generator.stop()