Compare commits
3 commits
master
...
caternuson
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
21c39e28db | ||
|
|
db15c20c2f | ||
|
|
103b2f2455 |
10 changed files with 408 additions and 0 deletions
9
src/adafruit_blinka/board/microchip_mcp2221.py
Normal file
9
src/adafruit_blinka/board/microchip_mcp2221.py
Normal file
|
|
@ -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
|
||||
0
src/adafruit_blinka/microcontroller/mcp2221/__init__.py
Normal file
0
src/adafruit_blinka/microcontroller/mcp2221/__init__.py
Normal file
24
src/adafruit_blinka/microcontroller/mcp2221/i2c.py
Normal file
24
src/adafruit_blinka/microcontroller/mcp2221/i2c.py
Normal file
|
|
@ -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)
|
||||
|
||||
238
src/adafruit_blinka/microcontroller/mcp2221/mcp2221.py
Normal file
238
src/adafruit_blinka/microcontroller/mcp2221/mcp2221.py
Normal file
|
|
@ -0,0 +1,238 @@
|
|||
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_writebuffer(self, address, cmd, buffer, start=0, end=None):
|
||||
end = end if end else len(buffer)
|
||||
total_size = end - start
|
||||
while (end - start) > 0:
|
||||
writing = min(end - start, 60)
|
||||
response = self._hid_xfer(bytes([cmd, # i2c write data
|
||||
total_size & 0xFF, # xfer length lo byte
|
||||
(total_size >> 8) & 0xFF, # xfer length hi byte
|
||||
address << 1]) + # i2c slave address
|
||||
buffer[start:(start+writing)]) # user data to be sent
|
||||
print(response[0:2])
|
||||
if response[1] != 0x0:
|
||||
time.sleep(0.001)
|
||||
continue # retry!
|
||||
start += writing
|
||||
|
||||
def i2c_writeto(self, address, buffer, *, start=0, end=None):
|
||||
self._i2c_writebuffer(address, 0x90, buffer, start, end)
|
||||
|
||||
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()
|
||||
74
src/adafruit_blinka/microcontroller/mcp2221/pin.py
Normal file
74
src/adafruit_blinka/microcontroller/mcp2221/pin.py
Normal file
|
|
@ -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()
|
||||
52
src/analogio.py
Normal file
52
src/analogio.py
Normal file
|
|
@ -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
|
||||
|
|
@ -100,6 +100,9 @@ elif board_id == ap_board.DRAGONBOARD_410C:
|
|||
elif board_id == ap_board.FTDI_FT232H:
|
||||
from adafruit_blinka.board.ftdi_ft232h import *
|
||||
|
||||
elif board_id == ap_board.MICROCHIP_MCP2221:
|
||||
from adafruit_blinka.board.microchip_mcp2221 import *
|
||||
|
||||
elif "sphinx" in sys.modules:
|
||||
pass
|
||||
|
||||
|
|
|
|||
|
|
@ -23,6 +23,10 @@ class I2C(Lockable):
|
|||
from adafruit_blinka.microcontroller.ft232h.i2c import I2C
|
||||
self._i2c = I2C()
|
||||
return
|
||||
if detector.board.microchip_mcp2221:
|
||||
from adafruit_blinka.microcontroller.mcp2221.i2c import I2C
|
||||
self._i2c = I2C()
|
||||
return
|
||||
elif detector.board.any_embedded_linux:
|
||||
from adafruit_blinka.microcontroller.generic_linux.i2c import I2C as _I2C
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -37,6 +37,8 @@ elif detector.board.ftdi_ft232h:
|
|||
from adafruit_blinka.microcontroller.ft232h.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):
|
||||
|
|
|
|||
|
|
@ -34,5 +34,7 @@ elif chip_id == ap_chip.IMX8MX:
|
|||
from adafruit_blinka.microcontroller.nxp_imx8m.pin import *
|
||||
elif chip_id == ap_chip.FT232H:
|
||||
from adafruit_blinka.microcontroller.ft232h.pin import *
|
||||
elif chip_id == ap_chip.MCP2221:
|
||||
from adafruit_blinka.microcontroller.mcp2221.pin import *
|
||||
else:
|
||||
raise NotImplementedError("Microcontroller not supported: ", chip_id)
|
||||
|
|
|
|||
Loading…
Reference in a new issue