diff --git a/Adafruit_BBIO/Encoder.py b/Adafruit_BBIO/Encoder.py index e978c90..0e94fa6 100644 --- a/Adafruit_BBIO/Encoder.py +++ b/Adafruit_BBIO/Encoder.py @@ -4,12 +4,26 @@ import os import logging import itertools +from .sysfs import Node +import platform +(major, minor, patch) = platform.release().split("-")[0].split(".") +if not (int(major) >= 4 and int(minor) >= 4): + raise ImportError( + 'The Encoder module requires Linux kernel version >= 4.4.x.\n' + 'Please upgrade your kernel to use this module.\n' + 'Your Linux kernel version is {}.'.format(platform.release())) + + +# eQEP module channel identifiers +# eQEP 2 and 2b are the same channel, exposed on two different sets of pins, +# which are mutually exclusive eQEP0 = 0 eQEP1 = 1 eQEP2 = 2 eQEP2b = 3 +# Definitions to initialize the eQEP modules _OCP_PATH = "/sys/devices/platform/ocp" _eQEP_DEFS = [ {'channel': 'eQEP0', 'pin_A': 'P9_92', 'pin_B': 'P9_27', @@ -33,7 +47,7 @@ def fromdict(cls, d): '''Creates a class instance from a dictionary''' allowed = ('channel', 'pin_A', 'pin_B', 'sys_path') - df = {k: v for k, v in d.iteritems() if k in allowed} + df = {k: v for k, v in d.items() if k in allowed} return cls(**df) def __init__(self, channel, pin_A, pin_B, sys_path): @@ -50,203 +64,236 @@ def __init__(self, channel, pin_A, pin_B, sys_path): rotary encoder sys_path (str): sys filesystem path to access the attributes of this eQEP module + node (str): sys filesystem device node that contains the + readable or writable attributes to control the QEP channel ''' self.channel = channel self.pin_A = pin_A self.pin_B = pin_B self.sys_path = sys_path + self.node = Node(sys_path) class RotaryEncoder(object): - - def _run_cmd(self, cmd): - '''Runs a command. If not successful (i.e. error code different than zero), - print the stderr output as a warning. + ''' + Rotary encoder class abstraction to control a given QEP channel. + + Constructor: + eqep_num: QEP object that determines which channel to control + + Properties: + position: current position of the encoder + frequency: frequency at which the encoder reports new positions + enabled: (read only) true if the module is enabled, false otherwise + mode: current mode of the encoder (absolute: 0, relative: 1) + + Methods: + enable: enable the QEP channel + disable: disable the QEP channel + setAbsolute: shortcut for setting the mode to absolute + setRelative: shortcut for setting the mode to relative + zero: shortcut for setting the position to 0 ''' - try: - output = check_output(cmd, stderr=STDOUT) - self._logger.info("_run_cmd(): cmd='{}' return code={} output={}".format( - " ".join(cmd), 0, output)) - except CalledProcessError as e: - self._logger.warning( - "_run_cmd(): cmd='{}' return code={} output={}".format( - " ".join(cmd), e.returncode, e.output)) + def _run_cmd(self, cmd): + '''Runs a command. If not successful (i.e. error code different than + zero), print the stderr output as a warning. - def config_pin(self, pin): - ''' - config_pin() - Config pin for QEP - ''' + ''' + try: + output = check_output(cmd, stderr=STDOUT) + self._logger.info( + "_run_cmd(): cmd='{}' return code={} output={}".format( + " ".join(cmd), 0, output)) + except CalledProcessError as e: + self._logger.warning( + "_run_cmd(): cmd='{}' return code={} output={}".format( + " ".join(cmd), e.returncode, e.output)) + + def _config_pin(self, pin): + '''Configures a pin in QEP mode using the `config-pin` binary''' + + self._run_cmd(["config-pin", pin, "qep"]) + + def __init__(self, eqep_num): + '''Creates an instance of the class RotaryEncoder. + + Arguments: + eqep_num: determines which eQEP pins are set up. + Allowed values: EQEP0, EQEP1, EQEP2 or EQEP2b, + based on which pins the physical rotary encoder + is connected to. - self._run_cmd(["config-pin", pin, "qep"]) + ''' + # nanoseconds factor to convert period to frequency and back + self._NS_FACTOR = 1000000000 - def cat_file(self, path): - ''' - cat_file() - Print contents of file - ''' + # Set up logging at the module level + self._logger = logging.getLogger(__name__) + self._logger.addHandler(logging.NullHandler()) - self._run_cmd(["cat", path]) + # Initialize the eQEP channel structures + self._eqep = eQEP.fromdict(_eQEP_DEFS[eqep_num]) + self._logger.info( + "Configuring: {}, pin A: {}, pin B: {}, sys path: {}".format( + self._eqep.channel, self._eqep.pin_A, self._eqep.pin_B, + self._eqep.sys_path)) - def __init__(self, eqep_num): - ''' - RotaryEncoder(eqep_num) - Creates an instance of the class RotaryEncoder. - eqep_num determines which eQEP pins are set up. - eqep_num can be: EQEP0, EQEP1, EQEP2 or EQEP2b based on which pins \ - the rotary encoder is connected to. - ''' + # Configure the pins for the given channel + self._config_pin(self._eqep.pin_A) + self._config_pin(self._eqep.pin_B) - self._logger = logging.getLogger(__name__) - self._logger.addHandler(logging.NullHandler()) + self._logger.debug( + "RotaryEncoder(): sys node: {0}".format(self._eqep.sys_path)) - # Configure eqep module - self._eqep = eQEP.fromdict(_eQEP_DEFS[eqep_num]) - self._logger.info( - "Configuring: {}, pin A: {}, pin B: {}, sys path: {}".format( - self._eqep.channel, self._eqep.pin_A, self._eqep.pin_B, - self._eqep.sys_path)) + # Enable the channel upon initialization + self.enable() - self.config_pin(self._eqep.pin_A) - self.config_pin(self._eqep.pin_B) + @property + def enabled(self): + '''Returns the enabled status of the module: - self.base_dir = self._eqep.sys_path - self._logger.debug( - "RotaryEncoder(): self.base_dir: {0}".format(self.base_dir)) + true: module is enabled + false: module is disabled + ''' + isEnabled = bool(int(self._eqep.node.enabled)) - self.enable() + return isEnabled - def enable(self): - ''' - enable() - Turns the eQEP hardware ON - ''' - enable_file = "%s/enabled" % self.base_dir - self._logger.debug("enable(): enable_file: {0}".format(enable_file)) - self._logger.warning( - "enable(): TODO: not implemented, write 1 to {}".format(enable_file)) - #return sysfs.kernelFileIO(enable_file, '1') + def _setEnable(self, enabled): + '''Turns the eQEP hardware ON or OFF - def disable(self): - ''' - disable() - Turns the eQEP hardware OFF - ''' - enable_file = "%s/enabled" % self.base_dir - self._logger.debug("disable(): enable_file: {0}".format(enable_file)) - self._logger.warning( - "disable(): TODO: not implemented, write 0 to {}".format(enable_file)) - #return sysfs.kernelFileIO(enable_file, '0') + value (int): 1 represents enabled, 0 is disabled - def setAbsolute(self): - ''' - setAbsolute() - Set mode as Absolute - The position starts at zero and is incremented or - decremented by the encoder's movement - ''' - mode_file = "%s/mode" % self.base_dir - self._logger.debug("setAbsolute(): mode_file: {0}".format(mode_file)) - self._logger.warning( - "setAbsolute(): TODO: not implemented, write 0 to {}".format(mode_file)) - #return sysfs.kernelFileIO(mode_file, '0') + ''' + enabled = int(enabled) + if enabled < 0 or enabled > 1: + raise ValueError( + 'The "enabled" attribute can only be set to 0 or 1. ' + 'You attempted to set it to {}.'.format(enabled)) - def setRelative(self): - ''' - setRelative() - Set mode as Relative - The position is reset when the unit timer overflows. - ''' - mode_file = "%s/mode" % self.base_dir - self._logger.debug("setRelative(): mode_file: {0}".format(mode_file)) - self._logger.warning( - "setRelative(): TODO: not implemented, write 1 to {}".format(mode_file)) - #return sysfs.kernelFileIO(mode_file, '1') + self._eqep.node.enabled = str(enabled) + self._logger.info("Channel: {}, enabled: {}".format( + self._eqep.channel, self._eqep.node.enabled)) - def getMode(self): - ''' - getMode() - Returns the mode the eQEP hardware is in. - ''' - mode_file = "%s/mode" % self.base_dir - self._logger.debug("getMode(): mode_file: {0}".format(mode_file)) - self._logger.warning("getMode(): TODO: read mode_file") - #return sysfs.kernelFileIO(mode_file) + def enable(self): + '''Turns the eQEP hardware ON''' - def getPosition(self): - ''' - getPosition() - Get the current position of the encoder. - In absolute mode, this attribute represents the current position - of the encoder. - In relative mode, this attribute represents the position of the - encoder at the last unit timer overflow. - ''' - self._logger.debug("Channel: {}".format(self._eqep.channel)) - position_file = "%s/position" % self.base_dir - self._logger.debug("getPosition(): position_file: {0}".format(position_file)) - position_handle = open(position_file, 'r') - self._logger.debug("getPosition(): position_handle: {0}".format(position_handle)) - position = position_handle.read() - self._logger.debug("getPosition(): position: {0}".format(position)) - #return sysfs.kernelFileIO(position_file) - - return position - - def setFrequency(self, freq): - ''' - setFrequency(freq) - Set the frequency in Hz at which the driver reports new positions. - ''' - period_file = "%s/period" % self.base_dir - self._logger.debug("setFrequency(): period_file: {0}".format(period_file)) - self._logger.debug("setFrequency(): freq: {0}".format(freq)) - self._logger.debug("setFrequency(): 1000000000/freq: {0}".format(1000000000/freq)) - self._logger.debug("setFrequency(): str(1000000000/freq)): {0}".format(str(1000000000/freq))) - self._logger.warning( - "setFrequency(): TODO: not implemented, set {} to {}".format( - period_file, str(1000000000/freq))) - #return sysfs.kernelFileIO(period_file, str(1000000000/freq)) - - def setPosition(self, val): - ''' - setPosition(value) - Give a new value to the current position - ''' - position_file = "%s/position" % self.base_dir - self._logger.warning( - "setPosition(): TODO: not implemented, write position to {}".format( - position_file)) - #return sysfs.kernelFileIO(position_file, str(val)) + self._setEnable(1) - def zero(self): - ''' - zero()s - Set the current position to 0 - ''' - return self.setPosition(0) - - -#""" -# encoder_test.py -# Rekha Seethamraju -# An example to demonstrate the use of the eQEP library -# for PyBBIO. -# This example program is in the public domain. -#""" -#from bbio import * -#from bbio.libraries.RotaryEncoder import RotaryEncoder -# -#encoder = RotaryEncoder(RotaryEncoder.EQEP2b) -# -#def setup(): -# encoder.setAbsolute() -# encoder.zero() -# -#def loop(): -# print("encoder position : "+encoder.getPosition()) -# delay(1000) -# -#run(setup, loop) + def disable(self): + '''Turns the eQEP hardware OFF''' + + self._setEnable(0) + + @property + def mode(self): + '''Returns the mode the eQEP hardware is in (absolute or relative). + + ''' + mode = int(self._eqep.node.mode) + + if mode == 0: + mode_name = "absolute" + elif mode == 1: + mode_name = "relative" + else: + mode_name = "invalid" + + self._logger.debug("getMode(): Channel {}, mode: {} ({})".format( + self._eqep.channel, mode, mode_name)) + + return mode + + @mode.setter + def mode(self, mode): + '''Sets the eQEP mode as absolute (0) or relative (1). + See the setAbsolute() and setRelative() methods for + more information. + + ''' + mode = int(mode) + if mode < 0 or mode > 1: + raise ValueError( + 'The "mode" attribute can only be set to 0 or 1. ' + 'You attempted to set it to {}.'.format(mode)) + + self._eqep.node.mode = str(mode) + self._logger.debug("Mode set to: {}".format( + self._eqep.node.mode)) + + def setAbsolute(self): + '''Sets the eQEP mode as Absolute: + The position starts at zero and is incremented or + decremented by the encoder's movement + + ''' + self.mode = 0 + + def setRelative(self): + '''Sets the eQEP mode as Relative: + The position is reset when the unit timer overflows. + + ''' + self.mode = 1 + + @property + def position(self): + '''Returns the current position of the encoder. + In absolute mode, this attribute represents the current position + of the encoder. + In relative mode, this attribute represents the position of the + encoder at the last unit timer overflow. + + ''' + position = self._eqep.node.position + + self._logger.debug("Get position: Channel {}, position: {}".format( + self._eqep.channel, position)) + + return int(position) + + @position.setter + def position(self, position): + '''Sets the current position to a new value''' + + position = int(position) + self._eqep.node.position = str(position) + + self._logger.debug("Set position: Channel {}, position: {}".format( + self._eqep.channel, position)) + + + @property + def frequency(self): + '''Sets the frequency in Hz at which the driver reports + new positions. + + ''' + frequency = self._eqep.node.period / self._NS_FACTOR + + self._logger.debug( + "Set frequency(): Channel {}, frequency: {} Hz, " + "period: {} ns".format( + self._eqep.channel, frequency, period)) + + return frequency + + @frequency.setter + def frequency(self, frequency): + '''Sets the frequency in Hz at which the driver reports + new positions. + + ''' + period = self._NS_FACTOR / frequency # Period in nanoseconds + self._eqep.node.period = str(period) + self._logger.debug( + "Set frequency(): Channel {}, frequency: {} Hz, " + "period: {} ns".format( + self._eqep.channel, frequency, period)) + + def zero(self): + '''Resets the current position to 0''' + + self.position = 0 diff --git a/Adafruit_BBIO/README.md b/Adafruit_BBIO/README.md index aa3642b..8bb4d7d 100644 --- a/Adafruit_BBIO/README.md +++ b/Adafruit_BBIO/README.md @@ -1,52 +1,72 @@ -# Adafruit_BBIO.Encoder module +# Adafruit_BBIO.Encoder -This module enables access to the Beaglebone Black enhanced Quadrature Encoder Pulse (eQEP) modules: eQEP0, eQEP1 and eQEP2. +This module enables access to the Beaglebone Black enhanced Quadrature Encoder Pulse (eQEP) modules: eQEP0, eQEP1 and eQEP2/eQEP2b. -Initially based on the [PyBBIO](https://github.com/graycatlabs/PyBBIO/bbio/libraries/RotaryEncoder/rotary_encoder.py) rotary encoder code. +## Usage -## Prerequisites +On a recent Beaglebone Debian image, access to the eQEP0 and eQEP2 channels should work out of the box: -These instructions are based on a 4.4.x Linux kernel. +```python +import Adafruit_BBIO.Encoder as Encoder -In order to use all eQEP pins the BeagleBone must boot with the [cape-universal](https://github.com/beagleboard/bb.org-overlays/tree/master/tools/beaglebone-universal-io) enabled, and load the cape-universal overlay +''' +Each channel can be accessed and initialized using its corresponding +channel name constants: -``` -enable_uboot_cape_universal=1 + Encoder.eQEP0 + Encoder.eQEP1 # Pins only available when video is disabled + Encoder.eQEP2 + Encoder.eQEP2b # Pins only available when video is disabled +''' + +# Instantiate the class to access channel eQEP2, and only initialize +# that channel +myEncoder = Encoder.RotaryEncoder(Encoder.eQEP2) ``` -Notes: -- It seems that the `cape-universal` cape _does only enable access to eQEP0 and eQEP2_. TBD: check how to load [`cape-universala`](https://github.com/cdsteinkuehler/beaglebone-universal-io/pull/30) -- An alternative option to the `cape-universal` overlay would be to load one of the [dedicated eQEP overlays](https://github.com/Teknoman117/beaglebot/tree/master/encoders/dts). +If you need to use further channels, read on the prerequisites in the following section. -### Install/upgrade the latest Device Tree overlays +## Prerequisites + +These instructions are based on: + +- Linux kernel: 4.4.x or later +- `bb-cape-overlays` package: version 4.4.20171120.0-0rcnee1~stretch+20171120 or later +- `bb-customizations` package: version 1.20171123-0rcnee0~stretch+20171123 or later + +It's recommended to run the following commands to ensure you have the latest required packages: ``` -sudo apt-get upgrade bb-cape-overlays +sudo apt update +sudo apt upgrade bb-cape-overlays bb-customizations ``` -### Load the universal cape +In order to use all eQEP pins the BeagleBone must boot with the [cape-universal](https://github.com/beagleboard/bb.org-overlays/tree/master/tools/beaglebone-universal-io) enabled, and load the `cape-universal` overlay. -If it doesn't already contain it, modify the `/boot/uEnv.txt` file to contain this line: +This is the default, thus **no further steps are initially required to use eQEP0 and eQEP2**. Simply double-check that the following line is present and not commented out on your `/boot/uEnv.txt` file: ``` enable_uboot_cape_universal=1 ``` -Notes: +Note: Some older documentation recommends using the `cmdline` and `cape_enable` options instead. They are meant to load deprecated kernel-based overlays and it's not recommended to use them. Use the new way of [loading overlays via uboot](https://elinux.org/Beagleboard:BeagleBoneBlack_Debian#U-Boot_Overlays) instead, as instructed above. -- Some older documentation recommends using these two lines instead. They are meant to load deprecated kernel-based overlays and it's not recommended to use them. Use the new way of [loading overlays via uboot](https://elinux.org/Beagleboard:BeagleBoneBlack_Debian#U-Boot_Overlays) instead, as instructed above. +### Enabling additional eQEP modules - ``` - cmdline=cape_universal=enable # Plus some other options - ``` - ``` - cape_enable=bone_capemgr.enable_partno=cape-universala - ``` -- TBD: check the overlays that are currently loaded +The `cape-universal` overlay will enable access to the eQEP0 and eQEP2 modules. As it does not expose pins that are shared with the HDMI interface, eQEP1 and eQEP2b will **not** be available. + +To disable the HDMI interface and gain access to the pins and peripherals that share its pins, comment out the following line on the `/boot/uEnv.txt` file and reboot: + +``` +disable_uboot_overlay_video=1 +``` ## eQEP configuraton -Note: if either eQEP1 or eQEP2b are used on the Beaglebone Black, video must be disabled, as their pins are shared with the LCD_DATAx lines of the HDMI interface. +Notes: + +- If either eQEP1 or eQEP2b are used on the Beaglebone Black, video must be disabled, as their pins are shared with the LCD_DATAx lines of the HDMI interface. +- eQEP2 and eQEP2b are the same module, but with the alternative of accessing it via two sets of pins. These are mutually exclusive. ### eQEP0 @@ -91,7 +111,12 @@ $ config-pin P8.41 qep $ config-pin P8.42 qep $ cat /sys/devices/platform/ocp/48304000.epwmss/48304180.eqep/position ``` + +## Credits + +Initially based on the [PyBBIO](https://github.com/graycatlabs/PyBBIO/bbio/libraries/RotaryEncoder/rotary_encoder.py) rotary encoder code. + ## Further reading -- [Beaglebone encoder inputs](https://github.com/Teknoman117/beaglebot/tree/master/encoders) -- [Beaglebone eQEP overlays](https://github.com/Teknoman117/beaglebot/tree/master/encoders/dts) +1. [Beaglebone encoder inputs](https://github.com/Teknoman117/beaglebot/tree/master/encoders) +1. [Beaglebone eQEP overlays](https://github.com/Teknoman117/beaglebot/tree/master/encoders/dts) diff --git a/Adafruit_BBIO/sysfs.py b/Adafruit_BBIO/sysfs.py new file mode 100644 index 0000000..43fd078 --- /dev/null +++ b/Adafruit_BBIO/sysfs.py @@ -0,0 +1,118 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright (c) 2014 MIT OpenCourseWare +# Copyright (c) 2017 Adafruit Industries +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# +# Code originally published at http://stackoverflow.com/questions/4648792/ and +# subsequently forked at https://github.com/ponycloud/python-sysfs +# +# Original author: Benedikt Reinartz +# Contributors: +# - Jan Dvořák +# - Jonathon Reinhart https://github.com/JonathonReinhart +# - Ondřej Koch +# - David Planella + +""" +Simplistic Python SysFS interface. It enables access to the sys filesystem device +nodes and to get and set their exposed attributes. + +Usage examples:: + from sysfs import sys + + # Print all block devices in /sys, with their sizes + for block_dev in sys.block: + print block_dev, str(int(block_dev.size) / 1048576) + ' M' + + >>> import sysfs + >>> # Read/write Beaglebone Black's eQEP module attributes + >>> eqep0 = sysfs.Node("/sys/devices/platform/ocp/48300000.epwmss/48300180.eqep") + >>> # Read eqep attributes + >>> eqep0.enabled + '1' + >>> eqep0.mode + '0' + >>> eqep0.period + '1000000000' + >>> eqep0.position + '0' + >>> # Write eqep attributes. They should be strings. + >>> eqep0.position = str(2) + >>> eqep0.position + '2' +""" + +from os import listdir +from os.path import isdir, isfile, join, realpath, basename + +__all__ = ['sys', 'Node'] + + +class Node(object): + __slots__ = ['_path_', '__dict__'] + + def __init__(self, path='/sys'): + self._path_ = realpath(path) + if not self._path_.startswith('/sys/') and not '/sys' == self._path_: + raise RuntimeError('Using this on non-sysfs files is dangerous!') + + self.__dict__.update(dict.fromkeys(listdir(self._path_))) + + def __repr__(self): + return '' % self._path_ + + def __str__(self): + return basename(self._path_) + + def __setattr__(self, name, val): + if name.startswith('_'): + return object.__setattr__(self, name, val) + + path = realpath(join(self._path_, name)) + if isfile(path): + with open(path, 'w') as fp: + fp.write(val) + else: + raise RuntimeError('Cannot write to non-files.') + + def __getattribute__(self, name): + if name.startswith('_'): + return object.__getattribute__(self, name) + + path = realpath(join(self._path_, name)) + if isfile(path): + with open(path, 'r') as fp: + return fp.read().strip() + elif isdir(path): + return Node(path) + + def __setitem__(self, name, val): + return setattr(self, name, val) + + def __getitem__(self, name): + return getattr(self, name) + + def __iter__(self): + return iter(getattr(self, name) for name in listdir(self._path_)) + + +sys = Node() diff --git a/README.md b/README.md index 32da985..6445bcf 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,22 @@ sudo pip install --upgrade Adafruit_BBIO Using the library is very similar to the excellent RPi.GPIO library used on the Raspberry Pi. Below are some examples. +### Pin Numbers + +Please note that there is no '0' prefix for the pin numbers. For example, pin 7 on header P8 is `P8_7`. + +**Correct:** +``` +GPIO.setup("P8_7", OUT ) +``` + +**INCORRECT:** +``` +GPIO.setup("P8_07", OUT ) +``` + +Refer to `pins_t table[]` in [common.c](https://github.com/adafruit/adafruit-beaglebone-io-python/blob/master/source/common.c#L73) all the pin labels. + ### config-pin [config-pin](https://github.com/beagleboard/bb.org-overlays/tree/master/tools/beaglebone-universal-io) is now used on the official BeagleBoard.org Debian Jessie and Stretch images to control pin mode (e.g. pin mux). diff --git a/install_all_python_versions.sh b/install_all_python_versions.sh new file mode 100755 index 0000000..932c462 --- /dev/null +++ b/install_all_python_versions.sh @@ -0,0 +1,9 @@ +#!/bin/bash +# useful for testing changes against all versions of python +make clean +echo "Install Python 2.7" +python2.7 ./setup.py install +echo "Install Python 3.5" +python3.5 ./setup.py install +echo "Install Python 3.6" +python3.6 ./setup.py install diff --git a/pytest_all_versions.sh b/pytest_all_versions.sh new file mode 100755 index 0000000..dca6919 --- /dev/null +++ b/pytest_all_versions.sh @@ -0,0 +1,11 @@ +#!/bin/bash +# useful for testing changes against all versions of python + +cd test +echo "Testing Python 2.7" +python2.7 -mpytest +echo "Testing Python 3.5" +python3.5 -mpytest +echo "Testing Python 3.6" +python3.6 -mpytest +cd .. diff --git a/source/py_pwm.c b/source/py_pwm.c index 49c539b..55f50f9 100644 --- a/source/py_pwm.c +++ b/source/py_pwm.c @@ -217,8 +217,8 @@ static const char moduledocstring[] = "PWM functionality of a BeagleBone using P PyMethodDef pwm_methods[] = { {"start", (PyCFunction)py_start_channel, METH_VARARGS | METH_KEYWORDS, "Set up and start the PWM channel. channel can be in the form of 'P8_10', or 'EHRPWM2A'"}, {"stop", (PyCFunction)py_stop_channel, METH_VARARGS | METH_KEYWORDS, "Stop the PWM channel. channel can be in the form of 'P8_10', or 'EHRPWM2A'"}, - { "set_duty_cycle", (PyCFunction)py_set_duty_cycle, METH_VARARGS, "Change the duty cycle\ndutycycle - between 0.0 and 100.0" }, - { "set_frequency", (PyCFunction)py_set_frequency, METH_VARARGS, "Change the frequency\nfrequency - frequency in Hz (freq > 0.0)" }, + { "set_duty_cycle", (PyCFunction)py_set_duty_cycle, METH_VARARGS | METH_KEYWORDS, "Change the duty cycle\ndutycycle - between 0.0 and 100.0" }, + { "set_frequency", (PyCFunction)py_set_frequency, METH_VARARGS | METH_KEYWORDS, "Change the frequency\nfrequency - frequency in Hz (freq > 0.0)" }, {"cleanup", py_cleanup, METH_VARARGS, "Clean up by resetting all GPIO channels that have been used by this program to INPUT with no pullup/pulldown and no event detection"}, //{"setwarnings", py_setwarnings, METH_VARARGS, "Enable or disable warning messages"}, {NULL, NULL, 0, NULL} diff --git a/test/test_rotary.py b/test/test_rotary.py deleted file mode 100755 index 2292cd0..0000000 --- a/test/test_rotary.py +++ /dev/null @@ -1,74 +0,0 @@ -#!/usr/bin/python -# -# BeagleBone must boot with cape-universal enabled -# and load the cape-universala overlay in order to -# use all the eQEP pins -# -# Install the latest Device Tree overlays: -# ======================================== -# sudo apt-get upgrade bb-cape-overlays -# -# File: /boot/uEnv.txt -# ==================== -# uname_r=4.4.62-ti-r99 -# cmdline=coherent_pool=1M quiet cape_universal=enable -# cape_enable=bone_capemgr.enable_partno=cape-universala -# -# File: /sys/devices/platform/bone_capemgr/slots -# ============================================== -# 0: PF---- -1 -# 1: PF---- -1 -# 2: PF---- -1 -# 3: PF---- -1 -# 4: P-O-L- 0 Override Board Name,00A0,Override Manuf,cape-universala -# -# eqep0: P9_27, P9_92 -# =================== -# config-pin P9_27 qep -# config-pin P9_92 qep # alias for P9_42.1 -# cat /sys/devices/platform/ocp/48300000.epwmss/48300180.eqep/position -# -# eqep1: P8.33, P8.35 -# =================== -# config-pin P8.33 qep -# config-pin P8.35 qep -# cat /sys/devices/platform/ocp/48302000.epwmss/48302180.eqep/position -# -# eqep2: P8.11, P8.12 -# =================== -# config-pin P8.11 qep -# config-pin P8.12 qep -# cat /sys/devices/platform/ocp/48304000.epwmss/48304180.eqep/position -# -# alternate pins for eqep2 (mutually exclusive) -# eqep2b: P8.41, P8.42 -# ==================== -# config-pin P8.41 qep -# config-pin P8.42 qep -# cat /sys/devices/platform/ocp/48304000.epwmss/48304180.eqep/position -# -# -# How To Run This Test: -# debian@beaglebone:~/ssh/adafruit-beaglebone-io-python$ sudo python ./setup.py install &> /dev/null && sudo python ./test/test_rotary.py -# -# - -import Adafruit_BBIO.Encoder as Encoder - -qep = Encoder.RotaryEncoder(0) -print("qep.getPosition(): {0}".format(qep.getPosition())) - -qep = Encoder.RotaryEncoder(1) -print("qep.getPosition(): {0}".format(qep.getPosition())) - -qep = Encoder.RotaryEncoder(2) -print("qep.getPosition(): {0}".format(qep.getPosition())) - - -#qep.getMode() -#qep.setAbsolute() -#qep.setRelative() -#qep.setFrequency(5000) -#qep.setPosition(100) -#qep.disable() -#print("qep.enable(): {0}".format(qep.enable()))