Big update

Needs testing
This commit is contained in:
shrkey
2016-08-13 20:48:37 +01:00
parent 8627bc1649
commit fbc0c00192
19 changed files with 1486 additions and 8636 deletions

View File

@@ -1,2 +0,0 @@
# escape
Library for the escape board

View File

@@ -1,2 +0,0 @@
# escape
Library for the escape board

View File

@@ -1,144 +0,0 @@
#!/usr/bin/python
import RPi.GPIO as GPIO
from Adafruit_PWM_Servo_Driver import PWM
import time
import math
class dw_PWM:
def __init__(self, controller, num, freq):
_SERVO_MIN_MS = 1.250 #ms
_SERVO_MAX_MS = 1.750 #ms
self.speed = 0
self.MC = controller
self.cnum = num
self.pin = 0
self.freq = freq
self.servo_min = math.trunc( ( _SERVO_MIN_MS * 4096 ) / (1000.0 / self.freq ) - 1 )
self.servo_max = math.trunc( ( _SERVO_MAX_MS * 4096 ) / (1000.0 / self.freq ) - 1 )
self.servo_zero = math.trunc( ( self.servo_min + self.servo_max ) / 2 ) # halfway = 0 degrees
if (num == 0):
self.pin = 9
elif (num == 1):
self.pin = 8
elif (num == 2):
self.pin = 10
elif (num == 3):
self.pin = 11
elif (num == 4):
self.pin = 12
elif (num == 5):
self.pin = 13
elif (num == 6):
self.pin = 0
elif (num == 7):
self.pin = 1
elif (num == 8):
self.pin = 2
elif (num == 9):
self.pin = 3
elif (num == 10):
self.pin = 5
elif (num == 11):
self.pin = 4
else:
raise NameError('Port must be between 1 and 12 inclusive')
# switch off
self.off()
def off(self):
self.MC.setPin(self.pin, 0)
def setAngle(self, angle):
pulse = self.servo_zero + ( (self.servo_zero - self.servo_min ) * angle / 80 )
print "angle=%s pulse=%s" % (angle, pulse)
#self.setPWMmS( pulse )
def setPWM(self, value):
if(value > 0):
self.MC._pwm.setPWM(self.pin, 0, int(value) )
if(value == 0):
self.off()
def setPWMmS(self, length_ms):
self.setPWM( round( length_ms * 4096 ) / ( 1000 / self.freq ) )
def setPWMuS(self, length_us):
self.setPWM( round( length_us * 4096 ) / ( 1000000 / self.freq ) )
def run(self, command, speed = 0):
if not self.MC:
return
class dw_PWMCONTROL:
def __init__(self, addr = 0x61, freq = 100, correctionFactor = 1.0):
self._i2caddr = addr # default addr on HAT
self._frequency = freq # default @60Hz PWM freq
# self.steppers = [ Adafruit_StepperMotor(self, 1), Adafruit_StepperMotor(self, 2) ]
self._pwm = PWM(addr, debug=False)
self._pwm.setPWMFreq(self._frequency, correctionFactor)
# Just gonna default to high for now
self.servo = [ dw_PWM(self, m, freq) for m in range(6) ]
self.esc = [ dw_PWM(self, m, freq) for m in range(6, 12) ]
def setPin(self, pin, value):
if (pin < 0) or (pin > 15):
raise NameError('PWM pin must be between 0 and 15 inclusive')
if (value != 0) and (value != 1):
raise NameError('Pin value must be 0 or 1!')
if (value == 0):
self._pwm.setPWM(pin, 0, 4096)
if (value == 1):
self._pwm.setPWM(pin, 4096, 0)
def setAllPin(self, value):
if (pin < 0) or (pin > 15):
raise NameError('PWM pin must be between 0 and 15 inclusive')
if (value != 0) and (value != 1):
raise NameError('Pin value must be 0 or 1!')
if (value == 0):
self._pwm.setAllPWM(0, 4096)
if (value == 1):
self._pwm.setAllPWM(4096, 0)
def getESC(self, num):
if (num < 1) or (num > 6):
raise NameError('ESC must be between 1 and 6 inclusive')
return self.esc[num-1]
def getSERVO(self, num):
if (num < 1) or (num > 6):
raise NameError('Servo must be between 1 and 6 inclusive')
return self.servo[num-1]
def setAllPWM(self, value):
if(value > 0):
self._pwm.setAllPWM(0, value)
if(value == 0):
self.allOff()
def setAllPWMmS(self, value):
if(value > 0):
self._pwm.setAllPWM(0, value)
if(value == 0):
self.allOff()
def setAllPWMuS(self, value):
if(value > 0):
self._pwm.setAllPWM(0, value)
if(value == 0):
self.allOff()
def allOff(self):
this.setAllPin( 0 );

View File

@@ -1,9 +1,2 @@
# ESCAPE ESC powered motor driver # escape
Files for the escape board Library for the escape board
#License
Designed by Dark Water. Distributed under a Creative Commons Attribution, Share-Alike license.
Development kits based on this product should be distributed under a similar license.
Commercial products using the Escape boards as a reference design need not comply with this license; further questions can be sent to team@darkwater.io.
Check license.txt for more information.

426
darkwater_escape/GPIO.py Normal file
View File

