From 07b6c14de5ead30144bc43e77847a7b1bf57c947 Mon Sep 17 00:00:00 2001 From: David Planella Date: Fri, 24 Nov 2017 14:30:00 +0100 Subject: [PATCH 1/7] Encoder: added sysfs module --- Adafruit_BBIO/sysfs.py | 116 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 Adafruit_BBIO/sysfs.py diff --git a/Adafruit_BBIO/sysfs.py b/Adafruit_BBIO/sysfs.py new file mode 100644 index 0000000..a79cfb9 --- /dev/null +++ b/Adafruit_BBIO/sysfs.py @@ -0,0 +1,116 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright (c) 2014 MIT OpenCourseWare +# +# 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. + +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() From 1fdedc59e1eebf17737f5706bc2eaa9e50d4a331 Mon Sep 17 00:00:00 2001 From: David Planella Date: Fri, 24 Nov 2017 14:32:21 +0100 Subject: [PATCH 2/7] Encoder: use sysfs to write QEP attributes --- Adafruit_BBIO/Encoder.py | 201 +++++++++++++++++++-------------------- 1 file changed, 97 insertions(+), 104 deletions(-) diff --git a/Adafruit_BBIO/Encoder.py b/Adafruit_BBIO/Encoder.py index 4c65c8a..35e3efb 100644 --- a/Adafruit_BBIO/Encoder.py +++ b/Adafruit_BBIO/Encoder.py @@ -4,6 +4,7 @@ import os import logging import itertools +import sysfs import platform if not platform.release().startswith('4.4'): @@ -57,12 +58,15 @@ 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 = sysfs.Node(sys_path) class RotaryEncoder(object): @@ -90,156 +94,145 @@ def config_pin(self, pin): self._run_cmd(["config-pin", pin, "qep"]) - def cat_file(self, path): - ''' - cat_file() - Print contents of file - ''' - - self._run_cmd(["cat", 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. - ''' + '''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. + + ''' + # Set up logging at the module level self._logger = logging.getLogger(__name__) self._logger.addHandler(logging.NullHandler()) - # Configure eqep module + # 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)) + # Configure the pins for the given channel self.config_pin(self._eqep.pin_A) self.config_pin(self._eqep.pin_B) - self.base_dir = self._eqep.sys_path self._logger.debug( - "RotaryEncoder(): self.base_dir: {0}".format(self.base_dir)) + "RotaryEncoder(): sys node: {0}".format(self._eqep.sys_path)) + # Enable the channel upon initialization self.enable() - def enable(self): - ''' - enable() - Turns the eQEP hardware ON + def _setEnable(self, value): + '''Turns the eQEP hardware ON or OFF + + value (int): 1 represents enabled, 0 is disabled ''' - 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') + if value < 0 or value > 1: + raise ValueError( + 'The "enabled" attribute can only be set to 0 or 1. ' + 'You attempted to set it to {}.'.format(value)) + + self._eqep.node.enabled = str(int(value)) + self._logger.info("Channel: {}, enabled: {}".format( + self._eqep.channel, self._eqep.node.enabled)) + + def enable(self): + '''Turns the eQEP hardware ON''' + + self._setEnable(1) def disable(self): + '''Turns the eQEP hardware OFF''' + + self._setEnable(0) + + def _setMode(self, value): + '''Sets the eQEP mode as absolute (0) or relative (1). + See the setAbsolute() and setRelative() methods for + more information. + ''' - 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') + if value < 0 or value > 1: + raise ValueError( + 'The "mode" attribute can only be set to 0 or 1. ' + 'You attempted to set it to {}.'.format(value)) + + self._eqep.node.mode = str(int(value)) + self._logger.debug("Mode set to: {}".format( + self._eqep.node.mode)) def setAbsolute(self): - ''' - setAbsolute() - Set mode as Absolute + '''Sets the eQEP 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') + self._setMode(0) def setRelative(self): - ''' - setRelative() - Set mode as Relative + '''Sets the eQEP 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._setMode(1) def getMode(self): + '''Returns the mode the eQEP hardware is in (absolute or relative). + ''' - 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) + + 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 def getPosition(self): - ''' - getPosition() - Get the current position of the encoder. + '''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. + ''' - 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) + position = self._eqep.node.position + + self._logger.debug("getPosition(): Channel {}, position: {}".format( + self._eqep.channel, position)) - return position + return int(position) def setFrequency(self, freq): + '''Sets the frequency in Hz at which the driver reports + new positions. + ''' - 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)) + ns_factor = 1000000000 + period = ns_factor/freq # Period in nanoseconds + self._eqep.node.period = str(period) 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)) + "setFrequency(): Channel {}, frequency: {} Hz, " + "period: {} ns".format( + self._eqep.channel, freq, period)) + + def setPosition(self, position): + '''Sets the current position to a new value''' + + position = int(position) + self._eqep.node.position = str(position) def zero(self): - ''' - zero()s - Set the current position to 0 - ''' + '''Resets the current position to 0''' + return self.setPosition(0) From c7e5c9416541a2bc8b85daeace11a7aebf7e75e4 Mon Sep 17 00:00:00 2001 From: David Planella Date: Tue, 28 Nov 2017 12:10:57 +0100 Subject: [PATCH 3/7] Encoder: convert get/set methods to properties, update apidoc strings --- Adafruit_BBIO/Encoder.py | 166 ++++++++++++++++++++++++++------------- 1 file changed, 113 insertions(+), 53 deletions(-) diff --git a/Adafruit_BBIO/Encoder.py b/Adafruit_BBIO/Encoder.py index 5c872d4..83c7a9f 100644 --- a/Adafruit_BBIO/Encoder.py +++ b/Adafruit_BBIO/Encoder.py @@ -14,11 +14,16 @@ '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', @@ -71,12 +76,31 @@ def __init__(self, channel, pin_A, pin_B, sys_path): class RotaryEncoder(object): + ''' + 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 + ''' 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. - ''' + ''' try: output = check_output(cmd, stderr=STDOUT) self._logger.info( @@ -87,11 +111,8 @@ def _run_cmd(self, cmd): "_run_cmd(): cmd='{}' return code={} output={}".format( " ".join(cmd), e.returncode, e.output)) - def config_pin(self, pin): - ''' - config_pin() - Config pin for QEP - ''' + def _config_pin(self, pin): + '''Configures a pin in QEP mode using the `config-pin` binary''' self._run_cmd(["config-pin", pin, "qep"]) @@ -103,8 +124,11 @@ def __init__(self, eqep_num): Allowed values: EQEP0, EQEP1, EQEP2 or EQEP2b, based on which pins the physical rotary encoder is connected to. - + ''' + # nanoseconds factor to convert period to frequency and back + self._NS_FACTOR = 1000000000 + # Set up logging at the module level self._logger = logging.getLogger(__name__) self._logger.addHandler(logging.NullHandler()) @@ -117,8 +141,8 @@ def __init__(self, eqep_num): self._eqep.sys_path)) # Configure the pins for the given channel - self.config_pin(self._eqep.pin_A) - self.config_pin(self._eqep.pin_B) + self._config_pin(self._eqep.pin_A) + self._config_pin(self._eqep.pin_B) self._logger.debug( "RotaryEncoder(): sys node: {0}".format(self._eqep.sys_path)) @@ -126,19 +150,32 @@ def __init__(self, eqep_num): # Enable the channel upon initialization self.enable() - def _setEnable(self, value): + @property + def enabled(self): + '''Returns the enabled status of the module: + + true: module is enabled + false: module is disabled + ''' + isEnabled = bool(int(self._eqep.node.enabled)) + + return isEnabled + + def _setEnable(self, enabled): '''Turns the eQEP hardware ON or OFF value (int): 1 represents enabled, 0 is disabled + ''' - if value < 0 or value > 1: + 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(value)) + 'You attempted to set it to {}.'.format(enabled)) - self._eqep.node.enabled = str(int(value)) + self._eqep.node.enabled = str(enabled) self._logger.info("Channel: {}, enabled: {}".format( - self._eqep.channel, self._eqep.node.enabled)) + self._eqep.channel, self._eqep.node.enabled)) def enable(self): '''Turns the eQEP hardware ON''' @@ -150,20 +187,41 @@ def disable(self): self._setEnable(0) - def _setMode(self, value): + @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. ''' - if value < 0 or value > 1: + 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(value)) + 'You attempted to set it to {}.'.format(mode)) - self._eqep.node.mode = str(int(value)) + self._eqep.node.mode = str(mode) self._logger.debug("Mode set to: {}".format( - self._eqep.node.mode)) + self._eqep.node.mode)) def setAbsolute(self): '''Sets the eQEP mode as Absolute: @@ -171,35 +229,17 @@ def setAbsolute(self): decremented by the encoder's movement ''' - self._setMode(0) + self.mode = 0 def setRelative(self): '''Sets the eQEP mode as Relative: The position is reset when the unit timer overflows. ''' - self._setMode(1) - - def getMode(self): - '''Returns the mode the eQEP hardware is in (absolute or relative). - - ''' + self.mode = 1 - 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 - - def getPosition(self): + @property + def position(self): '''Returns the current position of the encoder. In absolute mode, this attribute represents the current position of the encoder. @@ -209,31 +249,51 @@ def getPosition(self): ''' position = self._eqep.node.position - self._logger.debug("getPosition(): Channel {}, position: {}".format( + self._logger.debug("Get position: Channel {}, position: {}".format( self._eqep.channel, position)) return int(position) - def setFrequency(self, freq): + @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. ''' - ns_factor = 1000000000 - period = ns_factor/freq # Period in nanoseconds - self._eqep.node.period = str(period) + frequency = self._eqep.node.period / self._NS_FACTOR + self._logger.debug( - "setFrequency(): Channel {}, frequency: {} Hz, " + "Set frequency(): Channel {}, frequency: {} Hz, " "period: {} ns".format( - self._eqep.channel, freq, period)) + self._eqep.channel, frequency, period)) - def setPosition(self, position): - '''Sets the current position to a new value''' + return frequency - position = int(position) - self._eqep.node.position = str(position) + @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''' - return self.setPosition(0) + self.position = 0 From 6c04e3f63a92e513c9361eb00b0050ab25d56d13 Mon Sep 17 00:00:00 2001 From: David Planella Date: Tue, 28 Nov 2017 12:30:22 +0100 Subject: [PATCH 4/7] Encoder: updated README --- Adafruit_BBIO/README.md | 60 ++++++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/Adafruit_BBIO/README.md b/Adafruit_BBIO/README.md index aa3642b..000b6a8 100644 --- a/Adafruit_BBIO/README.md +++ b/Adafruit_BBIO/README.md @@ -1,52 +1,47 @@ -# 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. - -Initially based on the [PyBBIO](https://github.com/graycatlabs/PyBBIO/bbio/libraries/RotaryEncoder/rotary_encoder.py) rotary encoder code. +This module enables access to the Beaglebone Black enhanced Quadrature Encoder Pulse (eQEP) modules: eQEP0, eQEP1 and eQEP2/eQEP2b. ## Prerequisites -These instructions are based on a 4.4.x Linux kernel. - -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 - -``` -enable_uboot_cape_universal=1 -``` +These instructions are based on: -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). +- 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 -### Install/upgrade the latest Device Tree overlays +It's recommended to run the following command to ensure you have the latest required packages: ``` -sudo apt-get upgrade bb-cape-overlays +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. + +### Enabling additional eQEP modules -- 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. +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. - ``` - cmdline=cape_universal=enable # Plus some other options - ``` - ``` - cape_enable=bone_capemgr.enable_partno=cape-universala - ``` -- TBD: check the overlays that are currently loaded +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 +86,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) From 48f35d6c4a19a5b3915a076ae3f12d759a80bb15 Mon Sep 17 00:00:00 2001 From: David Planella Date: Tue, 28 Nov 2017 12:31:22 +0100 Subject: [PATCH 5/7] Encoder: add README apt install clarification --- Adafruit_BBIO/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Adafruit_BBIO/README.md b/Adafruit_BBIO/README.md index 000b6a8..d58a9b7 100644 --- a/Adafruit_BBIO/README.md +++ b/Adafruit_BBIO/README.md @@ -10,9 +10,10 @@ These instructions are based on: - `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 command to ensure you have the latest required packages: +It's recommended to run the following commands to ensure you have the latest required packages: ``` +sudo apt update sudo apt upgrade bb-cape-overlays bb-customizations ``` From 9c08cda98f6b4b43af9afa1646276845fe4e6649 Mon Sep 17 00:00:00 2001 From: David Planella Date: Tue, 28 Nov 2017 12:39:49 +0100 Subject: [PATCH 6/7] Encoder: copyright assignment note, updated comments --- Adafruit_BBIO/sysfs.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Adafruit_BBIO/sysfs.py b/Adafruit_BBIO/sysfs.py index a79cfb9..e6802f0 100644 --- a/Adafruit_BBIO/sysfs.py +++ b/Adafruit_BBIO/sysfs.py @@ -2,6 +2,8 @@ # -*- coding: utf-8 -*- # Copyright (c) 2014 MIT OpenCourseWare +# TODO: the copyright assignment above is boiler plate. Copyright needs to be +# properly assigned. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -32,7 +34,8 @@ # - David Planella """ -Simplistic Python SysFS interface. +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 From b23a861de38e2e3ceb71456601dc4efad85abb8e Mon Sep 17 00:00:00 2001 From: David Planella Date: Tue, 28 Nov 2017 12:46:51 +0100 Subject: [PATCH 7/7] Encoder: added usage notes --- Adafruit_BBIO/README.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/Adafruit_BBIO/README.md b/Adafruit_BBIO/README.md index d58a9b7..8bb4d7d 100644 --- a/Adafruit_BBIO/README.md +++ b/Adafruit_BBIO/README.md @@ -2,6 +2,30 @@ This module enables access to the Beaglebone Black enhanced Quadrature Encoder Pulse (eQEP) modules: eQEP0, eQEP1 and eQEP2/eQEP2b. +## Usage + +On a recent Beaglebone Debian image, access to the eQEP0 and eQEP2 channels should work out of the box: + +```python +import Adafruit_BBIO.Encoder as Encoder + +''' +Each channel can be accessed and initialized using its corresponding +channel name constants: + + 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) +``` + +If you need to use further channels, read on the prerequisites in the following section. + ## Prerequisites These instructions are based on: pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy