Code Monkey home page Code Monkey logo

micropython-mcp23017's Introduction

MicroPython MCP23017 16-bit I/O Expander

A MicroPython library for the MCP23017 16-bit I/O Expander with I2C Interface.

demo

Examples

from machine import Pin, I2C
import mcp23017
i2c = I2C(scl=Pin(22), sda=Pin(21))
mcp = mcp23017.MCP23017(i2c, 0x20)

# list interface
mcp[0].input()
mcp[1].input(pull=1)
mcp[1].value()
mcp[2].output(1)
mcp[3].output(0)

# method interface
mcp.pin(0, mode=1)
mcp.pin(1, mode=1, pullup=True)
mcp.pin(1)
mcp.pin(2, mode=0, value=1)
mcp.pin(3, mode=0, value=0)

mcp.config(interrupt_polarity=0, interrupt_mirror=1)

# property interface 16-bit
mcp.mode = 0xfffe
mcp.gpio = 0x0001

# property interface 8-bit
mcp.porta.mode = 0xfe
mcp.portb.mode = 0xff
mcp.porta.gpio = 0x01
mcp.portb.gpio = 0x02

For more detailed examples, see examples.

Pins

Pin Type Description
A0 I Address select 1, connect to VCC or GND
A1 I Address select 2, connect to VCC or GND
A2 I Address select 3, connect to VCC or GND
INTA O Interrupt output for port A
INTB O Interrupt output for port B
RESET I Reset, active LOW
GPA0 IO Port A, Pin 0
GPA1 IO Port A, Pin 1
GPA2 IO Port A, Pin 2
GPA3 IO Port A, Pin 3
GPA4 IO Port A, Pin 4
GPA5 IO Port A, Pin 5
GPA6 IO Port A, Pin 6
GPA7 IO Port A, Pin 7
GPB0 IO Port B, Pin 0
GPB1 IO Port B, Pin 1
GPB2 IO Port B, Pin 2
GPB3 IO Port B, Pin 3
GPB4 IO Port B, Pin 4
GPB5 IO Port B, Pin 5
GPB6 IO Port B, Pin 6
GPB7 IO Port B, Pin 7
VDD P Power (3V3 or 5V)
VSS P Ground
CS I Not used - SPI Chip Select (CS) on the SPI version (MCP23S17)
SCK I I2C Serial Clock - SPI Serial Clock (SCK) on the SPI version
SI I I2C Serial Data - SPI Serial Data In (MOSI) on the SPI version
SO O Not used - SPI Serial Data Out (MISO) on the SPI version

Methods

init(i2c, address=0x20)

Construct with a reference to I2C and set the device address (0x20-0x27). If are you not sure what it is, run an i2c.scan().

init()

Initialises the device.

config(interrupt_polarity=None, interrupt_open_drain=None, sda_slew=None, sequential_operation=None, interrupt_mirror=None, bank=None)

Configures the device by writing to the iocon register.

pin(pin, mode=None, value=None, pullup=None, polarity=None, interrupt_enable=None, interrupt_compare_default=None, default_value=None)

Method for getting, setting or configuring a single pin. If no value is provided, the port will read the value from the GPIO register and return for the current pin. If a value is provided, the port will write the value to the GPIO register. Valid pin range 0-15. All of the other optional arguments are booleans.

interrupt_triggered_gpio(port)

After an interrupt fires, this method tells you which pin triggered it and clears the interrupt so it can fire again. It's port specific as there are port specific interrupts.

interrupt_captured_gpio(port)

This method tells you the state of the GPIO register at the time the interrupt fired.

_flip_bit(value, condition, bit)

Private method for toggling a bit in a value based on a condition.

getitem(pin)

Provides the list interface for interacting with "virtual pins".

Virtual pin methods

init(pin, port)

Constructed with a specific pin (0-7) and the port it belongs to.

_flip_bit(value, condition)

Private method for toggling a bit in a value based on a condition.

_get_bit(value)

Private method for getting a single bit from the given value based on the current pin

value(val=None)

Reads or writes the current pins value (0-1).

input(pull=None)

Configures the pin as input and optionally configures it to be pulled up.

output(val=None)

Configures the pin to be output and optionally sets it's value (0-1).

Port methods

init(port, mcp)

Constructed with the port number (0-1) and a reference to the main class.

_which_reg(reg)

Private method for calculating which register to write to as their address can change when configured to use different addressing scheme (iocon.bank).

_flip_property_bit(reg, condition, bit)

Private method for toggling a bit in a register based on a condition.