@@ -0,0 +1,426 @@
# Copyright (c) 2014 Adafruit Industries
# Author: Tony DiCola
#
# 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.
import Platform as Platform
OUT = 0
IN = 1
HIGH = True
LOW = False
RISING = 1
FALLING = 2
BOTH = 3
PUD_OFF = 0
PUD_DOWN = 1
PUD_UP = 2
class BaseGPIO(object):
"""Base class for implementing simple digital IO for a platform.
Implementors are expected to subclass from this and provide an implementation
of the setup, output, and input functions."""
def setup(self, pin, mode, pull_up_down=PUD_OFF):
"""Set the input or output mode for a specified pin. Mode should be
either OUT or IN."""
raise NotImplementedError
def output(self, pin, value):
"""Set the specified pin the provided high/low value. Value should be
either HIGH/LOW or a boolean (true = high)."""
raise NotImplementedError
def input(self, pin):
"""Read the specified pin and return HIGH/true if the pin is pulled high,
or LOW/false if pulled low."""
raise NotImplementedError
def set_high(self, pin):
"""Set the specified pin HIGH."""
self.output(pin, HIGH)
def set_low(self, pin):
"""Set the specified pin LOW."""
self.output(pin, LOW)
def is_high(self, pin):
"""Return true if the specified pin is pulled high."""
return self.input(pin) == HIGH
def is_low(self, pin):
"""Return true if the specified pin is pulled low."""
return self.input(pin) == LOW
# Basic implementation of multiple pin methods just loops through pins and
# processes each one individually. This is not optimal, but derived classes can
# provide a more optimal implementation that deals with groups of pins
# simultaneously.
# See MCP230xx or PCF8574 classes for examples of optimized implementations.
def output_pins(self, pins):
"""Set multiple pins high or low at once. Pins should be a dict of pin
name to pin value (HIGH/True for 1, LOW/False for 0). All provided pins
will be set to the given values.
"""
# General implementation just loops through pins and writes them out
# manually. This is not optimized, but subclasses can choose to implement
# a more optimal batch output implementation. See the MCP230xx class for
# example of optimized implementation.
for pin, value in iter(pins.items()):
self.output(pin, value)
def setup_pins(self, pins):
"""Setup multiple pins as inputs or outputs at once. Pins should be a
dict of pin name to pin type (IN or OUT).
"""
# General implementation that can be optimized by derived classes.
for pin, value in iter(pins.items()):
self.setup(pin, value)
def input_pins(self, pins):
"""Read multiple pins specified in the given list and return list of pin values
GPIO.HIGH/True if the pin is pulled high, or GPIO.LOW/False if pulled low.
"""
# General implementation that can be optimized by derived classes.
return [self.input(pin) for pin in pins]
def add_event_detect(self, pin, edge):
"""Enable edge detection events for a particular GPIO channel. Pin
should be type IN. Edge must be RISING, FALLING or BOTH.
"""
raise NotImplementedError
def remove_event_detect(self, pin):
"""Remove edge detection for a particular GPIO channel. Pin should be
type IN.
"""
raise NotImplementedError
def add_event_callback(self, pin, callback):
"""Add a callback for an event already defined using add_event_detect().
Pin should be type IN.
"""
raise NotImplementedError
def event_detected(self, pin):
"""Returns True if an edge has occured on a given GPIO. You need to
enable edge detection using add_event_detect() first. Pin should be
type IN.
"""
raise NotImplementedError
def wait_for_edge(self, pin, edge):
"""Wait for an edge. Pin should be type IN. Edge must be RISING,
FALLING or BOTH."""
raise NotImplementedError
def cleanup(self, pin=None):
"""Clean up GPIO event detection for specific pin, or all pins if none
is specified.
"""
raise NotImplementedError
# helper functions useful to derived classes
def _validate_pin(self, pin):
# Raise an exception if pin is outside the range of allowed values.
if pin < 0 or pin >= self.NUM_GPIO:
raise ValueError('Invalid GPIO value, must be between 0 and {0}.'.format(self.NUM_GPIO))
def _bit2(self, src, bit, val):
bit = 1 << bit
return (src | bit) if val else (src & ~bit)
class RPiGPIOAdapter(BaseGPIO):
"""GPIO implementation for the Raspberry Pi using the RPi.GPIO library."""
def __init__(self, rpi_gpio, mode=None):
self.rpi_gpio = rpi_gpio
# Suppress warnings about GPIO in use.
rpi_gpio.setwarnings(False)
# Setup board pin mode.
if mode == rpi_gpio.BOARD or mode == rpi_gpio.BCM:
rpi_gpio.setmode(mode)
elif mode is not None:
raise ValueError('Unexpected value for mode. Must be BOARD or BCM.')
else:
# Default to BCM numbering if not told otherwise.
rpi_gpio.setmode(rpi_gpio.BCM)
# Define mapping of Adafruit GPIO library constants to RPi.GPIO constants.
self._dir_mapping = { OUT: rpi_gpio.OUT,
IN: rpi_gpio.IN }
self._pud_mapping = { PUD_OFF: rpi_gpio.PUD_OFF,
PUD_DOWN: rpi_gpio.PUD_DOWN,
PUD_UP: rpi_gpio.PUD_UP }
self._edge_mapping = { RISING: rpi_gpio.RISING,
FALLING: rpi_gpio.FALLING,
BOTH: rpi_gpio.BOTH }
def setup(self, pin, mode, pull_up_down=PUD_OFF):
"""Set the input or output mode for a specified pin. Mode should be
either OUTPUT or INPUT.
"""
self.rpi_gpio.setup(pin, self._dir_mapping[mode],
pull_up_down=self._pud_mapping[pull_up_down])
def output(self, pin, value):
"""Set the specified pin the provided high/low value. Value should be
either HIGH/LOW or a boolean (true = high).
"""
self.rpi_gpio.output(pin, value)
def input(self, pin):
"""Read the specified pin and return HIGH/true if the pin is pulled high,
or LOW/false if pulled low.
"""
return self.rpi_gpio.input(pin)
def input_pins(self, pins):
"""Read multiple pins specified in the given list and return list of pin values
GPIO.HIGH/True if the pin is pulled high, or GPIO.LOW/False if pulled low.
"""
# maybe rpi has a mass read... it would be more efficient to use it if it exists
return [self.rpi_gpio.input(pin) for pin in pins]
def add_event_detect(self, pin, edge, callback=None, bouncetime=-1):
"""Enable edge detection events for a particular GPIO channel. Pin
should be type IN. Edge must be RISING, FALLING or BOTH. Callback is a
function for the event. Bouncetime is switch bounce timeout in ms for
callback
"""
kwargs = {}
if callback:
kwargs['callback']=callback
if bouncetime > 0:
kwargs['bouncetime']=bouncetime
self.rpi_gpio.add_event_detect(pin, self._edge_mapping[edge], **kwargs)
def remove_event_detect(self, pin):
"""Remove edge detection for a particular GPIO channel. Pin should be
type IN.
"""
self.rpi_gpio.remove_event_detect(pin)
def add_event_callback(self, pin, callback):
"""Add a callback for an event already defined using add_event_detect().
Pin should be type IN.
"""
self.rpi_gpio.add_event_callback(pin, callback)
def event_detected(self, pin):
"""Returns True if an edge has occured on a given GPIO. You need to
enable edge detection using add_event_detect() first. Pin should be
type IN.
"""
return self.rpi_gpio.event_detected(pin)
def wait_for_edge(self, pin, edge):
"""Wait for an edge. Pin should be type IN. Edge must be RISING,
FALLING or BOTH.
"""
self.rpi_gpio.wait_for_edge(pin, self._edge_mapping[edge])
def cleanup(self, pin=None):
"""Clean up GPIO event detection for specific pin, or all pins if none
is specified.
"""
if pin is None:
self.rpi_gpio.cleanup()
else:
self.rpi_gpio.cleanup(pin)
class AdafruitBBIOAdapter(BaseGPIO):
"""GPIO implementation for the Beaglebone Black using the Adafruit_BBIO
library.
"""
def __init__(self, bbio_gpio):
self.bbio_gpio = bbio_gpio
# Define mapping of Adafruit GPIO library constants to RPi.GPIO constants.
self._dir_mapping = { OUT: bbio_gpio.OUT,
IN: bbio_gpio.IN }
self._pud_mapping = { PUD_OFF: bbio_gpio.PUD_OFF,
PUD_DOWN: bbio_gpio.PUD_DOWN,
PUD_UP: bbio_gpio.PUD_UP }
self._edge_mapping = { RISING: bbio_gpio.RISING,
FALLING: bbio_gpio.FALLING,
BOTH: bbio_gpio.BOTH }
def setup(self, pin, mode, pull_up_down=PUD_OFF):
"""Set the input or output mode for a specified pin. Mode should be
either OUTPUT or INPUT.
"""
self.bbio_gpio.setup(pin, self._dir_mapping[mode],
pull_up_down=self._pud_mapping[pull_up_down])
def output(self, pin, value):
"""Set the specified pin the provided high/low value. Value should be
either HIGH/LOW or a boolean (true = high).
"""
self.bbio_gpio.output(pin, value)
def input(self, pin):
"""Read the specified pin and return HIGH/true if the pin is pulled high,
or LOW/false if pulled low.
"""
return self.bbio_gpio.input(pin)
def input_pins(self, pins):
"""Read multiple pins specified in the given list and return list of pin values
GPIO.HIGH/True if the pin is pulled high, or GPIO.LOW/False if pulled low.
"""
# maybe bbb has a mass read... it would be more efficient to use it if it exists
return [self.bbio_gpio.input(pin) for pin in pins]
def add_event_detect(self, pin, edge, callback=None, bouncetime=-1):
"""Enable edge detection events for a particular GPIO channel. Pin
should be type IN. Edge must be RISING, FALLING or BOTH. Callback is a
function for the event. Bouncetime is switch bounce timeout in ms for
callback
"""
kwargs = {}
if callback:
kwargs['callback']=callback
if bouncetime > 0:
kwargs['bouncetime']=bouncetime
self.bbio_gpio.add_event_detect(pin, self._edge_mapping[edge], **kwargs)
def remove_event_detect(self, pin):
"""Remove edge detection for a particular GPIO channel. Pin should be
type IN.
"""
self.bbio_gpio.remove_event_detect(pin)
def add_event_callback(self, pin, callback, bouncetime=-1):
"""Add a callback for an event already defined using add_event_detect().
Pin should be type IN. Bouncetime is switch bounce timeout in ms for
callback
"""
kwargs = {}
if bouncetime > 0:
kwargs['bouncetime']=bouncetime
self.bbio_gpio.add_event_callback(pin, callback, **kwargs)
def event_detected(self, pin):
"""Returns True if an edge has occured on a given GPIO. You need to
enable edge detection using add_event_detect() first. Pin should be
type IN.
"""
return self.bbio_gpio.event_detected(pin)
def wait_for_edge(self, pin, edge):
"""Wait for an edge. Pin should be type IN. Edge must be RISING,
FALLING or BOTH.
"""
self.bbio_gpio.wait_for_edge(pin, self._edge_mapping[edge])
def cleanup(self, pin=None):
"""Clean up GPIO event detection for specific pin, or all pins if none
is specified.
"""
if pin is None:
self.bbio_gpio.cleanup()
else:
self.bbio_gpio.cleanup(pin)
class AdafruitMinnowAdapter(BaseGPIO):
"""GPIO implementation for the Minnowboard + MAX using the mraa library"""
def __init__(self,mraa_gpio):
self.mraa_gpio = mraa_gpio
# Define mapping of Adafruit GPIO library constants to mraa constants
self._dir_mapping = { OUT: self.mraa_gpio.DIR_OUT,
IN: self.mraa_gpio.DIR_IN }
self._pud_mapping = { PUD_OFF: self.mraa_gpio.MODE_STRONG,
PUD_UP: self.mraa_gpio.MODE_HIZ,
PUD_DOWN: self.mraa_gpio.MODE_PULLDOWN }
self._edge_mapping = { RISING: self.mraa_gpio.EDGE_RISING,
FALLING: self.mraa_gpio.EDGE_FALLING,
BOTH: self.mraa_gpio.EDGE_BOTH }
def setup(self,pin,mode):
"""Set the input or output mode for a specified pin. Mode should be
either DIR_IN or DIR_OUT.
"""
self.mraa_gpio.Gpio.dir(self.mraa_gpio.Gpio(pin),self._dir_mapping[mode])
def output(self,pin,value):
"""Set the specified pin the provided high/low value. Value should be
either 1 (ON or HIGH), or 0 (OFF or LOW) or a boolean.
"""
self.mraa_gpio.Gpio.write(self.mraa_gpio.Gpio(pin), value)
def input(self,pin):
"""Read the specified pin and return HIGH/true if the pin is pulled high,
or LOW/false if pulled low.
"""
return self.mraa_gpio.Gpio.read(self.mraa_gpio.Gpio(pin))
def add_event_detect(self, pin, edge, callback=None, bouncetime=-1):
"""Enable edge detection events for a particular GPIO channel. Pin
should be type IN. Edge must be RISING, FALLING or BOTH. Callback is a
function for the event. Bouncetime is switch bounce timeout in ms for
callback
"""
kwargs = {}
if callback:
kwargs['callback']=callback
if bouncetime > 0:
kwargs['bouncetime']=bouncetime
self.mraa_gpio.Gpio.isr(self.mraa_gpio.Gpio(pin), self._edge_mapping[edge], **kwargs)
def remove_event_detect(self, pin):
"""Remove edge detection for a particular GPIO channel. Pin should be
type IN.
"""
self.mraa_gpio.Gpio.isrExit(self.mraa_gpio.Gpio(pin))
def wait_for_edge(self, pin, edge):
"""Wait for an edge. Pin should be type IN. Edge must be RISING,
FALLING or BOTH.
"""
self.bbio_gpio.wait_for_edge(self.mraa_gpio.Gpio(pin), self._edge_mapping[edge])
def get_platform_gpio(**keywords):
"""Attempt to return a GPIO instance for the platform which the code is being
executed on. Currently supports only the Raspberry Pi using the RPi.GPIO
library and Beaglebone Black using the Adafruit_BBIO library. Will throw an
exception if a GPIO instance can't be created for the current platform. The
returned GPIO object is an instance of BaseGPIO.
"""
plat = Platform.platform_detect()
if plat == Platform.RASPBERRY_PI:
import RPi.GPIO
return RPiGPIOAdapter(RPi.GPIO, **keywords)
elif plat == Platform.BEAGLEBONE_BLACK:
import Adafruit_BBIO.GPIO
return AdafruitBBIOAdapter(Adafruit_BBIO.GPIO, **keywords)
elif plat == Platform.MINNOWBOARD:
import mraa
return AdafruitMinnowAdapter(mraa, **keywords)
elif plat == Platform.UNKNOWN:
raise RuntimeError('Could not determine platform.')

