diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 6344b2f..2916315 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,47 +1,13 @@ -Thank you for opening an issue on an Adafruit Python library repository. To -improve the speed of resolution please review the following guidelines and -common troubleshooting steps below before creating the issue: +Thank you for opening an issue on the Adafruit BeagleBone Python library repository. -- **Do not use GitHub issues for troubleshooting projects and issues.** Instead use - the forums at http://forums.adafruit.com to ask questions and troubleshoot why - something isn't working as expected. In many cases the problem is a common issue - that you will more quickly receive help from the forum community. GitHub issues - are meant for known defects in the code. If you don't know if there is a defect - in the code then start with troubleshooting on the forum first. -- **If following a tutorial or guide be sure you didn't miss a step.** Carefully - check all of the steps and commands to run have been followed. Consult the - forum if you're unsure or have questions about steps in a guide/tutorial. +In order to understand your system configuration better, please run: +``` +sudo /opt/scripts/tools/version.sh +``` -- **For Python/Raspberry Pi projects check these very common issues to ensure they don't apply**: +and paste the output in a reply. - - If you are receiving an **ImportError: No module named...** error then a - library the code depends on is not installed. Check the tutorial/guide or - README to ensure you have installed the necessary libraries. Usually the - missing library can be installed with the `pip` tool, but check the tutorial/guide - for the exact command. +This script should be present for any image downloaded from: +https://beagleboard.org/ or https://rcn-ee.com/ - - **Be sure you are supplying adequate power to the board.** Check the specs of - your board and power in an external power supply. In many cases just - plugging a board into your computer is not enough to power it and other - peripherals. - - - **Double check all soldering joints and connections.** Flakey connections - cause many mysterious problems. See the [guide to excellent soldering](https://learn.adafruit.com/adafruit-guide-excellent-soldering/tools) for examples of good solder joints. - -If you're sure this issue is a defect in the code and checked the steps above -please fill in the following fields to provide enough troubleshooting information. -You may delete the guideline and text above to just leave the following details: - -- Platform/operating system (i.e. Raspberry Pi with Raspbian operating system, - Windows 32-bit, Windows 64-bit, Mac OSX 64-bit, etc.): **INSERT PLATFORM/OPERATING - SYSTEM HERE** - -- Python version (run `python -version` or `python3 -version`): **INSERT PYTHON - VERSION HERE** - -- Error message you are receiving, including any Python exception traces: **INSERT - ERROR MESAGE/EXCEPTION TRACES HERE*** - -- List the steps to reproduce the problem below (if possible attach code or commands - to run): **LIST REPRO STEPS BELOW** diff --git a/.gitignore b/.gitignore index e9063bf..06eca82 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,31 @@ nosetests.xml # Annoying MacOSX files. .DS_Store ._.DS_Store + +# autotools +Makefile.in +aclocal.m4 +autom4te.cache/ +compile +configure +config.guess +config.h.in +config.sub +depcomp +install-sh +ltmain.sh +m4/libtool.m4 +m4/ltoptions.m4 +m4/ltsugar.m4 +m4/ltversion.m4 +m4/lt~obsolete.m4 +missing +source/Makefile.in +test-driver +docs/_build +docs/_static +docs/_templates + +# vim temp files +*~ +*.swp diff --git a/.travis.yml b/.travis.yml index df3bb3e..f515d75 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,6 @@ language: python +python: + - "3.6" install: - pip install tox script: diff --git a/Adafruit_BBIO/Encoder.py b/Adafruit_BBIO/Encoder.py new file mode 100644 index 0000000..84ca5f6 --- /dev/null +++ b/Adafruit_BBIO/Encoder.py @@ -0,0 +1,366 @@ +#!/usr/bin/python + +"""Quadrature Encoder Pulse interface. + +This module enables access to the enhanced Quadrature Encoder Pulse (eQEP) +channels, which can be used to seamlessly interface with rotary encoder +hardware. + +The channel identifiers are available as module variables :data:`eQEP0`, +:data:`eQEP1`, :data:`eQEP2` and :data:`eQEP2b`. + +======= ======= ======= =================================================== +Channel Pin A Pin B Notes +======= ======= ======= =================================================== +eQEP0 P9.27 P9.92 +eQEP1 P8.33 P8.35 Only available with video disabled +eQEP2 P8.11 P8.12 Only available with eQEP2b unused (same channel) +eQEP2b P8.41 P8.42 Only available with video disabled and eQEP2 unused +======= ======= ======= =================================================== + +Example: + To use the module, you can connect a rotary encoder to your Beaglebone + and then simply instantiate the :class:`RotaryEncoder` class to read its + position:: + + from Adafruit_BBIO.Encoder import RotaryEncoder, eQEP2 + + # Instantiate the class to access channel eQEP2, and initialize + # that channel + myEncoder = RotaryEncoder(eQEP2) + + # Get the current position + cur_position = myEncoder.position + + # Set the current position + next_position = 15 + myEncoder.position = next_position + + # Reset position to 0 + myEncoder.zero() + + # Change mode to relative (default is absolute) + # You can use setAbsolute() to change back to absolute + # Absolute: the position starts at zero and is incremented or + # decremented by the encoder's movement + # Relative: the position is reset when the unit timer overflows. + myEncoder.setRelative() + + # Read the current mode (0: absolute, 1: relative) + # Mode can also be set as a property + mode = myEncoder.mode + + # Get the current frequency of update in Hz + freq = myEncoder.frequency + + # Set the update frequency to 1 kHz + myEncoder.frequency = 1000 + + # Disable the eQEP channel + myEncoder.disable() + + # Check if the channel is enabled + # The 'enabled' property is read-only + # Use the enable() and disable() methods to + # safely enable or disable the module + isEnabled = myEncoder.enabled + +""" + +from subprocess import check_output, STDOUT, CalledProcessError +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) \ + and platform.node() == 'beaglebone': + 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())) + + +eQEP0 = 0 +'''eQEP0 channel identifier, pin A-- P9.92, pin B-- P9.27 on Beaglebone +Black.''' +eQEP1 = 1 +'''eQEP1 channel identifier, pin A-- P9.35, pin B-- P9.33 on Beaglebone +Black.''' +eQEP2 = 2 +'''eQEP2 channel identifier, pin A-- P8.12, pin B-- P8.11 on Beaglebone Black. +Note that there is only one eQEP2 module. This is one alternative set of pins +where it is exposed, which is mutually-exclusive with eQEP2b''' +eQEP2b = 3 +'''eQEP2(b) channel identifier, pin A-- P8.41, pin B-- P8.42 on Beaglebone +Black. Note that there is only one eQEP2 module. This is one alternative set of +pins where it is exposed, which is mutually-exclusive with eQEP2''' + +# Definitions to initialize the eQEP modules +_OCP_PATH = "/sys/devices/platform/ocp" +_eQEP_DEFS = [ + {'channel': 'eQEP0', 'pin_A': 'P9_92', 'pin_B': 'P9_27', + 'sys_path': os.path.join(_OCP_PATH, '48300000.epwmss/48300180.eqep')}, + {'channel': 'eQEP1', 'pin_A': 'P8_35', 'pin_B': 'P8_33', + 'sys_path': os.path.join(_OCP_PATH, '48302000.epwmss/48302180.eqep')}, + {'channel': 'eQEP2', 'pin_A': 'P8_12', 'pin_B': 'P8_11', + 'sys_path': os.path.join(_OCP_PATH, '48304000.epwmss/48304180.eqep')}, + {'channel': 'eQEP2b', 'pin_A': 'P8_41', 'pin_B': 'P8_42', + 'sys_path': os.path.join(_OCP_PATH, '48304000.epwmss/48304180.eqep')} +] + + +class _eQEP(object): + '''Enhanced Quadrature Encoder Pulse (eQEP) module class. Abstraction + for either of the three available channels (eQEP0, eQEP1, eQEP2) on + the Beaglebone''' + + @classmethod + 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.items() if k in allowed} + return cls(**df) + + def __init__(self, channel, pin_A, pin_B, sys_path): + '''Initialize the given eQEP channel + + Attributes: + channel (str): eQEP channel name. E.g. "eQEP0", "eQEP1", etc. + Note that "eQEP2" and "eQEP2b" are channel aliases for the + same module, but on different (mutually-exclusive) sets of + pins + pin_A (str): physical input pin for the A signal of the + rotary encoder + pin_B (str): physical input pin for the B signal of the + 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): + ''' + Rotary encoder class abstraction to control a given QEP channel. + + Args: + eqep_num (int): 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. + ''' + + 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( + "_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.''' + + # 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()) + + # 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._logger.debug( + "RotaryEncoder(): sys node: {0}".format(self._eqep.sys_path)) + + # Enable the channel upon initialization + self.enable() + + @property + def enabled(self): + '''Returns the enabled status of the module: + + Returns: + bool: True if the eQEP channel is enabled, False otherwise. + ''' + isEnabled = bool(int(self._eqep.node.enabled)) + + return isEnabled + + def _setEnable(self, enabled): + '''Turns the eQEP hardware ON or OFF + + Args: + enabled (int): enable the module with 1, disable it with 0. + + Raises: + ValueError: if the value for enabled is < 0 or > 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(enabled)) + + self._eqep.node.enabled = str(enabled) + 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) + + @property + def mode(self): + '''Returns the mode the eQEP hardware is in. + + Returns: + int: 0 if the eQEP channel is configured in absolute mode, + 1 if configured in relative mode. + ''' + 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._NS_FACTOR / int(self._eqep.node.period) + + self._logger.debug( + "Set frequency(): Channel {}, frequency: {} Hz, " + "period: {} ns".format( + self._eqep.channel, frequency, + self._eqep.node.period)) + + return frequency + + @frequency.setter + def frequency(self, frequency): + '''Sets the frequency in Hz at which the driver reports + new positions. + + ''' + # github issue #299: force period to be an integer + period = int(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 new file mode 100644 index 0000000..58cbfd7 --- /dev/null +++ b/Adafruit_BBIO/README.md @@ -0,0 +1,179 @@ +# Adafruit_BBIO.Encoder + +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, at least as root user. To ensure you can run the code as a regular user, read on the prerequisites section below. + +```python +from Adafruit_BBIO.Encoder import RotaryEncoder, eQEP2 + +''' +Each channel can be accessed and initialized using its corresponding +channel name constants: + + eQEP0 + eQEP1 # Pins only available when video is disabled + eQEP2 + eQEP2b # Pins only available when video is disabled +''' + +# Instantiate the class to access channel eQEP2, and only initialize +# that channel +myEncoder = RotaryEncoder(eQEP2) + +# Get the current position +cur_position = myEncoder.position + +# Position can also be set as a property +next_position = 15 +myEncoder.position = next_position + +# Reset position to 0 +myEncoder.zero() + +# Change mode to relative (default is absolute) +# You can use setAbsolute() to change back to absolute +# Absolute: the position starts at zero and is incremented or +# decremented by the encoder's movement +# Relative: the position is reset when the unit timer overflows. +myEncoder.setRelative() + +# Read the current mode (0: absolute, 1: relative) +# Mode can also be set as a property +mode = myEncoder.mode + +# Read the current frequency of update +# Returned value is in Hz +# If you want to set the frequency, specify it also in Hz +freq = myEncoder.frequency + +# Disable your eQEP channel +myEncoder.disable() + +# Check if the channel is enabled +# The 'enabled' property is read-only +# Use the enable() and disable() methods to +# safely enable or disable the module +isEnabled = myEncoder.enabled + +``` + +If you need to use further channels, read on the prerequisites in the following section. + +## Prerequisites + +### Kernel and packages + +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 update +sudo apt upgrade bb-cape-overlays bb-customizations +``` + +### User permissions + +In order to be able to run code that accesses the eQEP modules as a regular user, as opposed to root, that user must be part of the `eqep` group. + +To check which users are part of the `eqep` group: + +``` +cat /etc/group | grep eqep +``` + +To add user `userName` to the `eqep` group (run this command as root): +``` +usermod -a -G eqep userName +``` + +Note: you will need to log out and log back in for the group membership change to take effect. + +### Capes + +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. + +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 +``` + +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 + +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 + +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 + +Pins: `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 + +Pins: `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 + +#### eQEP2 + +Pins: `P8.11`, `P8.12` + +``` +$ config-pin P8.11 qep +$ config-pin P8.12 qep +$ cat /sys/devices/platform/ocp/48304000.epwmss/48304180.eqep/position +``` + +#### eQEP2b + +Note: alternate pins for eQEP2 (mutually exclusive) + +Pins: `P8.41`, `P8.42` + +``` +$ 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 + +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..f2273a1 --- /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/Adafruit_I2C.py b/Adafruit_I2C.py index 742e59e..6a7a3bc 100644 --- a/Adafruit_I2C.py +++ b/Adafruit_I2C.py @@ -1,123 +1,13 @@ #!/usr/bin/python -import smbus - -# =========================================================================== -# Adafruit_I2C Class -# Adafruit_I2C.py is essentially a fork of the Adafruit Raspberry Pi I2C module. -# Any pull requests for this module should be directed to the following, and I -# can pull them. I'd rather not deviate from the original: -# https://github.com/adafruit/Adafruit-Raspberry-Pi-Python-Code/tree/master/Adafruit_I2C -# =========================================================================== +# WARNING: THIS MODULE IS DEPRECATED! +# use Adafruit_GPIO.I2C instead: +# https://github.com/adafruit/Adafruit_Python_GPIO/blob/master/Adafruit_GPIO/I2C.py class Adafruit_I2C : def __init__(self, address, busnum=-1, debug=False): - self.address = address - self.bus = smbus.SMBus(busnum if busnum >= 0 else 1) - self.debug = debug - - def reverseByteOrder(self, data): - "Reverses the byte order of an int (16-bit) or long (32-bit) value" - # Courtesy Vishal Sapre - byteCount = len(hex(data)[2:].replace('L','')[::2]) - val = 0 - for i in range(byteCount): - val = (val << 8) | (data & 0xff) - data >>= 8 - return val - - def errMsg(self): - print("Error accessing 0x%02X: Check your I2C address" % self.address) - return -1 - - def write8(self, reg, value): - "Writes an 8-bit value to the specified register/address" - try: - self.bus.write_byte_data(self.address, reg, value) - if self.debug: - print("I2C: Wrote 0x%02X to register 0x%02X" % (value, reg)) - except IOError as err: - return self.errMsg() - - def write16(self, reg, value): - "Writes a 16-bit value to the specified register/address pair" - try: - self.bus.write_word_data(self.address, reg, value) - if self.debug: - print("I2C: Wrote 0x%02X to register pair 0x%02X,0x%02X" % - (value, reg, reg+1)) - except IOError as err: - return self.errMsg() - - def writeList(self, reg, list): - "Writes an array of bytes using I2C format" - try: - if self.debug: - print("I2C: Writing list to register 0x%02X:" % reg) - print(list) - self.bus.write_i2c_block_data(self.address, reg, list) - except IOError as err: - return self.errMsg() - - def readList(self, reg, length): - "Read a list of bytes from the I2C device" - try: - results = self.bus.read_i2c_block_data(self.address, reg, length) - if self.debug: - print("I2C: Device 0x%02X returned the following from reg 0x%02X" % - (self.address, reg)) - print(results) - return results - except IOError as err: - return self.errMsg() - - def readU8(self, reg): - "Read an unsigned byte from the I2C device" - try: - result = self.bus.read_byte_data(self.address, reg) - if self.debug: - print("I2C: Device 0x%02X returned 0x%02X from reg 0x%02X" % - (self.address, result & 0xFF, reg)) - return result - except IOError as err: - return self.errMsg() - - def readS8(self, reg): - "Reads a signed byte from the I2C device" - try: - result = self.bus.read_byte_data(self.address, reg) - if result > 127: result -= 256 - if self.debug: - print("I2C: Device 0x%02X returned 0x%02X from reg 0x%02X" % - (self.address, result & 0xFF, reg)) - return result - except IOError as err: - return self.errMsg() - - def readU16(self, reg): - "Reads an unsigned 16-bit value from the I2C device" - try: - result = self.bus.read_word_data(self.address,reg) - if (self.debug): - print("I2C: Device 0x%02X returned 0x%04X from reg 0x%02X" % (self.address, result & 0xFFFF, reg)) - return result - except IOError as err: - return self.errMsg() - - def readS16(self, reg): - "Reads a signed 16-bit value from the I2C device" - try: - result = self.bus.read_word_data(self.address,reg) - if (self.debug): - print("I2C: Device 0x%02X returned 0x%04X from reg 0x%02X" % (self.address, result & 0xFFFF, reg)) - return result - except IOError as err: - return self.errMsg() + print("WARNING: THIS MODULE IS DEPRECATED. Use Adafruit_GPIO.I2C instead.\n"); if __name__ == '__main__': - try: - bus = Adafruit_I2C(address=0) - print("Default I2C bus is accessible") - except: - print("Error accessing default I2C bus") + print("WARNING: THIS MODULE IS DEPRECATED. Use Adafruit_GPIO.I2C instead.\n"); diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..4f9325e --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,542 @@ +1.1.2 +--- +Daniel Nguyen (2): + Update common.c + Update common.c + +Drew Fustini (28): + upload to PyPI againt to resolve #293 + do not set pinmux on the beaglebone blue + remove deug output + Fix dead link to bone.js #296 + force Encoder period to be an integer #299 + Ignore new compiler warnings in gcc 8.2.0 + Update setup.py + do not set pin mode for built-in USRn LEDs + Change name of P1_3 to match bone.js + Fix warning about casting incompatible function types #308 + Fix warning print format strings being truncated #308 + Fix warning about casting incompatible function types #308 + Fix warnings on format truncation and sizeof in strncpy #308 + Fix warning about casting incompatible function types #308 + Update travis config to specify Python 3.6 + Update tox.ini to Python 3.6 + Merge pull request #321 from adafruit/issue308 + Update ISSUE_TEMPLATE.md + Update README.md + Update README.md + Merge pull request #327 from zer0cod3r/master + Merge pull request #337 from SamPovilus/docfix + Update README.md + Update README.md + Update README.md + remove -Werror from CFLAGS + Remove suppression of gcc warnings in CFLAGS #336 + Update version in setup.py to v1.2 + +Sam Povilus (1): + fixing document locaiton and version as current location dosn't load + +1.1.1 +--- +Attempt upload to PyPI again to avoid +error reported in issue #293 + +1.1.0 +--- +Aaron Marburg (1): + * Added usleep after successfully enabling PWM via udev. + +Drew Fustini (16): + * Merge pull request #233 from zsserg/fixed_segfault_in_event_detection + * Merge pull request #257 from zsserg/develop + * Merge pull request #251 from amarburg/master + * Merge pull request #271 from fcooper/documentation-updates + * Update ADC.rst + * Update Encoder.rst + * Update ADC.rst + * Add UART entries for the PocketBeagle (issue #242) + * update install and test shell scripts + * update UART section in README + * Merge pull request #282 from erikwelsh/master + * do not load overlays for the beaglebone blue #283 + * Merge pull request #284 from sam-bristow/py3-docs + * Merge pull request #285 from sam-bristow/bugfix/uart-error-reporting + * fix pwm on pocketbeagle and beaglebone blue #286 + * remove debug logging + +Erik Welsh (1): + * Fixed GPIO export problem; Leaves GPIO in bad state on latest BeagleBone image on PocketBeagle + +Franklin S Cooper Jr (3): + * docs/SPI.rst: Fix bus numbering in examples + * docs/GPIO.rst: Add information on blinking led + * docs/GPIO.rst Make documentation a bit newbie friendly + +Sam Bristow (3): + * Use print() function in all code and docs + * Use new python-serial API + * Return error-code for failing interface + +zserg (5): + * Fixed SEGFAULT when calling remove_event_detect() inside python callback function. + * Fixed SEGFAULT when calling remove_event_detect() inside python callback function. + * Fixed SEGFAULT in event_gpio,c run_callbacks() * Added more elaborate epoll() error logging + * Minor style fixes + +1.0.10 +---- +**features** +* automatically set pin modes for UART (PR #158) +* Encoder: README.md: added note about eqep group change (PR #214) +* deprecate out of date Adafruit_I2C.py (PR #215) +* Add Encoder module info to main README.md (PR #217) +* Add automatic API documentation generation (PR #219) +* Separate API docs into modules (PR #221) + +**shortlog** +* David Planella (46): + * Encoder: README.md: added note about eqep group change + * Add Encoder module info to main README.md + * Added docstrings using Google syntax and Sphinx support to generate the API documentation for the Encoder and PWM modules for now. + * Made kernel version check to happen only if running on a beaglebone. The readthedocs builders that import the Encoder module have an old 3.3 kernel and the autodoc build fails + * Use the default readthedocs theme + * Use readthedocs theme if building docs there, remove redundand search link + * Readthedocs theme tweaks + * Removed redundant TOC, added global description + * Added UART documentation + * Added documentation badge + * Added ADC API docs, fixed UART module definition + * API docs: added SPI module + * Added SPI module attribute docs + * Added Python badges to README file + * Added SPI pins table and first shot at GPIO module. Functions still need to be documented + * Merge branch 'readthedocs' of https://github.com/dplanella/adafruit-beaglebone-io-python into readthedocs + * Documented the API docs build process + * Added docstrings using Google syntax and Sphinx support to generate the API documentation for the Encoder and PWM modules for now. + * Made kernel version check to happen only if running on a beaglebone. The readthedocs builders that import the Encoder module have an old 3.3 kernel and the autodoc build fails + * Use the default readthedocs theme + * Use readthedocs theme if building docs there, remove redundand search link + * Readthedocs theme tweaks + * Removed redundant TOC, added global description + * Added UART documentation + * Added documentation badge + * Added ADC API docs, fixed UART module definition + * API docs: added SPI module + * Added SPI module attribute docs + * Added Python badges to README file + * Added SPI pins table and first shot at GPIO module. Functions still need to be documented + * Documented the API docs build process + * Merge branch 'readthedocs' of https://github.com/dplanella/adafruit-beaglebone-io-python into readthedocs + * Update README.md + * Added some more API doc content + * Sync from upstream master + * Minor documentation and configuration improvements + * Finished documenting GPIO + * rST fixes + * Update README.md + * Minor API doc improvements + * Merge branch 'readthedocs' of https://github.com/dplanella/adafruit-beaglebone-io-python into readthedocs + * Generate the API documentation from a master index and a separate file for each module + * Sync from upstream master + * Improvements to the API docs output config + * Update docs generation description to reflect new separate modules + * Updated ADC API docs + +* Drew Fustini (10): + * use set_pin_mode() to set uart pinmux (#158) + * Add SPI instructions to README (#158) + * Update README.md + * Fix spidev path mismatch (#216) + * Merge pull request #217 from dplanella/patch-2 + * Merge pull request #214 from dplanella/patch-1 + * Deprecate Adafruit_BBIO.I2C in favor of Adafruit_GPIO.I2C (#215) + * Merge pull request #219 from dplanella/readthedocs + * relocate doc dir to avoid confusion (#218) + * Merge pull request #221 from dplanella/readthedocs + + +1.0.9 +---- +**Features:** +* Issue #194: Encoder position cannot be set +* PR #205: Encoder: add support for reading/writing sysfs attributes + +**Fixes:** +* Issue #198: use https for DEFAULT_URL in distribute_setup.py +* Issue #197: Fix leak of pwm enable file descriptor +* Issue #189: Fix seg fault of PWM in Python 3.6 +* Issue #180: Clarify there is no 0 prefix for pin lables +* PR #201: Encoder: do kernel check, PEP8 cleanup +* PR #202: Encoder: corrected kernel check logic +* PR #207: Encoder: improved usage documentation +* PR #210: Encoder: fix sysfs import, make code Python 3 compatible +* PR #212: Encoder: fix Python 3 compatibility +* PR #213: Encoder: fix frequency calculation from period + +**shortlog:** +* David Planella (18): + * Encoder: initialize only the given channel + * Sync from master + * Encoder: do kernel check, PEP8 cleanup + * Encoder: added sysfs module + * Encoder: use sysfs to write QEP attributes + * Encoder: corrected kernel check logic + * Merge pull request #2 from adafruit/master + * Encoder: convert get/set methods to properties, update apidoc strings + * Encoder: updated README + * Encoder: add README apt install clarification + * Encoder: copyright assignment note, updated comments + * Encoder: added usage notes + * Encoder: improved usage documentation + * Encoder: minor fix to usage example + * Encoder: added a note about permissions + * Encoder: switched sysfs to be a relative import compatible with Python 2 and 3 + * Encoder: use items() instead of iteritems() to be Python 3 compatible + * Encoder: fix frequency getter + +* Drew Fustini (18): + * use https for DEFAULT_URL in distribute_setup.py (#198) + * fix except syntax for Python 3 + * use dict.items() instead of dict.iteritems() for Python 3 + * fix error in set_brightness() + * close enable_fd when stopping PWM output (#197) + * Merge pull request #199 from dplanella/patch-1 + * Fix leak of pwm enable file descriptor (#197) + * Merge pull request #201 from dplanella/encoder-cleanup + * remove test_rotary.py as not valid for pytest + * Fix seg fault of PWM in Python 3.6 (#189) + * Merge pull request #202 from dplanella/patch-2 + * Clarify there is no 0 prefix for pin lables (#180) + * Merge pull request #205 from dplanella/encoder-sysfs + * assign copyright for new file to Adafruit Industries + * Add bash scripts to help install and test + * Merge pull request #212 from dplanella/patch-4 + * Merge pull request #207 from dplanella/patch-3 + * Merge pull request #213 from dplanella/fix-encoder-frequency + +1.0.8 +---- +**Fixes:** +* Issue #196: cache board type to avoid poor performance +* Issue #192: fix PocketBeagle PWM pin typo +* Issue #191: turn off RotaryEncoder's debug output by default +* Issue #188: GPIO is extremely slow (20ms to toggle) +* Issue #186: problems with UART + +**shortlog:** +* David Planella (12): + * Copy Encoder module comments to README.md + * Formatted Encoder README in markdown + * Fixed Encoder README formatting + * Removed QEP instructions from Encoder module + * Fixes to Encoder README + * Updated Encoder README + * Encoder README: added info on dedicated overlays + * Encoder README: updated info on pre-requisites + * Encoder README update + * Encoder README update + * Add logging support, turn off unconditional debug output + * Encoder: remove unused logging code + +* Drew Fustini (3): + * Merge pull request #195 from dplanella/master + * Fix PocketBeagle PWM pin typo (#192) + * cache board type to avoid poor performance (#196) + +1.0.7 +---- +**Fixes:** +* Issue #188: GPIO is extremely slow (20ms to toggle) + +**shortlog:** +* Drew Fustini (4): + * Update README.md + * add config-pin example to README + * Filter DEBUG syslog to avoid poor performance #188 + * Change log level from INFO to DEBUG #188 + +1.0.6 +---- +* Currently recommended image: [Debian 9.2 "Stretch" iot (2017-10-29)](https://elinux.org/Beagleboard:BeagleBoneBlack_Debian#microSD.2FStandalone:_.28stretch-iot.29_.28All_BeagleBone_Variants_.26_PocketBeagle.29) + * Install [Linux kernel](https://elinux.org/Beagleboard:BeagleBoneBlack_Debian#Kernel_Options) [4.14.x](https://elinux.org/Beagleboard:BeagleBoneBlack_Debian#Mainline_.284.14.x_lts.29) to enable [non-root control of GPIO](https://github.com/rcn-ee/repos/blob/master/bb-customizations/suite/stretch/debian/80-gpio-noroot.rules) and [PWM](https://github.com/rcn-ee/repos/blob/master/bb-customizations/suite/stretch/debian/81-pwm-noroot.rules) [_(commit)_](https://github.com/adafruit/adafruit-beaglebone-io-python/commit/b65cbf8e41b444bad7c4ef6cfd4f88a30210fd78) + +**Features:** +* Add support for Linux 4.14 kernel including new "udev" style for PWM entries in /sys +* Fix GPIO regression due to BeagleBone Blue LED support (issue #178) +* Add support for the PocketBeagle (issue #172) + +**shortlog:** +* Drew Fustini (39): + * Add -Wno-unit_address_vs_reg to avoid dtc warning + * check if board is BeagleBone Blue or PocketBeagle + * check if BeagleBone Blue before accessing non-standard LEDs + * Add test for GPIO regression #178 + * change syslog mask level to DEBUG + * add "Adafruit_BBIO" to syslog() + * update test for issue #178 + * remove polarity "hack" for PWM #170 + * move pwm_set_polarity() after period is set + * add test for issue #170 + * only check kernel overlays if u-boot overlays are not being used + * Attempt to use udev ecap path for pwm path + * add test script for all BeagleBone PWM outputs + * update PWM test for 4.14 kernel udev paths + * minor change to pwm debug logging + * sleep to avoid udev race condition #185 + +* Mark A. Yoder (1): + * Added BAT25, BAT50, BAT75, BAT100 and WIFI LEDs + +* Peter Lawler (1): + * Missing CR/LF + +* Robert Nelson (10): + * source/common.c: add initial PocketBeagle values + * source/common.c: PocketBeagle, no slots file, everything built-in + * source/common.c: PocketBeagle, no slots file disable here too + * source/c_pwm.c: HACK: U-Boot pre-setup everything, dont search for specific overlay + * source/c_pwm.c: HACK: PocketBeagle: v4.14.x + * source/c_pwm.c: debug pwm_path/pwm_path_udev + * source/c_pwm.c: pwm: add support for pwmchipX/pwm-X:Y syntax + * source/c_pwm.c: disable pwm_set_polarity (broken in v4.9.x/v4.14.x) + * source/common.c: Blue Fix GP0_3 id + * source/common.c: PocketBeagle Fix P2.24 + +1.0.5 +---- +* @pdp7 (5): + * Merge pull request #153 from MarkAYoder/master + * Fix print syntax to avoid python3 errors + * Merge pull request #160 from MarkAYoder/master + * document how to read QEP1 + * Update rotary-encoder-eqep-test.md + +* @MarkAYoder (20): + * Have GP0_1 working + * Removed --force to speed things up + * Added GP0 1, 2 and 3 + * Flashes 4 LEDs + * Works with button + * Blinks red and gree LEDs + * Blinks all 6 GPIOs + * Added red and green LEDs + * i2c works + * PWD isn't working, yet + * Added port setup + * Switched to apt install + * Added tmp101 to name + * Added LED matrix example + * Removed newline from print + * Added fade + * Adding GPIO defs for uart1 + * Testing UT1_0, not working yet + * Switched GP0_0 to GP0_3, etc. + * Added PAUSE and MODE buttons. + +1.0.4 +---- +* @djsutton (1): + * fix TypeError: function takes exactly 3 arguments (2 given) from wait_for_edge + +* @pdp7 (29): + * Instruct users to open GitHub issue instead email + * add udev rules and script for non-root access to gpio + * fix file descriptor leak in gpio_set_value() + * document how to test read and write to all GPIO pins + * reduce ADC reads in pytest from 10,000 to 1,000 + * close file descriptor to avoid leak + * remove conditional logic for ctrl_dir and ocp_dir size + * increase size of ctrl_dir and ocp_dir for future use + * Document how to run config-pin at boot + * Document how to test eQEP with Rotary Encoder + * Add skeleton for Encoder module to read eQEP + * Add code to Encoder.QEP from PyBBIO.RotaryEncoder + * Adapt code from PyBBIO.RotaryEncoder + * add test for rotary encoder + * read from eqep position file + * return position from getPosition() + * document howo to enable all the eqep pins + * Document how to test eqep pins with rotary encoder + * run config-pin to set pin mux for qep + * update QEP test + * update QEP test for issue #122 + * Test if kernel booted wit u-boot overlays + * check if kernel cmdline for uboot overlay + * Add documentation about u-boot overlays + * Return BBIO_OK when u-boot overlays ared enabled + * remove debug printing + * Skip check for device tree loaded if u-boot overlays enabled + * Sleep after loading ADC overlay to allow driver load + * Workaround test failure until TIMERn bug is fixed + +* @ltjax (3): + * Use lookup table to prevent duplicate pin export + * Handle already exported pins + * Fix build_path memory leak + +* @Vadim-Stupakov (1): + * Fixed issue #145 GPIO library doesn't free GPIO file descriptor. File descriptor leak. Made a little bit refactoring + +* @cocasema (8): + * Declare PHONY targets in root Makefile + * Extract BBIO_err into a separate header + * Add syslog and debugging messages + * Add libadafruit-bbio with C++ wrappers for PWM/GPIO + * Add 2 versions of library with c++98 and c++11 abi + * Install header files properly + * Add default values to pwm::start() method. + * Add PWM c++ tests + +* @zsserg (2): + * Added error checking for setting pin direction in gpio.setup() (Python) + * Added debug output to set_pin_mode() + +1.0.3 +---- +* Add -Wno-strict-aliasing to CFLAGS to ignore gcc warning + * Resolves GitHub issue #133 by @archey + +1.0.2 +---- +* Merge pull request #130 from adafruit/issue129-usr-leds [1439133] + * Add support for alternate USR LED labels +* Merge pull request #131 from adafruit/fix-gcc-warnings [f0ee018] + * Fix gcc warnings +* Merge pull request #132 from buckket/setup_unicode_fix [4c67dfc] + * Make use of io.open() with explicit file encoding in setup.py + + +1.0.1 +---- +* Merge pull request #124 from jpbarraca/master [cf9771a] + * Timeout support for wait_for_edge (replaces PR #62) +* Merge pull request #123 from bubbapizza/master [8b4f7f2] + * Added a delay parameter for GPIO.setup() for udev permissions +* Merge pull request #121 from dengber/master [50e8883] + * ADC.read() returns wrong value +* Merge pull request #64 from olegantonyan/master [d1e8dc1] + * Wait until GPIO file appears on the /sys filesystem (issue #36) +* Merge pull request #106 from cocasema/master [12b79d7] + * Treat warnings as errors +* Merge pull request #119 from JesseMcL/pr [e7e987a] + * Add GPIO pullup configurations and fix PWM Segfault on kernel 4.1+ +* Merge pull request #116 from kozga/master [1b04cdf] + * Fix SPI: IOError: [Errno 22] Invalid argument in xfer and xfer2 funct… + +1.0.0 +---- +* Merge pull request #108 from MatthewWest for PWM support in Linux kernel 4.1+ +* Merge pull request #96 from PeteLawler for ADC support in Linux kernel 4.1+ +* Finally publish new version to PyPi +* Bump major version number to signify long duration since last release + +0.0.30 +----- +* Merge Python 3 compatibility fixes from Github user westphahl. +* Moved old Angstrom build fix for missing py_compile from setup.py to separate file. + +0.0.20 +---- +* Fix for SPI not loading spidevX.X correctly based on load order +* Initialize ctrl_dir in unload_device_tree #63 +* Clean up unused/dead code + +0.0.19 +---- +* Fix for SPI.xfer crashes python after 3 calls +* Added a retry to reading for the analog inputs to avoid a bug where reading back and forth between two analog inputs would cause the resource to be unavailable every 16 scans (zthorson) +* Updated the build_path to be more selective over what paths it chooses (zthorson) +* Update Debian installation instructions in README (justinledwards) +* Increase the size of the buffer used for storing device tree names (SaintGimp) + +0.0.18 +---- +* UART - Include UART overlays, and compile upon installation +* UART - Rename UART overlays +* Adafruit_I2C - Remove readU16Rev and readS16Rev +* Adafruit_I2C - Updated readU16/readS16 for correct 16-bit reads + +0.0.17 +---- +* Fix SPI memory leaks +* Clean up of PWM code (bit-hacker, jwcooper) +* Remove UART debug statements + +0.0.16 +---- +* Add polarity as optional fourth parameter to PWM.start(). Valid values are 0 and 1. Default is still 0. +* Fix for actually setting the polarity in start. +* Add new unit tests to check that the polarity is being set properly, and valid values passed in. + +0.0.15 +---- +* Fix PWM duty cycle so 0 is off and 100 is on. Set polarity to 0 by default. +* Give extra buffer space in export, and unexport functions for gpio that are more than 2 digits (Chris Desjardins) +* Add new test case for 3 digit gpio (Chris Desjardins) +* Fix for test_direction_readback. gpio_get_direction wasn't properly null terminating the direction string (Chris Desjardins) + +0.0.14 +---- +* Fix GPIO.gpio_function to work with the IO name (zthorson) +* Fix IOErrors not getting raised when fopen fails while loading overlays into device tree (bradfordboyle, jwcooper) +* Add new UART tests + +0.0.13 +---- +* Remove the gpio parameter from callbacks (cdesjardins) + +0.0.12 +---- +* Bump version due to pypi issues + +0.0.11 +---- +* New UART module to export UART overlays +* Alpha support for SPI +* Add small delay after loading any device tree overlays + +0.0.10 +____ +* Fix direction for event detection code +* Fix for segmentation faults on add_event_detect + +0.0.9 +____ +* Fix for ADC Segmentation Faults + +0.0.8 +____ +* Temp remove overlay compilation. Ubuntu failures. + +0.0.7 +____ +* Refactor and clean up adc and pwm +* Fix tests for Adafruit_BBIO rename + +0.0.6 +____ +* Include Adafruit_I2C.py as top-level module + +0.0.5 +---- +* Rename from BBIO to Adafruit_BBIO to reduce library conflicts and confusion. + +0.0.4 +---- +* Support for pip and easy_install + +0.0.3 +____ +* ADC enabled + +0.0.2 +____ +* PWM enabled + +0.0.1 +____ +* Initial Commit +* GPIO mostly working +* Initial GPIO unit tests +* PWM in progress diff --git a/CHANGELOG.rst b/CHANGELOG.rst deleted file mode 100644 index 0c1b67f..0000000 --- a/CHANGELOG.rst +++ /dev/null @@ -1,148 +0,0 @@ -1.0.3 ----- -* Add -Wno-strict-aliasing to CFLAGS to ignore gcc warning - * Resolves GitHub issue #133 by @archey - -1.0.2 ----- -* Merge pull request #130 from adafruit/issue129-usr-leds [1439133] - * Add support for alternate USR LED labels -* Merge pull request #131 from adafruit/fix-gcc-warnings [f0ee018] - * Fix gcc warnings -* Merge pull request #132 from buckket/setup_unicode_fix [4c67dfc] - * Make use of io.open() with explicit file encoding in setup.py - - -1.0.1 ----- -* Merge pull request #124 from jpbarraca/master [cf9771a] - * Timeout support for wait_for_edge (replaces PR #62) -* Merge pull request #123 from bubbapizza/master [8b4f7f2] - * Added a delay parameter for GPIO.setup() for udev permissions -* Merge pull request #121 from dengber/master [50e8883] - * ADC.read() returns wrong value -* Merge pull request #64 from olegantonyan/master [d1e8dc1] - * Wait until GPIO file appears on the /sys filesystem (issue #36) -* Merge pull request #106 from cocasema/master [12b79d7] - * Treat warnings as errors -* Merge pull request #119 from JesseMcL/pr [e7e987a] - * Add GPIO pullup configurations and fix PWM Segfault on kernel 4.1+ -* Merge pull request #116 from kozga/master [1b04cdf] - * Fix SPI: IOError: [Errno 22] Invalid argument in xfer and xfer2 funct… - -1.0.0 ----- -* Merge pull request #108 from MatthewWest for PWM support in Linux kernel 4.1+ -* Merge pull request #96 from PeteLawler for ADC support in Linux kernel 4.1+ -* Finally publish new version to PyPi -* Bump major version number to signify long duration since last release - -0.0.30 ------ -* Merge Python 3 compatibility fixes from Github user westphahl. -* Moved old Angstrom build fix for missing py_compile from setup.py to separate file. - -0.0.20 ----- -* Fix for SPI not loading spidevX.X correctly based on load order -* Initialize ctrl_dir in unload_device_tree #63 -* Clean up unused/dead code - -0.0.19 ----- -* Fix for SPI.xfer crashes python after 3 calls -* Added a retry to reading for the analog inputs to avoid a bug where reading back and forth between two analog inputs would cause the resource to be unavailable every 16 scans (zthorson) -* Updated the build_path to be more selective over what paths it chooses (zthorson) -* Update Debian installation instructions in README (justinledwards) -* Increase the size of the buffer used for storing device tree names (SaintGimp) - -0.0.18 ----- -* UART - Include UART overlays, and compile upon installation -* UART - Rename UART overlays -* Adafruit_I2C - Remove readU16Rev and readS16Rev -* Adafruit_I2C - Updated readU16/readS16 for correct 16-bit reads - -0.0.17 ----- -* Fix SPI memory leaks -* Clean up of PWM code (bit-hacker, jwcooper) -* Remove UART debug statements - -0.0.16 ----- -* Add polarity as optional fourth parameter to PWM.start(). Valid values are 0 and 1. Default is still 0. -* Fix for actually setting the polarity in start. -* Add new unit tests to check that the polarity is being set properly, and valid values passed in. - -0.0.15 ----- -* Fix PWM duty cycle so 0 is off and 100 is on. Set polarity to 0 by default. -* Give extra buffer space in export, and unexport functions for gpio that are more than 2 digits (Chris Desjardins) -* Add new test case for 3 digit gpio (Chris Desjardins) -* Fix for test_direction_readback. gpio_get_direction wasn't properly null terminating the direction string (Chris Desjardins) - -0.0.14 ----- -* Fix GPIO.gpio_function to work with the IO name (zthorson) -* Fix IOErrors not getting raised when fopen fails while loading overlays into device tree (bradfordboyle, jwcooper) -* Add new UART tests - -0.0.13 ----- -* Remove the gpio parameter from callbacks (cdesjardins) - -0.0.12 ----- -* Bump version due to pypi issues - -0.0.11 ----- -* New UART module to export UART overlays -* Alpha support for SPI -* Add small delay after loading any device tree overlays - -0.0.10 -____ -* Fix direction for event detection code -* Fix for segmentation faults on add_event_detect - -0.0.9 -____ -* Fix for ADC Segmentation Faults - -0.0.8 -____ -* Temp remove overlay compilation. Ubuntu failures. - -0.0.7 -____ -* Refactor and clean up adc and pwm -* Fix tests for Adafruit_BBIO rename - -0.0.6 -____ -* Include Adafruit_I2C.py as top-level module - -0.0.5 ----- -* Rename from BBIO to Adafruit_BBIO to reduce library conflicts and confusion. - -0.0.4 ----- -* Support for pip and easy_install - -0.0.3 -____ -* ADC enabled - -0.0.2 -____ -* PWM enabled - -0.0.1 -____ -* Initial Commit -* GPIO mostly working -* Initial GPIO unit tests -* PWM in progress diff --git a/MANIFEST.in b/MANIFEST.in index 50b245a..6b5f4a0 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,5 +1,5 @@ include distribute_setup.py -include README.rst -include CHANGELOG.rst +include README.md +include CHANGELOG.md recursive-include source *.h recursive-include overlays *.dts *.py diff --git a/Makefile b/Makefile index 48e32c6..ba726e8 100644 --- a/Makefile +++ b/Makefile @@ -1,14 +1,18 @@ + +all: build3 # build2 build3 + time: /usr/bin/ntpdate -b -s -u pool.ntp.org publish: clean python setup.py sdist upload -clean: +clean: cpp-clean rm -rf Adafruit_BBIO.* build dist rm -f *.pyo rm -f *.egg rm -f overlays/*.pyo overlays/*.pyc + tests: py.test @@ -25,8 +29,72 @@ install2: build2 python2 setup.py install --force build3: - python3 setup.py build --force + python3 setup.py build # --force install3: build3 - python3 setup.py install --force + python3 setup.py install # --force + +################################################################################ +# c++ library +# +# sudo apt-get install automake +# make cpp +# cd build && sudo make install +# +# libgtest-dev is needed for UT +# +# sudo sh -c 'echo cape-universaln > /sys/devices/platform/bone_capemgr/slots' +# sudo sh -c 'echo pwm > /sys/devices/platform/ocp/ocp\:P9_16_pinmux/state' +# sudo sh -c 'echo pwm > /sys/devices/platform/ocp/ocp\:P8_19_pinmux/state' +################################################################################ +configure: configure.ac + rm -f configure && \ + autoreconf --install -I m4 + +build/Makefile: configure + mkdir -p build && \ + cd build && \ + ../configure + +cpp: build/Makefile + cd build && \ + $(MAKE) build + +cpp-check: cpp + cd build && \ + $(MAKE) check + +cpp-install: cpp + cd build && \ + $(MAKE) install + +cpp-clean: + rm -rf \ + Makefile.in \ + aclocal.m4 \ + autom4te.cache/ \ + compile \ + configure \ + config.guess \ + config.h.in \ + config.sub \ + depcomp \ + install-sh \ + ltmain.sh \ + m4/libtool.m4 \ + m4/ltoptions.m4 \ + m4/ltsugar.m4 \ + m4/ltversion.m4 \ + m4/lt~obsolete.m4 \ + missing \ + source/Makefile.in \ + test-driver + +################################################################################ +.PHONY: all clean +.PHONY: tests +.PHONY: build install +.PHONY: build2 install2 +.PHONY: build3 install3 +.PHONY: cpp cpp-install cpp-clean diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..471948f --- /dev/null +++ b/Makefile.am @@ -0,0 +1,2 @@ +ACLOCAL_AMFLAGS = -I m4 +SUBDIRS = source \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..fba6863 --- /dev/null +++ b/README.md @@ -0,0 +1,295 @@ +# Adafruit Beaglebone I/O Python API + +[![Documentation Status](https://readthedocs.org/projects/adafruit-beaglebone-io-python/badge/?version=latest)](http://adafruit-beaglebone-io-python.readthedocs.io/en/latest/?badge=latest) +[![PyPI version](https://badge.fury.io/py/Adafruit_BBIO.svg)](https://badge.fury.io/py/Adafruit_BBIO) +[![PyPI pyversions](https://img.shields.io/pypi/pyversions/Adafruit_BBIO.svg)](https://pypi.python.org/pypi/Adafruit_BBIO/) + +Adafruit BBIO is an API to enable [GPIO](README.md#gpio-setup), [PWM](README.md#pwm), [ADC](README.md#adc), [UART](README.md#uart), [SPI](README.md#spi) and [eQEP](README.md#eqep) (Quadrature Encoder) hardware access from Python applications running on the Beaglebone. + +* It is recommended to use an [official BeagleBoard.org Debian image](https://beagleboard.org/latest-images) + * **Currently recommended image: [Debian 10.3 "Buster" IoT (2020-04-06)](http://beagleboard.org/latest-images)** _(default kernel is 4.19.x-ti)_ + +* Adafruit_BBIO supports Linux kernels 3.8 through 4.19 + +* New versions of Adafruit_BBIO may break backwards compatibility. Please read the [changelog](CHANGELOG.md). + +* It is recommended to use Python 3 + +## Installation on Debian + +Note: Follow the instructions on BeagleBoard.org to [get connected to the Internet](https://beagleboard.org/upgrade#connect) + +**Easiest:** +``` +sudo apt-get update +sudo apt-get install build-essential python3-dev python3-pip -y +sudo pip3 install Adafruit_BBIO +``` + +**Manual:** +``` +sudo apt-get update +sudo apt-get install build-essential python3-dev python3-pip -y +git clone git://github.com/adafruit/adafruit-beaglebone-io-python.git +cd adafruit-beaglebone-io-python +sudo python3 setup.py install +``` + +Upgrade Adafruit_BBIO to latest version on [PyPI](https://pypi.python.org/pypi/Adafruit_BBIO): +``` +sudo pip3 install --upgrade Adafruit_BBIO +``` + +## Usage + +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). + +``` +debian@beaglebone:~$ config-pin -q P9_14 +P9_14 Mode: pwm + +debian@beaglebone:~$ config-pin -l P9_14 +default gpio gpio_pu gpio_pd pwm + +debian@beaglebone:~$ config-pin P9_14 gpio + +debian@beaglebone:~$ config-pin -q P9_14 +P9_14 Mode: gpio Direction: in Value: 0 + +debian@beaglebone:~$ config-pin P9_14 pwm + +debian@beaglebone:~$ config-pin -q P9_14 +P9_14 Mode: pwm +``` + +### GPIO Setup + +Import the library, and setup as GPIO.OUT or GPIO.IN:: + + import Adafruit_BBIO.GPIO as GPIO + GPIO.setup("P8_14", GPIO.OUT) + +You can also refer to the pin names:: + + GPIO.setup("GPIO0_26", GPIO.OUT) + +### GPIO Output + +Setup the pin for output, and write GPIO.HIGH or GPIO.LOW. Or you can use 1 or 0.:: + + import Adafruit_BBIO.GPIO as GPIO + GPIO.setup("P8_14", GPIO.OUT) + GPIO.output("P8_14", GPIO.HIGH) + +### On-Board LEDs + +On-board LEDs (USR0-USR3) are handled by LED class driver rather than the GPIO pin driver. + +They have a different path in the /sys/ filesystem. + +Setup the pin for output and write GPIO.HIGH or GPIO.LOW:: + + import Adafruit_BBIO.GPIO as GPIO + import time + + for i in range(4): + GPIO.setup("USR%d" % i, GPIO.OUT) + + while True: + for i in range(4): + GPIO.output("USR%d" % i, GPIO.HIGH) + time.sleep(1) + for i in range(4): + GPIO.output("USR%d" % i, GPIO.LOW) + time.sleep(1) + +### GPIO Input + +Inputs work similarly to outputs.: + + import Adafruit_BBIO.GPIO as GPIO + GPIO.setup("P8_14", GPIO.IN) + +Polling inputs: + + if GPIO.input("P8_14"): + print("HIGH") + else: + print("LOW") + +Waiting for an edge (GPIO.RISING, GPIO.FALLING, or GPIO.BOTH: + + GPIO.wait_for_edge(channel, GPIO.RISING) + + or + + GPIO.wait_for_edge(channel, GPIO.RISING, timeout) + +Detecting events: + + GPIO.add_event_detect("P9_12", GPIO.FALLING) + #your amazing code here + #detect wherever: + if GPIO.event_detected("P9_12"): + print("event detected!") + +### PWM +**The PWM Duty Cycle range was reversed in 0.0.15 from 100(off)-0(on) to 0(off)-100(on). Please update your code accordingly.** + + import Adafruit_BBIO.PWM as PWM + #PWM.start(channel, duty, freq=2000, polarity=0) + #duty values are valid 0 (off) to 100 (on) + PWM.start("P9_14", 50) + PWM.set_duty_cycle("P9_14", 25.5) + PWM.set_frequency("P9_14", 10) + + PWM.stop("P9_14") + PWM.cleanup() + + #set polarity to 1 on start: + PWM.start("P9_14", 50, 2000, 1) + +### ADC + + import Adafruit_BBIO.ADC as ADC + ADC.setup() + + #read returns values 0-1.0 + value = ADC.read("P9_40") + + #read_raw returns non-normalized value + value = ADC.read_raw("P9_40") + +### [UART](https://learn.adafruit.com/setting-up-io-python-library-on-beaglebone-black/uart) +* Use [`config-pin` to set pin mode](https://github.com/beagleboard/bb.org-overlays/tree/master/tools/beaglebone-universal-io) for [UART1 and UART2 pins](http://beagleboard.org/static/images/cape-headers-serial.png) +``` +config-pin P9.21 uart # UART2_TXD +config-pin P9.22 uart # UART2_RXD +config-pin P9.24 uart # UART1_TXD +config-pin P9.26 uart # UART1_RXD +``` +* [Install pyserial](https://learn.adafruit.com/setting-up-io-python-library-on-beaglebone-black/uart#using-uart-with-python) +``` +sudo pip install pyserial +``` +* [Test UART1](https://learn.adafruit.com/setting-up-io-python-library-on-beaglebone-black/uart#using-uart-with-python) +``` +import Adafruit_BBIO.UART as UART +import serial + +UART.setup("UART1") + +with serial.Serial(port = "/dev/ttyO1", baudrate=9600) as ser: + print("Serial is open!") + ser.write(b"Hello World!") + +``` +* Available UART names on BeagleBone + * `UART1`: /dev/ttyO1, Rx: P9_26, Tx: P9_24 + * `UART2`: /dev/ttyO2, Rx: P9_22, Tx: P9_21 + * `UART4`: /dev/ttyO4, Rx: P9_11, Tx: P9_13 + * `UART5`: /dev/ttyO5, Rx: P8_38, Tx: P8_37 + * note: `UART5` requires `disable_uboot_overlay_video=1` in `/boot/uEnv.txt` +* Available UART names on PocketBeagle + * `PB-UART0`: /dev/ttyO0, Rx: P1_30, Tx: P1_32 + * `PB-UART1`: /dev/ttyO1, Rx: P2_11, Tx: P2_09 + * `PB-UART2`: /dev/ttyO2, Rx: P1_08, Tx: P1_10 +* [Loopback test with UART1 and UART2](https://learn.adafruit.com/setting-up-io-python-library-on-beaglebone-black/uart#testing-and-using-the-uart) + + +### [SPI](https://learn.adafruit.com/setting-up-io-python-library-on-beaglebone-black/spi) +* Use [`config-pin` to set pin mode](https://github.com/beagleboard/bb.org-overlays/tree/master/tools/beaglebone-universal-io) for [SPI pins](http://beagleboard.org/static/images/cape-headers-spi.png) + * SPI0 + * SPI0_CS0: `config-pin p9.17 spi_cs` + * SPI0_D0: `config-pin p9.21 spi` + * SPI0_D1: `config-pin p9.18 spi` + * SPI0_SCLK: `config-pin p9.22 spi_sclk` + * SPI1 + * SPI1_CS0: `config-pin p9.20 spi_cs` + * SPI1_CS0: `config-pin p9.28 spi_cs` + * SPI1_CS1: `config-pin p9.19 spi_cs` + * SPI1_CS1: `config-pin p9.42 spi_cs` + * SPI1_D0: `config-pin p9.29 spi` + * SPI1_D1: `config-pin p9.30 spi` + * SPI1_SCLK: `config-pin p9.31 spi_sclk` +* Example: +``` +from Adafruit_BBIO.SPI import SPI +#spi = SPI(bus, device) #/dev/spidev. + +# /dev/spidev0.0 +spi = SPI(1,0) +print(spi.xfer2([32, 11, 110, 22, 220])) +spi.close() + +# /dev/spidev0.1 +spi = SPI(1,1) +print(spi.xfer2([32, 11, 110, 22, 220])) +spi.close() + +# /dev/spidev1.0 +spi = SPI(2,0) +print(spi.xfer2([32, 11, 110, 22, 220])) +spi.close() + +# /dev/spidev1.1 +spi = SPI(2,1) +print(spi.xfer2([32, 11, 110, 22, 220])) +spi.close() +``` + +### eQEP + +To use the enhanced Quadrature Encoder Pulse (eQEP) module, please refer to the [`Encoder` module's documentation](https://github.com/adafruit/adafruit-beaglebone-io-python/tree/master/Adafruit_BBIO#usage). + +## Running tests + +Install py.test to run the tests. You'll also need the python compiler package for pytest: +``` +sudo pip3 install pytest +``` +Execute the following in the root of the project: +``` +pytest +``` +NOTE: `sudo` should not be required as udev configures group ownership and permission for [GPIO](https://github.com/rcn-ee/repos/blob/master/bb-customizations/suite/stretch/debian/80-gpio-noroot.rules) and [PWM](https://github.com/rcn-ee/repos/blob/master/bb-customizations/suite/stretch/debian/81-pwm-noroot.rules) + +## Reporting issues + +When reporting issues, plesae run the following script which will print the system configuration: +``` +sudo /opt/scripts/tools/version.sh +``` +and paste the output in a reply. + +This script should be present for any Debian or Ubunut image downloaded from: +https://beagleboard.org/ or https://rcn-ee.com/ + + +## Credits + +The BeagleBone IO Python library was originally forked from the excellent MIT Licensed [RPi.GPIO](https://code.google.com/p/raspberry-gpio-python) library written by Ben Croston. + +## License + +Written by Justin Cooper, Adafruit Industries. BeagleBone IO Python library is released under the MIT License. diff --git a/README.rst b/README.rst deleted file mode 100644 index 5be0509..0000000 --- a/README.rst +++ /dev/null @@ -1,171 +0,0 @@ -**PLEASE NOTE: This library may have breaking changes as development continues. Please read the changelog anytime you update the library!** - -**The PWM Duty Cycle range was reversed in 0.0.15 from 100(off)-0(on) to 0(off)-100(on). Please update your code accordingly.** - -**Adafruit's BeagleBone IO Python Library** - -This is a set of Python tools to allow GPIO, PWM, and ADC access on the BeagleBone using the Linux 3.8 Kernel and above (latest releases). - -It has been tested on the 5-20 and 6-6 Angstrom image on the BeagleBone Black. - -**Note: BBIO has been renamed to Adafruit_BBIO.** - -**Installation on Angstrom** - -Easiest:: - - /usr/bin/ntpdate -b -s -u pool.ntp.org - opkg update && opkg install python-pip python-setuptools - pip install Adafruit_BBIO - -Manual:: - - git clone git://github.com/adafruit/adafruit-beaglebone-io-python.git - #set the date and time - /usr/bin/ntpdate -b -s -u pool.ntp.org - #install dependency - opkg update && opkg install python-distutils - cd adafruit-beaglebone-io-python - python setup.py install - -**Installation on Ubuntu/Debian** - -Easiest:: - - sudo ntpdate pool.ntp.org - sudo apt-get update - sudo apt-get install build-essential python-dev python-pip -y - #easy_install -U distribute //debian only - sudo pip install Adafruit_BBIO - -Manual:: - - sudo ntpdate pool.ntp.org - sudo apt-get update - sudo apt-get install build-essential python-dev python-pip -y - git clone git://github.com/adafruit/adafruit-beaglebone-io-python.git - cd adafruit-beaglebone-io-python - sudo python setup.py install - cd .. - sudo rm -rf adafruit-beaglebone-io-python - -**Usage** - -Using the library is very similar to the excellent RPi.GPIO library used on the Raspberry Pi. Below are some examples. - -**GPIO Setup** - -Import the library, and setup as GPIO.OUT or GPIO.IN:: - - import Adafruit_BBIO.GPIO as GPIO - GPIO.setup("P8_14", GPIO.OUT) - -You can also refer to the pin names:: - - GPIO.setup("GPIO0_26", GPIO.OUT) - -**GPIO Output** - -Setup the pin for output, and write GPIO.HIGH or GPIO.LOW. Or you can use 1 or 0.:: - - import Adafruit_BBIO.GPIO as GPIO - GPIO.setup("P8_14", GPIO.OUT) GPIO.output("P8_14", GPIO.HIGH) - -**On-Board LEDs** - -On-board LEDs (USR0-USR3) are handled by LED class driver rather than the GPIO pin driver. - -They have a different path in the /sys/ filesystem. - -Setup the pin for output and write GPIO.HIGH or GPIO.LOW:: - - import Adafruit_BBIO.GPIO as GPIO - import time - - for i in range(4): - GPIO.setup("USR%d" % i, GPIO.OUT) - - while True: - for i in range(4): - GPIO.output("USR%d" % i, GPIO.HIGH) - time.sleep(1) - for i in range(4): - GPIO.output("USR%d" % i, GPIO.LOW) - time.sleep(1) - -**GPIO Input** - -Inputs work similarly to outputs.:: - - import Adafruit_BBIO.GPIO as GPIO - GPIO.setup("P8_14", GPIO.IN) - -Polling inputs:: - - if GPIO.input("P8_14"): - print("HIGH") - else: - print("LOW") - -Waiting for an edge (GPIO.RISING, GPIO.FALLING, or GPIO.BOTH:: - - GPIO.wait_for_edge(channel, GPIO.RISING) - - or - - GPIO.wait_for_edge(channel, GPIO.RISING, timeout) - -Detecting events:: - - GPIO.add_event_detect("P9_12", GPIO.FALLING) - #your amazing code here - #detect wherever: - if GPIO.event_detected("P9_12"): - print "event detected!" - -**PWM**:: - - import Adafruit_BBIO.PWM as PWM - #PWM.start(channel, duty, freq=2000, polarity=0) - #duty values are valid 0 (off) to 100 (on) - PWM.start("P9_14", 50) - PWM.set_duty_cycle("P9_14", 25.5) - PWM.set_frequency("P9_14", 10) - - PWM.stop("P9_14") - PWM.cleanup() - - #set polarity to 1 on start: - PWM.start("P9_14", 50, 2000, 1) - -**ADC**:: - - import Adafruit_BBIO.ADC as ADC - ADC.setup() - - #read returns values 0-1.0 - value = ADC.read("P9_40") - - #read_raw returns non-normalized value - value = ADC.read_raw("P9_40") - -**Running tests** - -Install py.test to run the tests. You'll also need the python compiler package for py.test.:: - - opkg update && opkg install python-compiler - #Either pip or easy_install - pip install -U pytest - easy_install -U pytest - -Execute the following in the root of the project:: - - py.test - -**Credits** - -The BeagleBone IO Python library was originally forked from the excellent MIT Licensed [RPi.GPIO](https://code.google.com/p/raspberry-gpio-python) library written by Ben Croston. - -**License** - -Written by Justin Cooper, Adafruit Industries. BeagleBone IO Python library is released under the MIT License. diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..0495c54 --- /dev/null +++ b/configure.ac @@ -0,0 +1,25 @@ +AC_INIT([Adafruit BeagleBone IO Library], + [1.0.0], + [https://github.com/adafruit/adafruit-beaglebone-io-python/issues], + [adafruit-bbio]) + +AC_PREREQ([2.59]) +#AC_CONFIG_SRCDIR([./LICENSE]) +AC_CONFIG_MACRO_DIR([m4]) +AC_CONFIG_HEADERS([config.h]) +AC_CONFIG_FILES([Makefile source/Makefile]) + +AM_INIT_AUTOMAKE([1.9 dist-bzip2 dist-zip foreign subdir-objects -Wall]) +AM_PROG_AR + +LT_INIT + +AC_PROG_CC +AC_PROG_CXX +AC_LANG([C]) +AC_LANG([C++]) + +CHECK_GTEST +CHECK_KERNEL_RELEASE + +AC_OUTPUT diff --git a/distribute_setup.py b/distribute_setup.py index c67b752..5b31634 100644 --- a/distribute_setup.py +++ b/distribute_setup.py @@ -50,7 +50,7 @@ def quote(arg): return os.spawnl(os.P_WAIT, sys.executable, *args) == 0 DEFAULT_VERSION = "0.6.45" -DEFAULT_URL = "http://pypi.python.org/packages/source/d/distribute/" +DEFAULT_URL = "https://pypi.python.org/packages/source/d/distribute/" SETUPTOOLS_FAKED_VERSION = "0.6c11" SETUPTOOLS_PKG_INFO = """\ diff --git a/docs/ADC.rst b/docs/ADC.rst new file mode 100644 index 0000000..dc9c64c --- /dev/null +++ b/docs/ADC.rst @@ -0,0 +1,45 @@ +:mod:`ADC` --- A/D Converter input interface +-------------------------------------------- + +This module enables reading analog input values from the analog to digital +converter (ADC) on the TI AM3358 SoC on the BeagleBone. + +Example:: + + import Adafruit_BBIO.ADC as ADC + + ADC.setup() + + # The read method returns normalized values from 0 to 1.0 + value = ADC.read("P9_40") + + # The read_raw returns non-normalized values from 0 to 4095 + value = ADC.read_raw("P9_40") + +.. module:: Adafruit_BBIO.ADC + +.. function:: setup_adc() + + Setup and start the ADC hardware. + +.. function:: setup_read(channel) + + Read the normalized analog value for the channel. + + :param str channel: GPIO channel to read the value from (e.g. "P8_16"). + :returns: normalized value reading from 0.0 to 1.0 + :rtype: float + + +.. function:: setup_read_raw(channel) + + Read the raw analog value for the channel. + + :note: Kernels older than 4.1.x returned a raw value range 0 - 1800 + based on the reference voltage of 1.8 V. + + :param str channel: GPIO channel to read the value from (e.g. "P8_16"). + :returns: raw value reading from 0 to 4095 (12 bits). + :rtype: float + + diff --git a/docs/Encoder.rst b/docs/Encoder.rst new file mode 100644 index 0000000..364eee3 --- /dev/null +++ b/docs/Encoder.rst @@ -0,0 +1,5 @@ +:mod:`Encoder` --- Quadrature Encoder interface (eQEP) +----------------------------------------------- + +.. automodule:: Adafruit_BBIO.Encoder + :members: diff --git a/docs/GPIO.rst b/docs/GPIO.rst new file mode 100644 index 0000000..70be0a1 --- /dev/null +++ b/docs/GPIO.rst @@ -0,0 +1,221 @@ +:mod:`GPIO` --- General Purpose I/O interface +--------------------------------------------- + +This module provides access and control of pins set up as General Purpose +I/O (GPIO). + +.. note:: + + You need to be part of the ``gpio`` group of the OS running on the + Beaglebone to be able to run GPIO code as a non-root user. The default + user created upon the Debian image installation should already be + part of the group. Otherwise, you can use + ``sudo usermod -a -G gpio userName`` to add ``userName`` to the group. + +.. note:: + + When coding with this module, you will be using pin names for + better readability. As such, you can specify them in the header 8 or 9 + form (e.g. "P8_16") or in pin name form (e.g. "GPIO1_14"). + For easy reference, you can use the + `Beaglebone pin names table `_ + + +.. note:: + + On-board LEDs (USR0-USR3) are handled by LED class driver rather than the GPIO pin driver. + + They have a different path in the /sys/ filesystem. + + Setup the pin for output and write GPIO.HIGH or GPIO.LOW + +Example:: + + # Use the config-pin command line tool to set a pin's function to GPIO + # Then you can control it with the GPIO module from Python + config-pin P9_14 gpio + + import Adafruit_BBIO.GPIO as GPIO + + # Set up pins as inputs or outputs + GPIO.setup("P8_13", GPIO.IN) + GPIO.setup("P8_14", GPIO.OUT) + GPIO.setup("GPIO0_26", GPIO.OUT) # Alternative: use actual pin names + + # Write a logic high or logic low + GPIO.output("P8_14", GPIO.HIGH) # You can also write '1' instead + GPIO.output("P8_14", GPIO.LOW) # You can also write '0' instead + + + # Blinking onboard led example + import Adafruit_BBIO.GPIO as GPIO + import time + + for i in range(4): + GPIO.setup("USR%d" % i, GPIO.OUT) + + while True: + for i in range(4): + GPIO.output("USR%d" % i, GPIO.HIGH) + time.sleep(1) + for i in range(4): + GPIO.output("USR%d" % i, GPIO.LOW) + time.sleep(1) + + +.. module:: Adafruit_BBIO.GPIO + +.. function:: setup(channel, direction[, pull_up_down=GPIO.PUD_OFF, initial=None, delay=0]) + + Set up the given GPIO channel, its direction and (optional) pull/up down control + + :param str channel: GPIO channel to set up (e.g. "P8_16"). + :param int direction: GPIO channel direction + (:data:`GPIO.IN` or :data:`GPIO.OUT`). + :param int pull_up_down: pull-up/pull-down resistor configuration + (:data:`GPIO.PUD_OFF`, :data:`GPIO.PUD_UP` or :data:`GPIO.PUD_DOWN`). + :param int initial: initial value for an output channel + (:data:`GPIO.LOW`/:data:`GPIO.HIGH`). + :param int delay: time in milliseconds to wait after exporting the GPIO pin. + +.. function:: cleanup() + + Clean up by resetting all GPIO channels that have been used by + the application to :data:`IN` with no pullup/pulldown and no event + detection. + + :note: It's recommended that you call this function upon exiting your + application. + +.. function:: output(channel, value) + + Set the given output channel to the given digital value. + + :param str channel: GPIO channel to output the value to (e.g. "P8_16"). + :param value: value to set the output to-- 0/1 or False/True + or :data:`GPIO.LOW`/:data:`GPIO.HIGH`. + :type value: int or bool + +.. function:: input(channel) + + Get the given input channel's digital value. + + :param str channel: GPIO channel to read the value from (e.g. "P8_16"). + :returns: Channel value–– 0 or 1. + :rtype: int + +.. function:: add_event_detect(channel, edge[, callback=None, bouncetime=0]) + + Enable edge detection events for the given GPIO channel. + + :param str channel: GPIO channel to detect events from (e.g. "P8_16"). + :param int edge: edge to detect–– :data:`GPIO.RISING`, :data:`GPIO.FALLING` + or :data:`GPIO.BOTH` + :param func callback: a function to call once the event has been detected. + :param int bouncetime: switch bounce timeout in ms for the callback. + +.. function:: remove_event_detect(channel) + + Remove edge detection for the given GPIO channel. + + :param str channel: GPIO channel to remove event detection + from (e.g. "P8_16"). + +.. function:: event_detected(channel) + + Checks if an edge event has occured on a given GPIO. + + :note: You need to enable edge detection using :func:`add_event_detect()` first. + + :param str channel: GPIO channel to check for event detection + for (e.g. "P8_16"). + :returns: True if an edge has occured on a given GPIO, False otherwise + :rtype: bool + +.. function:: add_event_callback(channel, callback[, bouncetime=0]) + + Add a callback for an event already defined using :func:`add_event_detect()` + + :param str channel: GPIO channel to add a callback to (e.g. "P8_16"). + :param func callback: a function to call once the event has been detected. + :param int bouncetime: switch bounce timeout in ms for the callback. + +.. function:: wait_for_edge(channel, edge[, timeout=-1]) + + Wait for an edge on the given channel. + + :param str channel: GPIO channel to wait on (e.g. "P8_16"). + :param int edge: edge to detect–– :data:`GPIO.RISING`, :data:`GPIO.FALLING` + or :data:`GPIO.BOTH` + :param int timeout: time to wait for an edge, in milliseconds. + -1 will wait forever. + +.. function:: gpio_function(channel) + + Return the current GPIO function + (:data:`IN`, :data:`IN`, :data:`ALT0`) of the given pin. + + :warning: Currently only returning the direction of the + pin (input or output) is supported. + + :param str channel: GPIO pin to query the status of. + :returns: 0 if :data:`IN`, 1 if :data:`OUT` + :rtype: int + +.. function:: setwarnings(gpio_warnings) + + Enable or disable GPIO warning messages. + + :warning: Currently enabling or disabling warnings + has no effect. + + :param int gpio_warnings: 0–– disable warnings; 1–– enable warnings + +.. attribute:: ALT0 + + Pin mode-- alternate function 0. + +.. attribute:: BOTH + + Edge detection-- detect both edges. + +.. attribute:: FALLING + + Edge detection-- detect falling edge. + +.. attribute:: HIGH + + Pin status-- logic low. + +.. attribute:: IN + + Pin mode-- input. + +.. attribute:: LOW + + Pin status-- logic low. + +.. attribute:: OUT + + Pin mode-- output. + +.. attribute:: PUD_OFF + + Pull-up/pull-down resistor type-- no pull-up/pull-down. + +.. attribute:: PUD_DOWN + + Pull-up/pull-down resistor type-- pull-down. + +.. attribute:: PUD_UP + + Pull-up/pull-down resistor type-- pull-up. + +.. attribute:: RISING + + Edge detection-- detect rising edge. + +.. attribute:: VERSION + + GPIO module version. Currently unused. + diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..4430e8a --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +SPHINXPROJ = Adafruit-BBIO +SOURCEDIR = . +BUILDDIR = _build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) \ No newline at end of file diff --git a/docs/PWM.rst b/docs/PWM.rst new file mode 100644 index 0000000..95d428a --- /dev/null +++ b/docs/PWM.rst @@ -0,0 +1,62 @@ +:mod:`PWM` --- Pulse Width Modulation interface +----------------------------------------------- + +Enables access to the Pulse Width Modulation (PWM) module, to easily and +accurately generate a PWM output signal of a given duty cycle and +frequency. + +.. note:: + + You need to be part of the ``pwm`` group of the OS running on the + Beaglebone to be able to run PWM code as a non-root user. The default + user created upon the Debian image installation should already be + part of the group. Otherwise, you can use + ``sudo usermod -a -G pwm userName`` to add ``userName`` to the group. + +.. module:: Adafruit_BBIO.PWM + +.. function:: start(channel, duty_cycle[, frequency=2000, polarity=0]) + + Set up and start the given PWM channel. + + :param str channel: PWM channel. It can be specified in the form + of of 'P8_10', or 'EHRPWM2A'. + :param int duty_cycle: PWM duty cycle. It must have a value from 0 to 100. + :param int frequency: PWM frequency, in Hz. It must be greater than 0. + :param int polarity: defines whether the value for ``duty_cycle`` affects the + rising edge or the falling edge of the waveform. Allowed values -- 0 + (rising edge, default) or 1 (falling edge). + +.. function:: stop(channel) + + Stop the given PWM channel. + + :param str channel: PWM channel. It can be specified in the form + of of 'P8_10', or 'EHRPWM2A'. + +.. function:: set_duty_cycle(channel, duty_cycle) + + Change the duty cycle of the given PWM channel. + + :note: You must have started the PWM channel with :func:`start()` + once, before changing the duty cycle. + + :param str channel: PWM channel. It can be specified in the form + of of 'P8_10', or 'EHRPWM2A'. + :param int duty_cycle: PWM duty cycle. It must have a value from 0 to 100. + +.. function:: set_frequency(channel, frequency) + + Change the frequency of the given PWM channel. + + :note: You must have started the PWM channel with :func:`start()` + once, before changing the frequency. + + :param str channel: PWM channel. It can be specified in the form + of of 'P8_10', or 'EHRPWM2A'. + :param int frequency: PWM frequency. It must be greater than 0. + +.. function:: cleanup() + + Clean up by resetting all GPIO channels that have been used by this + program to INPUT, with no pullup/pulldown and no event detection. diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..828f2c3 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,68 @@ +# Generating API documentation + +This folder contains the required files to automatically generate the Adafruit Beaglebone I/O Python API documentation, partly from the code docstrings and partly from files in reStructuredText format. + +``` +├── conf.py <- Sphinx configuration file +├── index.rst <- Documentation will be generated based on the master index +└── Makefile <- Auxiliary Makefile to build documentation +``` + +The tools used are [Sphinx](http://www.sphinx-doc.org) to extract the documentation and publish it in HTML format for online viewing, in combination with [Readthedocs](http://readthedocs.io). Readthedocs automatically executes Sphinx via webhooks triggered by Github commits, and publishes the resulting docs for all tracked branches or tags. Generally Readthedocs will be set up to track stable release tags and the master branch. + +## Building the documentation + +The documentation can also be built on a local checkout of the project: + +First ensure you've got sphinx installed: + +``` +sudo pip install sphinx +``` + +Then you can build the HTML docs: + +``` +cd docs +make html +``` + +Once Sphinx has built the documentation, you can open the main index file with your browser: `_build/html/index.html` + +Notes: + +- The build process will create three additional temporary directories: `_build`, `_static` and `_templates` that will not be version-controlled. You can use `make clean` to remove their contents if you wish to do so. +- The html theme from files built locally is different from the online readthedocs theme. See the `docs/config.py` `html_theme` variable. The main reason is not to introduce another dependency to install the readthedocs theme, but as a side effect, it also helps visually distinguishing the locally-built documentation from the online version. + +## Readthedocs maintenance + +At every release that includes documenation (most probably 1.0.10 will be the first one), the release's branch or tag needs to be selected in the web UI and marked as active. + +After this, documentation will automatically be generated and published for that release. It will be available at the same URL as the main documentation, and a link with the version number will be shown, where it can be accessed from. + +Optionally, the 'stable' URL slug can be pointed to that release branch. Otherwise, the 'stable' slug can also be deactivated for less maintenance overhead. + +The 'latest' URL slug will always be pointing at the repo's master branch. + +## Notes + +Ideally, all API documentation would be written in the source files as Python docstrings, and sphinx would simply extract it. This is actually the case with the `Encoder` module, which is pure Python. + +However, most of the code is written as C extensions. While they do provide docstrings once they are built, Sphinx does not natively support extracting them. There is [a workaround](https://stackoverflow.com/a/30110104/9022675) to do this, but it involves first building the extensions, installing them and hardcoding a path. While it might work for locally-built documentation, it's unlikely that readthedocs support this option. + +For the sake of keeping things simple and with less maintenance, the approach of documenting the C-generated API in separate `.rst` files (one for each Python module) has been taken. + +This has the advantage of having a definition of the API in one place, but it also poses the disadvantage of some duplication, as the C modules do define some docstrings for their objects. Then again, the API itself has hardly changed in the last few years, and the Beaglebone is a mature platform, so it's unlikely that this will add a significant maintenance overhead. + +- The documentation in the `.rst` files is written in [reStructuredText](http://docutils.sourceforge.net/rst.html), extended with Sphinx markup for defining the objects. +- The documentation in Python modules follows the Google readable docstring markup, which also builds upon reStructuredText and is fully supported by Sphinx. + +## Further reference + +- [Google readable docstring markup](https://google.github.io/styleguide/pyguide.html?showone=Comments#Comments) +- [Google docstring examples](http://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html) +- [More Google docstring examples](http://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html) +- [Sphinx docstring markup](http://www.sphinx-doc.org/en/stable/domains.html#the-python-domain) +- [reStructuredText primer](http://www.sphinx-doc.org/en/stable/rest.html#rst-primer) + + diff --git a/docs/SPI.rst b/docs/SPI.rst new file mode 100644 index 0000000..fcf4bf6 --- /dev/null +++ b/docs/SPI.rst @@ -0,0 +1,134 @@ +:mod:`SPI` --- Serial Peripheral Interface +------------------------------------------ + +This module defines an object type that allows Serial Peripheral Interface +(SPI) bus transactions on hosts running the Linux kernel. The host kernel +must have SPI support and SPI device interface support. + +Because the SPI device interface is opened R/W, users of this module +usually must have root permissions or be members of a group with granted +access rights. + +Pins used for SPI0 and SPI1 ++++++++++++++++++++++++++++ + +==== ===== ===== ===== ===== +PORT CS0 DO DI SCLK +==== ===== ===== ===== ===== +SPI0 P9_17 P9_21 P9_18 P9_22 +SPI1 P9_28 P9_29 P9_30 P9_31 +==== ===== ===== ===== ===== + +Example:: + + import Adafruit_BBIO.SPI as SPI + + from Adafruit_BBIO.SPI import SPI + # spi = SPI(bus, device) #/dev/spidev. + + # /dev/spidev0.0 + spi = SPI(0, 0) + print(spi.xfer2([32, 11, 110, 22, 220])) + spi.close() + + # /dev/spidev0.1 + spi = SPI(0, 1) + print(spi.xfer2([32, 11, 110, 22, 220])) + spi.close() + + # /dev/spidev1.0 + spi = SPI(1, 0) + print(spi.xfer2([32, 11, 110, 22, 220])) + spi.close() + + # /dev/spidev1.1 + spi = SPI(1, 1) + print(spi.xfer2([32, 11, 110, 22, 220])) + spi.close() + +.. module:: Adafruit_BBIO.SPI + +.. class:: SPI(bus, client) + + :param bus: bus number. + :param client: client device number. + :returns: a new SPI object, optionally connected to the specified SPI + device interface. + :rtype: :class:`SPI` + + .. attribute:: bpw + + Bits per word. + + .. attribute:: cshigh + + Chip Select (CS or Slave Select, SS) active high. + + .. attribute:: loop + + Loopback configuration. + + .. attribute:: lsbfirst + + Least Significant Bit (LSB) first. + + .. attribute:: mode + + SPI mode as two bit pattern of Clock Polarity and Phase [CPOL|CPHA]; min-- 0b00 = 0, max-- 0b11 = 3. + + .. attribute:: msh + + Maximum speed in Hz. + + .. attribute:: threewire + + SI/SO signals are shared. + + .. method:: open(bus, device) + + Connects the object to the specified SPI device. `open(X, Y)` will open + `/dev/spidev-X.Y` + + :param int bus: bus number + :param str device: device number + + .. method:: close() + + Disconnects the object from the interface. + + .. method:: readbytes(len) + + Read the specified length of bytes from the SPI device. + + :param int len: length of bytes to read, 1024 maximum. + :returns: values read + :rtype: list[int] + + .. method:: writebytes(values) + + Write bytes to the SPI device. + + :param values: list of values to write, with a maximum length of 1024. + :type values: list[int] + + .. method:: xfer(values[,delay=0]) + + Perform an SPI transaction of values. Slave Select (SS or CS) will be + released and reactivated between blocks. + + :param values: list of values to transfer, with a maximum length of 1024. + :type values: list[int] + :param delay: delay in microseconds between blocks. + :returns: values transferred + :rtype: list[int] + + .. method:: xfer2(values) + + Perform an SPI transaction of values. Slave Select (SS or CS) will be + held active between blocks. + + :param values: list of values to transfer, with a maximum length of 1024. + :type values: list[int] + :returns: values transferred + :rtype: list[int] + diff --git a/docs/UART.rst b/docs/UART.rst new file mode 100644 index 0000000..c3d238d --- /dev/null +++ b/docs/UART.rst @@ -0,0 +1,35 @@ +:mod:`UART` --- UART communications interface +--------------------------------------------- + +UART functionality of a BeagleBone using Python. Generally used to set up +and grant access to a given UART device, which will then be accessed by +other software or modules (e.g. `pyserial`):: + + sudo pip install pyserial + +Example:: + + import Adafruit_BBIO.UART as UART + import serial + + UART.setup("UART1") + + with serial.Serial(port = "/dev/ttyO1", baudrate=9600) as ser: + print("Serial is open!") + ser.write(b"Hello World!") + +.. module:: Adafruit_BBIO.UART + +.. function:: setup_uart(channel) + + Set up and start the UART channel. This function will effectively export + the given UART so that it can be accessed by other software that controls + its serial lines. + + :param str channel: UART channel to set up. One of "UART1", "UART2", + "UART4" or "UART5" + +.. function:: cleanup() + + Cleans up the UART. + diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..6dfd6ad --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,190 @@ +# -*- coding: utf-8 -*- +# +# Adafruit-BBIO documentation build configuration file, created by +# sphinx-quickstart on Fri Dec 1 12:56:03 2017. +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +import os +import sys +sys.path.insert(0, os.path.abspath('../')) + + +# -- General configuration ------------------------------------------------ + +# If your documentation needs a minimal Sphinx version, state it here. +# +# needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = ['sphinx.ext.autodoc', 'sphinx.ext.napoleon'] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# +# source_suffix = ['.rst', '.md'] +source_suffix = '.rst' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'Adafruit-BBIO' +copyright = u'2017, Adafruit Industries and contributors' +author = u'Justin Cooper and contributors' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = u'1.0' +# The full version, including alpha/beta/rc tags. +release = u'' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = None + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This patterns also effect to html_static_path and html_extra_path +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = False + + +# -- Options for HTML output ---------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +# html_theme = 'alabaster' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +# +# html_theme_options = {} + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# Custom sidebar templates, must be a dictionary that maps document names +# to template names. +# +# This is required for the alabaster theme +# refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars + +# Use the readthedocs theme if the documentation is being built there, or +# use a Sphinx theme if the documentation is being built locally +on_rtd = os.environ.get('READTHEDOCS') == 'True' +if on_rtd: + html_theme = 'default' + html_theme_options = { + 'collapse_navigation': False, + } +else: + html_theme = 'alabaster' + html_sidebars = { + '**': [ + 'searchbox.html', + 'localtoc.html' + ] + } + # If you want to use the same theme as readthedocs, uncomment the lines + # below and install the readthedocs theme (pip install sphinx_rtd_theme) + # before doing a new build: + #html_theme = 'sphinx_rtd_theme' + #html_theme_options = { + # 'collapse_navigation': False, + #} + +# Do not show the "View source" link that shows the .rst files +html_copy_source = False +html_show_sourcelink = False + +# -- Options for HTMLHelp output ------------------------------------------ + +# Output file base name for HTML help builder. +htmlhelp_basename = 'Adafruit-BBIOdoc' + + +# -- Options for LaTeX output --------------------------------------------- + +latex_elements = { + # The paper size ('letterpaper' or 'a4paper'). + # + # 'papersize': 'letterpaper', + + # The font size ('10pt', '11pt' or '12pt'). + # + # 'pointsize': '10pt', + + # Additional stuff for the LaTeX preamble. + # + # 'preamble': '', + + # Latex figure (float) alignment + # + # 'figure_align': 'htbp', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + (master_doc, 'Adafruit-BBIO.tex', u'Adafruit-BBIO Documentation', + u'Justin Cooper', 'manual'), +] + + +# -- Options for manual page output --------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + (master_doc, 'adafruit-bbio', u'Adafruit-BBIO Documentation', + [author], 1) +] + + +# -- Options for Texinfo output ------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + (master_doc, 'Adafruit-BBIO', u'Adafruit-BBIO Documentation', + author, 'Adafruit-BBIO', 'One line description of project.', + 'Miscellaneous'), +] + +autodoc_member_order = 'groupwise' +#autoclass_content = 'both' + diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..a663628 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,29 @@ +.. Adafruit-BBIO documentation master file, created by + sphinx-quickstart on Fri Dec 1 12:56:03 2017. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Adafruit Beaglebone IO Python API +================================= + +The Adafruit Beaglebone IO API enables access to the Beaglebone's GPIO, PWM, +ADC, UART, SPI and eQEP hardware modules from Python programs. + +.. toctree:: + :maxdepth: 4 + :caption: Contents + + ADC + Encoder + GPIO + PWM + SPI + UART + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` + + diff --git a/install_all_python_versions.sh b/install_all_python_versions.sh new file mode 100755 index 0000000..cc19011 --- /dev/null +++ b/install_all_python_versions.sh @@ -0,0 +1,7 @@ +#!/bin/bash +# useful for testing changes against all versions of python +make clean +echo "Install Python 2" +python2 ./setup.py install +echo "Install Python 3" +python3 ./setup.py install diff --git a/m4/.gitkeep b/m4/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/m4/gtest.m4 b/m4/gtest.m4 new file mode 100644 index 0000000..803f217 --- /dev/null +++ b/m4/gtest.m4 @@ -0,0 +1,16 @@ +AC_DEFUN([CHECK_GTEST], +[ + AC_MSG_CHECKING([for google test]) + + AC_LANG_PUSH([C++]) + AC_CHECK_HEADER([gtest/gtest.h],[gtest_found=yes]) + AC_LANG_POP + + #if test "x$gtest_found" = "xyes"; then + # GTEST_CPPFLAGS= + # AC_SUBST(GTEST_CPPFLAGS) + #fi + + AC_SUBST(HAVE_GTEST) + AM_CONDITIONAL([HAVE_GTEST], [test "x$gtest_found" = "xyes"]) +]) diff --git a/m4/kernel-release.m4 b/m4/kernel-release.m4 new file mode 100644 index 0000000..b776fdf --- /dev/null +++ b/m4/kernel-release.m4 @@ -0,0 +1,14 @@ +AC_DEFUN([CHECK_KERNEL_RELEASE], +[ + AC_MSG_CHECKING([if kernel release >= 4.1]) + + kernel_ge_41=$(python -c 'import platform; print("yes" if platform.release() >= "4.1.0" else "no")') + + if test "x$kernel_ge_41" = "xyes"; then + CPPFLAGS="$CPPFLAGS -DBBBVERSION41" + AC_SUBST(CPPFLAGS) + AC_MSG_RESULT(yes) + else + AC_MSG_RESULT(no) + fi +]) diff --git a/overlays/builder.py b/overlays/builder.py index a358415..cddedc2 100644 --- a/overlays/builder.py +++ b/overlays/builder.py @@ -5,13 +5,13 @@ def compile(): #SPI Overlays - call(["dtc", "-O", "dtb", "-o", "overlays/ADAFRUIT-SPI0-00A0.dtbo", "-b", "o", "-@", "overlays/ADAFRUIT-SPI0-00A0.dts"]) - call(["dtc", "-O", "dtb", "-o", "overlays/ADAFRUIT-SPI1-00A0.dtbo", "-b", "o", "-@", "overlays/ADAFRUIT-SPI1-00A0.dts"]) + call(["dtc", "-Wno-unit_address_vs_reg", "-O", "dtb", "-o", "overlays/ADAFRUIT-SPI0-00A0.dtbo", "-b", "o", "-@", "overlays/ADAFRUIT-SPI0-00A0.dts"]) + call(["dtc", "-Wno-unit_address_vs_reg", "-O", "dtb", "-o", "overlays/ADAFRUIT-SPI1-00A0.dtbo", "-b", "o", "-@", "overlays/ADAFRUIT-SPI1-00A0.dts"]) #UART Overlayss - call(["dtc", "-O", "dtb", "-o", "overlays/ADAFRUIT-UART1-00A0.dtbo", "-b", "o", "-@", "overlays/ADAFRUIT-UART1-00A0.dts"]) - call(["dtc", "-O", "dtb", "-o", "overlays/ADAFRUIT-UART2-00A0.dtbo", "-b", "o", "-@", "overlays/ADAFRUIT-UART2-00A0.dts"]) - call(["dtc", "-O", "dtb", "-o", "overlays/ADAFRUIT-UART4-00A0.dtbo", "-b", "o", "-@", "overlays/ADAFRUIT-UART4-00A0.dts"]) - call(["dtc", "-O", "dtb", "-o", "overlays/ADAFRUIT-UART5-00A0.dtbo", "-b", "o", "-@", "overlays/ADAFRUIT-UART5-00A0.dts"]) + call(["dtc", "-Wno-unit_address_vs_reg", "-O", "dtb", "-o", "overlays/ADAFRUIT-UART1-00A0.dtbo", "-b", "o", "-@", "overlays/ADAFRUIT-UART1-00A0.dts"]) + call(["dtc", "-Wno-unit_address_vs_reg", "-O", "dtb", "-o", "overlays/ADAFRUIT-UART2-00A0.dtbo", "-b", "o", "-@", "overlays/ADAFRUIT-UART2-00A0.dts"]) + call(["dtc", "-Wno-unit_address_vs_reg", "-O", "dtb", "-o", "overlays/ADAFRUIT-UART4-00A0.dtbo", "-b", "o", "-@", "overlays/ADAFRUIT-UART4-00A0.dts"]) + call(["dtc", "-Wno-unit_address_vs_reg", "-O", "dtb", "-o", "overlays/ADAFRUIT-UART5-00A0.dtbo", "-b", "o", "-@", "overlays/ADAFRUIT-UART5-00A0.dts"]) def copy(): for fl in glob.glob("/lib/firmware/ADAFRUIT-SPI*-00A0.dtbo"): os.remove(fl) @@ -24,4 +24,4 @@ def copy(): shutil.move("overlays/ADAFRUIT-UART1-00A0.dtbo", "/lib/firmware/ADAFRUIT-UART1-00A0.dtbo") shutil.move("overlays/ADAFRUIT-UART2-00A0.dtbo", "/lib/firmware/ADAFRUIT-UART2-00A0.dtbo") shutil.move("overlays/ADAFRUIT-UART4-00A0.dtbo", "/lib/firmware/ADAFRUIT-UART4-00A0.dtbo") - shutil.move("overlays/ADAFRUIT-UART5-00A0.dtbo", "/lib/firmware/ADAFRUIT-UART5-00A0.dtbo") \ No newline at end of file + shutil.move("overlays/ADAFRUIT-UART5-00A0.dtbo", "/lib/firmware/ADAFRUIT-UART5-00A0.dtbo") diff --git a/pytest_all_versions.sh b/pytest_all_versions.sh new file mode 100755 index 0000000..76d46e6 --- /dev/null +++ b/pytest_all_versions.sh @@ -0,0 +1,9 @@ +#!/bin/bash +# useful for testing changes against all versions of python + +cd test +echo "Testing Python 2" +python2 -mpytest +echo "Testing Python 3" +python3 -mpytest +cd .. diff --git a/setup.py b/setup.py index 269ec6e..a6f5891 100644 --- a/setup.py +++ b/setup.py @@ -21,7 +21,7 @@ else: kernel41 = None -CFLAGS = ['-Wall', '-Werror', '-Wextra', '-Wno-missing-field-initializers', '-Wno-strict-aliasing' ] +CFLAGS = ['-Wall'] classifiers = ['Development Status :: 3 - Alpha', 'Operating System :: POSIX :: Linux', @@ -33,21 +33,28 @@ 'Topic :: Home Automation', 'Topic :: System :: Hardware'] +extension_args = { + 'include_dirs': ['source/include/'], + 'extra_compile_args': CFLAGS, + 'define_macros': kernel41 +} + setup(name = 'Adafruit_BBIO', - version = '1.0.3', + version = '1.2.0', author = 'Justin Cooper', author_email = 'justin@adafruit.com', description = 'A module to control BeagleBone IO channels', - long_description = open_as_utf8('README.rst').read() + open_as_utf8('CHANGELOG.rst').read(), + long_description = open_as_utf8('README.md').read() + open_as_utf8('CHANGELOG.md').read(), + long_description_content_type = 'text/markdown', license = 'MIT', keywords = 'Adafruit BeagleBone IO GPIO PWM ADC', url = 'https://github.com/adafruit/adafruit-beaglebone-io-python/', classifiers = classifiers, packages = find_packages(), py_modules = ['Adafruit_I2C'], - ext_modules = [Extension('Adafruit_BBIO.GPIO', ['source/py_gpio.c', 'source/event_gpio.c', 'source/c_pinmux.c', 'source/constants.c', 'source/common.c'], extra_compile_args=CFLAGS, define_macros=kernel41), - Extension('Adafruit_BBIO.PWM', ['source/py_pwm.c', 'source/c_pwm.c', 'source/c_pinmux.c', 'source/constants.c', 'source/common.c'], extra_compile_args=CFLAGS, define_macros=kernel41), - Extension('Adafruit_BBIO.ADC', ['source/py_adc.c', 'source/c_adc.c', 'source/constants.c', 'source/common.c'], extra_compile_args=CFLAGS, define_macros=kernel41), - Extension('Adafruit_BBIO.SPI', ['source/spimodule.c', 'source/constants.c', 'source/common.c'], extra_compile_args=CFLAGS, define_macros=kernel41), - Extension('Adafruit_BBIO.UART', ['source/py_uart.c', 'source/c_uart.c', 'source/constants.c', 'source/common.c'], extra_compile_args=CFLAGS, define_macros=kernel41)] ) + ext_modules = [Extension('Adafruit_BBIO.GPIO', ['source/py_gpio.c', 'source/event_gpio.c', 'source/c_pinmux.c', 'source/constants.c', 'source/common.c'], **extension_args), + Extension('Adafruit_BBIO.PWM', ['source/py_pwm.c', 'source/c_pwm.c', 'source/c_pinmux.c', 'source/constants.c', 'source/common.c'], **extension_args), + Extension('Adafruit_BBIO.ADC', ['source/py_adc.c', 'source/c_adc.c', 'source/constants.c', 'source/common.c'], **extension_args), + Extension('Adafruit_BBIO.SPI', ['source/spimodule.c', 'source/c_pinmux.c', 'source/constants.c', 'source/common.c'], **extension_args), + Extension('Adafruit_BBIO.UART', ['source/py_uart.c', 'source/c_pinmux.c', 'source/c_uart.c', 'source/constants.c', 'source/common.c'], **extension_args)] ) diff --git a/source/Makefile.am b/source/Makefile.am new file mode 100644 index 0000000..a1ee6d1 --- /dev/null +++ b/source/Makefile.am @@ -0,0 +1,128 @@ +################################################################################ +# Copyright (c) 2017 Adafruit +# Copyright (c) 2017 Nikolay Semenov +################################################################################ + +################################################################################ +# Global flags +################################################################################ +AM_CPPFLAGS = + +AM_CPPFLAGS += \ + -DNDEBUG \ + -g -O2 \ + -fstack-protector-strong \ + -fPIC + +AM_CPPFLAGS += \ + -Wall \ + -Wextra \ + -Werror + +AM_CPPFLAGS += \ + -DNO_PYTHON + +AM_CPPFLAGS += \ + -I$(srcdir)/include/ + +AM_CXXFLAGS = \ + -std=c++11 + +################################################################################ +# AdafruitBBIO library +################################################################################ +# libadafruit-bbio1 - c++11 ABI +# libadafruit-bbio0 - c++98 ABI +lib_LTLIBRARIES = \ + libadafruit-bbio1.la \ + libadafruit-bbio0.la + +adafruitdir = $(includedir)/adafruit +adafruit_HEADERS = \ + include/adafruit/bbio.h + +adafruit_bbiodir = $(adafruitdir)/bbio +adafruit_bbio_HEADERS = \ + include/adafruit/bbio/error.h \ + include/adafruit/bbio/gpio.h \ + include/adafruit/bbio/pwm.h + +LIBSOURCES = \ + c_adc.c \ + c_pinmux.c \ + c_pwm.c \ + c_uart.c \ + common.c \ + event_gpio.c \ + library/bbio.cpp \ + library/gpio.cpp \ + library/pwm.cpp + +libadafruit_bbio1_la_SOURCES = $(LIBSOURCES) +libadafruit_bbio0_la_SOURCES = $(LIBSOURCES) + +libadafruit_bbio1_la_CXXFLAGS = $(AM_CXXFLAGS) -D_GLIBCXX_USE_CXX11_ABI=1 +libadafruit_bbio0_la_CXXFLAGS = $(AM_CXXFLAGS) -D_GLIBCXX_USE_CXX11_ABI=0 + +libadafruit_bbio1_la_LDFLAGS = $(AM_LDFLAGS) -shared +libadafruit_bbio0_la_LDFLAGS = $(AM_LDFLAGS) -shared + +################################################################################ +# Examples +################################################################################ +noinst_PROGRAMS = examples/cpp/pwm + +examples_cpp_pwm_SOURCES = examples/cpp/pwm.cpp +examples_cpp_pwm_LDADD = libadafruit-bbio1.la +examples_cpp_pwm_LDFLAGS = $(AM_LDFLAGS) -lpthread -static + +noinst_PROGRAMS += examples/cpp/gpio + +examples_cpp_gpio_SOURCES = examples/cpp/gpio.cpp +examples_cpp_gpio_LDADD = libadafruit-bbio1.la +examples_cpp_gpio_LDFLAGS = $(AM_LDFLAGS) -lpthread -static + +################################################################################ +# Tests +################################################################################ +check_PROGRAMS = +TESTS = $(check_PROGRAMS) + +check_PROGRAMS += test/cpp/abi-old + +test_cpp_abi_old_SOURCES = test/cpp/abi.cpp +test_cpp_abi_old_CXXFLAGS = $(AM_CXXFLAGS) -pthread -D_GLIBCXX_USE_CXX11_ABI=0 +test_cpp_abi_old_LDADD = libadafruit-bbio0.la +test_cpp_abi_old_LDFLAGS = $(AM_LDFLAGS) -lpthread -static + +check_PROGRAMS += test/cpp/abi-new + +test_cpp_abi_new_SOURCES = test/cpp/abi.cpp +test_cpp_abi_new_CXXFLAGS = $(AM_CXXFLAGS) -pthread -D_GLIBCXX_USE_CXX11_ABI=1 +test_cpp_abi_new_LDADD = libadafruit-bbio1.la +test_cpp_abi_new_LDFLAGS = $(AM_LDFLAGS) -lpthread -static + +if HAVE_GTEST + +check_PROGRAMS += test/cpp/unittest + +gtest/src/gtest-all.cc: /usr/src/gtest/src + mkdir -p gtest && cp -Rvf $< gtest + +gtest/src/gtest_main.cc: gtest/src/gtest-all.cc + +BUILT_SOURCES = \ + gtest/src/gtest-all.cc \ + gtest/src/gtest_main.cc + +test_cpp_unittest_SOURCES = \ + test/cpp/pwm.cpp \ + gtest/src/gtest-all.cc \ + gtest/src/gtest_main.cc + +test_cpp_unittest_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_CPPFLAGS) -isystem gtest +test_cpp_unittest_CXXFLAGS = $(AM_CXXFLAGS) -pthread -D_GLIBCXX_USE_CXX11_ABI=1 +test_cpp_unittest_LDADD = libadafruit-bbio1.la +test_cpp_unittest_LDFLAGS = $(AM_LDFLAGS) -lpthread -static + +endif # HAVE_GTEST diff --git a/source/c_adc.c b/source/c_adc.c index b7f8f93..d496366 100644 --- a/source/c_adc.c +++ b/source/c_adc.c @@ -41,9 +41,9 @@ int adc_initialized = 0; BBIO_err initialize_adc(void) { #ifdef BBBVERSION41 - char test_path[49]; + char test_path[149]; #else - char test_path[40]; + char test_path[140]; #endif FILE *fh; BBIO_err err; @@ -57,6 +57,7 @@ BBIO_err initialize_adc(void) if (err == BBIO_OK) { strncat(adc_prefix_dir, "/sys/bus/iio/devices/iio:device0/in_voltage", sizeof(adc_prefix_dir)); snprintf(test_path, sizeof(test_path), "%s%d_raw", adc_prefix_dir, 1); + sleep(1); fh = fopen(test_path, "r"); if (!fh) { @@ -93,10 +94,10 @@ BBIO_err read_value(unsigned int ain, float *value) { FILE * fh; #ifdef BBBVERSION41 - char ain_path[49]; + char ain_path[149]; snprintf(ain_path, sizeof(ain_path), "%s%d_raw", adc_prefix_dir, ain); #else - char ain_path[40]; + char ain_path[140]; snprintf(ain_path, sizeof(ain_path), "%s%d", adc_prefix_dir, ain); #endif diff --git a/source/c_pinmux.c b/source/c_pinmux.c index b392d2d..23f1030 100644 --- a/source/c_pinmux.c +++ b/source/c_pinmux.c @@ -1,5 +1,6 @@ #include #include +#include #include "c_pinmux.h" #include "common.h" @@ -8,11 +9,16 @@ BBIO_err set_pin_mode(const char *key, const char *mode) { // char ocp_dir[] defined in common.h - char path[60]; // "/sys/devices/platform/ocp/ocp:P#_##_pinmux/state" + char path[100]; // "/sys/devices/platform/ocp/ocp:P#_##_pinmux/state" char pinmux_dir[20]; // "ocp:P#_##_pinmux" char pin[6]; //"P#_##" FILE *f = NULL; - + + // pin mode can not be set for the built-in USRn LEDs + if (strncmp(key, "USR", 3) == 0) { + return BBIO_OK; + } + if (strlen(key) == 4) // Key P#_# format, must inject '0' to be P#_0# snprintf(pin, sizeof(pin), "%.3s0%c", key,key[3]); else //copy string @@ -31,13 +37,21 @@ BBIO_err set_pin_mode(const char *key, const char *mode) snprintf(pinmux_dir, sizeof(pinmux_dir), "ocp:%s_pinmux", pin); snprintf(path, sizeof(path), "%s/%s/state", ocp_dir, pinmux_dir); + /* beaglebone blue has complete dtb file and does not need overlays */ + if(beaglebone_blue()) { + fprintf(stderr, "DEBUG: Adafruit_BBIO: set_pin_mode() :: Pinmux file: %s, mode: %s", path, mode); + fprintf(stderr, "DEBUG: Adafruit_BBIO: set_pin_mode(): beaglebone_blue() is TRUE; return BBIO_OK\n"); + return BBIO_OK; + } + + f = fopen(path, "w"); if (NULL == f) { return BBIO_ACCESS; - } - + } + syslog(LOG_DEBUG, "Adafruit_BBIO: set_pin_mode() :: Pinmux file %s access OK", path); fprintf(f, "%s", mode); fclose(f); - + syslog(LOG_DEBUG, "Adafruit_BBIO: set_pin_mode() :: Set pinmux mode to %s for %s", mode, pin); return BBIO_OK; } diff --git a/source/c_pwm.c b/source/c_pwm.c index 060239f..a5ef5ec 100644 --- a/source/c_pwm.c +++ b/source/c_pwm.c @@ -24,9 +24,10 @@ SOFTWARE. #include #include #include +#include #include #include -#include +#include #include #include @@ -71,6 +72,7 @@ struct pwm_exp *lookup_exported_pwm(const char *key) pwm = pwm->next; } + syslog(LOG_DEBUG, "Adafruit_BBIO: lookup_exported_pwm: couldn't find '%s'", key); return NULL; /* standard for pointers */ } @@ -92,6 +94,14 @@ void export_pwm(struct pwm_exp *new_pwm) } } +int is_dmtimer_pin(pwm_t *p) { + char temp[6]; + strncpy(temp, p->module, 5); + temp[5] = '\0'; + + return (strcmp(temp, "timer") == 0); +} + BBIO_err initialize_pwm(void) { #ifdef BBBVERSION41 // don't load overlay in 4.1+ @@ -107,24 +117,29 @@ BBIO_err initialize_pwm(void) } #endif pwm_initialized = 1; + syslog(LOG_DEBUG, "Adafruit_BBIO: initialize_pwm: OK"); return BBIO_OK; } + syslog(LOG_DEBUG, "Adafruit_BBIO: initialize_pwm: OK"); return BBIO_OK; } BBIO_err pwm_set_frequency(const char *key, float freq) { int len; - char buffer[20]; + char buffer[100]; unsigned long period_ns; struct pwm_exp *pwm; - if (freq <= 0.0) + if (freq <= 0.0) { + syslog(LOG_ERR, "Adafruit_BBIO: pwm_set_frequency: %s freq %f <= 0.0", key, freq); return BBIO_INVARG; + } pwm = lookup_exported_pwm(key); if (pwm == NULL) { + syslog(LOG_ERR, "Adafruit_BBIO: pwm_set_frequency: %s couldn't find key", key); return BBIO_GEN; } @@ -141,6 +156,8 @@ BBIO_err pwm_set_frequency(const char *key, float freq) { len = snprintf(buffer, sizeof(buffer), "%lu", pwm->duty_ns); lseek(pwm->duty_fd, 0, SEEK_SET); // Seek to beginning of file if (write(pwm->duty_fd, buffer, len) < 0) { + syslog(LOG_ERR, "Adafruit_BBIO: pwm_set_frequency: %s couldn't write duty: %i-%s", + key, errno, strerror(errno)); return BBIO_SYSFS; } @@ -148,6 +165,8 @@ BBIO_err pwm_set_frequency(const char *key, float freq) { len = snprintf(buffer, sizeof(buffer), "%lu", period_ns); lseek(pwm->period_fd, 0, SEEK_SET); // Seek to beginning of file if (write(pwm->period_fd, buffer, len) < 0) { + syslog(LOG_ERR, "Adafruit_BBIO: pwm_set_frequency: %s couldn't write period: %i-%s", + key, errno, strerror(errno)); return BBIO_SYSFS; } @@ -161,6 +180,8 @@ BBIO_err pwm_set_frequency(const char *key, float freq) { len = snprintf(buffer, sizeof(buffer), "%lu", period_ns); lseek(pwm->period_fd, 0, SEEK_SET); // Seek to beginning of file if (write(pwm->period_fd, buffer, len) < 0) { + syslog(LOG_ERR, "Adafruit_BBIO: pwm_set_frequency: %s couldn't write period: %i-%s", + key, errno, strerror(errno)); return BBIO_SYSFS; } @@ -169,17 +190,20 @@ BBIO_err pwm_set_frequency(const char *key, float freq) { len = snprintf(buffer, sizeof(buffer), "%lu", pwm->duty_ns); lseek(pwm->duty_fd, 0, SEEK_SET); // Seek to beginning of file if (write(pwm->duty_fd, buffer, len) < 0) { + syslog(LOG_ERR, "Adafruit_BBIO: pwm_set_frequency: %s couldn't write duty: %i-%s", + key, errno, strerror(errno)); return BBIO_SYSFS; } } // else do nothing + syslog(LOG_DEBUG, "Adafruit_BBIO: pwm_set_frequency: %s %f OK", key, freq); return BBIO_OK; } // Only works before chip is enabled BBIO_err pwm_set_polarity(const char *key, int polarity) { int len; - char buffer[9]; /* allow room for trailing NUL byte */ + char buffer[100]; /* allow room for trailing NUL byte */ struct pwm_exp *pwm; #ifdef BBBVERSION41 int enabled; /* Maintain original state */ @@ -188,6 +212,7 @@ BBIO_err pwm_set_polarity(const char *key, int polarity) { pwm = lookup_exported_pwm(key); if (pwm == NULL) { + syslog(LOG_ERR, "Adafruit_BBIO: pwm_set_polarity: %s couldn't find key", key); return BBIO_GEN; } @@ -198,6 +223,8 @@ BBIO_err pwm_set_polarity(const char *key, int polarity) { memset(buffer, 0, 9); // Initialize buffer lseek(pwm->enable_fd, 0, SEEK_SET); if (read(pwm->enable_fd, buffer, len) < 0) { + syslog(LOG_ERR, "Adafruit_BBIO: pwm_set_polarity: %s couldn't read enable: %i-%s", + key, errno, strerror(errno)); return BBIO_SYSFS; } @@ -209,6 +236,8 @@ BBIO_err pwm_set_polarity(const char *key, int polarity) { lseek(pwm->enable_fd, 0, SEEK_SET); len = snprintf(buffer, sizeof(buffer), "0"); if (write(pwm->enable_fd, buffer, len) < 0) { + syslog(LOG_ERR, "Adafruit_BBIO: pwm_set_polarity: %s couldn't write enable: %i-%s", + key, errno, strerror(errno)); return BBIO_SYSFS; } } @@ -221,6 +250,7 @@ BBIO_err pwm_set_polarity(const char *key, int polarity) { } else if (polarity == 1) { len = snprintf(buffer, sizeof(buffer), "inversed"); } else { + syslog(LOG_ERR, "Adafruit_BBIO: pwm_set_polarity: %s invalid argument value: %i", key, polarity); return BBIO_INVARG; } #else @@ -229,6 +259,8 @@ BBIO_err pwm_set_polarity(const char *key, int polarity) { lseek(pwm->polarity_fd, 0, SEEK_SET); // Seek to beginning of file if (write(pwm->polarity_fd, buffer, len) < 0) { + syslog(LOG_ERR, "Adafruit_BBIO: pwm_set_polarity: %s couldn't write polarity: %i-%s", + key, errno, strerror(errno)); return BBIO_SYSFS; } @@ -238,17 +270,20 @@ BBIO_err pwm_set_polarity(const char *key, int polarity) { lseek(pwm->enable_fd, 0, SEEK_SET); len = snprintf(buffer, sizeof(buffer), "1"); if (write(pwm->enable_fd, buffer, len) < 0) { + syslog(LOG_ERR, "Adafruit_BBIO: pwm_set_polarity: %s couldn't write enable: %i-%s", + key, errno, strerror(errno)); return BBIO_SYSFS; } } #endif + syslog(LOG_DEBUG, "Adafruit_BBIO: pwm_set_polarity: %s %i OK", key, polarity); return BBIO_OK; } BBIO_err pwm_set_duty_cycle(const char *key, float duty) { int len; - char buffer[20]; + char buffer[100]; struct pwm_exp *pwm; if (duty < 0.0 || duty > 100.0) @@ -257,6 +292,7 @@ BBIO_err pwm_set_duty_cycle(const char *key, float duty) { pwm = lookup_exported_pwm(key); if (pwm == NULL) { + syslog(LOG_ERR, "Adafruit_BBIO: pwm_set_duty_cycle: %s couldn't find key", key); return BBIO_GEN; } @@ -266,9 +302,12 @@ BBIO_err pwm_set_duty_cycle(const char *key, float duty) { len = snprintf(buffer, sizeof(buffer), "%lu", pwm->duty_ns); lseek(pwm->duty_fd, 0, SEEK_SET); // Seek to beginning of file if (write(pwm->duty_fd, buffer, len) < 0) { + syslog(LOG_ERR, "Adafruit_BBIO: pwm_set_duty_cycle: %s couldn't write duty: %i-%s", + key, errno, strerror(errno)); return BBIO_SYSFS; } + syslog(LOG_DEBUG, "Adafruit_BBIO: pwm_set_duty_cycle: %s %f OK", key, duty); return BBIO_OK; } @@ -278,15 +317,17 @@ BBIO_err pwm_setup(const char *key, __attribute__ ((unused)) float duty, __attri struct pwm_exp *new_pwm; #ifdef BBBVERSION41 - char pwm_dev_path[45]; // "/sys/devices/platform/ocp/48300000.epwmss" - char pwm_addr_path[60]; // "/sys/devices/platform/ocp/48300000.epwmss/48300200.ehrpwm" - char pwm_chip_path[75]; // "/sys/devices/platform/ocp/48300000.epwmss/48300200.ehrpwm/pwm/pwmchip0" - char pwm_export_path[80]; // "/sys/devices/platform/ocp/48300000.epwmss/48300200.ehrpwm/pwm/pwmchip0/export" - char pwm_path[80]; // "/sys/devices/platform/ocp/48300000.epwmss/48300200.ehrpwm/pwm/pwmchip0/pwm1" - char duty_path[90]; // "/sys/devices/platform/ocp/48300000.epwmss/48300200.ehrpwm/pwm/pwmchip0/pwm1/duty_cycle" - char period_path[90]; - char polarity_path[90]; - char enable_path[90]; + char pwm_dev_path[100]; // "/sys/devices/platform/ocp/48300000.epwmss" + char pwm_addr_path[150]; // "/sys/devices/platform/ocp/48300000.epwmss/48300200.ehrpwm" + char pwm_chip_path[200]; // "/sys/devices/platform/ocp/48300000.epwmss/48300200.ehrpwm/pwm/pwmchip0" + char pwm_export_path[250]; // "/sys/devices/platform/ocp/48300000.epwmss/48300200.ehrpwm/pwm/pwmchip0/export" + char pwm_path[250]; // "/sys/devices/platform/ocp/48300000.epwmss/48300200.ehrpwm/pwm/pwmchip0/pwm2" + char pwm_path_udev[250]; // "/sys/devices/platform/ocp/48300000.epwmss/48300200.ehrpwm/pwm/pwmchip0/pwm-0:2" + char ecap_path_udev[300]; // "/sys/devices/platform/ocp/48300000.epwmss/48300200.ecap/pwm/pwmchip0/pwm-0:0/" + char duty_path[300]; // "/sys/devices/platform/ocp/48300000.epwmss/48300200.ehrpwm/pwm/pwmchip0/pwm2/duty_cycle" + char period_path[300]; + char polarity_path[300]; + char enable_path[300]; char pin_mode[PIN_MODE_LEN]; // "pwm" or "pwm2" int e; @@ -298,12 +339,19 @@ BBIO_err pwm_setup(const char *key, __attribute__ ((unused)) float duty, __attri if (!pwm_initialized) { err = initialize_pwm(); if (err != BBIO_OK) { + syslog(LOG_ERR, "Adafruit_BBIO: pwm_setup: %s couldn't initialize pwm: %i", key, err); return err; } } // Make sure that one of the universal capes is loaded - if (!( device_tree_loaded("cape-univ-audio") // from cdsteinkuehler/beaglebone-universal-io + if( !uboot_overlay_enabled() // only check kernel overlays if u-boot overlays are not being used + && + !beaglebone_blue() // beaglebone blue has complete dtb file and does not need overlays + && + !pocketbeagle() // pocketbeagle has complete dtb file and does not need overlays + && + !( device_tree_loaded("cape-univ-audio") // from cdsteinkuehler/beaglebone-universal-io || device_tree_loaded("cape-univ-emmc") // "" || device_tree_loaded("cape-univ-hdmi") // "" || device_tree_loaded("cape-universal") // "" @@ -315,6 +363,7 @@ BBIO_err pwm_setup(const char *key, __attribute__ ((unused)) float duty, __attri || device_tree_loaded("univ-hdmi") // "" || device_tree_loaded("univ-nhdmi"))) // "" { + syslog(LOG_ERR, "Adafruit_BBIO: pwm_setup: %s no suitable cape loaded", key); return BBIO_CAPE; } // Do pinmuxing @@ -330,25 +379,48 @@ BBIO_err pwm_setup(const char *key, __attribute__ ((unused)) float duty, __attri // Get info for pwm err = get_pwm_by_key(key, &p); if (err != BBIO_OK) { + syslog(LOG_ERR, "Adafruit_BBIO: pwm_setup: %s couldn't get pwm: %i", key, err); return err; } - err = build_path(ocp_dir, p->chip, pwm_dev_path, sizeof(pwm_dev_path)); - if (err != BBIO_OK) { - return err; - } + int dmtimer_pin = is_dmtimer_pin(p); - err = build_path(pwm_dev_path, p->addr, pwm_addr_path, sizeof(pwm_addr_path)); - if (err != BBIO_OK) { - return err; + if(!dmtimer_pin) { + err = build_path(ocp_dir, p->chip, pwm_dev_path, sizeof(pwm_dev_path)); + if (err != BBIO_OK) { + syslog(LOG_ERR, "Adafruit_BBIO: pwm_setup: %s couldn't build pwm_dev_path: %i", key, err); + return err; + } + + err = build_path(pwm_dev_path, p->addr, pwm_addr_path, sizeof(pwm_addr_path)); + if (err != BBIO_OK) { + syslog(LOG_ERR, "Adafruit_BBIO: pwm_setup: %s couldn't build pwm_addr_path: %i", key, err); + return err; + } + } + else { + err = build_path("/sys/devices/platform", p->addr, pwm_addr_path, sizeof(pwm_addr_path)); + if (err != BBIO_OK) { + syslog(LOG_ERR, "Adafruit_BBIO: pwm_setup: %s couldn't build pwm_addr_path, are you sure you've loaded the correct dmtimer device tree overlay?: %i", key, err); + return err; + } } err = build_path(pwm_addr_path, "pwm/pwmchip", pwm_chip_path, sizeof(pwm_chip_path)); if (err != BBIO_OK) { + syslog(LOG_ERR, "Adafruit_BBIO: pwm_setup: %s couldn't build pwm_chip_path: %i", key, err); return err; } snprintf(pwm_path, sizeof(pwm_path), "%s/pwm%d", pwm_chip_path, p->index); + syslog(LOG_DEBUG, "Adafruit_BBIO: pwm_start: key: %s, pwm_path: %s", key, pwm_path); + + //pwm with udev patch + snprintf(pwm_path_udev, sizeof(pwm_path_udev), "%s/pwm-%c:%d", pwm_chip_path, dmtimer_pin ? pwm_path[47] : pwm_path[66], p->index); + syslog(LOG_DEBUG, "Adafruit_BBIO: pwm_start: key: %s, pwm_path_udev: %s", key, pwm_path_udev); + //ecap output with udev patch + snprintf(ecap_path_udev, sizeof(ecap_path_udev), "%s/pwm-%c:%d", pwm_chip_path, dmtimer_pin ? pwm_path[47] : pwm_path[67], p->index); + syslog(LOG_DEBUG, "Adafruit_BBIO: pwm_start: key: %s, ecap_path_udev: %s", key, ecap_path_udev); // Export PWM if hasn't already been e = stat(pwm_path, &s); @@ -357,11 +429,19 @@ BBIO_err pwm_setup(const char *key, __attribute__ ((unused)) float duty, __attri snprintf(pwm_export_path, sizeof(pwm_export_path), "%s/export", pwm_chip_path); f = fopen(pwm_export_path, "w"); if (f == NULL) { // Can't open the export file + syslog(LOG_ERR, "Adafruit_BBIO: pwm_setup: %s couldn't open %s: %i-%s", + key, pwm_export_path, errno, strerror(errno)); return BBIO_ACCESS; } fprintf(f, "%d", p->index); fclose(f); + /* sleep to avoid race condition as udev needs the + opportunity to set group ownership and permission */ + syslog(LOG_DEBUG, "Adafruit_BBIO: pwm_start: sleep 100 ms after export to avoid udev race condition"); + usleep(100 * 1000); /* 100 ms */ } else { + syslog(LOG_ERR, "Adafruit_BBIO: pwm_setup: %s couldn't stat %s: %i-%s", + key, pwm_path, errno, strerror(errno)); perror("stat"); return BBIO_GEN; } @@ -370,6 +450,7 @@ BBIO_err pwm_setup(const char *key, __attribute__ ((unused)) float duty, __attri /* It is a directory. Already exported */ } else { /* It's a file. Shouldn't ever happen */ + syslog(LOG_ERR, "Adafruit_BBIO: pwm_setup: %s %s is not a directory", key, pwm_path); return BBIO_GEN; } } @@ -377,20 +458,43 @@ BBIO_err pwm_setup(const char *key, __attribute__ ((unused)) float duty, __attri e = stat(pwm_path, &s); if (-1 == e) { if (ENOENT == errno) { - // Directory still doesn't exist, exit with error - return BBIO_GEN; + // Directory still doesn't exist, try the new udev pwm path format in 4.14 kernel + syslog(LOG_DEBUG, "Adafruit_BBIO: pwm_setup: key: %s pwm_path: %s doesn't exist", key, pwm_path); + + e = stat(pwm_path_udev, &s); + if (-1 == e) { + if (ENOENT == errno) { + // Directory still doesn't exist, try the new udev ecap path format in 4.14 kernel + syslog(LOG_DEBUG, "Adafruit_BBIO: pwm_setup: key: %s pwm_path_udev: %s doesn't exist", key, pwm_path_udev); + e = stat(ecap_path_udev, &s); + if (-1 == e) { + if (ENOENT == errno) { + // Directory still doesn't exist, exit with error + syslog(LOG_DEBUG, "Adafruit_BBIO: pwm_setup: key: %s ecap_path_udev: %s doesn't exist", key, ecap_path_udev); + syslog(LOG_ERR, "Adafruit_BBIO: pwm_setup: path for %s doesn't exist", key); + return BBIO_GEN; + } + } else { + strncpy(pwm_path, ecap_path_udev, sizeof(pwm_path)); + } + } + } else { + strncpy(pwm_path, pwm_path_udev, sizeof(pwm_path)); + usleep(100*1000); + } } } + syslog(LOG_DEBUG, "Adafruit_BBIO: pwm_setup: pwm_path=%s\n", pwm_path); snprintf(duty_path, sizeof(duty_path), "%s/duty_cycle", pwm_path); snprintf(enable_path, sizeof(enable_path), "%s/enable", pwm_path); #else - char fragment[18]; - char pwm_fragment[20]; - char pwm_path[45]; - char duty_path[56]; - char period_path[50]; - char polarity_path[55]; + char fragment[100]; + char pwm_fragment[100]; + char pwm_path[100]; + char duty_path[200]; + char period_path[100]; + char polarity_path[100]; int period_fd, duty_fd, polarity_fd; if (!pwm_initialized) { @@ -432,13 +536,17 @@ BBIO_err pwm_setup(const char *key, __attribute__ ((unused)) float duty, __attri snprintf(polarity_path, sizeof(polarity_path), "%s/polarity", pwm_path); //add period and duty fd to pwm list - if ((period_fd = open(period_path, O_RDWR)) < 0) + if ((period_fd = open(period_path, O_RDWR)) < 0) { + syslog(LOG_ERR, "Adafruit_BBIO: pwm_setup: %s couldn't open %s: %i-%s", + key, period_path, errno, strerror(errno)); return BBIO_SYSFS; + } if ((duty_fd = open(duty_path, O_RDWR)) < 0) { //error, close already opened period_fd. close(period_fd); - + syslog(LOG_ERR, "Adafruit_BBIO: pwm_setup: %s couldn't open %s: %i-%s", + key, duty_path, errno, strerror(errno)); return BBIO_SYSFS; } @@ -446,6 +554,8 @@ BBIO_err pwm_setup(const char *key, __attribute__ ((unused)) float duty, __attri //error, close already opened period_fd and duty_fd. close(period_fd); close(duty_fd); + syslog(LOG_ERR, "Adafruit_BBIO: pwm_setup: %s couldn't open %s: %i-%s", + key, polarity_path, errno, strerror(errno)); return BBIO_SYSFS; } @@ -455,6 +565,8 @@ BBIO_err pwm_setup(const char *key, __attribute__ ((unused)) float duty, __attri close(period_fd); close(duty_fd); close(polarity_fd); + syslog(LOG_ERR, "Adafruit_BBIO: pwm_setup: %s couldn't open %s: %i-%s", + key, enable_path, errno, strerror(errno)); return BBIO_SYSFS; } #endif @@ -465,6 +577,11 @@ BBIO_err pwm_setup(const char *key, __attribute__ ((unused)) float duty, __attri close(period_fd); close(duty_fd); close(polarity_fd); +#ifdef BBBVERSION41 + close(enable_fd); +#endif + syslog(LOG_ERR, "Adafruit_BBIO: pwm_setup: %s couldn't malloc pwm_exp: %i-%s", + key, errno, strerror(errno)); return BBIO_MEM; // out of memory } @@ -482,19 +599,24 @@ BBIO_err pwm_setup(const char *key, __attribute__ ((unused)) float duty, __attri export_pwm(new_pwm); + syslog(LOG_DEBUG, "Adafruit_BBIO: pwm_setup: %s OK", key); return BBIO_OK; } BBIO_err pwm_start(const char *key, float duty, float freq, int polarity) { + syslog(LOG_DEBUG, "Adafruit_BBIO: pwm_start: %s, %f, %f, %i", key, duty, freq, polarity); + //fprintf(stderr, "Adafruit_BBIO: pwm_start: %s, %f, %f, %i\n", key, duty, freq, polarity); + BBIO_err err; - char buffer[20]; + char buffer[100]; ssize_t len; struct pwm_exp *pwm = lookup_exported_pwm(key); if (pwm == NULL) { err = pwm_setup(key, duty, freq, polarity); if (err != BBIO_OK) { + syslog(LOG_ERR, "Adafruit_BBIO: pwm_start: %s pwm setup failed: %i", key, err); return err; } @@ -503,20 +625,18 @@ BBIO_err pwm_start(const char *key, float duty, float freq, int polarity) // If we somehow didn't start successfully if (pwm == NULL) { + syslog(LOG_ERR, "Adafruit_BBIO: pwm_start: %s pwm is NULL", key); return BBIO_GEN; } - err = pwm_set_polarity(key, polarity); - if (err != BBIO_OK) { - return err; - } - // Read out current period_ns from the file, in order for it to behave // properly memset(buffer, 0, sizeof(buffer)); // Initialize buffer lseek(pwm->period_fd, 0, SEEK_SET); len = read(pwm->period_fd, buffer, sizeof(buffer)); if (len < 0) { + syslog(LOG_ERR, "Adafruit_BBIO: pwm_start: %s couldn't read period: %i-%s", + key, errno, strerror(errno)); return BBIO_SYSFS; } else if (len >= (ssize_t)sizeof(buffer)) { // If this is the case, there's more in the file. @@ -533,6 +653,8 @@ BBIO_err pwm_start(const char *key, float duty, float freq, int polarity) lseek(pwm->duty_fd, 0, SEEK_SET); len = read(pwm->duty_fd, buffer, sizeof(buffer)); if (len < 0) { + syslog(LOG_ERR, "Adafruit_BBIO: pwm_start: %s couldn't read duty: %i-%s", + key, errno, strerror(errno)); return BBIO_SYSFS; } else if (len >= (ssize_t)sizeof(buffer)) { // If this is the case, there's more in the file. @@ -545,13 +667,24 @@ BBIO_err pwm_start(const char *key, float duty, float freq, int polarity) // Initialize pwm->duty to avoid weirdness pwm->duty = duty; + syslog(LOG_DEBUG, "Adafruit_BBIO: pwm_start: call pwm_set_frequency(key=%s freq=%f)", key, freq); err = pwm_set_frequency(key, freq); if (err != BBIO_OK) { + syslog(LOG_ERR, "Adafruit_BBIO: pwm_start: %s couldn't set duty frequency: %i", key, err); return err; } + syslog(LOG_DEBUG, "Adafruit_BBIO: pwm_start: call pwm_set_duty_cycle(key=%s duty=%f)", key, duty); err = pwm_set_duty_cycle(key, duty); if (err != BBIO_OK) { + syslog(LOG_ERR, "Adafruit_BBIO: pwm_start: %s couldn't set duty cycle: %i", key, err); + return err; + } + + syslog(LOG_DEBUG, "Adafruit_BBIO: pwm_start: call pwm_set_polarity(key=%s polarity=%d)", key, polarity); + err = pwm_set_polarity(key, polarity); + if (err != BBIO_OK) { + syslog(LOG_ERR, "Adafruit_BBIO: pwm_start: %s couldn't set polarity: %i", key, err); return err; } @@ -560,12 +693,16 @@ BBIO_err pwm_start(const char *key, float duty, float freq, int polarity) return BBIO_GEN; } len = snprintf(buffer, sizeof(buffer), "1"); + syslog(LOG_DEBUG, "Adafruit_BBIO: pwm_start: write 1 to pwm->enable_fd\n"); lseek(pwm->enable_fd, 0, SEEK_SET); if (write(pwm->enable_fd, buffer, len) < 0) { + syslog(LOG_ERR, "Adafruit_BBIO: pwm_start: %s couldn't write enable: %i-%s", + key, errno, strerror(errno)); return BBIO_SYSFS; } #endif + syslog(LOG_DEBUG, "Adafruit_BBIO: pwm_start: %s OK", key); return BBIO_OK; } @@ -576,7 +713,7 @@ BBIO_err pwm_disable(const char *key) #ifndef BBBVERSION41 BBIO_err err; - char fragment[18]; + char fragment[100]; snprintf(fragment, sizeof(fragment), "bone_pwm_%s", key); err = unload_device_tree(fragment); if (err != BBIO_OK) @@ -589,23 +726,28 @@ BBIO_err pwm_disable(const char *key) { if (strcmp(pwm->key, key) == 0) { - + #ifdef BBBVERSION41 - char buffer[2]; + char buffer[100]; size_t len; - + // Disable the PWM lseek(pwm->enable_fd, 0, SEEK_SET); len = snprintf(buffer, sizeof(buffer), "0"); if (write(pwm->enable_fd, buffer, len) < 0) { + syslog(LOG_ERR, "Adafruit_BBIO: pwm_disable: %s couldn't write enable: %i-%s", + key, errno, strerror(errno)); return BBIO_SYSFS; } // Unexport the PWM // TODO later #endif - + //close the fd +#ifdef BBBVERSION41 + close(pwm->enable_fd); +#endif close(pwm->period_fd); close(pwm->duty_fd); close(pwm->polarity_fd); @@ -626,6 +768,8 @@ BBIO_err pwm_disable(const char *key) pwm = pwm->next; } } + + syslog(LOG_DEBUG, "Adafruit_BBIO: pwm_disable: %s OK", key); return BBIO_OK; } diff --git a/source/c_pwm.h b/source/c_pwm.h index ee1c4ae..ba9f3f2 100644 --- a/source/c_pwm.h +++ b/source/c_pwm.h @@ -25,6 +25,7 @@ SOFTWARE. #include "common.h" +BBIO_err initialize_pwm(void); BBIO_err pwm_start(const char *key, float duty, float freq, int polarity); BBIO_err pwm_disable(const char *key); BBIO_err pwm_set_frequency(const char *key, float freq); diff --git a/source/c_uart.c b/source/c_uart.c index 211c54e..471c2a9 100644 --- a/source/c_uart.c +++ b/source/c_uart.c @@ -46,13 +46,13 @@ BBIO_err uart_cleanup(void) if (e1 != BBIO_OK) return e1; if (e2 != BBIO_OK) - return e1; + return e2; if (e3 != BBIO_OK) - return e1; + return e3; if (e4 != BBIO_OK) - return e1; + return e4; if (e5 != BBIO_OK) - return e1; + return e5; return BBIO_OK; } diff --git a/source/common.c b/source/common.c index ce6936c..f911d49 100644 --- a/source/common.c +++ b/source/common.c @@ -30,11 +30,19 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#include "Python.h" +#ifndef NO_PYTHON +# include "Python.h" +#endif // !NO_PYTHON + #include -#include -#include +#include #include +#include +#include +#include +#include +#include + #include "common.h" #include @@ -44,6 +52,12 @@ SOFTWARE. # endif #endif +int gpio_mode; +int gpio_direction[120]; + +char ctrl_dir[CTRL_DIR_MAX]; +char ocp_dir[OCP_DIR_MAX]; + int setup_error = 0; int module_setup = 0; @@ -55,7 +69,7 @@ typedef struct pins_t { int ain; } pins_t; -//Table generated based on https://raw.github.com/jadonk/bonescript/master/node_modules/bonescript/bone.js +//Table generated based on https://github.com/jadonk/bonescript/blob/master/src/bone.js pins_t table[] = { { "USR0", "USR0", 53, -1, -1}, { "USR1", "USR1", 54, -1, -1}, @@ -67,10 +81,10 @@ pins_t table[] = { { "GPIO1_7", "P8_4", 39, -1, -1}, { "GPIO1_2", "P8_5", 34, -1, -1}, { "GPIO1_3", "P8_6", 35, -1, -1}, - { "TIMER4", "P8_7", 66, -1, -1}, - { "TIMER7", "P8_8", 67, -1, -1}, - { "TIMER5", "P8_9", 69, -1, -1}, - { "TIMER6", "P8_10", 68, -1, -1}, + { "TIMER4", "P8_7", 66, 2, -1}, + { "TIMER7", "P8_8", 67, 2, -1}, + { "TIMER5", "P8_9", 69, 2, -1}, + { "TIMER6", "P8_10", 68, 2, -1}, { "GPIO1_13", "P8_11", 45, -1, -1}, { "GPIO1_12", "P8_12", 44, -1, -1}, { "EHRPWM2B", "P8_13", 23, 4, -1}, @@ -153,16 +167,112 @@ pins_t table[] = { { "DGND", "P9_44", 0, -1, -1}, { "DGND", "P9_45", 0, -1, -1}, { "DGND", "P9_46", 0, -1, -1}, - { NULL, NULL, 0 } + + // These are for the Blue + { "GP0_3", "GP0_3", 57, -1, -1}, + { "GP0_4", "GP0_4", 49, -1, -1}, + { "GP0_5", "GP0_5", 116, -1, -1}, + { "GP0_6", "GP0_6", 113, -1, -1}, + { "GP1_3", "GP1_3", 98, -1, -1}, + { "GP1_4", "GP1_4", 97, -1, -1}, + { "RED_LED", "RED", 66, -1, -1}, // LEDs + { "GREEN_LED", "GREEN", 67, -1, -1}, + { "BAT25", "BAT25", 27, -1, -1}, + { "BAT50", "BAT50", 11, -1, -1}, + { "BAT75", "BAT75", 61, -1, -1}, + { "BAT100", "BAT100", 10000, -1, -1}, // Placeholder + { "WIFI", "WIFI", 10001, -1, -1}, // Placeholder + + { "PAUSE", "P8_9", 69, 1, -1}, + { "MODE", "P8_10", 68, 1, -1}, + + // These are for the PocketBeagle + { "VIN_AC", "P1_1", 0, -1, -1}, + { "GPIO2_23", "P1_2", 87, -1, -1}, + { "USB1_DRVVBUS", "P1_3", 0, -1, -1}, + { "GPIO2_25", "P1_4", 89, -1, -1}, + { "USB1_VBUS_IN", "P1_5", 0, -1, -1}, + { "SPI0_CS0", "P1_6", 5, -1, -1}, + { "VIN-USB", "P1_7", 0, -1, -1}, + { "SPI0_SCLK", "P1_8", 2, 3, -1}, + { "USB1-DN", "P1_9", 0, -1, -1}, + { "SPI0_D0", "P1_10", 3, 3, -1}, + { "USB1-DP", "P1_11", 0, -1, -1}, + { "SPI0_D1", "P1_12", 4, -1, -1}, + { "USB1-ID", "P1_13", 0, -1, -1}, + { "VOUT-3.3V", "P1_14", 0, -1, -1}, + { "GND", "P1_15", 0, -1, -1}, + { "GND", "P1_16", 0, -1, -1}, + { "VREFN", "P1_17", 0, -1, -1}, + { "VREFP", "P1_18", 0, -1, -1}, + { "AIN0", "P1_19", 0, -1, 0}, + { "GPIO0_20", "P1_20", 20, 4, -1}, + { "AIN1", "P1_21", 0, -1, 1}, + { "GND", "P1_22", 0, -1, -1}, + { "AIN2", "P1_23", 0, -1, 2}, + { "VOUT-5V", "P1_24", 0, -1, -1}, + { "AIN3", "P1_25", 0, -1, 3}, + { "I2C2_SDA", "P1_26", 12, 1, -1}, + { "AIN4", "P1_27", 0, -1, 4}, + { "I2C2_SCL", "P1_28", 13, 1, -1}, + { "GPIO3_21", "P1_29", 117, -1, -1}, + { "UART0_TXD", "P1_30", 43, -1, -1}, + { "GPIO3_18", "P1_31", 114, -1, -1}, + { "UART0_RXD", "P1_32", 42, -1, -1}, + { "GPIO3_15", "P1_33", 111, 1, -1}, + { "GPIO0_26", "P1_34", 26, -1, -1}, + { "GPIO2_24", "P1_35", 88, -1, -1}, + { "EHRPWM0A", "P1_36", 110, 1, -1}, + { "EHRPWM1A", "P2_1", 50, 6, -1}, + { "GPIO1_27", "P2_2", 59, -1, -1}, + { "GPIO0_23", "P2_3", 23, 4, -1}, + { "GPIO1_26", "P2_4", 58, -1, -1}, + { "UART4_RXD", "P2_5", 30, -1, -1}, + { "GPIO1_25", "P2_6", 57, -1, -1}, + { "UART4_TXD", "P2_7", 31, -1, -1}, + { "GPIO1_28", "P2_8", 60, -1, -1}, + { "I2C1_SCL", "P2_9", 15, -1, -1}, + { "GPIO1_20", "P2_10", 52, -1, -1}, + { "I2C1_SDA", "P2_11", 14, -1, -1}, + { "POWER_BUTTON", "P2_12", 0, -1, -1}, + { "VOUT-5V", "P2_13", 0, -1, -1}, + { "BAT-VIN", "P2_14", 0, -1, -1}, + { "GND", "P2_15", 0, -1, -1}, + { "BAT-TEMP", "P2_16", 0, -1, -1}, + { "GPIO2_1", "P2_17", 65, -1, -1}, + { "GPIO1_15", "P2_18", 47, -1, -1}, + { "GPIO0_27", "P2_19", 27, -1, -1}, + { "GPIO2_0", "P2_20", 64, -1, -1}, + { "GND", "P2_21", 0, -1, -1}, + { "GPIO1_14", "P2_22", 46, -1, -1}, + { "VOUT-3.3V", "P2_23", 0, -1, -1}, + { "GPIO1_12", "P2_24", 44, -1, -1}, + { "SPI1_CS0", "P2_25", 41, -1, -1}, + { "RESET#", "P2_26", 0, -1, -1}, + { "SPI1_D0", "P2_27", 40, 5, -1}, + { "GPIO3_20", "P2_28", 116, -1, -1}, + { "SPI1_SCLK", "P2_29", 7, -1, -1}, + { "GPIO3_17", "P2_30", 113, -1, -1}, + { "SPI1_CS1", "P2_31", 19, 2, -1}, + { "GPIO3_16", "P2_32", 112, -1, -1}, + { "GPIO1_13", "P2_33", 45, -1, -1}, + { "GPIO3_19", "P2_34", 115, -1, -1}, + { "GPIO2_22", "P2_35", 86, -1, -1}, + { "AIN7", "P2_36", 0, -1, 7}, + + { NULL, NULL, 0, 0, 0 } }; -typedef struct uart_t { - const char *name; - const char *path; - const char *dt; - const char *rx; - const char *tx; -} uart_t; +// Issue #243: UART setup not working for pocket beagle pins +// Add UART entries for the PocketBeagle: +// P1_30 uart0_txd +// P1_32 uart0_rxd +// P2_11 uart1_rxd +// P2_09 uart1_txd +// P1_08 uart2_rxd +// P1_10 uart2_txd +// P2_05 uart4_rxd +// P2_07 uart4_txd uart_t uart_table[] = { { "UART1", "/dev/ttyO1", "ADAFRUIT-UART1", "P9_26", "P9_24"}, @@ -170,12 +280,16 @@ uart_t uart_table[] = { { "UART3", "/dev/ttyO3", "ADAFRUIT-UART3", "P9_42", ""}, { "UART4", "/dev/ttyO4", "ADAFRUIT-UART4", "P9_11", "P9_13"}, { "UART5", "/dev/ttyO5", "ADAFRUIT-UART5", "P8_38", "P8_37"}, - { NULL, NULL, 0 } + { "PB-UART0", "/dev/ttyO0", "ADAFRUIT-UART0", "P1_30", "P1_32"}, + { "PB-UART1", "/dev/ttyO1", "ADAFRUIT-UART1", "P2_11", "P2_09"}, + { "PB-UART2", "/dev/ttyO2", "ADAFRUIT-UART2", "P1_08", "P1_10"}, + { "PB-UART4", "/dev/ttyO4", "ADAFRUIT-UART4", "P2_05", "P2_07"}, + { NULL, NULL, 0, 0, 0 } }; // Copied from https://github.com/jadonk/bonescript/blob/master/src/bone.js // See am335x technical manual, p. 183, for more info: -// http://www.ti.com/lit/ug/spruh73n/spruh73n.pdf +// https://www.ti.com/lit/ug/spruh73q/spruh73q.pdf pwm_t pwm_table[] = { { "ehrpwm2", 6, 1, 4, "ehrpwm.2:1", "EHRPWM2B", "48304000", "48304200", "P8_13"}, { "ehrpwm2", 5, 0, 4, "ehrpwm.2:0", "EHRPWM2A", "48304000", "48304200", "P8_19"}, @@ -191,6 +305,21 @@ pwm_t pwm_table[] = { { "ehrpwm0", 1, 1, 1, "ehrpwm.0:1", "EHRPWM0B", "48300000", "48300200", "P9_29"}, { "ehrpwm0", 0, 0, 1, "ehrpwm.0:0", "EHRPWM0A", "48300000", "48300200", "P9_31"}, { "ecap0", 2, 0, 0, "ecap.0", "ECAPPWM0", "48300000", "48300100", "P9_42"}, + { "timer4", 0, 0, 2, "", "", "", "dmtimer-pwm-4", "P8_7" }, + { "timer7", 0, 0, 2, "", "", "", "dmtimer-pwm-7", "P8_8" }, + { "timer5", 0, 0, 2, "", "", "", "dmtimer-pwm-5", "P8_9" }, + { "timer6", 0, 0, 2, "", "", "", "dmtimer-pwm-6", "P8_10" }, + { "ehrpwm0", 0, 0, 1, "ehrpwm.0:0", "EHRPWM0A", "48300000", "48300200", "P1_8"}, + { "ehrpwm0", 0, 0, 1, "ehrpwm.0:0", "EHRPWM0A", "48300000", "48300200", "P1_36"}, + { "ehrpwm0", 1, 1, 1, "ehrpwm.0:1", "EHRPWM0B", "48300000", "48300200", "P1_10"}, + { "ehrpwm0", 1, 1, 1, "ehrpwm.0:1", "EHRPWM0B", "48300000", "48300200", "P1_33"}, + { "ehrpwm1", 3, 0, 6, "ehrpwm.1:0", "EHRPWM1A", "48302000", "48302200", "P2_1"}, + { "ehrpwm2", 6, 1, 3, "ehrpwm.2:1", "EHRPWM2B", "48304000", "48304200", "P2_3"}, + { "timer7", 0, 0, 4, "", "", "", "dmtimer-pwm-7", "P1_20" }, + { "timer6", 0, 0, 1, "", "", "", "dmtimer-pwm-6", "P1_26" }, + { "timer5", 0, 0, 1, "", "", "", "dmtimer-pwm-5", "P1_28" }, + { "timer7", 0, 0, 5, "", "", "", "dmtimer-pwm-7", "P2_27" }, + { "timer4", 0, 0, 2, "", "", "", "dmtimer-pwm-4", "P2_31" }, { NULL, 0, 0, 0, NULL, NULL, NULL, NULL, NULL } }; @@ -376,6 +505,7 @@ BBIO_err build_path(const char *partial_path, const char *prefix, char *full_pat int (*errfunc) (const char *epath, int eerrno), glob_t *pglob); */ int err = glob(pattern, 0, NULL, &results); + free(pattern); if (err != BBIO_OK) { globfree(&results); if (err == GLOB_NOSPACE) @@ -422,25 +552,136 @@ int get_spi_bus_path_number(unsigned int spi) } } +/* + Refer to: http://elinux.org/Beagleboard:BeagleBoneBlack_Debian#U-Boot_Overlays + Robert C. Nelson maintains the BeagleBoard.org Debian images and + suggested adding this check to see if u-boot overlays are enabled. + + If u-boot overlays are enabled, then device tree overlays should not + be loaded with the cape manager by writing to the slots file. There + is currently a kernel bug that causes the write to hang. +*/ +int uboot_overlay_enabled(void) { + const char *cmd = "/bin/grep -c bone_capemgr.uboot_capemgr_enabled=1 /proc/cmdline"; + char uboot_overlay; + FILE *file = NULL; + + file = popen(cmd, "r"); + if (file == NULL) { + fprintf(stderr, "error: uboot_overlay_enabled() failed to run cmd=%s\n", cmd); + syslog(LOG_ERR, "Adafruit_BBIO: error: uboot_overlay_enabled() failed to run cmd=%s\n", cmd); + return -1; + } + uboot_overlay = fgetc(file); + pclose(file); + + if(uboot_overlay == '1') { + syslog(LOG_DEBUG, "Adafruit_BBIO: uboot_overlay_enabled() is true\n"); + return 1; + } else { + syslog(LOG_DEBUG, "Adafruit_BBIO: uboot_overlay_enabled() is false\n"); + return 0; + } +} + +/* + PocketBeagle has everything built-in to the main dtb +*/ +int pocketbeagle(void) { + const char *cmd = "/bin/grep -c 'TI AM335x PocketBeagle' /proc/device-tree/model"; + char pocketbeagle; + FILE *file = NULL; + + file = popen(cmd, "r"); + if (file == NULL) { + fprintf(stderr, "error: pocketbeagle() failed to run cmd=%s\n", cmd); + syslog(LOG_ERR, "Adafruit_BBIO: error: pocketbeagle() failed to run cmd=%s\n", cmd); + return -1; + } + pocketbeagle = fgetc(file); + pclose(file); + + if(pocketbeagle == '1') { + return 1; + } else { + return 0; + } +} + + +/* + Check if board is a BeagleBone Blue + + Refer to GitHub issue for more information: + https://github.com/adafruit/adafruit-beaglebone-io-python/issues/178 +*/ +int beaglebone_blue(void) { + const char *cmd = "/bin/grep -c 'TI AM335x BeagleBone Blue' /proc/device-tree/model"; + // cache the value to avoid poor performance + // in functions that are called frequently like + // gpio_set_value() in source/event_gpio.c + static int initialized = 0; + static int retval = 0; + FILE *file = NULL; + + //fprintf(stderr, "beaglebone_blue(): initialized=[%d] retval=[%d]\n", initialized, retval); + if(!initialized) { + initialized = 1; + //fprintf(stderr, "beaglebone_blue(): not initialized\n"); + file = popen(cmd, "r"); + if (file == NULL) { + fprintf(stderr, "Adafruit_BBIO: error in beaglebone_blue(): failed to run cmd=%s\n", cmd); + syslog(LOG_ERR, "Adafruit_BBIO: error in beaglebone_blue(): failed to run cmd=%s\n", cmd); + return -1; + } + if( fgetc(file) == '1' ) { + retval = 1; + } + pclose(file); + } + + return retval; +} + BBIO_err load_device_tree(const char *name) { + char line[256]; FILE *file = NULL; + char slots[100]; #ifdef BBBVERSION41 - char slots[41]; snprintf(ctrl_dir, sizeof(ctrl_dir), "/sys/devices/platform/bone_capemgr"); #else - char slots[40]; - build_path("/sys/devices", "bone_capemgr", ctrl_dir, sizeof(ctrl_dir)); + build_path("/sys/devices", "bone_capemgr", ctrl_dir, sizeof(ctrl_dir)); #endif - char line[256]; + /* Check if the Linux kernel booted with u-boot overlays enabled. + If enabled, do not load overlays via slots file as the write + will hang due to kernel bug in cape manager driver. Just return + BBIO_OK in order to avoid cape manager bug. */ + if(uboot_overlay_enabled()) { + return BBIO_OK; + } + + /* beaglebone blue has complete dtb file and does not need overlays */ + if(beaglebone_blue()) { + //fprintf(stderr, "common.c: load_device_tree(): beaglebone_blue(): TRUE\n"); + return BBIO_OK; + } + + /* pocketbeagle has complete dtb file and does not need overlays */ + if(pocketbeagle()) { + //fprintf(stderr, "common.c: load_device_tree(): pocketbeagle(): TRUE\n"); + return BBIO_OK; + } snprintf(slots, sizeof(slots), "%s/slots", ctrl_dir); file = fopen(slots, "r+"); if (!file) { +#ifndef NO_PYTHON PyErr_SetFromErrnoWithFilename(PyExc_IOError, slots); +#endif // !NO_PYTHON return BBIO_CAPE; } @@ -467,21 +708,42 @@ BBIO_err load_device_tree(const char *name) int device_tree_loaded(const char *name) { FILE *file = NULL; + char slots[100]; #ifdef BBBVERSION41 - char slots[41]; snprintf(ctrl_dir, sizeof(ctrl_dir), "/sys/devices/platform/bone_capemgr"); #else - char slots[40]; build_path("/sys/devices", "bone_capemgr", ctrl_dir, sizeof(ctrl_dir)); #endif char line[256]; + /* Check if the Linux kernel booted with u-boot overlays enabled. + If enabled, do not load overlays via slots file as the write + will hang due to kernel bug in cape manager driver. Return 1 + to fake the device tree is loaded to avoid cape manager bug */ + if(uboot_overlay_enabled()) { + return 1; + } + + /* beaglebone blue has complete dtb file and does not need overlays */ + if(beaglebone_blue()) { + //fprintf(stderr, "common.c: device_tree_loaded(): beaglebone_blue(): TRUE\n"); + return BBIO_OK; + } + + /* pocketbeagle has complete dtb file and does not need overlays */ + if(pocketbeagle()) { + //fprintf(stderr, "common.c: device_tree_loaded(): pocketbeagle(): TRUE\n"); + return BBIO_OK; + } + snprintf(slots, sizeof(slots), "%s/slots", ctrl_dir); file = fopen(slots, "r+"); if (!file) { +#ifndef NO_PYTHON PyErr_SetFromErrnoWithFilename(PyExc_IOError, slots); +#endif // !NO_PYTHON return -1; } @@ -502,20 +764,33 @@ int device_tree_loaded(const char *name) BBIO_err unload_device_tree(const char *name) { FILE *file = NULL; + char slots[100]; #ifdef BBBVERSION41 - char slots[41]; snprintf(ctrl_dir, sizeof(ctrl_dir), "/sys/devices/platform/bone_capemgr"); #else - char slots[40]; build_path("/sys/devices", "bone_capemgr", ctrl_dir, sizeof(ctrl_dir)); #endif char line[256]; char *slot_line; + /* beaglebone blue has complete dtb file and does not need overlays */ + if(beaglebone_blue()) { + //fprintf(stderr, "common.c: unload_device_tree(): beaglebone_blue(): TRUE\n"); + return BBIO_OK; + } + + /* pocketbeagle has complete dtb file and does not need overlays */ + if(pocketbeagle()) { + //fprintf(stderr, "common.c: unload_device_tree(): pocketbeagle(): TRUE\n"); + return BBIO_OK; + } + snprintf(slots, sizeof(slots), "%s/slots", ctrl_dir); file = fopen(slots, "r+"); if (!file) { +#ifndef NO_PYTHON PyErr_SetFromErrnoWithFilename(PyExc_IOError, slots); +#endif // !NO_PYTHON return BBIO_SYSFS; } @@ -538,3 +813,18 @@ BBIO_err unload_device_tree(const char *name) return BBIO_OK; } + +void initlog(int level, const char* ident, int option) +{ + static int initialized = 0; + if (initialized) return; + + setlogmask(LOG_UPTO(level)); + if (option == -1) { + option = BBIO_LOG_OPTION; + } + openlog(ident, option, LOG_LOCAL1); + syslog(LOG_NOTICE, "Adafruit_BBIO: version %s initialized", ""); + + initialized = 1; +} diff --git a/source/common.h b/source/common.h index eddd21f..7d60c1f 100644 --- a/source/common.h +++ b/source/common.h @@ -33,25 +33,23 @@ SOFTWARE. #include +#include "adafruit/bbio/error.h" +#ifdef __cplusplus +using adafruit::bbio::BBIO_err; +#endif + #define MODE_UNKNOWN -1 #define BOARD 10 #define BCM 11 +#define CTRL_DIR_MAX 50 +#define OCP_DIR_MAX 50 + #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) #define FILENAME_BUFFER_SIZE 128 #define MAX_PATH 256 -typedef enum { - BBIO_OK, // No error - BBIO_ACCESS, // Error accessing a file - BBIO_SYSFS, // Some error with Sysfs files - BBIO_CAPE, // some error with capes - BBIO_INVARG, // Invalid argument - BBIO_MEM, - BBIO_GEN // General error -} BBIO_err; - // Modeled after "pwm": submap in bone.js from bonescript // https://github.com/jadonk/bonescript/blob/master/src/bone.js#L680 typedef struct pwm_t { @@ -66,17 +64,23 @@ typedef struct pwm_t { const char *key; // Pin name eg P9_21 } pwm_t; -int gpio_mode; -int gpio_direction[120]; -#ifdef BBBVERSION41 - char ctrl_dir[43]; - char ocp_dir[33]; -#else - char ctrl_dir[35]; - char ocp_dir[25]; -#endif - +typedef struct uart_t { + const char *name; + const char *path; + const char *dt; + const char *rx; + const char *tx; +} uart_t; + +extern uart_t uart_table[]; + +extern int gpio_mode; +extern int gpio_direction[120]; + +extern char ctrl_dir[CTRL_DIR_MAX]; +extern char ocp_dir[OCP_DIR_MAX]; + BBIO_err get_gpio_number(const char *key, unsigned int *gpio); BBIO_err get_pwm_key(const char *input, char *key); BBIO_err get_adc_ain(const char *key, int *ain); @@ -87,9 +91,14 @@ BBIO_err load_device_tree(const char *name); BBIO_err unload_device_tree(const char *name); int device_tree_loaded(const char *name); BBIO_err get_pwm_by_key(const char *key, pwm_t **pwm); +int uboot_overlay_enabled(void); +int beaglebone_blue(void); +int pocketbeagle(void); +#define BBIO_LOG_OPTION LOG_CONS | LOG_PID | LOG_NDELAY +void initlog(int level, const char* ident, int option); -int setup_error; -int module_setup; +extern int setup_error; +extern int module_setup; #endif diff --git a/source/constants.c b/source/constants.c index 5e284c8..23753c6 100644 --- a/source/constants.c +++ b/source/constants.c @@ -35,39 +35,41 @@ SOFTWARE. void define_constants(PyObject *module) { - high = Py_BuildValue("i", HIGH); - PyModule_AddObject(module, "HIGH", high); + PyObject *object; - low = Py_BuildValue("i", LOW); - PyModule_AddObject(module, "LOW", low); + object = Py_BuildValue("i", HIGH); + PyModule_AddObject(module, "HIGH", object); - output = Py_BuildValue("i", OUTPUT); - PyModule_AddObject(module, "OUT", output); + object = Py_BuildValue("i", LOW); + PyModule_AddObject(module, "LOW", object); - input = Py_BuildValue("i", INPUT); - PyModule_AddObject(module, "IN", input); + object = Py_BuildValue("i", OUTPUT); + PyModule_AddObject(module, "OUT", object); - alt0 = Py_BuildValue("i", ALT0); - PyModule_AddObject(module, "ALT0", alt0); + object = Py_BuildValue("i", INPUT); + PyModule_AddObject(module, "IN", object); - pud_off = Py_BuildValue("i", PUD_OFF); - PyModule_AddObject(module, "PUD_OFF", pud_off); + object = Py_BuildValue("i", ALT0); + PyModule_AddObject(module, "ALT0", object); - pud_up = Py_BuildValue("i", PUD_UP); - PyModule_AddObject(module, "PUD_UP", pud_up); + object = Py_BuildValue("i", PUD_OFF); + PyModule_AddObject(module, "PUD_OFF", object); - pud_down = Py_BuildValue("i", PUD_DOWN); - PyModule_AddObject(module, "PUD_DOWN", pud_down); + object = Py_BuildValue("i", PUD_UP); + PyModule_AddObject(module, "PUD_UP", object); + + object = Py_BuildValue("i", PUD_DOWN); + PyModule_AddObject(module, "PUD_DOWN", object); - rising_edge = Py_BuildValue("i", RISING_EDGE); - PyModule_AddObject(module, "RISING", rising_edge); + object = Py_BuildValue("i", RISING_EDGE); + PyModule_AddObject(module, "RISING", object); - falling_edge = Py_BuildValue("i", FALLING_EDGE); - PyModule_AddObject(module, "FALLING", falling_edge); + object = Py_BuildValue("i", FALLING_EDGE); + PyModule_AddObject(module, "FALLING", object); - both_edge = Py_BuildValue("i", BOTH_EDGE); - PyModule_AddObject(module, "BOTH", both_edge); + object = Py_BuildValue("i", BOTH_EDGE); + PyModule_AddObject(module, "BOTH", object); - version = Py_BuildValue("s", "0.0.20"); - PyModule_AddObject(module, "VERSION", version); + object = Py_BuildValue("s", "0.0.20"); + PyModule_AddObject(module, "VERSION", object); } diff --git a/source/constants.h b/source/constants.h index 0f6fdf5..82ebe5e 100644 --- a/source/constants.h +++ b/source/constants.h @@ -1,19 +1,6 @@ #ifndef CONSTANTS_H #define CONSTANTS_H -PyObject *high; -PyObject *low; -PyObject *input; -PyObject *output; -PyObject *alt0; -PyObject *pud_off; -PyObject *pud_up; -PyObject *pud_down; -PyObject *rising_edge; -PyObject *falling_edge; -PyObject *both_edge; -PyObject *version; - void define_constants(PyObject *module); #endif diff --git a/source/event_gpio.c b/source/event_gpio.c index dfe36e4..9f5acd6 100644 --- a/source/event_gpio.c +++ b/source/event_gpio.c @@ -28,16 +28,23 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#include +#include #include -#include #include #include -#include -#include #include +#include +#include +#include + #include "event_gpio.h" #include "common.h" +#define GPIO_NOT_EXPORTED 0 +#define GPIO_EXPORTED 1 +#define GPIO_ALREADY_EXPORTED 2 + const char *stredge[4] = {"none", "rising", "falling", "both"}; // file descriptors @@ -60,58 +67,89 @@ struct callback }; struct callback *callbacks = NULL; -// gpio exports -struct gpio_exp -{ - unsigned int gpio; - struct gpio_exp *next; -}; -struct gpio_exp *exported_gpios = NULL; - pthread_t threads; +int exported_gpios[120] = { GPIO_NOT_EXPORTED }; int event_occurred[120] = { 0 }; int thread_running = 0; int epfd = -1; -int gpio_export(unsigned int gpio) +BBIO_err gpio_export(unsigned int gpio) { - int fd, len; + int fd = 0, len = 0, ret = BBIO_GEN; char str_gpio[10]; - struct gpio_exp *new_gpio, *g; - if ((fd = open("/sys/class/gpio/export", O_WRONLY)) < 0) + // already exported by us? + if (exported_gpios[gpio] != GPIO_NOT_EXPORTED) { + syslog(LOG_DEBUG, "Adafruit_BBIO: gpio_export(): %u already exported before", gpio); + ret = BBIO_OK; + goto exit; + } + + // Is GPIO an LED? + if ( ((gpio >= USR_LED_GPIO_MIN) && (gpio <= USR_LED_GPIO_MAX)) + || + ( beaglebone_blue() + && + ( + (gpio == USR_LED_RED) + || (gpio == USR_LED_GREEN) + || (gpio == BAT25) + || (gpio == BAT50) + || (gpio == BAT75) + || (gpio == BAT100) + || (gpio == WIFI) + ) + ) + ) { - return -1; + syslog(LOG_WARNING, "Adafruit_BBIO: gpio_export: %u not applicable to built-in LEDs", gpio); + return BBIO_OK; // export is not applicable to the USR LED pins + } + + // already exported by someone else? + char gpio_path[64]; + snprintf(gpio_path, sizeof(gpio_path), "/sys/class/gpio/gpio%d", gpio); + + if (access(gpio_path, R_OK|W_OK|X_OK) != -1) { + exported_gpios[gpio] = GPIO_ALREADY_EXPORTED; + syslog(LOG_DEBUG, "Adafruit_BBIO: gpio_export(): %u already exported externally", gpio); + ret = BBIO_OK; + goto exit; + } + + const char gpio_export[] = "/sys/class/gpio/export"; + + if ((fd = open(gpio_export, O_WRONLY)) < 0) { + syslog(LOG_ERR, "Adafruit_BBIO: gpio_export(): %u couldn't open \"%s\": %i-%s", + gpio, gpio_export, errno, strerror(errno)); + ret = BBIO_SYSFS; + goto exit; } + len = snprintf(str_gpio, sizeof(str_gpio), "%d", gpio); - int ret = write(fd, str_gpio, len); - close(fd); - if (ret < 0) { - return ret; + if(write(fd, str_gpio, len) < 0) { + syslog(LOG_ERR, "Adafruit_BBIO: gpio_export: %u couldn't write \"%s\": %i-%s", + gpio, gpio_export, errno, strerror(errno)); + ret = BBIO_SYSFS; + goto exit; } // add to list - new_gpio = malloc(sizeof(struct gpio_exp)); - if (new_gpio == 0) - return -1; // out of memory + exported_gpios[gpio] = GPIO_EXPORTED; - new_gpio->gpio = gpio; - new_gpio->next = NULL; + syslog(LOG_DEBUG, "Adafruit_BBIO: gpio_export(): %u OK", gpio); + ret = BBIO_OK; - if (exported_gpios == NULL) - { - // create new list - exported_gpios = new_gpio; - } else { - // add to end of existing list - g = exported_gpios; - while (g->next != NULL) - g = g->next; - g->next = new_gpio; + exit: + if(fd && (ret = close(fd))) { + syslog(LOG_ERR, "Adafruit_BBIO: gpio_export(): %u couldn't close \"%s\": %i-%s", + gpio, gpio_export, errno, strerror(errno)); + ret = BBIO_SYSFS; } - return 0; + usleep(200000); // Hack to wait for newly exported pins to get correct ownership + return ret; } - +// Closes fd corresponding to specified GPIO pin and removes it from fdx list void close_value_fd(unsigned int gpio) { struct fdx *f = fd_list; @@ -123,10 +161,12 @@ void close_value_fd(unsigned int gpio) if (f->gpio == gpio) { close(f->fd); + syslog(LOG_DEBUG, "Adafruit_BBIO: close_value_fd(): closed file descriptor %d", f->fd); if (prev == NULL) fd_list = f->next; else prev->next = f->next; + syslog(LOG_DEBUG, "Adafruit_BBIO: close_value_fd(): removing file descriptor %d for pin %u from opened descriptors list",f->fd, f->gpio); temp = f; f = f->next; free(temp); @@ -136,7 +176,7 @@ void close_value_fd(unsigned int gpio) } } } - +// Returns file descriptor corresponding to specified GPIO pin int fd_lookup(unsigned int gpio) { struct fdx *f = fd_list; @@ -148,7 +188,7 @@ int fd_lookup(unsigned int gpio) } return 0; } - +// Adds GPIO file descriptor to fdx list int add_fd_list(unsigned int gpio, int fd) { struct fdx *new_fd; @@ -167,80 +207,137 @@ int add_fd_list(unsigned int gpio, int fd) new_fd->next = fd_list; } fd_list = new_fd; + syslog(LOG_DEBUG, "Adafruit_BBIO: add_fd_list(): registered file descriptor %d for pin %u.",fd, gpio); return 0; } int open_value_file(unsigned int gpio) { int fd; - char filename[MAX_FILENAME]; + char filename[MAX_FILENAME] = ""; // create file descriptor of value file if ((gpio >= USR_LED_GPIO_MIN) && (gpio <= USR_LED_GPIO_MAX)) { snprintf(filename, sizeof(filename), "/sys/class/leds/beaglebone:green:usr%d/brightness", gpio - USR_LED_GPIO_MIN); + } else if (beaglebone_blue()) { + //syslog(LOG_DEBUG, "Adafruit_BBIO: gpio open_value_file: beaglebone_blue() is true\n"); + switch(gpio) { + case USR_LED_RED: + snprintf(filename, sizeof(filename), "/sys/class/leds/red/brightness"); + break; + case USR_LED_GREEN: + snprintf(filename, sizeof(filename), "/sys/class/leds/green/brightness"); + break; + case BAT25: + snprintf(filename, sizeof(filename), "/sys/class/leds/bat25/brightness"); + break; + case BAT50: + snprintf(filename, sizeof(filename), "/sys/class/leds/bat50/brightness"); + break; + case BAT75: + snprintf(filename, sizeof(filename), "/sys/class/leds/bat75/brightness"); + break; + case BAT100: + snprintf(filename, sizeof(filename), "/sys/class/leds/bat100/brightness"); + break; + case WIFI: + snprintf(filename, sizeof(filename), "/sys/class/leds/wifi/brightness"); + break; + default: + snprintf(filename, sizeof(filename), "/sys/class/gpio/gpio%d/value", gpio); + break; + } } else { + //syslog(LOG_DEBUG, "Adafruit_BBIO: gpio open_value_file: default gpio path\n"); snprintf(filename, sizeof(filename), "/sys/class/gpio/gpio%d/value", gpio); } + //syslog(LOG_DEBUG, "Adafruit_BBIO: gpio open_value_file: filename=%s\n", filename); + + // if(gpio == USR_LED_RED) { // red LED + // snprintf(filename, sizeof(filename), "/sys/class/leds/red/brightness"); + // } else if(gpio == USR_LED_GREEN) { // green LED + // snprintf(filename, sizeof(filename), "/sys/class/leds/green/brightness"); + // } else { + // snprintf(filename, sizeof(filename), "/sys/class/gpio/gpio%d/value", gpio); + // } if ((fd = open(filename, O_RDONLY | O_NONBLOCK)) < 0) { - return -1; + syslog(LOG_ERR, "Adafruit_BBIO: gpio open_value_file: %u couldn't open '%s': %i-%s", + gpio, filename, errno, strerror(errno)); + return -1; } + syslog(LOG_DEBUG, "Adafruit_BBIO: open_value_file(): opened file descriptor %d for pin %u.",fd, gpio); add_fd_list(gpio, fd); return fd; } -int gpio_unexport(unsigned int gpio) +BBIO_err gpio_unexport(unsigned int gpio) { int fd, len; char str_gpio[10]; - struct gpio_exp *g, *temp, *prev_g = NULL; + //If gpio is not exported by us - no need to do anything + if (exported_gpios[gpio] != GPIO_EXPORTED) + return 0; + //close gpio pin file descriptor close_value_fd(gpio); - if ((fd = open("/sys/class/gpio/unexport", O_WRONLY)) < 0) - return -1; +#define GPIO_UNEXPORT "/sys/class/gpio/unexport" + + if ((fd = open(GPIO_UNEXPORT, O_WRONLY)) < 0) { + syslog(LOG_ERR, "Adafruit_BBIO: gpio_unexport(): %u couldn't open '"GPIO_UNEXPORT"': %i-%s", + gpio, errno, strerror(errno)); + return BBIO_SYSFS; + } len = snprintf(str_gpio, sizeof(str_gpio), "%d", gpio); int ret = write(fd, str_gpio, len); close(fd); if (ret < 0) { - return ret; + syslog(LOG_ERR, "Adafruit_BBIO: gpio_unexport(): %u couldn't write '"GPIO_UNEXPORT"': %i-%s", + gpio, errno, strerror(errno)); + return BBIO_SYSFS; } // remove from list - g = exported_gpios; - while (g != NULL) - { - if (g->gpio == gpio) - { - if (prev_g == NULL) - exported_gpios = g->next; - else - prev_g->next = g->next; - temp = g; - g = g->next; - free(temp); - } else { - prev_g = g; - g = g->next; - } - } - return 0; + exported_gpios[gpio] = GPIO_NOT_EXPORTED; + + syslog(LOG_DEBUG, "Adafruit_BBIO: gpio_unexport(): %u OK", gpio); + return BBIO_OK; } -int gpio_set_direction(unsigned int gpio, unsigned int in_flag) +BBIO_err gpio_set_direction(unsigned int gpio, unsigned int in_flag) { int fd; char filename[40]; char direction[10] = { 0 }; - if ((gpio >= USR_LED_GPIO_MIN) && (gpio <= USR_LED_GPIO_MAX)) { - return 0; // direction is not applicable to the USR LED pins + if ( ((gpio >= USR_LED_GPIO_MIN) && (gpio <= USR_LED_GPIO_MAX)) + || + ( beaglebone_blue() + && + ( + (gpio == USR_LED_RED) + || (gpio == USR_LED_GREEN) + || (gpio == BAT25) + || (gpio == BAT50) + || (gpio == BAT75) + || (gpio == BAT100) + || (gpio == WIFI) + ) + ) + ) + { + syslog(LOG_WARNING, "Adafruit_BBIO: gpio_set_direction: %u not applicable to built-in LEDs", gpio); + return BBIO_OK; // direction is not applicable to the USR LED pins } snprintf(filename, sizeof(filename), "/sys/class/gpio/gpio%d/direction", gpio); - if ((fd = open(filename, O_WRONLY)) < 0) - return -1; + if ((fd = open(filename, O_WRONLY)) < 0) { + syslog(LOG_ERR, "Adafruit_BBIO: gpio_set_direction(): %u couldn't open '%s': %i-%s", + gpio, filename, errno, strerror(errno)); + return BBIO_SYSFS; + } if (in_flag) { strncpy(direction, "out", ARRAY_SIZE(direction) - 1); @@ -251,26 +348,35 @@ int gpio_set_direction(unsigned int gpio, unsigned int in_flag) int ret = write(fd, direction, strlen(direction)); close(fd); if (ret < 0) { - return ret; + syslog(LOG_ERR, "Adafruit_BBIO: gpio_set_direction(): %u couldn't write '%s': %i-%s", + gpio, filename, errno, strerror(errno)); + return BBIO_SYSFS; } - return 0; + //syslog(LOG_DEBUG, "Adafruit_BBIO: gpio_set_direction: %u OK", gpio); + return BBIO_OK; } -int gpio_get_direction(unsigned int gpio, unsigned int *value) +BBIO_err gpio_get_direction(unsigned int gpio, unsigned int *value) { int fd; char direction[4] = { 0 }; char filename[40]; snprintf(filename, sizeof(filename), "/sys/class/gpio/gpio%d/direction", gpio); - if ((fd = open(filename, O_RDONLY | O_NONBLOCK)) < 0) - return -1; + if ((fd = open(filename, O_RDONLY | O_NONBLOCK)) < 0) { + syslog(LOG_ERR, "Adafruit_BBIO: gpio_get_direction(): %u couldn't open '%s': %i-%s", + gpio, filename, errno, strerror(errno)); + return BBIO_SYSFS; + } lseek(fd, 0, SEEK_SET); int ret = read(fd, &direction, sizeof(direction) - 1); + close(fd); if (ret < 0) { - return ret; + syslog(LOG_ERR, "Adafruit_BBIO: gpio_get_direction(): %u couldn't read '%s': %i-%s", + gpio, filename, errno, strerror(errno)); + return BBIO_SYSFS; } if (strcmp(direction, "out") == 0) { @@ -278,33 +384,89 @@ int gpio_get_direction(unsigned int gpio, unsigned int *value) } else { *value = INPUT; } - - return 0; + + //syslog(LOG_DEBUG, "Adafruit_BBIO: gpio_get_direction: %u OK", gpio); + return BBIO_OK; } -int gpio_set_value(unsigned int gpio, unsigned int value) +BBIO_err gpio_set_value(unsigned int gpio, unsigned int value) { int fd; char filename[MAX_FILENAME]; char vstr[10]; + // TODO: remove this debug output created for issue #178 + /* + if(uboot_overlay_enabled()) { + fprintf(stderr, "gpio_set_value: uboot_overlay_enabled() is true\n"); + } else { + fprintf(stderr, "gpio_set_value: uboot_overlay_enabled() is FASLE\n"); + } + + if(pocketbeagle()) { + fprintf(stderr, "gpio_set_value: pocketbeagle() is true\n"); + } else { + fprintf(stderr, "gpio_set_value: pocketbeagle() is FASLE\n"); + } + + if(beaglebone_blue()) { + fprintf(stderr, "gpio_set_value: beaglebone_blue() is true\n"); + } else { + fprintf(stderr, "gpio_set_value: beaglebone_blue() is FALSE\n"); + } + */ + + if ((gpio >= USR_LED_GPIO_MIN) && (gpio <= USR_LED_GPIO_MAX)) { char *usr_led_trigger[] = { "heartbeat", "mmc0", "cpu0", "mmc1" }; int led = gpio - USR_LED_GPIO_MIN; - snprintf(filename, sizeof(filename), "/sys/class/leds/beaglebone:green:usr%d/brightness", led); + //syslog(LOG_DEBUG, "Adafruit_BBIO: gpio_set_value: USR LED path\n"); - if ((fd = open(filename, O_WRONLY)) < 0) { + snprintf(filename, sizeof(filename), "/sys/class/leds/beaglebone:green:usr%d/brightness", led); + if (access(filename, W_OK) < 0) { snprintf(filename, sizeof(filename), "/sys/class/leds/beaglebone:green:%s/brightness", usr_led_trigger[led]); } - + } else if (beaglebone_blue()) { + //syslog(LOG_DEBUG, "Adafruit_BBIO: gpio_set_value: beaglebone_blue() is true\n"); + switch(gpio) { + case USR_LED_RED: + snprintf(filename, sizeof(filename), "/sys/class/leds/red/brightness"); + break; + case USR_LED_GREEN: + snprintf(filename, sizeof(filename), "/sys/class/leds/green/brightness"); + break; + case BAT25: + snprintf(filename, sizeof(filename), "/sys/class/leds/bat25/brightness"); + break; + case BAT50: + snprintf(filename, sizeof(filename), "/sys/class/leds/bat50/brightness"); + break; + case BAT75: + snprintf(filename, sizeof(filename), "/sys/class/leds/bat75/brightness"); + break; + case BAT100: + snprintf(filename, sizeof(filename), "/sys/class/leds/bat100/brightness"); + break; + case WIFI: + snprintf(filename, sizeof(filename), "/sys/class/leds/wifi/brightness"); + break; + default: + snprintf(filename, sizeof(filename), "/sys/class/gpio/gpio%d/value", gpio); + break; + } } else { + //syslog(LOG_DEBUG, "Adafruit_BBIO: gpio_set_value: default gpio path\n"); snprintf(filename, sizeof(filename), "/sys/class/gpio/gpio%d/value", gpio); } + //syslog(LOG_DEBUG, "Adafruit_BBIO: gpio_set_value: filename=%s\n", filename); - if ((fd = open(filename, O_WRONLY)) < 0) { - return -1; + fd = open(filename, O_WRONLY); + if (fd < 0) { + syslog(LOG_ERR, "Adafruit_BBIO: gpio_set_value(): %u couldn't open '%s': %i-%s", + gpio, filename, errno, strerror(errno)); + return BBIO_SYSFS; } if (value) { @@ -316,27 +478,35 @@ int gpio_set_value(unsigned int gpio, unsigned int value) int ret = write(fd, vstr, strlen(vstr)); close(fd); if (ret < 0) { - return ret; + syslog(LOG_ERR, "Adafruit_BBIO: gpio_set_value(): %u couldn't write '%s': %i-%s", + gpio, filename, errno, strerror(errno)); + return BBIO_SYSFS; } - return 0; + //syslog(LOG_DEBUG, "Adafruit_BBIO: gpio_set_value: %u %u OK", gpio, value); + return BBIO_OK; } -int gpio_get_value(unsigned int gpio, unsigned int *value) +BBIO_err gpio_get_value(unsigned int gpio, unsigned int *value) { int fd = fd_lookup(gpio); char ch; if (!fd) { - if ((fd = open_value_file(gpio)) == -1) - return -1; + if ((fd = open_value_file(gpio)) == -1) { + syslog(LOG_ERR, "Adafruit_BBIO: gpio_set_value(): %u couldn't open value file: %i-%s", + gpio, errno, strerror(errno)); + return BBIO_SYSFS; + } } lseek(fd, 0, SEEK_SET); int ret = read(fd, &ch, sizeof(ch)); if (ret < 0) { - return ret; + syslog(LOG_ERR, "Adafruit_BBIO: gpio_set_value(): %u couldn't read value file: %i-%s", + gpio, errno, strerror(errno)); + return BBIO_SYSFS; } if (ch != '0') { @@ -345,7 +515,8 @@ int gpio_get_value(unsigned int gpio, unsigned int *value) *value = 0; } - return 0; + //syslog(LOG_DEBUG, "Adafruit_BBIO: gpio_get_value: %u OK", gpio); + return BBIO_OK; } int gpio_set_edge(unsigned int gpio, unsigned int edge) @@ -366,7 +537,7 @@ int gpio_set_edge(unsigned int gpio, unsigned int edge) return 0; } - +//Returns gpio number corresponding to fd file descriptor unsigned int gpio_lookup(int fd) { struct fdx *f = fd_list; @@ -381,9 +552,10 @@ unsigned int gpio_lookup(int fd) void exports_cleanup(void) { + int i; // unexport everything - while (exported_gpios != NULL) - gpio_unexport(exported_gpios->gpio); + for (i = 0; i < 120; ++i) + gpio_unexport(i); } int add_edge_callback(unsigned int gpio, void (*func)(unsigned int gpio)) @@ -408,17 +580,32 @@ int add_edge_callback(unsigned int gpio, void (*func)(unsigned int gpio)) cb = cb->next; cb->next = new_cb; } + syslog(LOG_DEBUG, "Adafruit_BBIO: add_edge_callback(): added callback to %p for pin %u", new_cb->func, gpio); return 0; } void run_callbacks(unsigned int gpio) { struct callback *cb = callbacks; + //Memory cookie + unsigned char cookie[2] = {0}; while (cb != NULL) { - if (cb->gpio == gpio) + //Store memory contents of the first byte of current callback structure as a "magic cookie" + memcpy(&cookie[0], cb, 1); + syslog(LOG_DEBUG, "Adafruit_BBIO: run_callbacks(): running callback %p for pin %u", cb->func, gpio); + if (cb->gpio == gpio) cb->func(cb->gpio); - cb = cb->next; + + //Check the first byte of callback structure after executing callback function body + memcpy(&cookie[1], cb, 1); + + // Current callback pointer might have changed _only_ if linked list structure has been altered from within the callback function, which should happen _only_ in remove_event_detect() call + // If that happened, cb* pointer will be now addressing different memory location with different data. + if (cookie[0] != cookie[1]) break; + + if (cb != NULL) + cb = cb->next; } } @@ -432,6 +619,7 @@ void remove_callbacks(unsigned int gpio) { if (cb->gpio == gpio) { + syslog(LOG_DEBUG, "Adafruit_BBIO: remove_callbacks(): removing callback to %p for pin %u", cb->func, cb->gpio); if (prev == NULL) callbacks = cb->next; else @@ -445,7 +633,7 @@ void remove_callbacks(unsigned int gpio) } } } - +// Resets flag for the corresponding gpio void set_initial_false(unsigned int gpio) { struct fdx *f = fd_list; @@ -457,7 +645,7 @@ void set_initial_false(unsigned int gpio) f = f->next; } } - +// Checks if flag is set for the corresponding gpio int gpio_initial(unsigned int gpio) { struct fdx *f = fd_list; @@ -481,28 +669,38 @@ void *poll_thread(__attribute__ ((unused)) void *threadarg) thread_running = 1; while (thread_running) { + // epoll_wait() returns -1 on error/timeout if ((n = epoll_wait(epfd, &events, 1, -1)) == -1) { thread_running = 0; + syslog(LOG_ERR, "Adafruit_BBIO: poll_thread(): exiting due to error/timeout returned by epoll_wait()"); pthread_exit(NULL); } + // otherwise it returns number of file descriptors ready if (n > 0) { + // Set read/write offset to the beginning of the file lseek(events.data.fd, 0, SEEK_SET); + // Try to check if there's new data available on fd by reading from it, i.e. no error ocurred if (read(events.data.fd, &buf, 1) != 1) { thread_running = 0; + syslog(LOG_ERR, "Adafruit_BBIO: poll_thread(): exiting due to no data available to read"); pthread_exit(NULL); } + // Find out gpio number corresponding to fd on which event has happened gpio = gpio_lookup(events.data.fd); if (gpio_initial(gpio)) { // ignore first epoll trigger + syslog(LOG_DEBUG, "Adafruit_BBIO: poll_thread(): discarding first epoll() event for pin %u",gpio); set_initial_false(gpio); } else { event_occurred[gpio] = 1; + syslog(LOG_DEBUG, "Adafruit_BBIO: poll_thread(): running callbacks for pin %u",gpio); run_callbacks(gpio); } } } thread_running = 0; + syslog(LOG_DEBUG, "Adafruit_BBIO: poll_thread(): normal exit"); pthread_exit(NULL); } @@ -544,6 +742,7 @@ int gpio_event_remove(unsigned int gpio) if (f->gpio == gpio) { f->is_evented = 0; + f->initial = 1; return 0; } f = f->next; @@ -554,17 +753,17 @@ int gpio_event_remove(unsigned int gpio) int add_edge_detect(unsigned int gpio, unsigned int edge) // return values: // 0 - Success -// 1 - Edge detection already added -// 2 - Other error +// -1 - Even detection already enabled for that GPIO +// Other error codes are system-wide { int fd = fd_lookup(gpio); pthread_t threads; struct epoll_event ev; long t = 0; - // check to see if this gpio has been added already + // check to see if this gpio has been added already to the list of gpios with event detection enabled if (gpio_event_add(gpio) != 0) - return 1; + return -1; // export /sys/class/gpio interface gpio_export(gpio); @@ -574,24 +773,37 @@ int add_edge_detect(unsigned int gpio, unsigned int edge) if (!fd) { if ((fd = open_value_file(gpio)) == -1) - return 2; + { + syslog(LOG_ERR, "Adafruit_BBIO: add_edge_detect(): open_value_file() error %i-%s", errno, strerror(errno)); + return errno; + } } // create epfd if not already open if ((epfd == -1) && ((epfd = epoll_create(1)) == -1)) - return 2; + { + syslog(LOG_ERR, "Adafruit_BBIO: add_edge_detect(): epoll_create() error %i-%s", errno, strerror(errno)); + return errno; + } + // add to epoll fd ev.events = EPOLLIN | EPOLLET | EPOLLPRI; ev.data.fd = fd; if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev) == -1) - return 2; + { + syslog(LOG_ERR, "Adafruit_BBIO: add_edge_detect(): epoll_ctl() error %i-%s", errno, strerror(errno)); + return errno; + } // start poll thread if it is not already running if (!thread_running) { if (pthread_create(&threads, NULL, poll_thread, (void *)t) != 0) - return 2; + { + syslog(LOG_ERR, "Adafruit_BBIO: add_edge_detect(): pthread_create() error %i-%s", errno, strerror(errno)); + return errno; + } } return 0; @@ -605,17 +817,21 @@ void remove_edge_detect(unsigned int gpio) // delete callbacks for gpio remove_callbacks(gpio); - // delete epoll of fd + // delete fd from epoll epoll_ctl(epfd, EPOLL_CTL_DEL, fd, &ev); // set edge to none gpio_set_edge(gpio, NO_EDGE); - // unexport gpio + //clear event gpio_event_remove(gpio); + // clear detected flag event_occurred[gpio] = 0; + + syslog(LOG_DEBUG, "Adafruit_BBIO: remove_edge_detect(): event detection disabled for pin %u",gpio); + } int event_detected(unsigned int gpio) diff --git a/source/event_gpio.h b/source/event_gpio.h index 326c4ef..e06f344 100644 --- a/source/event_gpio.h +++ b/source/event_gpio.h @@ -30,6 +30,8 @@ SOFTWARE. #ifndef EVENT_GPIO_H #define EVENT_GPIO_H +#include "adafruit/bbio/error.h" + #define NO_EDGE 0 #define RISING_EDGE 1 #define FALLING_EDGE 2 @@ -46,23 +48,31 @@ SOFTWARE. #define USR_LED_GPIO_MIN 53 #define USR_LED_GPIO_MAX 56 +#define USR_LED_RED 66 +#define USR_LED_GREEN 67 +#define BAT25 27 +#define BAT50 11 +#define BAT75 61 +#define BAT100 10000 // Placeholder until I find the real number +#define WIFI 10001 // Ditto #define PUD_OFF 0 #define PUD_DOWN 1 #define PUD_UP 2 -int gpio_export(unsigned int gpio); -int gpio_unexport(unsigned int gpio); +BBIO_err gpio_export(unsigned int gpio); +BBIO_err gpio_unexport(unsigned int gpio); void exports_cleanup(void); -int gpio_set_direction(unsigned int gpio, unsigned int in_flag); -int gpio_get_direction(unsigned int gpio, unsigned int *value); -int gpio_set_value(unsigned int gpio, unsigned int value); -int gpio_get_value(unsigned int gpio, unsigned int *value); +BBIO_err gpio_set_direction(unsigned int gpio, unsigned int in_flag); +BBIO_err gpio_get_direction(unsigned int gpio, unsigned int *value); +BBIO_err gpio_set_value(unsigned int gpio, unsigned int value); +BBIO_err gpio_get_value(unsigned int gpio, unsigned int *value); int add_edge_detect(unsigned int gpio, unsigned int edge); void remove_edge_detect(unsigned int gpio); int add_edge_callback(unsigned int gpio, void (*func)(unsigned int gpio)); int event_detected(unsigned int gpio); +int gpio_initial(unsigned int gpio); int gpio_event_add(unsigned int gpio); int gpio_event_remove(unsigned int gpio); int gpio_is_evented(unsigned int gpio); diff --git a/source/examples/cpp/gpio.cpp b/source/examples/cpp/gpio.cpp new file mode 100644 index 0000000..78f0ec3 --- /dev/null +++ b/source/examples/cpp/gpio.cpp @@ -0,0 +1,44 @@ +/* +Copyright (c) 2017 Adafruit +Copyright (c) 2017 Nikolay Semenov + +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. +*/ + +#include "adafruit/bbio.h" + +#include +#include + +int main(int /*argc*/, char** /*argv*/) +{ + using adafruit::bbio::lib_options; + using adafruit::bbio::Gpio; + + adafruit::bbio::init(lib_options(LOG_DEBUG, nullptr, LOG_PERROR)); + + Gpio gpio("P8_10", Gpio::Direction::Output); + + for (int i = 0; i < 100; ++i) { + gpio.set_value((i % 2 == 1) ? Gpio::Value::High : Gpio::Value::Low); + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + + return 0; +} diff --git a/source/examples/cpp/pwm.cpp b/source/examples/cpp/pwm.cpp new file mode 100644 index 0000000..a702d94 --- /dev/null +++ b/source/examples/cpp/pwm.cpp @@ -0,0 +1,44 @@ +/* +Copyright (c) 2017 Adafruit +Copyright (c) 2017 Nikolay Semenov + +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. +*/ + +#include "adafruit/bbio.h" + +#include +#include + +int main(int /*argc*/, char** /*argv*/) +{ + using adafruit::bbio::Pwm; + + adafruit::bbio::init({LOG_DEBUG, nullptr, LOG_PERROR}); + + Pwm pwm("P8_19"); + pwm.start(0., 1e9 / (1 * 1000 * 1000), Pwm::Polarity::Normal); + + for (int i = 0; i < 1000; ++i) { + pwm.set_duty_cycle(i / 10.0); + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + } + + return 0; +} diff --git a/source/examples/python/blinkLEDs.py b/source/examples/python/blinkLEDs.py new file mode 100755 index 0000000..5ab93b9 --- /dev/null +++ b/source/examples/python/blinkLEDs.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python3 +import Adafruit_BBIO.GPIO as GPIO +import time + +LEDs = ["USR0", "USR1", "USR2", "USR3", + "GP0_3", "GP0_5", "GP0_6", + "GP1_3", "GP1_4", + "RED", "GREEN", + "BAT25", "BAT50", "BAT75", "BAT100", + "WIFI" + ] +delay = 0.25 + +for LED in LEDs: + print(LED) + GPIO.setup(LED, GPIO.OUT) + +while True: + for LED in LEDs: + GPIO.output(LED, 1) + time.sleep(delay) + for LED in LEDs: + GPIO.output(LED, 0) + time.sleep(delay) \ No newline at end of file diff --git a/source/examples/python/button.py b/source/examples/python/button.py new file mode 100755 index 0000000..62dc70b --- /dev/null +++ b/source/examples/python/button.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +# Reads the PAUSE button using interupts and sets the LED +# Pin table at https://github.com/beagleboard/beaglebone-blue/blob/master/BeagleBone_Blue_Pin_Table.csv + +# Import PyBBIO library: +import Adafruit_BBIO.GPIO as GPIO +import time + +button= "PAUSE" # PAUSE=P8_9, MODE=P8_10 +LED1 = "RED" +LED2 = "WIFI" + +# Set the GPIO pins: +GPIO.setup(LED1, GPIO.OUT) +GPIO.setup(LED2, GPIO.OUT) +GPIO.setup(button, GPIO.IN) + +print("Running...") + +while True: + state = GPIO.input(button) + GPIO.output(LED1, state) + GPIO.output(LED2, state) + + GPIO.wait_for_edge(button, GPIO.BOTH) + print("Pressed") \ No newline at end of file diff --git a/source/examples/python/gpio.py b/source/examples/python/gpio.py new file mode 100755 index 0000000..9a2c4df --- /dev/null +++ b/source/examples/python/gpio.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python3 +# Blinks some gpio pins on the Blue +import Adafruit_BBIO.GPIO as GPIO +import time + +LEDs = ["GP0_3", "GP0_4", "GP0_5", "GP0_6", "GP1_3", "GP1_4", + "RED_LED", "GREEN_LED"] +for LED in LEDs: + print(LED) + GPIO.setup(LED, GPIO.OUT) + +while True: + for LED in LEDs: + GPIO.output(LED, GPIO.HIGH) + time.sleep(0.1) + GPIO.output(LED, GPIO.LOW) diff --git a/source/examples/python/i2cmatrix.py b/source/examples/python/i2cmatrix.py new file mode 100755 index 0000000..c0142e4 --- /dev/null +++ b/source/examples/python/i2cmatrix.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 +# Write an 8x8 Red/Green LED matrix +# https://www.adafruit.com/product/902 + +import smbus +import time +bus = smbus.SMBus(1) +matrix = 0x70 + +delay = 1; # Delay between images in s + +bus.write_byte_data(matrix, 0x21, 0) # Start oscillator (p10) +bus.write_byte_data(matrix, 0x81, 0) # Disp on, blink off (p11) +bus.write_byte_data(matrix, 0xe7, 0) # Full brightness (page 15) + +# The first byte is GREEN, the second is RED. +smile = [0x00, 0x3c, 0x00, 0x42, 0x28, 0x89, 0x04, 0x85, + 0x04, 0x85, 0x28, 0x89, 0x00, 0x42, 0x00, 0x3c +] +frown = [0x3c, 0x00, 0x42, 0x00, 0x85, 0x20, 0x89, 0x00, + 0x89, 0x00, 0x85, 0x20, 0x42, 0x00, 0x3c, 0x00 +] +neutral = [0x3c, 0x3c, 0x42, 0x42, 0xa9, 0xa9, 0x89, 0x89, + 0x89, 0x89, 0xa9, 0xa9, 0x42, 0x42, 0x3c, 0x3c +] + +bus.write_i2c_block_data(matrix, 0, frown) +for fade in range(0xef, 0xe0, -1): + bus.write_byte_data(matrix, fade, 0) + time.sleep(delay/10) + +bus.write_i2c_block_data(matrix, 0, neutral) +for fade in range(0xe0, 0xef, 1): + bus.write_byte_data(matrix, fade, 0) + time.sleep(delay/10) + +bus.write_i2c_block_data(matrix, 0, smile) diff --git a/source/examples/python/i2ctmp101.py b/source/examples/python/i2ctmp101.py new file mode 100755 index 0000000..ab916c0 --- /dev/null +++ b/source/examples/python/i2ctmp101.py @@ -0,0 +1,12 @@ +#!/usr/bin/env python3 +# Read a TMP101 sensor + +import smbus +import time +bus = smbus.SMBus(1) +address = 0x49 + +while True: + temp = bus.read_byte_data(address, 0) + print(temp, end="\r") + time.sleep(0.25) diff --git a/source/examples/python/install.sh b/source/examples/python/install.sh new file mode 100644 index 0000000..3b7b158 --- /dev/null +++ b/source/examples/python/install.sh @@ -0,0 +1,3 @@ +# Here's what you need to install to run these. + +sudo apt install python3-smbus diff --git a/source/examples/python/leds.py b/source/examples/python/leds.py new file mode 100755 index 0000000..95957e9 --- /dev/null +++ b/source/examples/python/leds.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python3 +# Blinks some gpio pins on the Blue +import Adafruit_BBIO.GPIO as GPIO +import time + +LEDs = ["RED", "GREEN"] + +for LED in LEDs: + GPIO.setup(LED, GPIO.OUT) + +while True: + for LED in LEDs: + GPIO.output(LED, GPIO.HIGH) + time.sleep(0.1) + GPIO.output(LED, GPIO.LOW) + time.sleep(0.1) \ No newline at end of file diff --git a/source/examples/python/pwm.old.py b/source/examples/python/pwm.old.py new file mode 100644 index 0000000..7edc8fd --- /dev/null +++ b/source/examples/python/pwm.old.py @@ -0,0 +1,14 @@ +import Adafruit_BBIO.PWM as PWM + +#set polarity to 1 on start: +#PWM.start("P9_14", 50, 2000, 1) + +#PWM.start(channel, duty, freq=2000, polarity=0) +#duty values are valid 0 (off) to 100 (on) + +PWM.start("P9_14", 50) +PWM.set_duty_cycle("P9_14", 25.5) +PWM.set_frequency("P9_14", 10) + +PWM.stop("P9_14") +PWM.cleanup() diff --git a/source/examples/python/pwm.py b/source/examples/python/pwm.py new file mode 100755 index 0000000..3d352c8 --- /dev/null +++ b/source/examples/python/pwm.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python3 +import Adafruit_BBIO.PWM as PWM + +#set polarity to 1 on start: +#PWM.start("P9_14", 50, 2000, 1) + +#PWM.start(channel, duty, freq=2000, polarity=0) +#duty values are valid 0 (off) to 100 (on) + +SERVO="P9_14" +PWM.start(SERVO, 50) +PWM.set_duty_cycle(SERVO, 25.5) +PWM.set_frequency(SERVO, 10) + +PWM.stop(SERVO) +PWM.cleanup() diff --git a/source/include/adafruit/bbio.h b/source/include/adafruit/bbio.h new file mode 100644 index 0000000..888392b --- /dev/null +++ b/source/include/adafruit/bbio.h @@ -0,0 +1,59 @@ +/* +Copyright (c) 2017 Adafruit +Copyright (c) 2017 Nikolay Semenov + +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. +*/ +#pragma once + +#include +#include +#include + +#include + +namespace adafruit { +namespace bbio { + +struct lib_options +{ + int syslog_level; + + // If NULL then syslog uses process name. + char const* syslog_identifier; + + // Add LOG_PERROR to print to console + int syslog_option; + + lib_options(int syslog_level = LOG_INFO, + char const* syslog_identifier = nullptr, + int syslog_option = LOG_CONS | LOG_PID | LOG_NDELAY) + : syslog_level(syslog_level) + , syslog_identifier(syslog_identifier) + , syslog_option(syslog_option) + {} +}; + +/* + * Initialize the library. + */ +void init(lib_options = lib_options()); + +} // namespace bbio +} // namespace adafruit diff --git a/source/include/adafruit/bbio/error.h b/source/include/adafruit/bbio/error.h new file mode 100644 index 0000000..20ee319 --- /dev/null +++ b/source/include/adafruit/bbio/error.h @@ -0,0 +1,89 @@ +/* +Copyright (c) 2017 Adafruit +Copyright (c) 2017 Nikolay Semenov + +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. +*/ +#pragma once + +#ifdef __cplusplus + +#include + +namespace adafruit { +namespace bbio { + +#endif // __cplusplus + +typedef enum { + BBIO_OK, // No error + BBIO_ACCESS, // Error accessing a file + BBIO_SYSFS, // Some error with Sysfs files + BBIO_CAPE, // some error with capes + BBIO_INVARG, // Invalid argument + BBIO_MEM, + BBIO_GEN // General error +} BBIO_err; + +#ifdef __cplusplus + +class BBIOError : public std::runtime_error +{ +public: + BBIOError(BBIO_err code, std::string const& message = "") + : std::runtime_error( + std::to_string(code) + "-" + to_c_str(code) + " " + message) + {} + + static char const* to_c_str(BBIO_err err) + { + switch (err) { + case BBIO_OK: return "OK"; + case BBIO_ACCESS: return "ACCESS"; + case BBIO_SYSFS: return "SYSFS"; + case BBIO_CAPE: return "CAPE"; + case BBIO_INVARG: return "INVARG"; + case BBIO_MEM: return "MEMORY"; + case BBIO_GEN: return "GENERAL"; + default: return ""; + } + } +}; + +class CheckError +{ +public: + CheckError(BBIO_err code) + { + if (code != BBIO_OK) { + throw BBIOError(code); + } + } + CheckError(int code) + { + if (code != BBIO_OK) { + throw BBIOError((BBIO_err)code); + } + } +}; + +} // namespace bbio +} // namespace adafruit + +#endif // __cplusplus diff --git a/source/include/adafruit/bbio/gpio.h b/source/include/adafruit/bbio/gpio.h new file mode 100644 index 0000000..3d17be0 --- /dev/null +++ b/source/include/adafruit/bbio/gpio.h @@ -0,0 +1,66 @@ +/* +Copyright (c) 2017 Adafruit +Copyright (c) 2017 Nikolay Semenov + +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. +*/ +#pragma once + +#include + +namespace adafruit { +namespace bbio { + +class Gpio +{ +public: + enum class Direction + { + Input = 0, + Output = 1 + }; + + enum class Value + { + Low = 0, + High = 1 + }; + + enum class Resistor + { + None = 0, + PullDown = 1, + PullUp = 2 + }; + + Gpio(std::string const& key, Direction, Resistor = Resistor::None); + ~Gpio(); + + void set_direction(Direction, Resistor = Resistor::None); + void set_value(Value); + Value get_value() const; + +private: + std::string key_; + uint32_t pin_; + Direction direction_; +}; + +} // namespace bbio +} // namespace adafruit diff --git a/source/include/adafruit/bbio/pwm.h b/source/include/adafruit/bbio/pwm.h new file mode 100644 index 0000000..d38076b --- /dev/null +++ b/source/include/adafruit/bbio/pwm.h @@ -0,0 +1,55 @@ +/* +Copyright (c) 2017 Adafruit +Copyright (c) 2017 Nikolay Semenov + +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. +*/ +#pragma once + +#include + +namespace adafruit { +namespace bbio { + +class Pwm +{ +public: + enum class Polarity + { + Normal = 0, + Inversed = 1, + }; + + Pwm(std::string const& key); + ~Pwm(); + + void start(float duty_cycle = 0.0, float frequency = 2000.0, Polarity = Polarity::Normal); + void stop(); + + // 0.0 <= duty_cycle <= 100.0 + void set_duty_cycle(float); + void set_frequency(float); + void set_polarity(Polarity); + +private: + std::string key_; +}; + +} // namespace bbio +} // namespace adafruit diff --git a/source/library/bbio.cpp b/source/library/bbio.cpp new file mode 100644 index 0000000..38e66db --- /dev/null +++ b/source/library/bbio.cpp @@ -0,0 +1,44 @@ +/* +Copyright (c) 2017 Adafruit +Copyright (c) 2017 Nikolay Semenov + +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. +*/ + +#include "adafruit/bbio.h" +#include "adafruit/bbio/error.h" + +extern "C" { +#include "common.h" +#include "c_pwm.h" +} + +namespace adafruit { +namespace bbio { + +void init(lib_options lib_options) +{ + initlog(lib_options.syslog_level, + lib_options.syslog_identifier, + lib_options.syslog_option); + initialize_pwm(); +} + +} // namespace bbio +} // namespace adafruit diff --git a/source/library/gpio.cpp b/source/library/gpio.cpp new file mode 100644 index 0000000..c2603c3 --- /dev/null +++ b/source/library/gpio.cpp @@ -0,0 +1,105 @@ +/* +Copyright (c) 2017 Adafruit +Copyright (c) 2017 Nikolay Semenov + +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. +*/ + +#include "adafruit/bbio/gpio.h" + +#include "adafruit/bbio/error.h" + +extern "C" { +#include "common.h" +#include "c_pinmux.h" +#include "event_gpio.h" +} + +namespace adafruit { +namespace bbio { + +/* + * Gpio + */ +Gpio::Gpio(std::string const& key, Direction direction, Resistor resistor) + : key_(key) + , pin_(0) +{ + (CheckError)get_gpio_number(key_.c_str(), &pin_); + (CheckError)gpio_export(pin_); + + timespec ts[] = {{0, 1 * 1000000}}; + nanosleep(ts, NULL); + + set_direction(direction, resistor); +} + +Gpio::~Gpio() +{ + if (pin_) { + gpio_unexport(pin_); + } +} + +void Gpio::set_direction(Direction direction, Resistor resistor) +{ + (CheckError)gpio_set_direction(pin_, (uint32_t)direction); + direction_ = direction; + + if (direction == Direction::Output) { + // TODO: default value + // (CheckError)gpio_set_value(pin_, (uint32_t)Value::Low); + } + else { + switch (resistor) { + case Resistor::None: + (CheckError)set_pin_mode(key_.c_str(), "gpio"); + break; + case Resistor::PullDown: + (CheckError)set_pin_mode(key_.c_str(), "gpio_pd"); + break; + case Resistor::PullUp: + (CheckError)set_pin_mode(key_.c_str(), "gpio_pu"); + break; + } + } +} + +void Gpio::set_value(Value value) +{ + if (direction_ != Direction::Output) { + throw BBIOError(BBIO_INVARG, "Gpio: can't set value when direction is not Output!"); + } + + (CheckError)gpio_set_value(pin_, (uint32_t)value); +} + +Gpio::Value Gpio::get_value() const +{ + if (direction_ != Direction::Input) { + throw BBIOError(BBIO_INVARG, "Gpio: can't get value when direction is not Input!"); + } + + uint32_t value; + (CheckError)gpio_get_value(pin_, &value); + return (Value)value; +} + +} // namespace bbio +} // namespace adafruit diff --git a/source/library/pwm.cpp b/source/library/pwm.cpp new file mode 100644 index 0000000..f1ba57e --- /dev/null +++ b/source/library/pwm.cpp @@ -0,0 +1,72 @@ +/* +Copyright (c) 2017 Adafruit +Copyright (c) 2017 Nikolay Semenov + +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. +*/ + +#include "adafruit/bbio/pwm.h" + +extern "C" { +#include "c_pwm.h" +} + +namespace adafruit { +namespace bbio { + +/* + * Pwm + */ +Pwm::Pwm(std::string const& key) + : key_(key) +{} + +Pwm::~Pwm() +{ + stop(); +} + +void Pwm::start(float duty_cycle, float frequency, Polarity polarity) +{ + (CheckError)pwm_start(key_.c_str(), duty_cycle, frequency, (int)polarity); +} + +void Pwm::stop() +{ + (CheckError)pwm_disable(key_.c_str()); +} + +void Pwm::set_duty_cycle(float duty_cycle) +{ + (CheckError)pwm_set_duty_cycle(key_.c_str(), duty_cycle); +} + +void Pwm::set_frequency(float frequency) +{ + (CheckError)pwm_set_frequency(key_.c_str(), frequency); +} + +void Pwm::set_polarity(Polarity polarity) +{ + (CheckError)pwm_set_polarity(key_.c_str(), (int)polarity); +} + +} // namespace bbio +} // namespace adafruit + diff --git a/source/py_gpio.c b/source/py_gpio.c index 098ad2d..99cb0e7 100644 --- a/source/py_gpio.c +++ b/source/py_gpio.c @@ -34,6 +34,7 @@ SOFTWARE. #include "event_gpio.h" #include "c_pinmux.h" #include +#include static int gpio_warnings = 1; @@ -131,18 +132,24 @@ static PyObject *py_setup_channel(__attribute__ ((unused)) PyObject *self, PyObj // Set the pin value and bail if we get an error. res = gpio_set_value(gpio, initial); - if(res != 0) { + if (res != BBIO_OK) { PyErr_SetString(PyExc_ValueError, "Set gpio value failed, missing file or invalid permissions."); return NULL; } } else { - if (pud == PUD_DOWN) - set_pin_mode(channel, "gpio_pd"); + if (pud == PUD_DOWN) + res = set_pin_mode(channel, "gpio_pd"); else if (pud == PUD_UP) - set_pin_mode(channel, "gpio_pu"); + res = set_pin_mode(channel, "gpio_pu"); else - set_pin_mode(channel, "gpio"); + res = set_pin_mode(channel, "gpio"); + } + + //Check if set_pin_mode() returned no error + if (res != BBIO_OK) { + PyErr_SetString(PyExc_ValueError, "Set gpio mode failed, missing file or invalid permissions."); + return NULL; } gpio_direction[gpio] = direction; @@ -211,6 +218,7 @@ static void run_py_callbacks(unsigned int gpio) PyObject *result; PyGILState_STATE gstate; struct py_callback *cb = py_callbacks; + unsigned char cookie[2] = {0}; struct timeval tv_timenow; unsigned long long timenow; @@ -218,28 +226,40 @@ static void run_py_callbacks(unsigned int gpio) { if (cb->gpio == gpio) { - gettimeofday(&tv_timenow, NULL); - timenow = tv_timenow.tv_sec*1E6 + tv_timenow.tv_usec; - if (cb->bouncetime == 0 || timenow - cb->lastcall > cb->bouncetime*1000 || cb->lastcall == 0 || cb->lastcall > timenow) { - - // save lastcall before calling func to prevent reentrant bounce + //Store memory contents of the first byte of current callback structure as a "magic cookie" + memcpy(&cookie[0], cb, 1); + gettimeofday(&tv_timenow, NULL); + timenow = tv_timenow.tv_sec*1E6 + tv_timenow.tv_usec; + if (cb->bouncetime == 0 || timenow - cb->lastcall > cb->bouncetime*1000 || cb->lastcall == 0 || cb->lastcall > timenow) { + + // save lastcall before calling func to prevent reentrant bounce + cb->lastcall = timenow; + + // run callback + gstate = PyGILState_Ensure(); + result = PyObject_CallFunction(cb->py_cb, "s", cb->channel); + //Check the first byte of callback structure after executing callback function body + memcpy(&cookie[1], cb, 1); + + if (result == NULL && PyErr_Occurred()) + { + PyErr_Print(); + PyErr_Clear(); + } + Py_XDECREF(result); + PyGILState_Release(gstate); + } + + // Current callback pointer might have changed _only_ if linked list structure has been altered from within the callback function, which should happen _only_ in remove_event_detect() call + // If that happened, cb* pointer will be now addressing different memory location with different data. + if (cookie[0] != cookie[1]) break; + + if (cb != NULL) cb->lastcall = timenow; - - // run callback - gstate = PyGILState_Ensure(); - result = PyObject_CallFunction(cb->py_cb, "s", cb->channel); - - if (result == NULL && PyErr_Occurred()) - { - PyErr_Print(); - PyErr_Clear(); - } - Py_XDECREF(result); - PyGILState_Release(gstate); - } - cb->lastcall = timenow; } - cb = cb->next; + // If callback just executed was the only one in chain and it was removed inside cb->py_cb() body, cb->next will be pointing to NULL now + if (cb != NULL) + cb = cb->next; } } @@ -357,12 +377,12 @@ static PyObject *py_add_event_detect(__attribute__ ((unused)) PyObject *self, Py if ((result = add_edge_detect(gpio, edge)) != 0) // starts a thread { - if (result == 1) + if (result == -1) { - PyErr_SetString(PyExc_RuntimeError, "Edge detection already enabled for this GPIO channel"); + PyErr_SetString(PyExc_KeyError, "Edge detection already enabled for this GPIO channel"); return NULL; } else { - PyErr_SetString(PyExc_RuntimeError, "Failed to add edge detection"); + PyErr_SetFromErrno(PyExc_RuntimeError); return NULL; } } @@ -404,7 +424,9 @@ static PyObject *py_remove_event_detect(__attribute__ ((unused)) PyObject *self, temp = cb; cb = cb->next; free(temp); - } else { + } + else + { prev = cb; cb = cb->next; } @@ -444,10 +466,9 @@ static PyObject *py_wait_for_edge(__attribute__ ((unused)) PyObject *self, PyObj char error[30]; BBIO_err err; - if (!PyArg_ParseTuple(args, "sii", &channel, &edge, &timeout)){ - timeout = -1; - if (!PyArg_ParseTuple(args, "si", &channel, &edge)) - return NULL; + timeout = -1; + if (!PyArg_ParseTuple(args, "si|i", &channel, &edge, &timeout)){ + return NULL; } err = get_gpio_number(channel, &gpio); @@ -534,14 +555,14 @@ static PyObject *py_setwarnings(__attribute__ ((unused)) PyObject *self, __attri static const char moduledocstring[] = "GPIO functionality of a BeagleBone using Python"; PyMethodDef gpio_methods[] = { - {"setup", (PyCFunction)py_setup_channel, METH_VARARGS | METH_KEYWORDS, "Set up the GPIO channel, direction and (optional) pull/up down control\nchannel - Either: RPi board pin number (not BCM GPIO 00..nn number). Pins start from 1\n or : BCM GPIO number\ndirection - INPUT or OUTPUT\n[pull_up_down] - PUD_OFF (default), PUD_UP or PUD_DOWN\n[initial] - Initial value for an output channel\n[delay] - Time in milliseconds to wait after exporting gpio pin"}, + {"setup", (PyCFunction)(void *)py_setup_channel, METH_VARARGS | METH_KEYWORDS, "Set up the GPIO channel, direction and (optional) pull/up down control\nchannel - Either: RPi board pin number (not BCM GPIO 00..nn number). Pins start from 1\n or : BCM GPIO number\ndirection - INPUT or OUTPUT\n[pull_up_down] - PUD_OFF (default), PUD_UP or PUD_DOWN\n[initial] - Initial value for an output channel\n[delay] - Time in milliseconds to wait after exporting gpio pin"}, {"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"}, {"output", py_output_gpio, METH_VARARGS, "Output to a GPIO channel\ngpio - gpio channel\nvalue - 0/1 or False/True or LOW/HIGH"}, {"input", py_input_gpio, METH_VARARGS, "Input from a GPIO channel. Returns HIGH=1=True or LOW=0=False\ngpio - gpio channel"}, - {"add_event_detect", (PyCFunction)py_add_event_detect, METH_VARARGS | METH_KEYWORDS, "Enable edge detection events for a particular GPIO channel.\nchannel - either board pin number or BCM number depending on which mode is set.\nedge - RISING, FALLING or BOTH\n[callback] - A callback function for the event (optional)\n[bouncetime] - Switch bounce timeout in ms for callback"}, + {"add_event_detect", (PyCFunction)(void *)py_add_event_detect, METH_VARARGS | METH_KEYWORDS, "Enable edge detection events for a particular GPIO channel.\nchannel - either board pin number or BCM number depending on which mode is set.\nedge - RISING, FALLING or BOTH\n[callback] - A callback function for the event (optional)\n[bouncetime] - Switch bounce timeout in ms for callback"}, {"remove_event_detect", py_remove_event_detect, METH_VARARGS, "Remove edge detection for a particular GPIO channel\ngpio - gpio channel"}, {"event_detected", py_event_detected, METH_VARARGS, "Returns True if an edge has occured on a given GPIO. You need to enable edge detection using add_event_detect() first.\ngpio - gpio channel"}, - {"add_event_callback", (PyCFunction)py_add_event_callback, METH_VARARGS | METH_KEYWORDS, "Add a callback for an event already defined using add_event_detect()\ngpio - gpio channel\ncallback - a callback function\n[bouncetime] - Switch bounce timeout in ms"}, + {"add_event_callback", (PyCFunction)(void *)py_add_event_callback, METH_VARARGS | METH_KEYWORDS, "Add a callback for an event already defined using add_event_detect()\ngpio - gpio channel\ncallback - a callback function\n[bouncetime] - Switch bounce timeout in ms"}, {"wait_for_edge", py_wait_for_edge, METH_VARARGS, "Wait for an edge.\ngpio - gpio channel\nedge - RISING, FALLING or BOTH\ntimeout (optional) - time to wait in miliseconds. -1 will wait forever (default)"}, {"gpio_function", py_gpio_function, METH_VARARGS, "Return the current GPIO function (IN, OUT, ALT0)\ngpio - gpio channel"}, {"setwarnings", py_setwarnings, METH_VARARGS, "Enable or disable warning messages"}, @@ -576,8 +597,12 @@ PyMODINIT_FUNC initGPIO(void) define_constants(module); + initlog(LOG_INFO, NULL, BBIO_LOG_OPTION); + +#if PY_VERSION_HEX < 0x03090000 if (!PyEval_ThreadsInitialized()) PyEval_InitThreads(); +#endif if (Py_AtExit(event_cleanup) != 0) { diff --git a/source/py_pwm.c b/source/py_pwm.c index 72f1b09..e5f10f8 100644 --- a/source/py_pwm.c +++ b/source/py_pwm.c @@ -25,6 +25,7 @@ SOFTWARE. #include "constants.h" #include "common.h" #include "c_pwm.h" +#include // python function cleanup() static PyObject *py_cleanup(__attribute__ ((unused)) PyObject *self, __attribute__ ((unused)) PyObject *args) @@ -214,10 +215,10 @@ static PyObject *py_set_frequency(__attribute__ ((unused)) PyObject *self, PyObj static const char moduledocstring[] = "PWM functionality of a BeagleBone using Python"; 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)" }, + {"start", (PyCFunction)(void *)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)(void *)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)(void *)py_set_duty_cycle, METH_VARARGS | METH_KEYWORDS, "Change the duty cycle\ndutycycle - between 0.0 and 100.0" }, + { "set_frequency", (PyCFunction)(void *)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} @@ -251,6 +252,7 @@ PyMODINIT_FUNC initPWM(void) define_constants(module); + initlog(LOG_INFO, NULL, BBIO_LOG_OPTION); #if PY_MAJOR_VERSION > 2 return module; diff --git a/source/py_uart.c b/source/py_uart.c index 78b2535..c70d327 100644 --- a/source/py_uart.c +++ b/source/py_uart.c @@ -25,6 +25,7 @@ SOFTWARE. #include "constants.h" #include "common.h" #include "c_uart.h" +#include "c_pinmux.h" const char *valid_uarts[4] = {"UART1", "UART2", "UART4", "UART5"}; @@ -61,6 +62,29 @@ static PyObject *py_setup_uart(__attribute__ ((unused)) PyObject *self, PyObject return NULL; } +#ifdef BBBVERSION41 + uart_t *p; + for (p = uart_table; p->name != NULL; ++p) { + if (strcmp(p->name, channel) == 0) { + err = set_pin_mode(p->rx, "uart"); + //Check if set_pin_mode() returned no error + if (err != BBIO_OK) { + fprintf(stderr, "py_setup_uart(%s): set_pin_mode() failed for pin=%s", channel, p->rx); + PyErr_SetString(PyExc_ValueError, "Set pin mode failed for uart channel."); + return NULL; + } + + err = set_pin_mode(p->tx, "uart"); + //Check if set_pin_mode() returned no error + if (err != BBIO_OK) { + fprintf(stderr, "py_setup_uart(%s): set_pin_mode() failed for pin=%s", channel, p->tx); + PyErr_SetString(PyExc_ValueError, "Set pin mode failed for uart channel."); + return NULL; + } + } + } +#endif + Py_RETURN_NONE; } diff --git a/source/spimodule.c b/source/spimodule.c index 5b1f6ad..7e438bd 100644 --- a/source/spimodule.c +++ b/source/spimodule.c @@ -703,7 +703,6 @@ static PyObject * SPI_open(SPI *self, PyObject *args, PyObject *kwds) { int bus, device; - int bus_path; int max_dt_length = 15; char device_tree_name[max_dt_length]; char path[MAXPATH]; @@ -722,14 +721,7 @@ SPI_open(SPI *self, PyObject *args, PyObject *kwds) return NULL; } - bus_path = get_spi_bus_path_number(bus); - if (bus_path == -1) { - PyErr_SetString(PyExc_OverflowError, - "Unable to find loaded spi bus path."); - return NULL; - } - - if (snprintf(path, MAXPATH, "/dev/spidev%d.%d", bus_path, device) >= MAXPATH) { + if (snprintf(path, MAXPATH, "/dev/spidev%d.%d", bus, device) >= MAXPATH) { PyErr_SetString(PyExc_OverflowError, "Bus and/or device number is invalid."); return NULL; @@ -785,17 +777,17 @@ PyDoc_STRVAR(SPI_type_doc, "specified SPI device interface.\n"); static PyMethodDef SPI_methods[] = { - {"open", (PyCFunction)SPI_open, METH_VARARGS | METH_KEYWORDS, + {"open", (PyCFunction)(void *)SPI_open, METH_VARARGS | METH_KEYWORDS, SPI_open_doc}, - {"close", (PyCFunction)SPI_close, METH_NOARGS, + {"close", (PyCFunction)(void *)SPI_close, METH_NOARGS, SPI_close_doc}, - {"readbytes", (PyCFunction)SPI_readbytes, METH_VARARGS, + {"readbytes", (PyCFunction)(void *)SPI_readbytes, METH_VARARGS, SPI_read_doc}, - {"writebytes", (PyCFunction)SPI_writebytes, METH_VARARGS, + {"writebytes", (PyCFunction)(void *)SPI_writebytes, METH_VARARGS, SPI_write_doc}, - {"xfer", (PyCFunction)SPI_xfer, METH_VARARGS, + {"xfer", (PyCFunction)(void *)SPI_xfer, METH_VARARGS, SPI_xfer_doc}, - {"xfer2", (PyCFunction)SPI_xfer2, METH_VARARGS, + {"xfer2", (PyCFunction)(void *)SPI_xfer2, METH_VARARGS, SPI_xfer2_doc}, {NULL}, }; diff --git a/source/test/cpp/abi.cpp b/source/test/cpp/abi.cpp new file mode 100644 index 0000000..edcdaba --- /dev/null +++ b/source/test/cpp/abi.cpp @@ -0,0 +1,30 @@ +/* +Copyright (c) 2017 Adafruit +Copyright (c) 2017 Nikolay Semenov + +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. +*/ + +#include "adafruit/bbio.h" + +int main(int /*argc*/, char** /*argv*/) +{ + adafruit::bbio::init({LOG_DEBUG, nullptr, LOG_PERROR}); + return 0; +} diff --git a/source/test/cpp/pwm.cpp b/source/test/cpp/pwm.cpp new file mode 100644 index 0000000..eece8fd --- /dev/null +++ b/source/test/cpp/pwm.cpp @@ -0,0 +1,321 @@ +/* + Copyright (c) 2017 Adafruit + Copyright (c) 2017 Nikolay Semenov + + 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. + */ + +#include "adafruit/bbio.h" + +#include + +#include + +#include + +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,1,0) +# ifndef BBBVERSION41 +# define BBBVERSION41 +# endif +#endif + +using adafruit::bbio::Pwm; +using adafruit::bbio::BBIOError; + +class PwmTest : public testing::Test +{ +public: + PwmTest() + { + adafruit::bbio::init({ LOG_DEBUG, nullptr, LOG_PERROR }); + } + + void SetUp() override {} + void TearDown() override {} + + static std::string get_pwm_dir() + { + struct auto_glob + { + glob_t value; + ~auto_glob() + { + globfree(&value); + } + }; + +#ifdef BBBVERSION41 + // On 4.1+, the pwm subdirectory sometimes takes different names: + // .pwm or .ehrpwm, etc. + + std::string pattern = "/sys/devices/platform/ocp/48302000.*/" + "48302200.*/pwm/pwmchip?/pwm0"; +#else + std::string pattern = "/sys/devices/ocp.*/pwm_test_P9_14*"; +#endif + + auto_glob results; + int err = glob(pattern.c_str(), 0, NULL, &results.value); + if (err) { + return NULL; + } + + // We will return the first match + return results.value.gl_pathv[0]; + } + + static std::string read_file(std::string const& path) + { + std::ifstream fstream(path); + std::stringstream buffer; + buffer << fstream.rdbuf(); + std::string str = buffer.str(); + if (!str.empty() && str.back() == '\n') { + str.resize(str.length()-1); + } + return str; + } + + static std::string read_duty_cycle(std::string const& pwm_dir) + { +#ifdef BBBVERSION41 + return read_file(pwm_dir + "/duty_cycle"); +#else + return read_file(pwm_dir + "/duty"); +#endif + } + + static std::string read_period(std::string const& pwm_dir) + { + return read_file(pwm_dir + "/period"); + } + + static std::string read_polarity(std::string const& pwm_dir) + { + return read_file(pwm_dir + "/polarity"); + } + + static char const* polarity_str(Pwm::Polarity value) + { +#ifdef BBBVERSION41 + return value == Pwm::Polarity::Normal ? "normal" : "inversed"; +#else + return value == Pwm::Polarity::Normal ? "0" : "1"; +#endif + } +}; + +TEST_F(PwmTest, start_pwm) +{ + Pwm pwm("P9_14"); + pwm.start(); + + auto pwm_dir = get_pwm_dir(); + ASSERT_FALSE(pwm_dir.empty()); + + auto duty = read_duty_cycle(pwm_dir); + auto period = read_period(pwm_dir); + ASSERT_EQ(duty, "0"); + ASSERT_EQ(period, "500000"); +} + +TEST_F(PwmTest, start_pwm_ecap0) +{ + Pwm pwm("P9_42"); + pwm.start(0); + + auto pwm_dir = get_pwm_dir(); + ASSERT_FALSE(pwm_dir.empty()); + + auto duty = read_duty_cycle(pwm_dir); + auto period = read_period(pwm_dir); + + ASSERT_EQ(duty, "0"); + ASSERT_EQ(period, "500000"); +} + +TEST_F(PwmTest, start_pwm_with_polarity_one) +{ + Pwm pwm("P9_14"); + pwm.start(0, 2000, Pwm::Polarity::Inversed); + + auto pwm_dir = get_pwm_dir(); + ASSERT_FALSE(pwm_dir.empty()); + + auto duty = read_duty_cycle(pwm_dir); + auto period = read_period(pwm_dir); + auto polarity = read_polarity(pwm_dir); + + ASSERT_EQ(duty, "0"); + ASSERT_EQ(period, "500000"); + ASSERT_EQ(polarity, polarity_str(Pwm::Polarity::Inversed)); +} + +TEST_F(PwmTest, start_pwm_with_polarity_default) +{ + Pwm pwm("P9_14"); + pwm.start(0, 2000); + + auto pwm_dir = get_pwm_dir(); + ASSERT_FALSE(pwm_dir.empty()); + + auto duty = read_duty_cycle(pwm_dir); + auto period = read_period(pwm_dir); + auto polarity = read_polarity(pwm_dir); + + ASSERT_EQ(duty, "0"); + ASSERT_EQ(period, "500000"); + ASSERT_EQ(polarity, polarity_str(Pwm::Polarity::Normal)); +} + +TEST_F(PwmTest, start_pwm_with_polarity_zero) +{ + Pwm pwm("P9_14"); + pwm.start(0, 2000, Pwm::Polarity::Normal); + + auto pwm_dir = get_pwm_dir(); + ASSERT_FALSE(pwm_dir.empty()); + + auto duty = read_duty_cycle(pwm_dir); + auto period = read_period(pwm_dir); + auto polarity = read_polarity(pwm_dir); + + ASSERT_EQ(duty, "0"); + ASSERT_EQ(period, "500000"); + ASSERT_EQ(polarity, polarity_str(Pwm::Polarity::Normal)); +} + +TEST_F(PwmTest, pwm_start_invalid_pwm_key) +{ + Pwm pwm("P8_25"); + ASSERT_THROW(pwm.start(), BBIOError); +} + +TEST_F(PwmTest, pwm_start_invalid_duty_cycle_negative) +{ + Pwm pwm("P9_14"); + ASSERT_THROW(pwm.start(-1), BBIOError); +} + +TEST_F(PwmTest, pwm_start_valid_duty_cycle_min) +{ + // testing an exception isn't thrown + Pwm pwm("P9_14"); + pwm.start(0); +} + +TEST_F(PwmTest, pwm_start_valid_duty_cycle_max) +{ + // testing an exception isn't thrown + Pwm pwm("P9_14"); + pwm.start(100); +} + +TEST_F(PwmTest, pwm_start_invalid_duty_cycle_high) +{ + Pwm pwm("P9_14"); + ASSERT_THROW(pwm.start(101), BBIOError); +} + +TEST_F(PwmTest, pwm_start_invalid_frequency_negative) +{ + Pwm pwm("P9_14"); + ASSERT_THROW(pwm.start(0, -1), BBIOError); +} + +TEST_F(PwmTest, pwm_start_negative_polarity) +{ + Pwm pwm("P9_14"); + ASSERT_THROW(pwm.start(0, 100, static_cast(-1)), BBIOError); +} + +TEST_F(PwmTest, pwm_start_invalid_positive_polarity) +{ + Pwm pwm("P9_14"); + ASSERT_THROW(pwm.start(0, 100, static_cast(2)), BBIOError); +} + +TEST_F(PwmTest, pwm_duty_modified) +{ + Pwm pwm("P9_14"); + pwm.start(0); + + auto pwm_dir = get_pwm_dir(); + ASSERT_FALSE(pwm_dir.empty()); + + { + auto duty = read_duty_cycle(pwm_dir); + auto period = read_period(pwm_dir); + ASSERT_EQ(duty, "0"); + ASSERT_EQ(period, "500000"); + } + { + pwm.set_duty_cycle(100); + auto duty = read_duty_cycle(pwm_dir); + auto period = read_period(pwm_dir); + ASSERT_EQ(duty, "500000"); + ASSERT_EQ(period, "500000"); + } +} + +TEST_F(PwmTest, pwm_duty_cycle_non_setup_key) +{ + Pwm pwm("P9_14"); + ASSERT_THROW(pwm.set_duty_cycle(100), BBIOError); +} + +TEST_F(PwmTest, pwm_duty_cycle_invalid_key) +{ + Pwm pwm("P9_15"); + ASSERT_THROW(pwm.set_duty_cycle(100), BBIOError); +} + +TEST_F(PwmTest, pwm_duty_cycle_invalid_value_high) +{ + Pwm pwm("P9_14"); + pwm.start(0); + ASSERT_THROW(pwm.set_duty_cycle(101), BBIOError); +} + +TEST_F(PwmTest, pwm_duty_cycle_invalid_value_negative) +{ + Pwm pwm("P9_14"); + pwm.start(0); + ASSERT_THROW(pwm.set_duty_cycle(-1), BBIOError); +} + +TEST_F(PwmTest, pwm_frequency_invalid_value_negative) +{ + Pwm pwm("P9_14"); + pwm.start(0); + ASSERT_THROW(pwm.set_frequency(-1), BBIOError); +} + +TEST_F(PwmTest, pwm_freq_non_setup_key) +{ + Pwm pwm("P9_14"); + ASSERT_THROW(pwm.set_frequency(100), BBIOError); +} + +TEST_F(PwmTest, pwm_freq_non_setup_invalid_key) +{ + Pwm pwm("P9_15"); + ASSERT_THROW(pwm.set_frequency(100), BBIOError); +} diff --git a/test/issue170-pwm.py b/test/issue170-pwm.py new file mode 100644 index 0000000..9e45674 --- /dev/null +++ b/test/issue170-pwm.py @@ -0,0 +1,5 @@ +import Adafruit_BBIO.PWM as PWM +PWM.start("P9_14", 50, 2000, 1) +PWM.cleanup() +PWM.start("P9_14", 50, 2000, 0) +PWM.cleanup() diff --git a/test/issue178.py b/test/issue178.py new file mode 100644 index 0000000..85a7d4b --- /dev/null +++ b/test/issue178.py @@ -0,0 +1,53 @@ +# These are all the LEDs defined in source/event_gpio.h: +# #define USR_LED_GPIO_MIN 53 +# #define USR_LED_GPIO_MAX 56 +# #define USR_LED_RED 66 +# #define USR_LED_GREEN 67 +# #define BAT25 27 +# #define BAT50 11 +# #define BAT75 61 +# #define BAT100 10000 // Placeholder until I find the real number +# #define WIFI 10001 // Ditto +# +# which map to entries in pins_t table[] in source/common.c +# +# 66 +# BeagleBone (not Blue): { "TIMER4", "P8_7", 66, -1, -1}, +# BeagleBone Blue: { "RED_LED", "RED", 66, -1, -1}, // LEDs +# +# 67 +# BeagleBone (not Blue): { "TIMER7", "P8_8", 67, -1, -1}, +# BeagleBone Blue: { "GREEN_LED", "GREEN", 67, -1, -1}, +# +# 27 +# BeagleBone (not Blue): { "GPIO0_27", "P8_17", 27, -1, -1}, +# BeagleBone Blue: { "BAT25", "BAT25", 27, -1, -1}, +# PocketBeagle: { "GPIO0_27", "P2_19", 27, -1, -1}, +# +# 11 +# BeagleBone (not Blue): { "UART5_RTSN", "P8_32", 11, -1, -1}, +# BeagleBone Blue: { "BAT50", "BAT50", 11, -1, -1}, +# +# 61 +# BeagleBone (not Blue): { "GPIO1_29", "P8_26", 61, -1, -1}, +# BeagleBone Blue: { "BAT75", "BAT75", 61, -1, -1}, +# + +import Adafruit_BBIO.GPIO as GPIO + +test_pins = [ + "USR0", "USR1", "USR2", "USR3", + "RED_LED", "GREEN_LED", "BAT25", "BAT50", "BAT75", + "P8_7", "P8_8", "P8_17", "P8_32", "P8_26" + ] + +for pin in test_pins: + print("========================") + print("test GPIO.setup(): {0}".format(pin)) + GPIO.setup(pin, GPIO.OUT) + print("test GPIO.output(): {0}".format(pin)) + GPIO.output(pin, GPIO.HIGH) + value = GPIO.input(pin); + print("test GPIO.input(): {0}={1}".format(pin, value)); + +GPIO.cleanup() diff --git a/test/notes/rotary-encoder-eqep-test.md b/test/notes/rotary-encoder-eqep-test.md new file mode 100644 index 0000000..d140820 --- /dev/null +++ b/test/notes/rotary-encoder-eqep-test.md @@ -0,0 +1,101 @@ +I have documented how-to to enable all the eqep pins: +https://github.com/adafruit/adafruit-beaglebone-io-python/commit/c418cdae9a2a2c0d52412561c0125b0d227af4eb + +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 +``` + +### eqep2b: P8.41, P8.42 +_alternate pins for eqep2 (mutually exclusive)_ +``` +config-pin P8.41 qep +config-pin P8.42 qep +cat /sys/devices/platform/ocp/48304000.epwmss/48304180.eqep/position +``` + +### TODO: implement in corresponding methods in `Encoder.py` + + +## Read QEP1 +* Updated: 2017-08-31 +* I was able to enable QEP1 with the following: + +#### /boot/uEnv.txt +``` +uname_r=4.9.35-ti-r44 +enable_uboot_overlays=1 +disable_uboot_overlay_video=1 +cmdline=coherent_pool=1M net.ifnames=0 cape_universal=enable +cape_enable=bone_capemgr.enable_partno=cape-universala +``` + +#### Read QEP1: +``` +$ config-pin P8.33 qep +$ config-pin P8.35 qep +$ cat /sys/devices/platform/ocp/ocp\:P8_33_pinmux/state +qep +$ cat /sys/devices/platform/ocp/ocp\:P8_35_pinmux/state +qep +$ cat /sys/devices/platform/ocp/48302000.epwmss/48302180.eqep/position +0 +``` + +#### Version info: +``` +$ sudo /opt/scripts/tools/version.sh +git:/opt/scripts/:[2ce750d881941c5189db9e189af90517e11c079f] +eeprom:[A335BNLT000C3014BBBK1316] +dogtag:[BeagleBoard.org Debian Image 2017-06-18] +bootloader:[microSD-(push-button)]:[/dev/mmcblk0]:[U-Boot +2017.09-rc2-00002-g7c9353] +bootloader:[eMMC-(default)]:[/dev/mmcblk1]:[U-Boot 2017.09-rc2-00002-g84a7f2] +kernel:[4.9.35-ti-r44] +nodejs:[v4.8.4] +uboot_overlay_options:[enable_uboot_overlays=1] +uboot_overlay_options:[disable_uboot_overlay_video=1] +pkg:[bb-cape-overlays]:[4.4.20170728.0-0rcnee1~jessie+20170728] +pkg:[bb-wl18xx-firmware]:[1.20170612-0rcnee0~jessie+20170612] +WARNING:pkg:[firmware-ti-connectivity]:[NOT_INSTALLED] +``` diff --git a/test/notes/run_config-pin_during_startup.md b/test/notes/run_config-pin_during_startup.md new file mode 100644 index 0000000..ce1cefe --- /dev/null +++ b/test/notes/run_config-pin_during_startup.md @@ -0,0 +1,55 @@ +## Create file `/usr/bin/enable-i2c-pins.sh` + **sudo vi /usr/bin/enable-i2c-pins.sh** +``` +#!/bin/bash + +config-pin p9.17 i2c +config-pin p9.18 i2c +``` +**sudo chmod 755 /usr/bin/enable-i2c-pins.sh** + +## Create file `/lib/systemd/system/enable-i2c-pins.service` +**sudo vi /lib/systemd/system/enable-i2c-pins.service** +``` +[Unit] +Description=Enable I2C pins +After=generic-board-startup.service + +[Service] +Type=simple +ExecStart=/usr/bin/enable-i2c-pins.sh + +[Install] +WantedBy=multi-user.target +``` +## Enable the new systemd service +**sudo systemctl daemon-reload** +**sudo systemctl enable enable-i2c-pins.service** +``` +Created symlink /etc/systemd/system/multi-user.target.wants/enable-i2c-pins.service → /lib/systemd/system/enable-i2c-pins.service. +``` + +## Reboot and test +**sudo systemctl status enable-i2c-pins.service** +``` +debian@beaglebone:~$ sudo systemctl status enable-i2c-pins.service +● enable-i2c-pins.service - Enable I2C pins + Loaded: loaded (/lib/systemd/system/enable-i2c-pins.service; enabled) + Active: inactive (dead) since Wed 2017-04-19 06:35:06 UTC; 48s ago + Process: 649 ExecStart=/usr/bin/enable-i2c-pins.sh (code=exited, status=0/SUCCESS) + Main PID: 649 (code=exited, status=0/SUCCESS) + +Apr 19 06:35:05 beaglebone sudo[664]: root : TTY=unknown ; PWD=/ ; USER=root ; COMMAND=/bin/bash -c echo i2c > /sys/devices/platform/ocp/ocp:P9_17_pinmux/state +Apr 19 06:35:05 beaglebone sudo[664]: pam_unix(sudo:session): session opened for user root by (uid=0) +Apr 19 06:35:05 beaglebone sudo[664]: pam_unix(sudo:session): session closed for user root +Apr 19 06:35:06 beaglebone sudo[677]: root : TTY=unknown ; PWD=/ ; USER=root ; COMMAND=/bin/bash -c echo i2c > /sys/devices/platform/ocp/ocp:P9_18_pinmux/state +Apr 19 06:35:06 beaglebone sudo[677]: pam_unix(sudo:session): session opened for user root by (uid=0) +Apr 19 06:35:06 beaglebone sudo[677]: pam_unix(sudo:session): session closed for user root +``` + +**config-pin -q p9.17 && config-pin -q p9.18** +``` +debian@beaglebone:~$ config-pin -q p9.17 && config-pin -q p9.18 +P9_17 Mode: i2c +P9_18 Mode: i2c +``` diff --git a/doc/spi_loopback_test.md b/test/notes/spi_loopback_test.md similarity index 97% rename from doc/spi_loopback_test.md rename to test/notes/spi_loopback_test.md index 0f93d0c..7041852 100644 --- a/doc/spi_loopback_test.md +++ b/test/notes/spi_loopback_test.md @@ -46,7 +46,7 @@ from Adafruit_BBIO.SPI import SPI #spi = SPI(1,1) #/dev/spidev2.1 spi = SPI(0,0) -print spi.xfer2([32, 11, 110, 22, 220]) +print(spi.xfer2([32, 11, 110, 22, 220])) spi.close() ``` diff --git a/test/notes/test_read_write_all_gpio_pins.md b/test/notes/test_read_write_all_gpio_pins.md new file mode 100644 index 0000000..59eb06d --- /dev/null +++ b/test/notes/test_read_write_all_gpio_pins.md @@ -0,0 +1,28 @@ +This Python program will open, read and write to all the GPIO pins and the USR LEDs: +``` +import Adafruit_BBIO.GPIO as GPIO +import time + +for i in range(4): + GPIO.setup("USR%d" % i, GPIO.OUT) + GPIO.output("USR%d" % i, GPIO.HIGH) + GPIO.output("USR%d" % i, GPIO.LOW) + +for i in [3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46]: + GPIO.setup("P8_%d" % i, GPIO.OUT) + GPIO.output("P8_%d" % i, GPIO.HIGH) + GPIO.output("P8_%d" % i, GPIO.LOW) + +for i in [11,12,13,14,15,16,17,18,21,22,23,24,25,26,27,28,29,30,31,41,42]: + GPIO.setup("P9_%d" % i, GPIO.OUT) + GPIO.output("P9_%d" % i, GPIO.HIGH) + GPIO.output("P9_%d" % i, GPIO.LOW) +``` + +This can be useful when run with strace to verify that the library is operating as expected: +``` +sudo strace -e file python ~/gpio_output_test.py 2>&1 |grep '/sys/'|less +``` + +refer to this gist for more info: +https://gist.github.com/pdp7/e7753faff9a9bd27389a0173c71f9312 diff --git a/test/start_all_pwm.py b/test/start_all_pwm.py new file mode 100644 index 0000000..f19ef97 --- /dev/null +++ b/test/start_all_pwm.py @@ -0,0 +1,27 @@ +import Adafruit_BBIO.PWM as PWM + +pins = [ +"P9_16", #PWM1B +"P9_21", #PWM0B +"P9_22", #PWM0A +"P9_28", #ECAP2 +"P9_29", #PWM0B +"P9_31", #PWM0A +"P9_42", #ECAP0 +"P8_13", #PWM2B +"P8_19", #PWM2A +"P8_34", #PWM1B +"P8_36", #PWM1A +"P8_45", #PWM2A +"P8_46" #PWM2B +] + +# /sys/devices/platform/ocp/48300000.epwmss/48300100.ecap/pwm/pwmchip0/pwm-0:0/duty_cycle +# /sys/devices/platform/ocp/48304000.epwmss/48304100.ecap/pwm/pwmchip5/pwm-5:0/duty_cycle + +for pin in pins: + print(pin) + PWM.start(pin, 50, 2000, 1) + PWM.stop(pin) + PWM.cleanup() + diff --git a/test/test_adc.py b/test/test_adc.py index 096959f..8be8517 100644 --- a/test/test_adc.py +++ b/test/test_adc.py @@ -54,7 +54,7 @@ def test_many_read_adc(self): ADC.setup() - for x in range(0,10000): + for x in range(0,1000): start = time.time() value = -1 value = ADC.read("AIN1") diff --git a/test/test_gpio_setup.py b/test/test_gpio_setup.py index c4332f9..8eac2c9 100644 --- a/test/test_gpio_setup.py +++ b/test/test_gpio_setup.py @@ -20,7 +20,11 @@ def test_setup_output_key(self): GPIO.cleanup() def test_setup_output_name(self): - GPIO.setup("TIMER6", GPIO.OUT) + # WARNING: TIMERn syntax is not working on newer kernels + # such as 4.4. Originally discovered while testing + # Pull Request #152. See issue #156 for details. + #GPIO.setup("TIMER6", GPIO.OUT) + GPIO.setup("P8_10", GPIO.OUT) assert os.path.exists('/sys/class/gpio/gpio68') direction = open('/sys/class/gpio/gpio68/direction').read() assert direction == 'out\n' @@ -34,7 +38,11 @@ def test_setup_input_key(self): GPIO.cleanup() def test_setup_input_name(self): - GPIO.setup("TIMER6", GPIO.IN) + # WARNING: TIMERn syntax is not working on newer kernels + # such as 4.4. Originally discovered while testing + # Pull Request #152. See issue #156 for details. + #GPIO.setup("TIMER6", GPIO.IN) + GPIO.setup("P8_10", GPIO.IN) assert os.path.exists('/sys/class/gpio/gpio68') direction = open('/sys/class/gpio/gpio68/direction').read() assert direction == 'in\n' diff --git a/test/test_led.py b/test/test_led.py index fa197a2..97dca13 100644 --- a/test/test_led.py +++ b/test/test_led.py @@ -14,7 +14,7 @@ def set_brightness(self, state, led, name): prefix = "/sys/class/leds/beaglebone:green:{0}/brightness" path = prefix.format(led.lower()) value = self.read_led_file(path) - if value < 0: + if value == "": path = prefix.format(name) value = self.read_led_file(path) if state == 1: @@ -25,16 +25,16 @@ def set_brightness(self, state, led, name): def read_led_file(self, path): try: return open(path).read() - except IOError, e: + except (IOError, e): if e.errno == errno.ENOENT: - return -1 + return "" def set_all_leds(self, state): test_leds = { "USR0": "heartbeat", \ "USR1": "mmc0", \ "USR2": "cpu0", \ "USR3": "mmc1" } - for led, name in test_leds.iteritems(): + for led, name in test_leds.items(): self.set_brightness(state, led, name) GPIO.cleanup() diff --git a/test/test_pwm_setup.py b/test/test_pwm_setup.py index d34c240..afa4863 100644 --- a/test/test_pwm_setup.py +++ b/test/test_pwm_setup.py @@ -18,7 +18,7 @@ def get_pwm_dir(): # .pwm or .ehrpwm, etc. results = glob.glob( "/sys/devices/platform/ocp/48302000.*/" + - "48302200.*/pwm/pwmchip?/pwm0") + "48302200.*/pwm/pwmchip?/pwm*") # We expect that there will be a result (a directory fitting # our path exists) so test that with an assertion. assert len(results) > 0 @@ -96,10 +96,17 @@ def test_start_pwm_with_polarity_one(self): assert int(duty) == 0 assert int(period) == 500000 - if kernel >= '4.1.0': - assert polarity == "inversed\n" - else: - assert int(polarity) == 1 + # TEMPORARY FIX: disable polarity check + # due to issue in 4.9.x+ kernels + # refer to issue #170: + # https://github.com/adafruit/adafruit-beaglebone-io-python/issues/170 + # and commit c35e4cb from pull request #173: + # "source/c_pwm.c: disable pwm_set_polarity (broken in v4.9.x/v4.14.x)" + # https://github.com/adafruit/adafruit-beaglebone-io-python/pull/173/commits/c35e4cb98a1f14c85aca7259132bcc97e93d78f8 + #if kernel >= '4.1.0': + # assert polarity == "inversed\n" + #else: + # assert int(polarity) == 1 PWM.cleanup() def test_start_pwm_with_polarity_default(self): @@ -118,10 +125,17 @@ def test_start_pwm_with_polarity_default(self): assert int(duty) == 0 assert int(period) == 500000 - if kernel >= '4.1.0': - assert polarity == 'normal\n' - else: - assert int(polarity) == 0 + # TEMPORARY FIX: disable polarity check + # due to issue in 4.9.x+ kernels + # refer to issue #170: + # https://github.com/adafruit/adafruit-beaglebone-io-python/issues/170 + # and commit c35e4cb from pull request #173: + # "source/c_pwm.c: disable pwm_set_polarity (broken in v4.9.x/v4.14.x)" + # https://github.com/adafruit/adafruit-beaglebone-io-python/pull/173/commits/c35e4cb98a1f14c85aca7259132bcc97e93d78f8 + #if kernel >= '4.1.0': + # assert polarity == 'normal\n' + #else: + # assert int(polarity) == 0 PWM.cleanup() def test_start_pwm_with_polarity_zero(self): diff --git a/test/test_uart.py b/test/test_uart.py index 4fd4ac3..38f3ba7 100644 --- a/test/test_uart.py +++ b/test/test_uart.py @@ -1,4 +1,5 @@ import pytest +import serial import platform import Adafruit_BBIO.UART as UART @@ -11,7 +12,7 @@ def teardown_module(module): # ADC.cleanup() -class TestAdc: +class TestUart: def test_setup_uart_wrong_name(self): if kernel >= '4.1.0': pass @@ -25,9 +26,39 @@ def test_setup_adc(self): else: UART.setup("UART1") - def test_setup_adc_multiple(self): + def test_setup_uart_multiple(self): if kernel >= '4.1.0': pass else: UART.setup("UART1") UART.setup("UART1") + + # test UART entries for the PocketBeagle (issue #243) + def test_pocketbeagle(self): + if kernel < '4.1.0': + pass + value = open('/proc/device-tree/model').read() + if(value.startswith("TI AM335x PocketBeagle")): + uarts = { + 'PB-UART0': '/dev/ttyO0', + 'PB-UART1': '/dev/ttyO1', + 'PB-UART2': '/dev/ttyO2', + } + else: + uarts = { + 'UART1': '/dev/ttyO1', + 'UART2': '/dev/ttyO2', + 'UART4': '/dev/ttyO4' + # note: UART5 requires + # "disable_uboot_overlay_video=1" in /boot/uEnv.txt + #'UART5': '/dev/ttyO5' + } + + for name, device in sorted(uarts.items()): + UART.setup(name) + uart = serial.Serial(port = device, baudrate=9600) + uart.close() + uart.open() + if uart.isOpen(): + uart.write("hello world".encode("utf-8")) + uart.close() diff --git a/tox.ini b/tox.ini index abaae1f..084648a 100644 --- a/tox.ini +++ b/tox.ini @@ -4,7 +4,7 @@ # and then run "tox" from this directory. [tox] -envlist = py27, py34 +envlist = py27, py36 [testenv] commands = echo "run pytest on beaglebone" diff --git a/udev/80-non-root-gpio-permissions.rules b/udev/80-non-root-gpio-permissions.rules new file mode 100644 index 0000000..daf7e1d --- /dev/null +++ b/udev/80-non-root-gpio-permissions.rules @@ -0,0 +1 @@ +KERNEL=="gpio*", SUBSYSTEM=="gpio", ACTION=="add", PROGRAM="/usr/local/bin/udev-non-root-gpio-permissions.sh" diff --git a/udev/udev-non-root-gpio-permissions.sh b/udev/udev-non-root-gpio-permissions.sh new file mode 100755 index 0000000..eb928df --- /dev/null +++ b/udev/udev-non-root-gpio-permissions.sh @@ -0,0 +1,46 @@ +#!/bin/bash + +# Result: chown debian:debian /sys/class/gpio + +chown -R debian:debian /sys/devices/gpio +chown -R debian:debian /sys/class/gpio +chown -R debian:debian /sys/devices/platform/ocp +chown -R debian:debian /sys/class/pwm + +#GPIO ( active_low device direction edge power subsystem uevent value ) +find /sys/class/gpio -type d -exec chmod 2770 {} \; +find /sys/class/gpio -name "direction" -exec chmod 0660 {} \; +find /sys/class/gpio -name "edge" -exec chmod 0660 {} \; +find /sys/class/gpio -name "value" -exec chmod 0660 {} \; +find /sys/class/gpio -name "active_low" -exec chmod 0660 {} \; +find /sys/class/gpio -name "device" -exec chmod 0660 {} \; +find /sys/class/gpio -name "power" -exec chmod 0660 {} \; +find /sys/class/gpio -name "subsystem" -exec chmod 0660 {} \; + +#PWM ( duty_cycle enable period polarity power uevent ) +find /sys/devices/platform/ocp -name "duty_cycle" -exec chmod 0660 {} \; +find /sys/devices/platform/ocp -name "enable" -exec chmod 0660 {} \; +find /sys/devices/platform/ocp -name "period" -exec chmod 0660 {} \; +find /sys/devices/platform/ocp -name "polarity" -exec chmod 0660 {} \; +find /sys/devices/platform/ocp -name "power" -exec chmod 0660 {} \; +find /sys/class/pwm -name "duty_cycle" -exec chmod 0660 {} \; +find /sys/class/pwm -name "enable" -exec chmod 0660 {} \; +find /sys/class/pwm -name "period" -exec chmod 0660 {} \; +find /sys/class/pwm -name "polarity" -exec chmod 0660 {} \; +find /sys/class/pwm -name "power" -exec chmod 0660 {} \; + +chmod 0220 /sys/class/gpio/export +chmod 0220 /sys/class/gpio/unexport +chmod 0220 /sys/class/pwm/export +chmod 0220 /sys/class/pwm/unexport + +find /sys/class/gpio -name "uevent" -exec chmod 0660 {} \; +find /sys/class/gpio -name "autosuspend_delay_ms" -exec chmod 0660 {} \; +find /sys/class/gpio -name "control" -exec chmod 0660 {} \; +find /sys/class/pwm -name "uevent" -exec chmod 0660 {} \; +find /sys/devices/platform/ocp -name "uevent" -exec chmod 0660 {} \; + +# Additional code for getting AIN pins set up +ain_activator=/sys/devices/platform/bone_capemgr/slots +chown -R debian:debian $ain_activator/ +chmod -R 2770 $ain_activator/ 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