# 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 os import subprocess import Adafruit_GPIO.Platform as Platform def reverseByteOrder(data): """DEPRECATED: See https://github.com/adafruit/Adafruit_Python_GPIO/issues/48""" # # 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 raise RuntimeError('reverseByteOrder is deprecated! See: https://github.com/adafruit/Adafruit_Python_GPIO/issues/48') 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 and os.path.exists('/sys/module/i2c_bcm2708/parameters/combined'): # 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)