]> Repositories - Adafruit_Blinka-hackapet.git/commitdiff
initial working MCP2221
authorcaternuson <caternuson@gmail.com>
Thu, 21 Nov 2019 05:00:09 +0000 (21:00 -0800)
committercaternuson <caternuson@gmail.com>
Thu, 5 Dec 2019 20:10:11 +0000 (12:10 -0800)
src/adafruit_blinka/board/microchip_mcp2221.py [new file with mode: 0644]
src/adafruit_blinka/microcontroller/mcp2221/__init__.py [new file with mode: 0644]
src/adafruit_blinka/microcontroller/mcp2221/i2c.py [new file with mode: 0644]
src/adafruit_blinka/microcontroller/mcp2221/mcp2221.py [new file with mode: 0644]
src/adafruit_blinka/microcontroller/mcp2221/pin.py [new file with mode: 0644]
src/analogio.py [new file with mode: 0644]
src/board.py
src/busio.py
src/digitalio.py
src/microcontroller/pin.py

diff --git a/src/adafruit_blinka/board/microchip_mcp2221.py b/src/adafruit_blinka/board/microchip_mcp2221.py
new file mode 100644 (file)
index 0000000..bdb3ba2
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..ef7f784
--- /dev/null
@@ -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 (file)
index 0000000..118d27c
--- /dev/null
@@ -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 (file)
index 0000000..c66263e
--- /dev/null
@@ -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 (file)
index 0000000..229495d
--- /dev/null
@@ -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
index ef2f086c26c40f65d4dfccef3007aa430193b3b3..c9fac91dc7cfb5e903f0ae7e3e3a3b1102d18353 100755 (executable)
@@ -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:
     from adafruit_blinka.board.beaglebone_black import *
-       
+
 elif board_id == ap_board.BEAGLEBONE_BLACK_INDUSTRIAL:
     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.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
 
 elif "sphinx" in sys.modules:
     pass
 
index 74cfa89eb75d2dabe4bc68bedeae80aab87701ea..5ec726bc572ab0ea2500e20da927a141dde69e41 100755 (executable)
@@ -25,6 +25,8 @@ class I2C(Lockable):
             return
         elif detector.board.binho_nova:
             from adafruit_blinka.microcontroller.nova.i2c import I2C
             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:
             self._i2c = I2C()
             return
         elif detector.board.any_embedded_linux:
index ed497cc76addd339b7ad68c9a43fab9be11bf58c..bb2a48ef32a2b5ef09c5c81b26b78a7930bf65fb 100755 (executable)
@@ -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
     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):
 from adafruit_blinka import Enum, ContextManaged
 
 class DriveMode(Enum):
index d6e18d8ac50f1218206b63fd961293fb54fbc12f..d1b5b8fea3ec7b8ebe0aceb6f3d9c20901d32030 100755 (executable)
@@ -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 *
     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)
 else:
     raise NotImplementedError("Microcontroller not supported: ", chip_id)