200
darkwater_escape/I2C.py Normal file
View File

@@ -0,0 +1,200 @@
# Copyright (c) 2014 Adafruit Industries
# Author: Tony DiCola
# Based on Adafruit_I2C.py created by Kevin Townsend.
#
# 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.
import logging
import subprocess
import Platform as Platform
def reverseByteOrder(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 get_default_bus():
"""Return the default bus number based on the device platform. For a
Raspberry Pi either bus 0 or 1 (based on the Pi revision) will be returned.
For a Beaglebone Black the first user accessible bus, 1, will be returned.
"""
plat = Platform.platform_detect()
if plat == Platform.RASPBERRY_PI:
if Platform.pi_revision() == 1:
# Revision 1 Pi uses I2C bus 0.
return 0
else:
# Revision 2 Pi uses I2C bus 1.
return 1
elif plat == Platform.BEAGLEBONE_BLACK:
# Beaglebone Black has multiple I2C buses, default to 1 (P9_19 and P9_20).
return 1
else:
raise RuntimeError('Could not determine default I2C bus for platform.')
def get_i2c_device(address, busnum=None, i2c_interface=None, **kwargs):
"""Return an I2C device for the specified address and on the specified bus.
If busnum isn't specified, the default I2C bus for the platform will attempt
to be detected.
"""
if busnum is None:
busnum = get_default_bus()
return Device(address, busnum, i2c_interface, **kwargs)
def require_repeated_start():
"""Enable repeated start conditions for I2C register reads. This is the
normal behavior for I2C, however on some platforms like the Raspberry Pi
there are bugs which disable repeated starts unless explicitly enabled with
this function. See this thread for more details:
http://www.raspberrypi.org/forums/viewtopic.php?f=44&t=15840
"""
plat = Platform.platform_detect()
if plat == Platform.RASPBERRY_PI:
# On the Raspberry Pi there is a bug where register reads don't send a
# repeated start condition like the kernel smbus I2C driver functions
# define. As a workaround this bit in the BCM2708 driver sysfs tree can
# be changed to enable I2C repeated starts.
subprocess.check_call('chmod 666 /sys/module/i2c_bcm2708/parameters/combined', shell=True)
subprocess.check_call('echo -n 1 > /sys/module/i2c_bcm2708/parameters/combined', shell=True)
# Other platforms are a no-op because they (presumably) have the correct
# behavior and send repeated starts.
class Device(object):
"""Class for communicating with an I2C device using the adafruit-pureio pure
python smbus library, or other smbus compatible I2C interface. Allows reading
and writing 8-bit, 16-bit, and byte array values to registers
on the device."""
def __init__(self, address, busnum, i2c_interface=None):
"""Create an instance of the I2C device at the specified address on the
specified I2C bus number."""
self._address = address
if i2c_interface is None:
# Use pure python I2C interface if none is specified.
import Adafruit_PureIO.smbus
self._bus = Adafruit_PureIO.smbus.SMBus(busnum)
else:
# Otherwise use the provided class to create an smbus interface.
self._bus = i2c_interface(busnum)
self._logger = logging.getLogger('Adafruit_I2C.Device.Bus.{0}.Address.{1:#0X}' \
.format(busnum, address))
def writeRaw8(self, value):
"""Write an 8-bit value on the bus (without register)."""
value = value & 0xFF
self._bus.write_byte(self._address, value)
self._logger.debug("Wrote 0x%02X",
value)
def write8(self, register, value):
"""Write an 8-bit value to the specified register."""
value = value & 0xFF
self._bus.write_byte_data(self._address, register, value)
self._logger.debug("Wrote 0x%02X to register 0x%02X",
value, register)
def write16(self, register, value):
"""Write a 16-bit value to the specified register."""
value = value & 0xFFFF
self._bus.write_word_data(self._address, register, value)
self._logger.debug("Wrote 0x%04X to register pair 0x%02X, 0x%02X",
value, register, register+1)
def writeList(self, register, data):
"""Write bytes to the specified register."""
self._bus.write_i2c_block_data(self._address, register, data)
self._logger.debug("Wrote to register 0x%02X: %s",
register, data)
def readList(self, register, length):
"""Read a length number of bytes from the specified register. Results
will be returned as a bytearray."""
results = self._bus.read_i2c_block_data(self._address, register, length)
self._logger.debug("Read the following from register 0x%02X: %s",
register, results)
return results
def readRaw8(self):
"""Read an 8-bit value on the bus (without register)."""
result = self._bus.read_byte(self._address) & 0xFF
self._logger.debug("Read 0x%02X",
result)
return result
def readU8(self, register):
"""Read an unsigned byte from the specified register."""
result = self._bus.read_byte_data(self._address, register) & 0xFF
self._logger.debug("Read 0x%02X from register 0x%02X",
result, register)
return result
def readS8(self, register):
"""Read a signed byte from the specified register."""
result = self.readU8(register)
if result > 127:
result -= 256
return result
def readU16(self, register, little_endian=True):
"""Read an unsigned 16-bit value from the specified register, with the
specified endianness (default little endian, or least significant byte
first)."""
result = self._bus.read_word_data(self._address,register) & 0xFFFF
self._logger.debug("Read 0x%04X from register pair 0x%02X, 0x%02X",
result, register, register+1)
# Swap bytes if using big endian because read_word_data assumes little
# endian on ARM (little endian) systems.
if not little_endian:
result = ((result << 8) & 0xFF00) + (result >> 8)
return result
def readS16(self, register, little_endian=True):
"""Read a signed 16-bit value from the specified register, with the
specified endianness (default little endian, or least significant byte
first)."""
result = self.readU16(register, little_endian)
if result > 32767:
result -= 65536
return result
def readU16LE(self, register):
"""Read an unsigned 16-bit value from the specified register, in little
endian byte order."""
return self.readU16(register, little_endian=True)
def readU16BE(self, register):
"""Read an unsigned 16-bit value from the specified register, in big
endian byte order."""
return self.readU16(register, little_endian=False)
def readS16LE(self, register):
"""Read a signed 16-bit value from the specified register, in little
endian byte order."""
return self.readS16(register, little_endian=True)
def readS16BE(self, register):
"""Read a signed 16-bit value from the specified register, in big
endian byte order."""
return self.readS16(register, little_endian=False)

167
darkwater_escape/PCA9685.py Normal file
View File

@@ -0,0 +1,167 @@
# Copyright (c) 2016 Adafruit Industries
# Author: Tony DiCola
#
# Updated by: Dark Water
#
# 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.
from __future__ import division
import logging
import time
import math
# Registers/etc:
PCA9685_ADDRESS = 0x60
MODE1 = 0x00
MODE2 = 0x01
SUBADR1 = 0x02
SUBADR2 = 0x03
SUBADR3 = 0x04
PRESCALE = 0xFE
LED0_ON_L = 0x06
LED0_ON_H = 0x07
LED0_OFF_L = 0x08
LED0_OFF_H = 0x09
ALL_LED_ON_L = 0xFA
ALL_LED_ON_H = 0xFB
ALL_LED_OFF_L = 0xFC
ALL_LED_OFF_H = 0xFD
# Bits:
RESTART = 0x80
SLEEP = 0x10
ALLCALL = 0x01
INVRT = 0x10
OUTDRV = 0x04
logger = logging.getLogger(__name__)
def software_reset(i2c=None, **kwargs):
"""Sends a software reset (SWRST) command to all servo drivers on the bus."""
# Setup I2C interface for device 0x00 to talk to all of them.
if i2c is None:
import I2C as I2C
i2c = I2C
self._device = i2c.get_i2c_device(0x00, **kwargs)
self._device.writeRaw8(0x06) # SWRST
class PCA9685(object):
"""PCA9685 PWM LED/servo controller."""
def __init__(self, address=PCA9685_ADDRESS, i2c=None, **kwargs):
"""Initialize the PCA9685."""
# Setup I2C interface for the device.
if i2c is None:
import I2C as I2C
i2c = I2C
self._device = i2c.get_i2c_device(address, **kwargs)
self.set_all_pwm(0, 0)
self._device.write8(MODE2, OUTDRV)
self._device.write8(MODE1, ALLCALL)
time.sleep(0.005) # wait for oscillator
mode1 = self._device.readU8(MODE1)
mode1 = mode1 & ~SLEEP # wake up (reset sleep)
self._device.write8(MODE1, mode1)
time.sleep(0.005) # wait for oscillator
def set_pwm_freq(self, freq_hz):
"""Set the PWM frequency to the provided value in hertz."""
prescaleval = 25000000.0 # 25MHz
prescaleval /= 4096.0 # 12-bit
prescaleval /= float(freq_hz)
prescaleval -= 1.0
logger.debug('Setting PWM frequency to {0} Hz'.format(freq_hz))
logger.debug('Estimated pre-scale: {0}'.format(prescaleval))
prescale = int(math.floor(prescaleval + 0.5))
logger.debug('Final pre-scale: {0}'.format(prescale))
oldmode = self._device.readU8(MODE1);
newmode = (oldmode & 0x7F) | 0x10 # sleep
self._device.write8(MODE1, newmode) # go to sleep
self._device.write8(PRESCALE, prescale)
self._device.write8(MODE1, oldmode)
time.sleep(0.005)
self._device.write8(MODE1, oldmode | 0x80)
def set_pwm_freq_min(self, freq_hz, correctionFactor=1.0):
"Sets the PWM frequency"
prescaleval = 25000000.0 # 25MHz
prescaleval /= 4096.0 # 12-bit
prescaleval /= float(freq_hz)
prescaleval -= 1.0
if (self.debug):
print "Setting PWM frequency to %d Hz" % freq_hz
print "Estimated pre-scale: %d" % prescaleval
prescale = math.floor(prescaleval * correctionFactor + 0.5)
if (self.debug):
print "Final pre-scale: %d" % prescale
oldmode = self.i2c.readU8(self.__MODE1);
newmode = (oldmode & 0x7F) | 0x10 # sleep
self.i2c.write8(self.__MODE1, newmode) # go to sleep
self.i2c.write8(self.__PRESCALE, int(math.floor(prescale)))
self.i2c.write8(self.__MODE1, oldmode)
time.sleep(0.005)
self.i2c.write8(self.__MODE1, oldmode | 0x80)
def set_pwm_freq_max(self, freq_hz, correctionFactor=1.0):
"Sets the PWM frequency"
prescaleval = 25000000.0 # 25MHz
prescaleval /= 4096.0 # 12-bit
prescaleval /= float(freq_hz)
prescaleval -= 1.0
if (self.debug):
print "Setting PWM frequency to %d Hz" % freq_hz
print "Estimated pre-scale: %d" % prescaleval
prescale = math.ceil(prescaleval * correctionFactor + 0.5)
if (self.debug):
print "Final pre-scale: %d" % prescale
oldmode = self.i2c.readU8(self.__MODE1);
newmode = (oldmode & 0x7F) | 0x10 # sleep
self.i2c.write8(self.__MODE1, newmode) # go to sleep
self.i2c.write8(self.__PRESCALE, int(math.floor(prescale)))
self.i2c.write8(self.__MODE1, oldmode)
time.sleep(0.005)
self.i2c.write8(self.__MODE1, oldmode | 0x80)
def get_pwm_freq(self):
prescale = self.i2c.readU8(self.__PRESCALE)
calcfreq = 25000000.0 / 4096.0 / ( float(prescale) + 1 )
if (self.debug):
print "Got pre-scale: %d" % prescale
print "Calculated Frequency: %d" % calcfreq
return calcfreq
def set_pwm(self, channel, on, off):
"""Sets a single PWM channel."""
self._device.write8(LED0_ON_L+4*channel, on & 0xFF)
self._device.write8(LED0_ON_H+4*channel, on >> 8)
self._device.write8(LED0_OFF_L+4*channel, off & 0xFF)
self._device.write8(LED0_OFF_H+4*channel, off >> 8)
def set_all_pwm(self, on, off):
"""Sets all PWM channels."""
self._device.write8(ALL_LED_ON_L, on & 0xFF)
self._device.write8(ALL_LED_ON_H, on >> 8)
self._device.write8(ALL_LED_OFF_L, off & 0xFF)
self._device.write8(ALL_LED_OFF_H, off >> 8)

View File

@@ -0,0 +1,106 @@
# Copyright (c) 2014 Adafruit Industries
# Author: Tony DiCola
# 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.
import platform
import re
# Platform identification constants.
UNKNOWN = 0
RASPBERRY_PI = 1
BEAGLEBONE_BLACK = 2
MINNOWBOARD = 3
def platform_detect():
"""Detect if running on the Raspberry Pi or Beaglebone Black and return the
platform type. Will return RASPBERRY_PI, BEAGLEBONE_BLACK, or UNKNOWN."""
# Handle Raspberry Pi
pi = pi_version()
if pi is not None:
return RASPBERRY_PI
# Handle Beaglebone Black
# TODO: Check the Beaglebone Black /proc/cpuinfo value instead of reading
# the platform.
plat = platform.platform()
if plat.lower().find('armv7l-with-debian') > -1:
return BEAGLEBONE_BLACK
elif plat.lower().find('armv7l-with-ubuntu') > -1:
return BEAGLEBONE_BLACK
elif plat.lower().find('armv7l-with-glibc2.4') > -1:
return BEAGLEBONE_BLACK
# Handle Minnowboard
# Assumption is that mraa is installed
try:
import mraa
if mraa.getPlatformName()=='MinnowBoard MAX':
return MINNOWBOARD
except ImportError:
pass
# Couldn't figure out the platform, just return unknown.
return UNKNOWN
def pi_revision():
"""Detect the revision number of a Raspberry Pi, useful for changing
functionality like default I2C bus based on revision."""
# Revision list available at: http://elinux.org/RPi_HardwareHistory#Board_Revision_History
with open('/proc/cpuinfo', 'r') as infile:
for line in infile:
# Match a line of the form "Revision : 0002" while ignoring extra
# info in front of the revsion (like 1000 when the Pi was over-volted).
match = re.match('Revision\s+:\s+.*(\w{4})$', line, flags=re.IGNORECASE)
if match and match.group(1) in ['0000', '0002', '0003']:
# Return revision 1 if revision ends with 0000, 0002 or 0003.
return 1
elif match:
# Assume revision 2 if revision ends with any other 4 chars.
return 2
# Couldn't find the revision, throw an exception.
raise RuntimeError('Could not determine Raspberry Pi revision.')
def pi_version():
"""Detect the version of the Raspberry Pi. Returns either 1, 2 or
None depending on if it's a Raspberry Pi 1 (model A, B, A+, B+),
Raspberry Pi 2 (model B+), or not a Raspberry Pi.
"""
# Check /proc/cpuinfo for the Hardware field value.
# 2708 is pi 1
# 2709 is pi 2
# Anything else is not a pi.
with open('/proc/cpuinfo', 'r') as infile:
cpuinfo = infile.read()
# Match a line like 'Hardware : BCM2709'
match = re.search('^Hardware\s+:\s+(\w+)$', cpuinfo,
flags=re.MULTILINE | re.IGNORECASE)
if not match:
# Couldn't find the hardware, assume it isn't a pi.
return None
if match.group(1) == 'BCM2708':
# Pi 1
return 1
elif match.group(1) == 'BCM2709':
# Pi 2
return 2
else:
# Something else, not a pi.
return None

336
darkwater_escape/SPI.py Normal file
View File

@@ -0,0 +1,336 @@
# Copyright (c) 2014 Adafruit Industries
# Author: Tony DiCola
#
# 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.
import operator
import time
import Adafruit_GPIO as GPIO
MSBFIRST = 0
LSBFIRST = 1
class SpiDev(object):
"""Hardware-based SPI implementation using the spidev interface."""
def __init__(self, port, device, max_speed_hz=500000):
"""Initialize an SPI device using the SPIdev interface. Port and device
identify the device, for example the device /dev/spidev1.0 would be port
1 and device 0.
"""
import spidev
self._device = spidev.SpiDev()
self._device.open(port, device)
self._device.max_speed_hz=max_speed_hz
# Default to mode 0.
self._device.mode = 0
def set_clock_hz(self, hz):
"""Set the speed of the SPI clock in hertz. Note that not all speeds
are supported and a lower speed might be chosen by the hardware.
"""
self._device.max_speed_hz=hz
def set_mode(self, mode):
"""Set SPI mode which controls clock polarity and phase. Should be a
numeric value 0, 1, 2, or 3. See wikipedia page for details on meaning:
http://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus
"""
if mode < 0 or mode > 3:
raise ValueError('Mode must be a value 0, 1, 2, or 3.')
self._device.mode = mode
def set_bit_order(self, order):
"""Set order of bits to be read/written over serial lines. Should be
either MSBFIRST for most-significant first, or LSBFIRST for
least-signifcant first.
"""
if order == MSBFIRST:
self._device.lsbfirst = False
elif order == LSBFIRST:
self._device.lsbfirst = True
else:
raise ValueError('Order must be MSBFIRST or LSBFIRST.')
def close(self):
"""Close communication with the SPI device."""
self._device.close()
def write(self, data):
"""Half-duplex SPI write. The specified array of bytes will be clocked
out the MOSI line.
"""
self._device.writebytes(data)
def read(self, length):
"""Half-duplex SPI read. The specified length of bytes will be clocked
in the MISO line and returned as a bytearray object.
"""
return bytearray(self._device.readbytes(length))
def transfer(self, data):
"""Full-duplex SPI read and write. The specified array of bytes will be
clocked out the MOSI line, while simultaneously bytes will be read from
the MISO line. Read bytes will be returned as a bytearray object.
"""
return bytearray(self._device.xfer2(data))
class SpiDevMraa(object):
"""Hardware SPI implementation with the mraa library on Minnowboard"""
def __init__(self, port, device, max_speed_hz=500000):
import mraa
self._device = mraa.Spi(0)
self._device.mode(0)
def set_clock_hz(self, hz):
"""Set the speed of the SPI clock in hertz. Note that not all speeds
are supported and a lower speed might be chosen by the hardware.
"""
self._device.frequency(hz)
def set_mode(self,mode):
"""Set SPI mode which controls clock polarity and phase. Should be a
numeric value 0, 1, 2, or 3. See wikipedia page for details on meaning:
http://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus
"""
if mode < 0 or mode > 3:
raise ValueError('Mode must be a value 0, 1, 2, or 3.')
self._device.mode(mode)
def set_mode(self,mode):
"""Set SPI mode which controls clock polarity and phase. Should be a
numeric value 0, 1, 2, or 3. See wikipedia page for details on meaning:
http://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus
"""
if mode < 0 or mode > 3:
raise ValueError('Mode must be a value 0, 1, 2, or 3.')
self._device.mode(mode)
def set_bit_order(self, order):
"""Set order of bits to be read/written over serial lines. Should be
either MSBFIRST for most-significant first, or LSBFIRST for
least-signifcant first.
"""
if order == MSBFIRST:
self._device.lsbmode(False)
elif order == LSBFIRST:
self._device.lsbmode(True)
else:
raise ValueError('Order must be MSBFIRST or LSBFIRST.')
def close(self):
"""Close communication with the SPI device."""
self._device.Spi()
def write(self, data):
"""Half-duplex SPI write. The specified array of bytes will be clocked
out the MOSI line.
"""
self._device.write(bytearray(data))
class BitBang(object):
"""Software-based implementation of the SPI protocol over GPIO pins."""
def __init__(self, gpio, sclk, mosi=None, miso=None, ss=None):
"""Initialize bit bang (or software) based SPI. Must provide a BaseGPIO
class, the SPI clock, and optionally MOSI, MISO, and SS (slave select)
pin numbers. If MOSI is set to None then writes will be disabled and fail
with an error, likewise for MISO reads will be disabled. If SS is set to
None then SS will not be asserted high/low by the library when
transfering data.
"""
self._gpio = gpio
self._sclk = sclk
self._mosi = mosi
self._miso = miso
self._ss = ss
# Set pins as outputs/inputs.
gpio.setup(sclk, GPIO.OUT)
if mosi is not None:
gpio.setup(mosi, GPIO.OUT)
if miso is not None:
gpio.setup(miso, GPIO.IN)
if ss is not None:
gpio.setup(ss, GPIO.OUT)
# Assert SS high to start with device communication off.
gpio.set_high(ss)
# Assume mode 0.
self.set_mode(0)
# Assume most significant bit first order.
self.set_bit_order(MSBFIRST)
def set_clock_hz(self, hz):
"""Set the speed of the SPI clock. This is unsupported with the bit
bang SPI class and will be ignored.
"""
pass
def set_mode(self, mode):
"""Set SPI mode which controls clock polarity and phase. Should be a
numeric value 0, 1, 2, or 3. See wikipedia page for details on meaning:
http://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus
"""
if mode < 0 or mode > 3:
raise ValueError('Mode must be a value 0, 1, 2, or 3.')
if mode & 0x02:
# Clock is normally high in mode 2 and 3.
self._clock_base = GPIO.HIGH
else:
# Clock is normally low in mode 0 and 1.
self._clock_base = GPIO.LOW
if mode & 0x01:
# Read on trailing edge in mode 1 and 3.
self._read_leading = False
else:
# Read on leading edge in mode 0 and 2.
self._read_leading = True
# Put clock into its base state.
self._gpio.output(self._sclk, self._clock_base)
def set_bit_order(self, order):
"""Set order of bits to be read/written over serial lines. Should be
either MSBFIRST for most-significant first, or LSBFIRST for
least-signifcant first.
"""
# Set self._mask to the bitmask which points at the appropriate bit to
# read or write, and appropriate left/right shift operator function for
# reading/writing.
if order == MSBFIRST:
self._mask = 0x80
self._write_shift = operator.lshift
self._read_shift = operator.rshift
elif order == LSBFIRST:
self._mask = 0x01
self._write_shift = operator.rshift
self._read_shift = operator.lshift
else:
raise ValueError('Order must be MSBFIRST or LSBFIRST.')
def close(self):
"""Close the SPI connection. Unused in the bit bang implementation."""
pass
def write(self, data, assert_ss=True, deassert_ss=True):
"""Half-duplex SPI write. If assert_ss is True, the SS line will be
asserted low, the specified bytes will be clocked out the MOSI line, and
if deassert_ss is True the SS line be put back high.
"""
# Fail MOSI is not specified.
if self._mosi is None:
raise RuntimeError('Write attempted with no MOSI pin specified.')
if assert_ss and self._ss is not None:
self._gpio.set_low(self._ss)
for byte in data:
for i in range(8):
# Write bit to MOSI.
if self._write_shift(byte, i) & self._mask:
self._gpio.set_high(self._mosi)
else:
self._gpio.set_low(self._mosi)
# Flip clock off base.
self._gpio.output(self._sclk, not self._clock_base)
# Return clock to base.
self._gpio.output(self._sclk, self._clock_base)
if deassert_ss and self._ss is not None:
self._gpio.set_high(self._ss)
def read(self, length, assert_ss=True, deassert_ss=True):
"""Half-duplex SPI read. If assert_ss is true, the SS line will be
asserted low, the specified length of bytes will be clocked in the MISO
line, and if deassert_ss is true the SS line will be put back high.
Bytes which are read will be returned as a bytearray object.
"""
if self._miso is None:
raise RuntimeError('Read attempted with no MISO pin specified.')
if assert_ss and self._ss is not None:
self._gpio.set_low(self._ss)
result = bytearray(length)
for i in range(length):
for j in range(8):
# Flip clock off base.
self._gpio.output(self._sclk, not self._clock_base)
# Handle read on leading edge of clock.
if self._read_leading:
if self._gpio.is_high(self._miso):
# Set bit to 1 at appropriate location.
result[i] |= self._read_shift(self._mask, j)
else:
# Set bit to 0 at appropriate location.
result[i] &= ~self._read_shift(self._mask, j)
# Return clock to base.
self._gpio.output(self._sclk, self._clock_base)
# Handle read on trailing edge of clock.
if not self._read_leading:
if self._gpio.is_high(self._miso):
# Set bit to 1 at appropriate location.
result[i] |= self._read_shift(self._mask, j)
else:
# Set bit to 0 at appropriate location.
result[i] &= ~self._read_shift(self._mask, j)
if deassert_ss and self._ss is not None:
self._gpio.set_high(self._ss)
return result
def transfer(self, data, assert_ss=True, deassert_ss=True):
"""Full-duplex SPI read and write. If assert_ss is true, the SS line
will be asserted low, the specified bytes will be clocked out the MOSI
line while bytes will also be read from the MISO line, and if
deassert_ss is true the SS line will be put back high. Bytes which are
read will be returned as a bytearray object.
"""
if self._mosi is None:
raise RuntimeError('Write attempted with no MOSI pin specified.')
if self._mosi is None:
raise RuntimeError('Read attempted with no MISO pin specified.')
if assert_ss and self._ss is not None:
self._gpio.set_low(self._ss)
result = bytearray(len(data))
for i in range(len(data)):
for j in range(8):
# Write bit to MOSI.
if self._write_shift(data[i], j) & self._mask:
self._gpio.set_high(self._mosi)
else:
self._gpio.set_low(self._mosi)
# Flip clock off base.
self._gpio.output(self._sclk, not self._clock_base)
# Handle read on leading edge of clock.
if self._read_leading:
if self._gpio.is_high(self._miso):
# Set bit to 1 at appropriate location.
result[i] |= self._read_shift(self._mask, j)
else:
# Set bit to 0 at appropriate location.
result[i] &= ~self._read_shift(self._mask, j)
# Return clock to base.
self._gpio.output(self._sclk, self._clock_base)
# Handle read on trailing edge of clock.
if not self._read_leading:
if self._gpio.is_high(self._miso):
# Set bit to 1 at appropriate location.
result[i] |= self._read_shift(self._mask, j)
else:
# Set bit to 0 at appropriate location.
result[i] &= ~self._read_shift(self._mask, j)
if deassert_ss and self._ss is not None:
self._gpio.set_high(self._ss)
return result

View File

@@ -0,0 +1,249 @@
#!/usr/bin/python
import RPi.GPIO as GPIO
from PCA9685 import PCA9685
import time
import math
class dw_Motor:
def __init__(self, controller, num, freq):
_SERVO_MIN_MS = 1.250 #ms
_SERVO_MAX_MS = 1.750 #ms
self.speed = 0
self.MC = controller
self.cnum = num
self.pin = 0
self.freq = freq
self.servo_min = math.trunc( ( _SERVO_MIN_MS * 4096 ) / (1000.0 / self.freq ) - 1 )
self.servo_max = math.trunc( ( _SERVO_MAX_MS * 4096 ) / (1000.0 / self.freq ) - 1 )
self.servo_zero = math.trunc( ( self.servo_min + self.servo_max ) / 2 ) # halfway = 0 degrees
if (num == 0):
self.pin = 9
elif (num == 1):
self.pin = 8
elif (num == 2):
self.pin = 10
elif (num == 3):
self.pin = 11
elif (num == 4):
self.pin = 12
elif (num == 5):
self.pin = 13
elif (num == 6):
self.pin = 0
elif (num == 7):
self.pin = 1
elif (num == 8):
self.pin = 2
elif (num == 9):
self.pin = 3
elif (num == 10):
self.pin = 5
elif (num == 11):
self.pin = 4
else:
raise NameError('Motors must be between 1 and 12 inclusive')
# switch off
self.off()
def off(self):
self.MC.setPin(self.pin, 0)
def setAngle(self, angle):
pulse = self.servo_zero + ( (self.servo_zero - self.servo_min ) * angle / 80 )
print "angle=%s pulse=%s" % (angle, pulse)
#self.setPWMmS( pulse )
def setPWM(self, value):
if(value > 0):
self.MC._pwm.setPWM(self.pin, 0, int(value) )
if(value == 0):
self.off()
def setPWMmS(self, length_ms):
self.setPWM( round( length_ms * 4096 ) / ( 1000 / self.freq ) )
def setPWMuS(self, length_us):
self.setPWM( round( length_us * 4096 ) / ( 1000000 / self.freq ) )
def setMotorSpeed(self, value):
# Check for PWM values
if(value > 1000) and (value < 2000):
self.setPWMmS(value)
# Translate for motor values
if(value > 0) and (value <= 255):
self.setPWMmS( round(translate(value, 0, 255, 1500, 2000)))
if(value == 0):
self.setPWMmS(1500)
if(value < 0) and (value >= -255):
self.setPWMmS(round(translate(abs(value), 0, 255, 1500, 1000)))
def run(self, command, speed = 0):
if not self.MC:
return
if (command == dw_Controller.FORWARD):
self.MC.setPin(self.PHpin, 0)
self.MC._pwm.set_pwm(self.ENpin, 0, speed*16)
if (command == dw_Controller.BACKWARD):
self.MC.setPin(self.PHpin, 1)
self.MC._pwm.set_pwm(self.ENpin, 0, speed*16)
if (command == dw_Controller.RELEASE):
self.MC.setPin(self.PHpin, 0)
self.MC.setPin(self.ENpin, 0)
class dw_Servo:
def __init__(self, controller, num, freq):
_SERVO_MIN_MS = 1.250 #ms
_SERVO_MAX_MS = 1.750 #ms
self.speed = 0
self.MC = controller
self.cnum = num
self.pin = 0
self.freq = freq
self.servo_min = math.trunc( ( _SERVO_MIN_MS * 4096 ) / (1000.0 / self.freq ) - 1 )
self.servo_max = math.trunc( ( _SERVO_MAX_MS * 4096 ) / (1000.0 / self.freq ) - 1 )
self.servo_zero = math.trunc( ( self.servo_min + self.servo_max ) / 2 ) # halfway = 0 degrees
if (num == 0):
self.pin = 9
elif (num == 1):
self.pin = 8
elif (num == 2):
self.pin = 10
elif (num == 3):
self.pin = 11
elif (num == 4):
self.pin = 12
elif (num == 5):
self.pin = 13
elif (num == 6):
self.pin = 0
elif (num == 7):
self.pin = 1
elif (num == 8):
self.pin = 2
elif (num == 9):
self.pin = 3
elif (num == 10):
self.pin = 5
elif (num == 11):
self.pin = 4
else:
raise NameError('Port must be between 1 and 12 inclusive')
# switch off
self.off()
def off(self):
self.MC.setPin(self.pin, 0)
def setAngle(self, angle):
pulse = self.servo_zero + ( (self.servo_zero - self.servo_min ) * angle / 80 )
print "angle=%s pulse=%s" % (angle, pulse)
#self.setPWMmS( pulse )
def setPWM(self, value):
if(value > 0):
self.MC._pwm.setPWM(self.pin, 0, int(value) )
if(value == 0):
self.off()
def setPWMmS(self, length_ms):
self.setPWM( round( length_ms * 4096 ) / ( 1000 / self.freq ) )
def setPWMuS(self, length_us):
self.setPWM( round( length_us * 4096 ) / ( 1000000 / self.freq ) )
def run(self, command, speed = 0):
if not self.MC:
return
class dw_Controller:
def __init__(self, addr = 0x61, freq = 100, correctionFactor = 1.0):
self._i2caddr = addr # default addr on HAT
self._frequency = freq # default @60Hz PWM freq
# self.steppers = [ Adafruit_StepperMotor(self, 1), Adafruit_StepperMotor(self, 2) ]
self._pwm = PCA9685(addr)
self._pwm.set_pwm_freq(self._frequency, correctionFactor)
# Just gonna default to high for now
self.servo = [ dw_PWM(self, m, freq) for m in range(6) ]
self.esc = [ dw_PWM(self, m, freq) for m in range(6, 12) ]
def setPin(self, pin, value):
if (pin < 0) or (pin > 15):
raise NameError('PWM pin must be between 0 and 15 inclusive')
if (value != 0) and (value != 1):
raise NameError('Pin value must be 0 or 1!')
if (value == 0):
self._pwm.set_pwm(pin, 0, 4096)
if (value == 1):
self._pwm.set_pwm(pin, 4096, 0)
def setAllPin(self, value):
if (pin < 0) or (pin > 15):
raise NameError('PWM pin must be between 0 and 15 inclusive')
if (value != 0) and (value != 1):
raise NameError('Pin value must be 0 or 1!')
if (value == 0):
self._pwm.set_all_pwm(0, 4096)
if (value == 1):
self._pwm.set_all_pwm(4096, 0)
def getMotor(self, num):
if (num < 1) or (num > 6):
raise NameError('Motors must be between 1 and 6 inclusive')
return self.esc[num-1]
def getServo(self, num):
if (num < 1) or (num > 6):
raise NameError('Servos must be between 1 and 6 inclusive')
return self.servo[num-1]
def setAllPWM(self, value):
if(value > 0):
self._pwm.set_all_pwm(0, value)
if(value == 0):
self.allOff()
def setAllPWMmS(self, value):
if(value > 0):
self._pwm.set_all_pwm(0, value)
if(value == 0):
self.allOff()
def setAllPWMuS(self, value):
if(value > 0):
self._pwm.set_all_pwm(0, value)
if(value == 0):
self.allOff()
def allOff(self):
this.setAllPin( 0 );
def translate(value, leftMin, leftMax, rightMin, rightMax):
# Figure out how 'wide' each range is
leftSpan = leftMax - leftMin
rightSpan = rightMax - rightMin
# Convert the left range into a 0-1 range (float)
valueScaled = float(value - leftMin) / float(leftSpan)
# Convert the 0-1 range into a value in the right range.
return rightMin + (valueScaled * rightSpan)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@@ -1 +0,0 @@
Placeholder for the Escape board hardware files

View File

@@ -1,425 +0,0 @@
Attribution-ShareAlike 4.0 International
=======================================================================
Creative Commons Corporation ("Creative Commons") is not a law firm and
does not provide legal services or legal advice. Distribution of
Creative Commons public licenses does not create a lawyer-client or
other relationship. Creative Commons makes its licenses and related
information available on an "as-is" basis. Creative Commons gives no
warranties regarding its licenses, any material licensed under their
terms and conditions, or any related information. Creative Commons
disclaims all liability for damages resulting from their use to the
fullest extent possible.
Using Creative Commons Public Licenses
Creative Commons public licenses provide a standard set of terms and
conditions that creators and other rights holders may use to share
original works of authorship and other material subject to copyright
and certain other rights specified in the public license below. The
following considerations are for informational purposes only, are not
exhaustive, and do not form part of our licenses.
Considerations for licensors: Our public licenses are
intended for use by those authorized to give the public
permission to use material in ways otherwise restricted by
copyright and certain other rights. Our licenses are
irrevocable. Licensors should read and understand the terms
and conditions of the license they choose before applying it.
Licensors should also secure all rights necessary before
applying our licenses so that the public can reuse the
material as expected. Licensors should clearly mark any
material not subject to the license. This includes other CC-
licensed material, or material used under an exception or
limitation to copyright. More considerations for licensors:
wiki.creativecommons.org/Considerations_for_licensors
Considerations for the public: By using one of our public
licenses, a licensor grants the public permission to use the
licensed material under specified terms and conditions. If
the licensor's permission is not necessary for any reason--for
example, because of any applicable exception or limitation to
copyright--then that use is not regulated by the license. Our
licenses grant only permissions under copyright and certain
other rights that a licensor has authority to grant. Use of
the licensed material may still be restricted for other
reasons, including because others have copyright or other
rights in the material. A licensor may make special requests,
such as asking that all changes be marked or described.
Although not required by our licenses, you are encouraged to
respect those requests where reasonable. More_considerations
for the public:
wiki.creativecommons.org/Considerations_for_licensees
=======================================================================
Creative Commons Attribution-ShareAlike 4.0 International Public
License
By exercising the Licensed Rights (defined below), You accept and agree
to be bound by the terms and conditions of this Creative Commons
Attribution-ShareAlike 4.0 International Public License ("Public
License"). To the extent this Public License may be interpreted as a
contract, You are granted the Licensed Rights in consideration of Your
acceptance of these terms and conditions, and the Licensor grants You
such rights in consideration of benefits the Licensor receives from
making the Licensed Material available under these terms and
conditions.
Section 1 -- Definitions.
a. Adapted Material means material subject to Copyright and Similar
Rights that is derived from or based upon the Licensed Material
and in which the Licensed Material is translated, altered,
arranged, transformed, or otherwise modified in a manner requiring
permission under the Copyright and Similar Rights held by the
Licensor. For purposes of this Public License, where the Licensed
Material is a musical work, performance, or sound recording,
Adapted Material is always produced where the Licensed Material is
synched in timed relation with a moving image.
b. Adapter's License means the license You apply to Your Copyright
and Similar Rights in Your contributions to Adapted Material in
accordance with the terms and conditions of this Public License.
c. BY-SA Compatible License means a license listed at
creativecommons.org/compatiblelicenses, approved by Creative
Commons as essentially the equivalent of this Public License.
d. Copyright and Similar Rights means copyright and/or similar rights
closely related to copyright including, without limitation,
performance, broadcast, sound recording, and Sui Generis Database
Rights, without regard to how the rights are labeled or
categorized. For purposes of this Public License, the rights
specified in Section 2(b)(1)-(2) are not Copyright and Similar
Rights.
e. Effective Technological Measures means those measures that, in the
absence of proper authority, may not be circumvented under laws
fulfilling obligations under Article 11 of the WIPO Copyright
Treaty adopted on December 20, 1996, and/or similar international
agreements.
f. Exceptions and Limitations means fair use, fair dealing, and/or
any other exception or limitation to Copyright and Similar Rights
that applies to Your use of the Licensed Material.
g. License Elements means the license attributes listed in the name
of a Creative Commons Public License. The License Elements of this
Public License are Attribution and ShareAlike.
h. Licensed Material means the artistic or literary work, database,
or other material to which the Licensor applied this Public
License.
i. Licensed Rights means the rights granted to You subject to the
terms and conditions of this Public License, which are limited to
all Copyright and Similar Rights that apply to Your use of the
Licensed Material and that the Licensor has authority to license.
j. Licensor means the individual(s) or entity(ies) granting rights
under this Public License.
k. Share means to provide material to the public by any means or
process that requires permission under the Licensed Rights, such
as reproduction, public display, public performance, distribution,
dissemination, communication, or importation, and to make material
available to the public including in ways that members of the
public may access the material from a place and at a time
individually chosen by them.
l. Sui Generis Database Rights means rights other than copyright
resulting from Directive 96/9/EC of the European Parliament and of
the Council of 11 March 1996 on the legal protection of databases,
as amended and/or succeeded, as well as other essentially
equivalent rights anywhere in the world.
m. You means the individual or entity exercising the Licensed Rights
under this Public License. Your has a corresponding meaning.
Section 2 -- Scope.
a. License grant.
1. Subject to the terms and conditions of this Public License,
the Licensor hereby grants You a worldwide, royalty-free,
non-sublicensable, non-exclusive, irrevocable license to
exercise the Licensed Rights in the Licensed Material to:
a. reproduce and Share the Licensed Material, in whole or
in part; and
b. produce, reproduce, and Share Adapted Material.
2. Exceptions and Limitations. For the avoidance of doubt, where
Exceptions and Limitations apply to Your use, this Public
License does not apply, and You do not need to comply with
its terms and conditions.
3. Term. The term of this Public License is specified in Section
6(a).
4. Media and formats; technical modifications allowed. The
Licensor authorizes You to exercise the Licensed Rights in
all media and formats whether now known or hereafter created,
and to make technical modifications necessary to do so. The
Licensor waives and/or agrees not to assert any right or
authority to forbid You from making technical modifications
necessary to exercise the Licensed Rights, including
technical modifications necessary to circumvent Effective
Technological Measures. For purposes of this Public License,
simply making modifications authorized by this Section 2(a)
(4) never produces Adapted Material.
5. Downstream recipients.
a. Offer from the Licensor -- Licensed Material. Every
recipient of the Licensed Material automatically
receives an offer from the Licensor to exercise the
Licensed Rights under the terms and conditions of this
Public License.
b. Additional offer from the Licensor -- Adapted Material.
Every recipient of Adapted Material from You
automatically receives an offer from the Licensor to
exercise the Licensed Rights in the Adapted Material
under the conditions of the Adapter's License You apply.
c. No downstream restrictions. You may not offer or impose
any additional or different terms or conditions on, or
apply any Effective Technological Measures to, the
Licensed Material if doing so restricts exercise of the
Licensed Rights by any recipient of the Licensed
Material.
6. No endorsement. Nothing in this Public License constitutes or
may be construed as permission to assert or imply that You
are, or that Your use of the Licensed Material is, connected
with, or sponsored, endorsed, or granted official status by,
the Licensor or others designated to receive attribution as
provided in Section 3(a)(1)(A)(i).
b. Other rights.
1. Moral rights, such as the right of integrity, are not
licensed under this Public License, nor are publicity,
privacy, and/or other similar personality rights; however, to
the extent possible, the Licensor waives and/or agrees not to
assert any such rights held by the Licensor to the limited
extent necessary to allow You to exercise the Licensed
Rights, but not otherwise.
2. Patent and trademark rights are not licensed under this
Public License.
3. To the extent possible, the Licensor waives any right to
collect royalties from You for the exercise of the Licensed
Rights, whether directly or through a collecting society
under any voluntary or waivable statutory or compulsory
licensing scheme. In all other cases the Licensor expressly
reserves any right to collect such royalties.
Section 3 -- License Conditions.
Your exercise of the Licensed Rights is expressly made subject to the
following conditions.
a. Attribution.
1. If You Share the Licensed Material (including in modified
form), You must:
a. retain the following if it is supplied by the Licensor
with the Licensed Material:
i. identification of the creator(s) of the Licensed
Material and any others designated to receive
attribution, in any reasonable manner requested by
the Licensor (including by pseudonym if
designated);
ii. a copyright notice;
iii. a notice that refers to this Public License;
iv. a notice that refers to the disclaimer of
warranties;
v. a URI or hyperlink to the Licensed Material to the
extent reasonably practicable;
b. indicate if You modified the Licensed Material and
retain an indication of any previous modifications; and
c. indicate the Licensed Material is licensed under this
Public License, and include the text of, or the URI or
hyperlink to, this Public License.
2. You may satisfy the conditions in Section 3(a)(1) in any
reasonable manner based on the medium, means, and context in
which You Share the Licensed Material. For example, it may be
reasonable to satisfy the conditions by providing a URI or
hyperlink to a resource that includes the required
information.
3. If requested by the Licensor, You must remove any of the
information required by Section 3(a)(1)(A) to the extent
reasonably practicable.
b. ShareAlike.
In addition to the conditions in Section 3(a), if You Share
Adapted Material You produce, the following conditions also apply.
1. The Adapter's License You apply must be a Creative Commons
license with the same License Elements, this version or
later, or a BY-SA Compatible License.
2. You must include the text of, or the URI or hyperlink to, the
Adapter's License You apply. You may satisfy this condition
in any reasonable manner based on the medium, means, and
context in which You Share Adapted Material.
3. You may not offer or impose any additional or different terms
or conditions on, or apply any Effective Technological
Measures to, Adapted Material that restrict exercise of the
rights granted under the Adapter's License You apply.
Section 4 -- Sui Generis Database Rights.
Where the Licensed Rights include Sui Generis Database Rights that
apply to Your use of the Licensed Material:
a. for the avoidance of doubt, Section 2(a)(1) grants You the right
to extract, reuse, reproduce, and Share all or a substantial
portion of the contents of the database;
b. if You include all or a substantial portion of the database
contents in a database in which You have Sui Generis Database
Rights, then the database in which You have Sui Generis Database
Rights (but not its individual contents) is Adapted Material,
including for purposes of Section 3(b); and
c. You must comply with the conditions in Section 3(a) if You Share
all or a substantial portion of the contents of the database.
For the avoidance of doubt, this Section 4 supplements and does not
replace Your obligations under this Public License where the Licensed
Rights include other Copyright and Similar Rights.
Section 5 -- Disclaimer of Warranties and Limitation of Liability.
a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.
b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
c. The disclaimer of warranties and limitation of liability provided
above shall be interpreted in a manner that, to the extent
possible, most closely approximates an absolute disclaimer and
waiver of all liability.
Section 6 -- Term and Termination.
a. This Public License applies for the term of the Copyright and
Similar Rights licensed here. However, if You fail to comply with
this Public License, then Your rights under this Public License
terminate automatically.
b. Where Your right to use the Licensed Material has terminated under
Section 6(a), it reinstates:
1. automatically as of the date the violation is cured, provided
it is cured within 30 days of Your discovery of the
violation; or
2. upon express reinstatement by the Licensor.
For the avoidance of doubt, this Section 6(b) does not affect any
right the Licensor may have to seek remedies for Your violations
of this Public License.
c. For the avoidance of doubt, the Licensor may also offer the
Licensed Material under separate terms or conditions or stop
distributing the Licensed Material at any time; however, doing so
will not terminate this Public License.
d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
License.
Section 7 -- Other Terms and Conditions.
a. The Licensor shall not be bound by any additional or different
terms or conditions communicated by You unless expressly agreed.
b. Any arrangements, understandings, or agreements regarding the
Licensed Material not stated herein are separate from and
independent of the terms and conditions of this Public License.
Section 8 -- Interpretation.
a. For the avoidance of doubt, this Public License does not, and
shall not be interpreted to, reduce, limit, restrict, or impose
conditions on any use of the Licensed Material that could lawfully
be made without permission under this Public License.
b. To the extent possible, if any provision of this Public License is
deemed unenforceable, it shall be automatically reformed to the
minimum extent necessary to make it enforceable. If the provision
cannot be reformed, it shall be severed from this Public License
without affecting the enforceability of the remaining terms and
conditions.
c. No term or condition of this Public License will be waived and no
failure to comply consented to unless expressly agreed to by the
Licensor.
d. Nothing in this Public License constitutes or may be interpreted
as a limitation upon, or waiver of, any privileges and immunities
that apply to the Licensor or You, including from the legal
processes of any jurisdiction or authority.
=======================================================================
Creative Commons is not a party to its public licenses.
Notwithstanding, Creative Commons may elect to apply one of its public
licenses to material it publishes and in those instances will be
considered the "Licensor." Except for the limited purpose of indicating
that material is shared under a Creative Commons public license or as
otherwise permitted by the Creative Commons policies published at
creativecommons.org/policies, Creative Commons does not authorize the
use of the trademark "Creative Commons" or any other trademark or logo
of Creative Commons without its prior written consent including,
without limitation, in connection with any unauthorized modifications
to any of its public licenses or any other arrangements,
understandings, or agreements concerning use of licensed material. For
the avoidance of doubt, this paragraph does not form part of the public
licenses.
Creative Commons may be contacted at creativecommons.org.