Full API reference

Device Initialization

class Device(VID=None, PID=None, devnum=None, trace_packets=None)

MCP2221(A) device

Parameters:
  • VID (int, optional) – Vendor Id (default to 0x04D8)

  • PID (int, optional) – Product Id (default to 0x00DD)

  • devnum (int, optional) – Device index if multiple device found with the same PID and VID. Default is first device (index 0).

  • trace_packets (bool, optional) – For debug only. See trace_packets.

Raises:

RuntimeError – if no device found with given VID and PID.

Example

>>> import EasyMCP2221
>>> mcp = EasyMCP2221.Device()
>>> print(mcp)
{
    "Chip settings": {
        "Interrupt detection edge": "both",
        "Power management options": "enabled",
        "USB PID": "0x00DD",
        "USB VID": "0x04D8",
        "USB requested number of mA": 100
    },
    "Factory Serial": "01234567",
    "GP settings": {},
    "USB Manufacturer": "Microchip Technology Inc.",
    "USB Product": "MCP2221 USB-I2C/UART Combo",
    "USB Serial": "0000000000"
}

Pin configuration

set_pin_function(gp0=None, gp1=None, gp2=None, gp3=None, out0=False, out1=False, out2=False, out3=False)

Configure pin function and, optionally, output value.

You can set multiple pins at once.

Accepted functions depends on the pin.

_images/MCP2221_pinout.svg
GP0 functions:
  • GPIO_IN (in) : Digital input

  • GPIO_OUT (out): Digital output

  • SSPND (out): Signals when the host has entered Suspend mode

  • LED_URX (out): UART Rx LED activity output (factory default)

GP1 functions:
  • GPIO_IN (in) : Digital input

  • GPIO_OUT (out): Digital output

  • ADC (in) : ADC Channel 1

  • CLK_OUT (out): Clock Reference Output

  • IOC (in) : External Interrupt Edge Detector

  • LED_UTX (out): UART Tx LED activity output (factory default)

GP2 functions:
  • GPIO_IN (in) : Digital input

  • GPIO_OUT (out): Digital output

  • ADC (in) : ADC Channel 2

  • DAC (out): DAC Output 1

  • USBCFG (out): USB device-configured status (factory default)

GP3 functions:
  • GPIO_IN (in) : Digital input

  • GPIO_OUT (out): Digital output

  • ADC (in) : ADC Channel 3

  • DAC (out): DAC Output 2

  • LED_I2C (out): USB/I2C traffic indicator (factory default)

Parameters:
  • gp0 (str, optional) – Function for pin GP0. If None, don’t alter function.

  • gp1 (str, optional) – Function for pin GP1. If None, don’t alter function.

  • gp2 (str, optional) – Function for pin GP2. If None, don’t alter function.

  • gp3 (str, optional) – Function for pin GP3. If None, don’t alter function.

  • out0 (bool, optional) – Logic output for GP0 if configured as GPIO_OUT (default: False).

  • out1 (bool, optional) – Logic output for GP1 if configured as GPIO_OUT (default: False).

  • out2 (bool, optional) – Logic output for GP2 if configured as GPIO_OUT (default: False).

  • out3 (bool, optional) – Logic output for GP3 if configured as GPIO_OUT (default: False).

Raises:
  • ValueError – If invalid function for that pin is specified.

  • ValueError – If given out value for non GPIO_OUT pin.

Examples

Set all pins at once:

>>> mcp.set_pin_function(
...     gp0 = "GPIO_IN",
...     gp1 = "GPIO_OUT", out1 = True,
...     gp2 = "ADC",
...     gp3 = "LED_I2C")
>>>

Change pin function at runtime:

>>> mcp.set_pin_function(gp1 = "GPIO_IN")
>>>

It is not permitted to set the output of a non GPIO_OUT pin.