_read(reg)

Private method for reading the register over I2C.

_write(reg, val)

Private method for writing to the register over I2C.

Properties

There are two sets of properties, one on the main class and one on each port class.

Properties on the main class wrap the properties in each port.

You can get and set on the main class properties with a 16-bit integer and it splits it into two 8-bit integers and forwards to each port.

You can also get and set on the ports a and b directly with 8-bit integers.

The registers accessible using the property interface are:

Property Register Type Description
mode iodir R/W Direction 0=output, 1=input
input_polarity ipol R/W Input polarity 0=normal, 1=invert
interrupt_enable gpinten R/W Interrupt enable 0=disabled, 1=enabled
default_value defval R/W Interrupt default value
interrupt_compare_default intcon R/W Interrupt config
io_config iocon R/W IO config
pullup gppu R/W Pull-up
interrupt_flag intf R Interrupt flag 1=this pin triggered interrupt
interrupt_captured intcap R Interrupt captured - state of pins at interrupt
gpio gpio R/W General purpose IO
output_latch olat R/W Output latch - which output pins are high

Getter on the main class reads the GPIO register and returns a 16-bit integer. The lower 8 bits represent port a.

gpio

Getter on the nested port classes reads their GPIO registers and returns 8-bit integers.

porta.gpio
portb.gpio

Setter on the main class writes to the both of the GPIO registers in port a and b. The lower 8 bits represent port a.

gpio = 0xffee

Setter on the nested port classes writes to their GPIO registers.

porta.gpio = 0xee
portb.gpio = 0xff

You can use bitwise assignment operators to toggle specific pins, which performs a read, modify, write.

# set all pins low
porta.gpio = 0
# set the first 4 pins high
porta.gpio |= 0x0f
# set the first 2 pins low
porta.gpio &= ~0x03
# invert the first 4 pins
porta.gpio ^= 0x0f

Ports

Featuring a 16-bit bidirectional I/O port where each pin can be configured as active-high, active-low or open-drain. Polarity can be inverted on any of the pins. Work on both 3V3 or 5V logic.

  • A0-A7
  • B0-B7

Interrupts

Two independent interrupts, one for each 8-bit port, which can be linked/mirrored using interrupt_mirror config, so that any pin change triggers both.

Interrupt can be configured to watch any specific pins and fire on pin change or when the pin differs from the defaults you set.

  • INTA - for pins A0-A7
  • INTB - for pins B0-B7

Once an interrupt fires, you need to read either the interrupt_flag (INTCAP) or gpio (GPIO) registers to clear it.

I2C Interface

There are three address select pins (A0,A1,A2) providing addresses 0x20-0x27 for up to 8 of these devices on the I2C bus.

Requires 10k pull-up resistors on the SCL + SDA lines.

A0 A1 A2 I2C Address
GND GND GND 0x20
3V3 GND GND 0x21
GND 3V3 GND 0x22
3V3 3V3 GND 0x23
GND GND 3V3 0x24
3V3 GND 3V3 0x25
GND 3V3 3V3 0x26
3V3 3V3 3V3 0x27

Parts

Connections

TinyPICO ESP32

from machine import Pin, I2C
import mcp23017
i2c = I2C(scl=Pin(22), sda=Pin(21))
mcp = mcp23017.MCP23017(i2c, 0x20)
MCP23017 TinyPICO (ESP32)
VCC 3V3
GND GND
SCL 22 (SCL)
SDA 21 (SDA)
INTA 4 (Optional)

Wemos D1 Mini (ESP8266)

from machine import Pin, I2C
import mcp23017
i2c = I2C(scl=Pin(5), sda=Pin(4))
mcp = mcp23017.MCP23017(i2c, 0x20)
MCP23017 Wemos D1 Mini (ESP8266)
VCC 3V3
GND GND
SCL D1 (GPIO5)
SDA D2 (GPIO4)
INTA D5 (GPIO14 Optional)

Links

License

Licensed under the MIT License.

Copyright (c) 2019 Mike Causer

micropython-mcp23017's People

Contributors

maveric avatar mcauser avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

micropython-mcp23017's Issues

finding the mcp pin which caused the interrupt

Hi,
I am trying to use the wonderful library that you have provided.

I have connected 1 MCP23017 with esp32, and configured all 16 pins as input.
my requirement is as follows:

I need to need to know which pin of the mcp23017 triggered the input.

My code is as below:
###################################

