From: Francis Guevarra Date: Thu, 21 Nov 2019 06:02:06 +0000 (-0800) Subject: Add pulseio support with nova pwmout X-Git-Tag: 3.1.0^2~12 X-Git-Url: https://git.ayoreis.com/Adafruit_Blinka-hackapet.git/commitdiff_plain/9584ded68bb4b291c0c7aaa99a15b61962f265f3 Add pulseio support with nova pwmout --- diff --git a/src/adafruit_blinka/microcontroller/nova/pin.py b/src/adafruit_blinka/microcontroller/nova/pin.py index 945de25..320e67b 100644 --- a/src/adafruit_blinka/microcontroller/nova/pin.py +++ b/src/adafruit_blinka/microcontroller/nova/pin.py @@ -53,3 +53,11 @@ IO1 = Pin(1) IO2 = Pin(2) IO3 = Pin(3) IO4 = Pin(4) + +PWM0 = IO0 +PWM1 = IO1 +PWM2 = IO2 +# No PWM support on IO3 +PWM4 = IO4 + +pwmOuts = ( ((1, 0), PWM0), ((1, 1), PWM1), ((1, 2), PWM2), ((1, 4), PWM4) ) \ No newline at end of file diff --git a/src/adafruit_blinka/microcontroller/nova/pwmout.py b/src/adafruit_blinka/microcontroller/nova/pwmout.py new file mode 100644 index 0000000..57aa0e5 --- /dev/null +++ b/src/adafruit_blinka/microcontroller/nova/pwmout.py @@ -0,0 +1,226 @@ +import os +import digitalio + +try: + from microcontroller.pin import pwmOuts +except ImportError: + raise RuntimeError("No PWM outputs defined for this board") + +from microcontroller.pin import Pin + +class PWMError(IOError): + """Base class for PWM errors.""" + pass + + +class PWMOut(object): + # Nova instance + _nova = None + MAX_CYCLE_LEVEL = 1024 + + def __init__(self, pin, *, frequency=750, duty_cycle=0, variable_frequency=False): + """Instantiate a PWM object and open the sysfs PWM corresponding to the + specified channel and pin. + + Args: + pin (Pin): CircuitPython Pin object to output to + duty_cycle (int) : The fraction of each pulse which is high. 16-bit + frequency (int) : target frequency in Hertz (32-bit) + variable_frequency (bool) : True if the frequency will change over time + + Returns: + PWMOut: PWMOut object. + + Raises: + PWMError: if an I/O or OS error occurs. + TypeError: if `channel` or `pin` types are invalid. + ValueError: if PWM channel does not exist. + + """ + if PWMOut._nova is None: + if Pin._nova is not None: + # check if Pin already connected to Binho Nova + PWMOut._nova = Pin._nova + else: + from binhoHostAdapter import binhoHostAdapter + from binhoHostAdapter import binhoUtilities + + utilities = binhoUtilities.binhoUtilities() + devices = utilities.listAvailableDevices() + + if len(devices) > 0: + PWMOut._nova = binhoHostAdapter.binhoHostAdapter(devices[0]) + else: + raise RuntimeError('No Binho Host Adapter found!') + + self._pwmpin = None + self._open(pin, duty_cycle, frequency, variable_frequency) + + def __del__(self): + self.deinit() + + def __enter__(self): + return self + + def __exit__(self, t, value, traceback): + self.deinit() + + def _open(self, pin, duty=0, freq=750, variable_frequency=False): + self._channel = None + for pwmpair in pwmOuts: + if pwmpair[1] == pin: + self._channel = pwmpair[0][0] + self._pwmpin = pwmpair[0][1] + + self._pin = pin + if self._channel is None: + raise RuntimeError("No PWM channel found for this Pin") + + PWMOut._nova.setIOpinMode(self._pwmpin, Pin.PWM) + + # set frequency + self.frequency = freq + PWMOut._nova.setIOpinPWMFreq(self._pwmpin, self.frequency) + # set period + self._period = self._get_period() + + # set duty + self.duty_cycle = duty + + self._set_enabled(True) + + def deinit(self): + try: + """Deinit the Nova PWM.""" + if self._channel is not None: + #self.duty_cycle = 0 + self._set_enabled(False) # make to disable before unexport + """ + try: + #unexport_path = os.path.join(channel_path, self._unexport_path) + with open(os.path.join(channel_path, self._unexport_path), "w") as f_unexport: + f_unexport.write("%d\n" % self._pwmpin) + except IOError as e: + raise PWMError(e.errno, "Unexporting PWM pin: " + e.strerror) + """ + except Exception as e: + # due to a race condition for which I have not yet been + # able to find the root cause, deinit() often fails + # but it does not effect future usage of the pwm pin + print("warning: failed to deinitialize pwm pin {0}:{1} due to: {2}\n".format(self._channel, self._pwmpin, type(e).__name__)) + finally: + self._channel = None + self._pwmpin = None + + def _is_deinited(self): + if self._pwmpin is None: + raise ValueError("Object has been deinitialize and can no longer " + "be used. Create a new object.") + + # Mutable properties + + def _get_period(self): + return 1.0 / self._get_frequency() + + def _set_period(self, period): + if not isinstance(period, (int, float)): + raise TypeError("Invalid period type, should be int or float.") + + self._set_frequency(1.0 / period) + + period = property(_get_period, _set_period) + + """Get or set the PWM's output period in seconds. + + Raises: + PWMError: if an I/O or OS error occurs. + TypeError: if value type is not int or float. + + :type: int, float + """ + + def _get_duty_cycle(self): + duty_cycle = Pin._nova.getIOpinValue(self._pwmpin) + + # Convert duty cycle to ratio from 0.0 to 1.0 + duty_cycle = duty_cycle / PWMOut.MAX_CYCLE_LEVEL + + # convert to 16-bit + duty_cycle = int(duty_cycle * 65535) + return duty_cycle + + def _set_duty_cycle(self, duty_cycle): + if not isinstance(duty_cycle, (int, float)): + raise TypeError("Invalid duty cycle type, should be int or float.") + + # convert from 16-bit + duty_cycle /= 65535.0 + if not 0.0 <= duty_cycle <= 1.0: + raise ValueError("Invalid duty cycle value, should be between 0.0 and 1.0.") + + # Convert duty cycle from ratio to 1024 levels + duty_cycle = duty_cycle * PWMOut.MAX_CYCLE_LEVEL + + # Set duty cycle + Pin._nova.setIOpinValue(self._pwmpin, duty_cycle) + + duty_cycle = property(_get_duty_cycle, _set_duty_cycle) + """Get or set the PWM's output duty cycle as a ratio from 0.0 to 1.0. + + Raises: + PWMError: if an I/O or OS error occurs. + TypeError: if value type is not int or float. + ValueError: if value is out of bounds of 0.0 to 1.0. + + :type: int, float + """ + + def _get_frequency(self): + return int(PWMOut._nova.getIOpinPWMFreq(self._pwmpin).split('PWMFREQ ')[1]) + + def _set_frequency(self, frequency): + if not isinstance(frequency, (int, float)): + raise TypeError("Invalid frequency type, should be int or float.") + + PWMOut._nova.setIOpinPWMFreq(self._pwmpin, frequency) + + frequency = property(_get_frequency, _set_frequency) + """Get or set the PWM's output frequency in Hertz. + + Raises: + PWMError: if an I/O or OS error occurs. + TypeError: if value type is not int or float. + + :type: int, float + """ + + def _get_enabled(self): + enabled = self._enable + + if enabled == "1": + return True + elif enabled == "0": + return False + + raise PWMError(None, "Unknown enabled value: \"%s\"" % enabled) + + def _set_enabled(self, value): + if not isinstance(value, bool): + raise TypeError("Invalid enabled type, should be string.") + self._enable = value + if not self._enable: + self._set_duty_cycle(0.0) + """Get or set the PWM's output enabled state. + + Raises: + PWMError: if an I/O or OS error occurs. + TypeError: if value type is not bool. + + :type: bool + """ + + # String representation + + def __str__(self): + return "PWM%d, pin %s (freq=%f Hz, duty_cycle=%f%%)" % \ + (self._pin, self._pin, self.frequency, self.duty_cycle * 100,) diff --git a/src/microcontroller/__init__.py b/src/microcontroller/__init__.py index 5550543..5d5f094 100755 --- a/src/microcontroller/__init__.py +++ b/src/microcontroller/__init__.py @@ -50,5 +50,7 @@ elif chip_id == ap_chip.APQ8016: from adafruit_blinka.microcontroller.snapdragon.apq8016.pin import * elif chip_id == ap_chip.IMX8MX: from adafruit_blinka.microcontroller.nxp_imx8m import * +elif chip_id == ap_chip.BINHO: + from adafruit_blinka.microcontroller.nova import * else: raise NotImplementedError("Microcontroller not supported:", chip_id) diff --git a/src/microcontroller/pin.py b/src/microcontroller/pin.py index b7298f3..d6e18d8 100755 --- a/src/microcontroller/pin.py +++ b/src/microcontroller/pin.py @@ -34,7 +34,7 @@ elif chip_id == ap_chip.IMX8MX: from adafruit_blinka.microcontroller.nxp_imx8m.pin import * elif chip_id == ap_chip.FT232H: from adafruit_blinka.microcontroller.ft232h.pin import * -elif chip_id == ap_chip.NOVA: +elif chip_id == ap_chip.BINHO: from adafruit_blinka.microcontroller.nova.pin import * else: raise NotImplementedError("Microcontroller not supported: ", chip_id) diff --git a/src/pulseio.py b/src/pulseio.py index bb8a04b..239a4ba 100644 --- a/src/pulseio.py +++ b/src/pulseio.py @@ -8,3 +8,5 @@ if detector.board.any_giant_board: from adafruit_blinka.microcontroller.generic_linux.sysfs_pwmout import PWMOut if detector.board.any_beaglebone: from adafruit_blinka.microcontroller.am335x.sysfs_pwmout import PWMOut +if detector.board.binho_nova: + from adafruit_blinka.microcontroller.nova.pwmout import PWMOut