>>> mcp.set_pin_function(
...     gp1 = "GPIO_OUT", out1 = True,
...     gp2 = "ADC", out2 = True)
Traceback (most recent call last):
...
ValueError: Pin output value can only be set if pin function is GPIO_OUT.
>>>

Only some functions are allowed for each pin.

>>> mcp.set_pin_function(gp0 = "ADC")
Traceback (most recent call last):
...
ValueError: Invalid function for GP0. Could be: GPIO_IN, GPIO_OUT, SSPND, LED_URX
>>>

Hint

Pin assignments are active until reset or power cycle. Use save_config() to make this configuration the default at next start.

save_config()

Write current status (pin assignments, GPIO output values, DAC reference and value, ADC reference, etc.) to flash memory.

You can save a new configuration as many times as you wish. That will be the default state at power up.

Raises:
  • RuntimeError – if command failed.

  • AssertionError – if an accidental flash protection attempt was prevented.

Example

Set all GPIO pins as digital inputs (high impedance state) at start-up to prevent short circuits while breadboarding.

>>> mcp.set_pin_function(
...     gp0 = "GPIO_IN",
...     gp1 = "GPIO_IN",
...     gp2 = "GPIO_IN",
...     gp3 = "GPIO_IN")
>>> mcp.DAC_config(ref = "OFF")
>>> mcp.ADC_config(ref = "VDD")
>>> mcp.save_config()

GPIO

GPIO_read()

Read all GPIO pins logic state.

Returned values can be True, False or None if the pin is not set for GPIO operation. For an output pin, the returned status is the actual value.

Returns:

4 logic values for the pins status gp0, gp1, gp2 and gp3.

Return type:

tuple of bool

Example

>>> mcp.GPIO_read()
(None, 0, 1, None)
GPIO_write(gp0=None, gp1=None, gp2=None, gp3=None)

Set pin output values.

If a pin is omitted, it will preserve the value.

To change the output state of a pin, it must be assigned to GPIO_IN or GPIO_OUT (see set_pin_function()).

Parameters:
  • gp0 (bool, optional) – Set GP0 logic value.

  • gp1 (bool, optional) – Set GP1 logic value.

  • gp2 (bool, optional) – Set GP2 logic value.

  • gp3 (bool, optional) – Set GP3 logic value.

Raises:

RuntimeError – If given pin is not assigned to GPIO function.

Examples

Configure GP1 as output (defaults to False) and then set the value to logical True.

>>> mcp.set_pin_function(gp1 = "GPIO_OUT")
>>> mcp.GPIO_write(gp1 = True)

If will fail if the pin is not assigned to GPIO:

>>> mcp.set_pin_function(gp2 = 'DAC')
>>> mcp.GPIO_write(gp2 = False)
Traceback (most recent call last):
    ...
RuntimeError: Pin GP2 is not assigned to GPIO function.

ADC - Analog input

ADC_read(norm=False)

Read all Analog to Digital Converter (ADC) channels.

Analog value is always available regardless of pin function (see set_pin_function()). If pin is configured as output (GPIO_OUT or LED_I2C), the read value is always the output state.

ADC is 10 bits, so the minimum value is 0 and the maximum value is 1023.

Parameters:

norm (bool, optional) – Divide output values by 1024 and return output between 0 and 1. Default is False.

Returns:

Value of 3 channels (gp1, gp2, gp3).

Return type:

tuple

Examples

All three pins configured as ADC inputs.

>>> mcp.ADC_config(ref = "VDD")
>>> mcp.set_pin_function(
...    gp1 = "ADC",
...    gp2 = "ADC",
...    gp3 = "ADC")
>>> mcp.ADC_read()
(185, 136, 198)

Reading the ADC value of a digital output gives the actual voltage in the pin. For a logic output 1 is equal to Vdd unless something is pulling that pin low (i.e. a LED).

>>> mcp.set_pin_function(
...    gp1 = "GPIO_OUT", out1 = True,
...    gp2 = "GPIO_OUT", out2 = False)
>>> mcp.ADC_read()
(1023, 0, 198)
ADC_config(ref='VDD')