from machine import Pin, SoftI2C
import mcp23017, time, gc
i2c = SoftI2C(scl=Pin(5), sda=Pin(4), freq=400000, timeout=255)
mcp = mcp23017.MCP23017(i2c, 0x20)

# method interface
mcp.pin(0, mode=1, pullup=0, polarity=0, interrupt_enable=1)
mcp.pin(1, mode=1, pullup=0, polarity=0, interrupt_enable=1)
mcp.pin(2, mode=1, pullup=0, polarity=0, interrupt_enable=1)
mcp.pin(3, mode=1, pullup=0, polarity=0, interrupt_enable=1)
mcp.pin(4, mode=1, pullup=0, polarity=0, interrupt_enable=1)
mcp.pin(5, mode=1, pullup=0, polarity=0, interrupt_enable=1)
mcp.pin(6, mode=1, pullup=0, polarity=0, interrupt_enable=1)
mcp.pin(7, mode=1, pullup=0, polarity=0, interrupt_enable=1)
mcp.pin(8, mode=1, pullup=0, polarity=0, interrupt_enable=1)
mcp.pin(9, mode=1, pullup=0, polarity=0, interrupt_enable=1)
mcp.pin(10, mode=1, pullup=0, polarity=0, interrupt_enable=1)
mcp.pin(11, mode=1, pullup=0, polarity=0, interrupt_enable=1)
mcp.pin(12, mode=1, pullup=0, polarity=0, interrupt_enable=1)
mcp.pin(13, mode=1, pullup=0, polarity=0, interrupt_enable=1)
mcp.pin(14, mode=1, pullup=0, polarity=0, interrupt_enable=1)
mcp.pin(15, mode=1, pullup=0, polarity=0, interrupt_enable=1)

mcp.config(interrupt_polarity=0, interrupt_mirror=1)

# mcp.interrupt_triggered_gpio(port=0)
# mcp.interrupt_captured_gpio(port=0)

# mcp.interrupt_triggered_gpio(port=1)
# mcp.interrupt_captured_gpio(port=1)

p5 = Pin(5, Pin.IN, Pin.PULL_DOWN)
p6 = Pin(6, Pin.IN, Pin.PULL_DOWN)

def int1(pin):

    if Pin(5).value() == 1:
        print ('Int A Triggered')
    elif Pin(5).value() == 0:
        print ('Int A Released')
    gc.collect()
    # print(mcp.porta.interrupt_flag)
    # print(mcp.porta.interrupt_captured)


def int2(pin):

    if Pin(6).value() == 1:
        print ('Int B Triggered')
    elif Pin(6).value() == 0:
        print ('Int B Released')
    gc.collect()

p5.irq(trigger=Pin.IRQ_RISING | Pin.IRQ_FALLING, handler=int1)
p6.irq(trigger=Pin.IRQ_RISING | Pin.IRQ_FALLING, handler=int2)

#################################################

If I uncomment the "print(mcp.porta.interrupt_flag)" i get error as follows:
Traceback (most recent call last):
File "mcptest.py", line 44, in int1
File "mcp23017.py", line 269, in interrupt_triggered_gpio
File "mcp23017.py", line 135, in interrupt_flag
File "mcp23017.py", line 73, in _read
OSError: [Errno 19] ENODEV

Could you kindly help me out in letting me know how to get the pin which generated the interrupt ?

Thanks

Documentation for setting a callback

Hello, I'm fairly new to uPython. I'm using this library for my ESP-32 and MCP23017 expander. Can you provide an example of using INTA/B for callback functions?

OSError: [Errno 5] EIO

Hi Mike! Thank you so much for the great work~!

I attempted to connect Raspberry Pi Pico w to an MCP23017 and encountered the OSError: [Errno 5] EIO.

image

Raspberry Pi Pico was able to find the MCP23017, but the error shows up when creating an MCP23017 class
mcp = mcp23017.MCP23017(i2c,0x20)

could you help me with this issue?

MCP23017 Notworking if pico externally powered

Hello,
I have a really strange situation the MCP23017 library works fine when connected to a USB port but when the Pico is connected to external power it just stops, at the beginning I thought it was an I2C /frequency issue, but I have other connected devices working fine with same multiplexer tca9548a with no errors on external power.
My system is PICO PI connected to a TCA9548A I have 3 LCDs SSD1306 and 3 potentiometers from Pimorony the 7's channel in TCA is connected to MCP23017.
I have stripped the system till only the MCP23017 is connected, my challenge is? , I cannot debug or get any response, until I connect the USB which then the MCP works fine.

