From: caternuson Date: Thu, 21 Nov 2019 05:00:09 +0000 (-0800) Subject: initial working MCP2221 X-Git-Tag: 3.2.0~1^2~2 X-Git-Url: https://git.ayoreis.com/Adafruit_Blinka-hackapet.git/commitdiff_plain/c72b2913f8b11bc650b5a17f087a058cf7948fb9?ds=inline initial working MCP2221 --- diff --git a/src/adafruit_blinka/board/microchip_mcp2221.py b/src/adafruit_blinka/board/microchip_mcp2221.py new file mode 100644 index 0000000..bdb3ba2 --- /dev/null +++ b/src/adafruit_blinka/board/microchip_mcp2221.py @@ -0,0 +1,9 @@ +from adafruit_blinka.microcontroller.mcp2221 import pin + +G0 = pin.G0 +G1 = pin.G1 +G2 = pin.G2 +G3 = pin.G3 + +SCL = pin.SCL +SDA = pin.SDA \ No newline at end of file diff --git a/src/adafruit_blinka/microcontroller/mcp2221/__init__.py b/src/adafruit_blinka/microcontroller/mcp2221/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/adafruit_blinka/microcontroller/mcp2221/i2c.py b/src/adafruit_blinka/microcontroller/mcp2221/i2c.py new file mode 100644 index 0000000..ef7f784 --- /dev/null +++ b/src/adafruit_blinka/microcontroller/mcp2221/i2c.py @@ -0,0 +1,24 @@ +from adafruit_blinka.microcontroller.mcp2221.pin import Pin +from .mcp2221 import mcp2221 + +class I2C: + + def __init__(self, *, baudrate=100000): + mcp2221.i2c_configure(baudrate) + + def scan(self): + return mcp2221.i2c_scan() + + def writeto(self, address, buffer, *, start=0, end=None, stop=True): + mcp2221.i2c_writeto(address, buffer, start=start, end=end) + + def readfrom_into(self, address, buffer, *, start=0, end=None, stop=True): + mcp2221.i2c_readfrom_into(address, buffer, start=start, end=end) + + def writeto_then_readfrom(self, address, buffer_out, buffer_in, *, + out_start=0, out_end=None, + in_start=0, in_end=None, stop=False): + mcp2221.i2c_writeto_then_readfrom(address, buffer_out, buffer_in, + out_start=out_start, out_end=out_end, + in_start=in_start, in_end=in_end) + diff --git a/src/adafruit_blinka/microcontroller/mcp2221/mcp2221.py b/src/adafruit_blinka/microcontroller/mcp2221/mcp2221.py new file mode 100644 index 0000000..118d27c --- /dev/null +++ b/src/adafruit_blinka/microcontroller/mcp2221/mcp2221.py @@ -0,0 +1,227 @@ +import time +import hid + +class MCP2221: + + VID = 0x04D8 + PID = 0x00DD + + GP_GPIO = 0b000 + GP_DEDICATED = 0b001 + GP_ALT0 = 0b010 + GP_ALT1 = 0b011 + GP_ALT2 = 0b100 + + def __init__(self): + self._hid = hid.device() + self._hid.open(MCP2221.VID, MCP2221.PID) + + def _hid_xfer(self, report, response=True): + # first byte is report ID, which =0 for MCP2221 + # remaing bytes = 64 byte report data + # https://github.com/libusb/hidapi/blob/083223e77952e1ef57e6b77796536a3359c1b2a3/hidapi/hidapi.h#L185 + self._hid.write(b'\0' + report + b'\0'*(64-len(report))) + if response: + # return is 64 byte response report + return self._hid.read(64) + + #---------------------------------------------------------------- + # MISC + #---------------------------------------------------------------- + def gp_get_mode(self, pin): + return self._hid_xfer(bytes([0x61]))[22+pin] & 0x07 + + def gp_set_mode(self, pin, mode): + # get current settings + current = self._hid_xfer(bytes([0x61])) + # empty report, this is safe since 0's = no change + report = bytearray([0x60]+[0]*63) + # set the alter GP flag byte + report[7] = 0xFF + # each pin can be set individually + # but all 4 get set at once, so we need to + # transpose current settings + report[8] = current[22] # GP0 + report[9] = current[23] # GP1 + report[10] = current[24] # GP2 + report[11] = current[25] # GP3 + # then change only the one + report[8+pin] = mode & 0x07 + # and make it so + self._hid_xfer(report) + + def _pretty_report(self, report): + print(" 0 1 2 3 4 5 6 7 8 9") + index = 0 + for row in range(7): + print("{} : ".format(row), end='') + for _ in range(10): + print("{:02x} ".format(report[index]), end='') + index += 1 + if index > 63: + break + print() + + def _status_dump(self): + self._pretty_report(self._hid_xfer(bytes([0x10]))) + + def _sram_dump(self): + self._pretty_report(self._hid_xfer(bytes([0x61]))) + + def _reset(self): + self._hid_xfer(b'\x70\xAB\xCD\xEF', response=False) + time.sleep(1) + self._hid.open(MCP2221.VID, MCP2221.PID) + + #---------------------------------------------------------------- + # GPIO + #---------------------------------------------------------------- + def gpio_set_direction(self, pin, mode): + report = bytearray([0x50]+[0]*63) # empty set GPIO report + offset = 4 * (pin + 1) + report[offset] = 0x01 # set pin direction + report[offset+1] = mode # to this + self._hid_xfer(report) + + def gpio_set_pin(self, pin, value): + report = bytearray([0x50]+[0]*63) # empty set GPIO report + offset = 2 + 4 * pin + report[offset] = 0x01 # set pin value + report[offset+1] = value # to this + self._hid_xfer(report) + + def gpio_get_pin(self, pin): + resp = self._hid_xfer(bytes([0x51])) + offset = 2 + 2 * pin + if resp[offset] == 0xEE: + raise RuntimeError("Pin is not set for GPIO operation.") + else: + return resp[offset] + + #---------------------------------------------------------------- + # I2C + # + # cribbed from the C driver: + # define RESP_ERR_NOERR 0x00 + # define RESP_ADDR_NACK 0x25 + # define RESP_READ_ERR 0x7F + # define RESP_READ_COMPL 0x55 + # define RESP_I2C_IDLE 0x00 + # define RESP_I2C_START_TOUT 0x12 + # define RESP_I2C_RSTART_TOUT 0x17 + # define RESP_I2C_WRADDRL_TOUT 0x23 + # define RESP_I2C_WRADDRL_WSEND 0x21 + # define RESP_I2C_WRADDRL_NACK 0x25 + # define RESP_I2C_WRDATA_TOUT 0x44 + # define RESP_I2C_RDDATA_TOUT 0x52 + # define RESP_I2C_STOP_TOUT 0x62 + #---------------------------------------------------------------- + def i2c_configure(self, baudrate=100000): + self._hid_xfer(bytes([0x10, # set parameters + 0x00, # don't care + 0x00, # no effect + 0x20, # next byte is clock divider + 12000000 // baudrate - 3])) + + def i2c_writeto(self, address, buffer, *, start=0, end=None): + end = end if end else len(buffer) + self._hid_xfer(bytes([0x90, # i2c write data + end - start & 0xFF, # xfer length lo byte + end - start >> 8 & 0xFF, # xfer length hi byte + address << 1]) + # i2c slave address + buffer[start:end]) # user data to be sent + + def i2c_readfrom_into(self, address, buffer, *, start=0, end=None): + end = end if end else len(buffer) + retries = 0 + while retries < 5: + # + # why does this require two xfers? + # + # 1. tell it we want to read + self._hid_xfer(bytes([0x91, # i2c read data + end - start & 0xFF, # xfer length lo byte + end - start >> 8 & 0xFF, # xfer length hi byte + address << 1 | 0x01])) # i2c slave address + # 2. and then actually read + response = self._hid_xfer(bytes([0x40])) + # check for success + if response[1] == 0x00: + break + retries += 1 + if retries >= 5: + raise RuntimeError("I2C read error, max retries reached.") + # move data into buffer + for i in range(end - start): + buffer[start + i] = response[4 + i] + + def i2c_writeto_then_readfrom(self, address, out_buffer, in_buffer, *, + out_start=0, out_end=None, + in_start=0, in_end=None): + out_end = out_end if out_end else len(buffer_out) + in_end = in_end if in_end else len(buffer_in) + self._hid_xfer(bytes([0x94, # i2c write data no stop + out_end - out_start & 0xFF, # xfer length lo byte + out_end - out_start >> 8 & 0xFF, # xfer length hi byte + address << 1]) + # i2c slave address + out_buffer[out_start:out_end]) # user data to be sent + retries = 5 + while retries < 5: + # + # why does this require two xfers? + # + # 1. tell it we want to read + self._hid_xfer(bytes([0x93, # i2c read data repeated start + in_end - in_start & 0xFF, # xfer length lo byte + in_end - in_start >> 8 & 0xFF, # xfer length hi byte + address << 1 | 0x01])) # i2c slave address + # 2. and then actually read + response = self._hid_xfer(bytes([0x40])) + # check for success + if response[1] == 0x00: + break + retries += 1 + if retries >= 5: + raise RuntimeError("I2C read error, max retries reached.") + # move data into buffer + for i in range(in_end - in_start): + in_buffer[in_start + i] = response[4 + i] + + def i2c_scan(self, *, start=0, end=0x79): + found = [] + for addr in range(start, end+1): + # try a write + self.i2c_writeto(addr, b'\x00') + # store if success + if self._hid_xfer(b'\x10')[8] == 0x00: + found.append(addr) + # cancel and continue + self._hid_xfer(b'\x10\x00\x10') + return found + + #---------------------------------------------------------------- + # ADC + #---------------------------------------------------------------- + def adc_configure(self, vref=0): + report = bytearray([0x60]+[0]*63) + report[5] = 1 << 7 | (vref & 0b111) + self._hid_xfer(report) + + def adc_read(self, pin): + resp = self._hid_xfer(bytes([0x10])) + return resp[49 + 2 * pin] << 8 | resp[48 + 2 * pin] + + #---------------------------------------------------------------- + # DAC + #---------------------------------------------------------------- + def dac_configure(self, vref=0): + report = bytearray([0x60]+[0]*63) + report[3] = 1 << 7 | (vref & 0b111) + self._hid_xfer(report) + + def dac_write(self, pin, value): + report = bytearray([0x60]+[0]*63) + report[4] = 1 << 7 | (value & 0b11111) + self._hid_xfer(report) + +mcp2221 = MCP2221() diff --git a/src/adafruit_blinka/microcontroller/mcp2221/pin.py b/src/adafruit_blinka/microcontroller/mcp2221/pin.py new file mode 100644 index 0000000..c66263e --- /dev/null +++ b/src/adafruit_blinka/microcontroller/mcp2221/pin.py @@ -0,0 +1,74 @@ +from .mcp2221 import mcp2221 + +class Pin: + """A basic Pin class for use with MCP2221.""" + + # pin modes + OUT = 0 + IN = 1 + ADC = 2 + DAC = 3 + # pin values + LOW = 0 + HIGH = 1 + + def __init__(self, pin_id=None): + self.id = pin_id + self._mode = None + + def init(self, mode=IN, pull=None): + if self.id is None: + raise RuntimeError("Can not init a None type pin.") + if mode in (Pin.IN, Pin.OUT): + mcp2221.gp_set_mode(self.id, mcp2221.GP_GPIO) + mcp2221.gpio_set_direction(self.id, mode) + elif mode == Pin.ADC: + mcp2221.gp_set_mode(self.id, mcp2221.GP_ALT0) + mcp2221.adc_configure() + elif mode == Pin.DAC: + mcp2221.gp_set_mode(self.id, mcp2221.GP_ALT1) + mcp2221.dac_configure() + else: + raise ValueError("Incorrect pin mode: {}".format(mode)) + self._mode = mode + + def value(self, val=None): + # Digital In / Out + if self._mode in (Pin.IN, Pin.OUT): + # digital read + if val is None: + return mcp2221.gpio_get_pin(self.id) + # digital write + elif val in (Pin.LOW, Pin.HIGH): + mcp2221.gpio_set_pin(self.id, val) + # nope + else: + raise ValueError("Invalid value for pin.") + # Analog In + elif self._mode == Pin.ADC: + if val is None: + # MCP2221 ADC is 10 bit, scale to 16 bit per CP API + return mcp2221.adc_read(self.id) * 64 + else: + # read only + raise AttributeError("'AnalogIn' object has no attribute 'value'") + # Analog Out + elif self._mode == Pin.DAC: + if val is None: + # write only + raise AttributeError("unreadable attribute") + else: + # scale 16 bit value to MCP2221 5 bit DAC (yes 5 bit) + mcp2221.dac_write(self.id, val // 2048) + else: + raise RuntimeError("No action for mode {} with value {}".format(self._mode, val)) + + +# create pin instances for each pin +G0 = Pin(0) +G1 = Pin(1) +G2 = Pin(2) +G3 = Pin(3) + +SCL = Pin() +SDA = Pin() \ No newline at end of file diff --git a/src/analogio.py b/src/analogio.py new file mode 100644 index 0000000..229495d --- /dev/null +++ b/src/analogio.py @@ -0,0 +1,52 @@ +""" +`analogio` - Analog input and output control +================================================= +See `CircuitPython:analogio` in CircuitPython for more details. +* Author(s): Carter Nelson +""" + +from adafruit_blinka.agnostic import board_id, detector + +# pylint: disable=ungrouped-imports,wrong-import-position + +if detector.board.microchip_mcp2221: + from adafruit_blinka.microcontroller.mcp2221.pin import Pin +else: + raise NotImplementedError("analogio not supported for this board.") + +from adafruit_blinka import ContextManaged + +class AnalogIn(ContextManaged): + + def __init__(self, pin): + self._pin = Pin(pin.id) + self._pin.init(mode=Pin.ADC) + + @property + def value(self): + return self._pin.value() + + @value.setter + def value(self, value): + # emulate what CircuitPython does + raise AttributeError("'AnalogIn' object has no attribute 'value'") + + def deinit(self): + del self._pin + +class AnalogOut(ContextManaged): + def __init__(self, pin): + self._pin = Pin(pin.id) + self._pin.init(mode=Pin.DAC) + + @property + def value(self): + # emulate what CircuitPython does + raise AttributeError("unreadable attribute") + + @value.setter + def value(self, value): + self._pin.value(value) + + def deinit(self): + del self._pin \ No newline at end of file diff --git a/src/board.py b/src/board.py index ef2f086..c9fac91 100755 --- a/src/board.py +++ b/src/board.py @@ -57,7 +57,7 @@ elif detector.board.RASPBERRY_PI_B_REV2: elif board_id == ap_board.BEAGLEBONE_BLACK: from adafruit_blinka.board.beaglebone_black import * - + elif board_id == ap_board.BEAGLEBONE_BLACK_INDUSTRIAL: from adafruit_blinka.board.beaglebone_black import * @@ -109,6 +109,9 @@ elif board_id == ap_board.FTDI_FT232H: elif board_id == ap_board.BINHO_NOVA: from adafruit_blinka.board.binho_nova import * +elif board_id == ap_board.MICROCHIP_MCP2221: + from adafruit_blinka.board.microchip_mcp2221 import * + elif "sphinx" in sys.modules: pass diff --git a/src/busio.py b/src/busio.py index 74cfa89..5ec726b 100755 --- a/src/busio.py +++ b/src/busio.py @@ -25,6 +25,8 @@ class I2C(Lockable): return elif detector.board.binho_nova: from adafruit_blinka.microcontroller.nova.i2c import I2C + elif detector.board.microchip_mcp2221: + from adafruit_blinka.microcontroller.mcp2221.i2c import I2C self._i2c = I2C() return elif detector.board.any_embedded_linux: diff --git a/src/digitalio.py b/src/digitalio.py index ed497cc..bb2a48e 100755 --- a/src/digitalio.py +++ b/src/digitalio.py @@ -39,6 +39,8 @@ elif detector.board.binho_nova: from adafruit_blinka.microcontroller.nova.pin import Pin elif detector.chip.STM32: from machine import Pin +elif detector.board.microchip_mcp2221: + from adafruit_blinka.microcontroller.mcp2221.pin import Pin from adafruit_blinka import Enum, ContextManaged class DriveMode(Enum): diff --git a/src/microcontroller/pin.py b/src/microcontroller/pin.py index d6e18d8..d1b5b8f 100755 --- a/src/microcontroller/pin.py +++ b/src/microcontroller/pin.py @@ -36,5 +36,7 @@ elif chip_id == ap_chip.FT232H: from adafruit_blinka.microcontroller.ft232h.pin import * elif chip_id == ap_chip.BINHO: from adafruit_blinka.microcontroller.nova.pin import * +elif chip_id == ap_chip.MCP2221: + from adafruit_blinka.microcontroller.mcp2221.pin import * else: raise NotImplementedError("Microcontroller not supported: ", chip_id)