Configure ADC reference voltage.

ref values:
  • “OFF”

  • “1.024V”

  • “2.048V”

  • “4.096V”

  • “VDD”

Parameters:

ref (str, optional) – ADC reference value. Default to supply voltage (Vdd).

Raises:

ValueError – if ref value is not valid.

Examples

>>> mcp.ADC_config()
>>> mcp.ADC_config("1.024V")
>>> mcp.ADC_config(ref = "5V")
Traceback (most recent call last):
...
ValueError: Accepted values for ref are 'OFF', '1.024V', '2.048V', '4.096V' and 'VDD'.

Hint

ADC configuration is saved when you call save_config() and reloaded at power-up. You only need to call this function if you want to change it.

DAC - Analog output

DAC_write(out, norm=False)

Set the DAC output value.

Valid out values are 0 to 31.

To use a GP pin as DAC, you must assign the function “DAC” (see set_pin_function()). MCP2221 only have 1 DAC. So if you assign to “DAC” GP2 and GP3 you will see the same output value in both.

Parameters:
  • out (int) – Value to output (max. 32) referenced to DAC ref voltage.

  • norm (bool, optional) – Accept input values as floats between 0 and 1. Default is False.

Examples

>>> mcp.set_pin_function(gp2 = "DAC")
>>> mcp.DAC_config(ref = "VDD")
>>> mcp.DAC_write(31)
>>>
>>> mcp.DAC_write(32)
Traceback (most recent call last):
...
ValueError: Accepted values for out are from 0 to 31.
DAC_config(ref='VDD', out=None)

Configure Digital to Analog Converter (DAC) reference.

ref values:
  • “OFF”

  • “1.024V”

  • “2.048V”

  • “4.096V”

  • “VDD”

MCP2221’s DAC is 5 bits. So valid values for out are from 0 to 31.

out parameter is optional and defaults last value. Use DAC_write() to set the DAC output value.

Parameters:
  • ref (str, optional) – Reference voltage for DAC. Default to supply voltage (Vdd).

  • out (int, optional) – value to output. Default is last value.

Raises:

ValueError – if ref or out values are not valid.

Examples

>>> mcp.set_pin_function(gp2 = "DAC")
>>> mcp.DAC_config(ref = "4.096V")
>>> mcp.DAC_config(ref = 0)
Traceback (most recent call last):
...
ValueError: Accepted values for ref are 'OFF', '1.024V', '2.048V', '4.096V' and 'VDD'.

Hint

DAC configuration is saved when you call save_config() and reloaded at power-up. You only need to call this function if you want to change it.

Interrupt On Change

IOC_config(edge='both')

Configure Interruption On Change edge.

Valid values for edge:
  • none: disable interrupt detection

  • rising: fire interruption on rising edge (i.e. when GP1 goes from Low to High).

  • falling: fire interruption on falling edge (i.e. when GP1 goes from High to Low).

  • both: fire interruption on both (i.e. when GP1 state changes).

Remember to call save_config() to persist this configuration when reset the chip.

Parameters:

edge (str) – which edge triggers the interruption (see description).

Raises:

ValueError – if edge detection value is not valid.

Example

>>> mcp.IOC_config(edge = "rising")
>>>
IOC_read()

Read Interruption On Change flag.

To enable Interruption Detection mechanism, pin designation must be IOC. See set_pin_function().

Returns:

Value of interrupt flag.

Return type:

int

Example

>>> mcp.IOC_read()
1
IOC_clear()

Clear Interruption On Change flag.

Example

>>> mcp.IOC_read()
1
>>> mcp.IOC_clear()
>>> mcp.IOC_read()
0
>>>

Clock output

clock_config(duty, freq)

Configure clock output frequency and Duty Cycle.

duty values:
  • 0

  • 25

  • 50

  • 75