I'm using from the MCP23017 one port as an input (no load), I've checked the power usage, and I have no problem.
did anybody get a similar issue? any suggestion?
Kind regards

Random OSError: [Errno 5] EIO

Hi,

I am using this library to connect an MCP23017 to a Raspberry Pi Pico. Randomly, every few minutes, the code crashes with the following error:

Traceback (most recent call last):
File "", line 17, in
File "mcp23017.py", line 263, in pin
File "mcp23017.py", line 144, in gpio
File "mcp23017.py", line 73, in _read
OSError: [Errno 5] EIO

I build a very simple circuit on a protoboard to debug the issue, right now its just a Raspberry Pi Pico and an MCP23017. The code I'm running is also as simple as possible:

from machine import I2C, Pin, RTC, ADC, SPI, UART
from mcp23017 import MCP23017

Set-up the I2C bus.

i2c_bus = I2C(1,
scl = Pin(19),
sda = Pin(18),
freq = 10_000)

Set-up the MCP23017 I/O expander.

IO_expander = MCP23017(i2c_bus, 0x20)

while (True):
button_UP = IO_expander.pin(0)
button_DOWN = IO_expander.pin(1)
button_MODE = IO_expander.pin(2)
button_PULSE = IO_expander.pin(3)
button_DIS_ARM = IO_expander.pin(4)

print(" ")
print(str(button_UP)+" : "+str(button_DOWN)+" : "+str(button_MODE)+" : "+str(button_PULSE)+" : "+str(button_DIS_ARM))

Could you please tell me what might be wrong here? The code works perfectly fine for a few minutes and then crashes at random times.

Thanks!
Best,
Diego

interrupt only on keypress, not keyrelease

Hi,

i want to use your library to read keypresses.
the mcp2307 fires an interrupt when the key is pressed and also when the key is released.

Is there a way around it? I want to have the interrupt only when the key is pressed, not when it is released

install?

hi,
I use a raspberry pi 4 with buster and have a CJMCU-2317 at it.
how i get it installed or what are the requirements? Where can i get an example code to switch on B1 or A1 to learn how it works?

Working with uasyncio

I was able to get this library working with uasyncio using the inputs as buttons with a very simple addition.

Adding:

def __call__(self):
        return self.value()

to the VirtualPin class allows extremely simple addition of inputs / buttons with asyncio - example:

from primitives import Pushbutton
import uasyncio as asyncio
import mcp23017
BLUE_INPUT_PIN = 0

...

mcp.pin(BLUE_INPUT_PIN, mode=1, pullup=1, polarity=1)
blue_button = Pushbutton(self.mcp[BLUE_INPUT_PIN])
blue_button.press_func(None)
asyncio.create_task(self.buttonPressed(self.blue_button.press, 'blue'))

I'm a github noob and don't know how to offer a pull request.

Debounce

How would I sort out switch debounce? I'm using the micropython-async (https://github.com/peterhinch/micropython-async) pushbutton class for buttons directly connected to the STM32F405 but want to expand to many more inputs. The pushbutton class relies on hardware interrupts. Do I need to do debounce in hardware (RC circuit with schmitt trigger output) or is there a way I can debounce inputs off the mcp23017?

Are any more examples available for study?

I am finding the documentation text on this a bit esoteric, especially as concerning the 'virtual ports'. Are any more examples available for study? Thanks and best regards.

pin order on the list interface

Hi @mcauser, this is a great library, thanks. One small thing, it would help if you told the (virtual) pin assignments on the list interface. You just say mcp[0], ... mcp[15] but it's not clear how this aligns with the pins on the 2 ports. I assume the order is A0,..., A7, B0, ... B7 but I'm not sure.
Thanks,

Will this work for MCP23008

Unfortunately doesn't seem to work for MCP23008. Was able to create the mcp object but can't configure a pin for input or output.

AttributeError: 'module' object has no attribute 'MCP23017'

Hi,

I try this library. But i have this error.

>>> from machine import Pin, I2C
>>> import mcp23017
>>> i2c= I2C(scl=pin(5), sda=Pin(4))
>>> mcp = mcp23017.MCP23017(i2c, 0x20)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'module' object has no attribute 'MCP23017'

rotary example

Does the rotary example work properly ?

I do get the switch: 1 when pressed, but when rotating, I get the same thing, it does not update the value, just repeating switch 1

unexpected keyword argument 'pullup'

Getting this error when following the list interface example

>>> mcp[0].input(pullup=1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unexpected keyword argument 'pullup'
>>> 

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.