freq values:
  • “375kHz”

  • “750kHz”

  • “1.5MHz”

  • “3MHz”

  • “6MHz”

  • “12MHz”

  • “24MHz”

To output clock signal, you also need to assign GP1 function to CLK_OUT (see set_pin_function()).

Parameters:
  • duty (int) – Output duty cycle in percent.

  • freq (str) – Output frequency.

Raises:

ValueError – if any of the parameters is not valid.

Examples

>>> mcp.set_pin_function(gp1 = "CLK_OUT")
>>> mcp.clock_config(50, "375kHz")
>>>
>>> mcp.clock_config(100, "375kHz")
Traceback (most recent call last):
...
ValueError: Accepted values for duty are 0, 25, 50, 75.
>>> mcp.clock_config(25, "175kHz")
Traceback (most recent call last):
...
ValueError: Freq is one of 375kHz, 750kHz, 1.5MHz, 3MHz, 6MHz, 12MHz or 24MHz

I2C bus

I2C_Slave(addr, force=False, speed=100000, reg_bytes=1, reg_byteorder='big')

Create a new I2C_Slave object.

See EasyMCP2221.I2C_Slave.I2C_Slave for detailed information.

Parameters:
  • addr (int) – Slave’s I2C bus address

  • force (bool, optional) – Create an I2C_Slave even if the target device does not answer. Default: False.

  • speed (int, optional) – I2C bus speed. Valid values from 50000 to 400000. See EasyMCP2221.Device.I2C_speed().

  • reg_bytes (int, optional) – How many bytes is the register, position or command to send (default 1 byte).

  • reg_byteorder (str, optional) – Byte order of the register address. ‘little’ or ‘big’. Default ‘big’.

Returns:

I2C_Slave object.

Example

>>> pcf    = mcp.I2C_Slave(0x48)
>>> eeprom = mcp.I2C_Slave(0x50, reg_bytes = 2)
>>> eeprom
EasyMCP2221's I2C slave device at bus address 0x50.
I2C_write(addr, data, kind='regular', timeout_ms=20)

Write data to an address on I2C bus.

Valid values for kind are:

regular

It will send start, data, stop (this is the default)

restart

It will send repeated start, data, stop

nonstop

It will send start, data to write, (no stop). Please note that you must use ‘restart’ mode to read or write after a nonstop write.

Parameters:
  • addr (int) – I2C slave device base address.

  • data (bytes) – bytes to write. Maximum length is 65535 bytes, minimum is 1.

  • kind (str, optional) – kind of transfer (see description).

  • timeout_ms (int, optional) – maximum time to write data chunk in milliseconds (default 20 ms). Note this time applies for each 60 bytes chunk. The whole write operation may take much longer.

Raises:
  • ValueError – if any parameter is not valid.

  • NotAckError – if the I2C slave didn’t acknowledge.

  • TimeoutError – if the writing timeout is exceeded.

  • LowSDAError – if I2C engine detects the SCL line does not go up (read exception description).

  • LowSCLError – if I2C engine detects the SDA line does not go up (read exception description).

  • RuntimeError – if some other error occurs.

Examples

>>> mcp.I2C_write(0x50, b'This is data')
>>>

Writing data to a non-existent device:

>>> mcp.I2C_write(0x60, b'This is data'))
Traceback (most recent call last):
...
EasyMCP2221.exceptions.NotAckError: Device did not ACK.

Note

MCP2221 writes data in 60-byte chunks.

The default timeout of 20 ms is twice the time required to send 60 bytes at the minimum supported rate (47 kHz).

MCP2221’s internal I2C engine has additional timeout controls.

I2C_read(addr, size=1, kind='regular', timeout_ms=20)

Read data from I2C bus.

Valid values for kind are:

regular

It will send start, data, stop (this is the default)

restart

It will send repeated start, data, stop

Parameters:
  • addr (int) – I2C slave device base address.

  • size (int, optional) – how many bytes to read. Maximum is 65535 bytes. Minimum is 1 byte.

  • kind (str, optional) – kind of transfer (see description).

  • timeout_ms (int, optional) – time to wait for the data in milliseconds (default 20 ms). Note this time applies for each 60 bytes chunk. The whole read operation may take much longer.

Returns:

data read

Return type:

bytes

Raises:
  • ValueError – if any parameter is not valid.

  • NotAckError – if the I2C slave didn’t acknowledge.

  • TimeoutError – if the writing timeout is exceeded.

  • LowSDAError – if I2C engine detects the SCL line does not go up (read exception description).

  • LowSCLError – if I2C engine detects the SDA line does not go up (read exception description).

  • RuntimeError – if some other error occurs.

Examples

>>> mcp.I2C_read(0x50, 12)
b'This is data'

Write then Read without releasing the bus:

>>> mcp.I2C_write(0x50, position, 'nonstop')
>>> mcp.I2C_read(0x50, length, 'restart')
b'En un lugar de la Mancha...'

Hint

You can use I2C_read() with size 1 to check if there is any device listening with that address.

There is a device in 0x50 (EEPROM):

>>> mcp.I2C_read(0x50)
b'1'

No device in 0x60:

>>> mcp.I2C_read(0x60)
Traceback (most recent call last):
...
EasyMCP2221.exceptions.NotAckError: Device did not ACK.

Note

MCP2221 reads data in 60-byte chunks.

The default timeout of 20 ms is twice the time required to receive 60 bytes at the minimum supported rate (47 kHz). If a timeout or other error occurs in the middle of character reading, the I2C may get locked. See LowSDAError.

I2C_speed(speed=100000)

Set I2C bus speed.

Acceptable values are between 47kHz and 400kHz. This is not stored on the flash configuration.

Parameters:

speed (int) – Bus clock frequency in Hz. Default bus speed is 100kHz.

Raises:
  • ValueError – if speed parameter is out of range.

  • RuntimeError – if command failed (I2C engine is busy).

Example

>>> mcp.I2C_speed(100000)
>>>

Note

The recommended values are between 47kHz and 400kHz. Out of this range, the minimum value is 46693, which corresponds to a clock of approximately 46.5kHz. And the maximum is 6000000, that generates about 522kHz clock.

USB wake-up

enable_power_management(enable=True)

Enable or disable USB Power Management options for this device.

Set or clear Remote Wake-up Capability bit. Remember to call save_config() after this function to save the new settings.

Remote wake-up is triggered by Interrupt detection on GP1 (see set_pin_function() and IOC_config()).

When enabled, Power Management Tab is available for this device in the Device Manager (Windows). To wake-up the computer “Allow this device to wake the computer” option must be set in Device Manager.

USB power attributes are only read while USB device enumeration. So reset() (or power supply cycle) is needed in order for changes to take effect.

Parameters:

enable (bool) – Enable or disable Power Management.

Raises:

RuntimeError – If flash read command failed.

Example

>>> mcp.enable_power_management(True)
>>> mcp.save_config()
>>> print(mcp)
...
    "Chip settings": {
        "Power management options": "enabled",
...
>>> mcp.reset()

Device reset

reset()

Reset MCP2221.

Reboot the device and load stored configuration from flash.

This operation do not reset any I2C slave devices.

Note

The host needs to re-enumerate the device after a reset command. There is a 5 seconds timeout to do that.

Low level and debug

read_flash_info(raw=False, human=False)

Read flash data.

Return USB enumeration strings, power-up GPIO settings and internal chip configuration.

Parameters:
  • raw (bool, optional) – If False, return only parsed data (this is the default). If True, return all data unparsed.

  • human (bool, optional) – If False, return variable names untranslated, for API (this is the default). If True, return variable names in readable text.

Returns:

Flash data (parsed or raw)

Return type:

dict

Example

>>> mcp.read_flash_info()
{
    "CHIP_SETTINGS": {
        "adc_ref": "VDD",
        "clk_duty": 50,
        "clk_freq": "12MHz",
        "dac_ref": "VDD",
        "dac_val": 0,
        "ioc": "both",
        "ma": 100,
        "pid": "0x00DD",
        "pwr": "disabled",
        "vid": "0x04D8"
    },
    "GP_SETTINGS": {
        "GP0": {
            "func": "GPIO_IN",
            "outval": 0
        },
        "GP1": {
            "func": "GPIO_IN",
            "outval": 0
        },
        "GP2": {
            "func": "GPIO_IN",
            "outval": 0
        },
        "GP3": {
            "func": "GPIO_IN",
            "outval": 0
        }
    },
    "USB_FACT_SERIAL": "01234567",
    "USB_PRODUCT": "MCP2221 USB-I2C/UART Combo",
    "USB_SERIAL": "0000033333",
    "USB_VENDOR": "Microchip Technology Inc."
}

Hint

When called with human = true parameter, this is the function used to stringfy the object.

revision()

Get the hardware and firmware revision number.

Returns:

Value of mayor and minor revisions of hardware and software.

Return type:

dict

Example

>>> mcp.revision()
{'firmware': {'mayor': 'A', 'minor': '6'},
'hardware': {'mayor': '1', 'minor': '2'}}
SRAM_config(clk_output=None, dac_ref=None, dac_value=None, adc_ref=None, int_conf=None, gp0=None, gp1=None, gp2=None, gp3=None)

Low level SRAM configuration.

Configure Runtime GPIO pins and parameters. All arguments are optional. Apply given settings, preserve the rest.

Parameters:
  • clk_output (int, optional) – settings

  • dac_ref (int, optional) – settings

  • dac_value (int, optional) – settings

  • adc_ref (int, optional) – settings

  • int_conf (int, optional) – settings

  • gp0 (int, optional) – settings

  • gp1 (int, optional) – settings

  • gp2 (int, optional) – settings

  • gp3 (int, optional) – settings

Raises:

RuntimeError – if command failed.

Examples

>>> from EasyMCP2221.Constants import *
>>> mcp.SRAM_config(gp1 = GPIO_FUNC_GPIO | GPIO_DIR_IN)
>>> mcp.SRAM_config(dac_ref = ADC_REF_VRM | ADC_VRM_2048)

Note

Calling this function to change GPIO when DAC is active and DAC reference is not Vdd will create a 2ms gap in DAC output.

send_cmd(buf)

Write a raw USB command to device and get the response.

Write 64 bytes to the HID interface, starting by buf bytes. Then read 64 bytes from HID and return them as a list. In case of failure (USB read/write or command error) it will retry. To prevent this, set cmd_retries to zero.

Parameters:

buf (list of bytes) – Full data to write, including command (64 bytes max).

Returns:

Full response data (64 bytes).

Return type:

list of bytes

Example

>>> from EasyMCP2221.Constants import *
>>> r = mcp.send_cmd([CMD_GET_GPIO_VALUES])
[81, 0, 238, 239, 238, 239, 238, 239, 238, 239, 0, 0, 0, ... 0, 0]

See also

Class variables cmd_retries, debug_messages and trace_packets.

Hint

The response does not wait until the actual command execution is finished. Instead, it is generated right after the device receives the command. So an error response might indicate:

  • the most recent command is not valid

  • the previous command finished with an error condition (case of I2C write).

_i2c_release()

Try to make the I2C bus ready for the next operation.

This is a private method, the API can change without previous notice.

If there is an active transfer, cancel it. Try multiple times.

Determine if the bus is ready monitoring SDA and SCL lines.

Raises:
  • LowSDAError – if SCL line is down (read exception description).

  • LowSCLError – if SDA line is down (read exception description).

  • RuntimeError – if multiple cancel attempts did not work. Undetermined cause.

Note

Calling Cancel command on an uninitialized I2C engine can make it crash in 0x62 status until next reset. This function uses _i2c_status() heuristics to determine if it can issue a Cancel now or not.

_i2c_status()

Return I2C status based on POLL_STATUS_SET_PARAMETERS command.

This is a private method, the API could change without previous notice.

Returns:

Dictionary with I2C internal details.

{
  'rlen' : 65,  <- Value of the requested I2C transfer length
  'txlen': 0,   <- Value of the already transferred (through I2C) number of bytes
  'div'  : 118, <- Current I2C communication speed divider value
  'ack'  : 0,   <- If ACK was received from client value is 0, else 1.
  'st'   : 98,  <- Internal state of I2C status machine
  'scl'  : 1,   <- SCL line value as read from the pin
  'sda'  : 0,   <- SDA line value as read from the pin
  'confused': False,   <- see note
  'initialized': True  <- see note
}

Hint

If your project does not use I2C, you could reuse SCL and SDA as digital inputs. Call this method to get its logic value.

Note

About confused status.

For some reason, ticking SDA line while I2C bus is initialized but idle will cause the next transfer to be bogus. To prevent this, you need to issue a Cancel command before the next read or write command.

Unfortunately, there is no official way to determine that we are in this situation. The only byte that changes when it happens seems to be byte 18, which is not documented.

About initialized status:

Same way, calling cancel when the I2C engine has not been used yet will make it to stall and stay in status 0x62 until next reset.

Unfortunately, there is no official way to determine when it is appropriate to call Cancel and when it’s not. Moreover, MCP2221’s I2C status after a reset is different from MCP2221A’s (the last one clears the last transfer length and the former does not). I found that Cancel fails when byte 21 is 0x00 and works when it is 0x60. This is, again, not documented.

Device.cmd_retries = 1

Times to retry an USB command if it fails.

Type:

int

Device.debug_messages = False

Print debugging messages.

Type:

bool

Device.trace_packets = False

Print all binary commands and responses.

Type:

bool

Exceptions

To capture EasyMCP2221.exceptions you must qualify them as EasyMCP2221.exceptions:

try:
    mcp.I2C_read(0x51, 1)
except EasyMCP2221.exceptions.NotAckError:
    print("No device")
    exit()
except EasyMCP2221.exceptions.LowSCLError:
    print("SCL low")

or import them explicitly:

from EasyMCP2221.exceptions import *

...

try:
    mcp.I2C_read(0x51, 1)
except NotAckError:
    print("No device")
    exit()
except LowSCLError:
    print("SCL low")
exception NotAckError

I2C slave device did not acknowledge last command or data. Possible causes are incorrect I2C address, device missing or busy.

exception TimeoutError

I2C transaction timed out.

Possible causes:
  • I2C bus noise

  • incorrect command, protocol or speed

  • slave device busy (e.g. EEPROM write cycle)

exception LowSCLError

SCL remains low.

SCL should go up when I2C bus is idle.

Possible causes:
  • Missing pull-up resistor or too high.

  • Signal integrity issues due to noise.

  • A slave device is using clock stretching to indicate it is busy.

  • Another device is using the bus.

exception LowSDAError

SDA remains low.

SDA should go up when I2C bus is idle.

Possible causes:
  • Missing pull-up resistor or too high.

  • Signal integrity issues due to noise.

  • An I2C read transfer timed out while slave was sending data, and now the I2C bus is locked-up. Read the Hint.

Hint

About the I2C bus locking-up.

Sometimes, due to a glitch or premature timeout, the master terminates the transfer. But the slave was in the middle of sending a byte. So it is expecting a few more clocks cycles to send the rest of the byte.

Since the master gave up, it will not clock the bus anymore, and so the slave won’t release SDA line. The master, seeing SDA line busy, refuses to initiate any new I2C transfer. If the slave does not implement any timeout (SMB slaves do have it, but I2C ones don’t), the I2C bus is locked-up forever.

MCP2221’s I2C engine cannot solve this problem. You can either manually clock the bus using any GPIO line, or cycle the power supply.