0

I am currently using a raspberry pi zero, which I connect directly to my computer via usb. I have connected eight MCP23017 GPIO expander chips, which is the maximum amount you can connect to a single i2c bus by changing the chip address with the supplied a0 a1 a2 pins (2^3).

I would like to have access to even more GPIO pins, while also reducing the amount of time it takes the raspberry pi to iteratively set the on-off values of the newly added GPIO pins. Each MCP23017 controls 16 GPIO pins. Currently, accessing and changing the value of a pin on the MCP23017 GPIO expander chip takes around 0.0005-0.0008 seconds, which means that changing the value of 100 pins already takes 0.05+ seconds in total. I would like to avoid connecting more MCP23017 chips to my current setup, where the single raspberry pi is already struggling to effectively manage the 8*16 GPIO pins I want to control. I would also be able to control the timing of when a pin is set with an accuracy of ~0.001s, even when multiple pins are set at once.

One idea I had was to connect multiple arduino nano/ arduino pro micros to my raspberry pi and distribute the GPIO pins across those, so the strain on a single i2c bus is reduced and the accuracy when setting multiple pins at once is improved. So far the only way I have found to do this is by having them read the stdout of my raspberry pi, which might also cause performance problems. I was also thinking about simply using multiple raspberry pi zeros for this purpose, though I am not sure how to connect them all to my computer, which is currently using a single raspberrypi.local address to connect via usb and eliminate possible ping from a wifi connection.

Is this a recommeded approach for this problem? I am using all those GPIO pins to control motor driver chips, which is why I need this setup to be as accurate as possible. What would be the best way to achieve a setup where I am able to control those chips using multiple i2c busses instead of just one? There are also chips that multiply a single i2c bus by eight like the TCA9548A, but I don't know if this is a recommended way to potentially control over 200 GPIO pins in a parallel manner, especially when timing accuracy and parallelization plays a role.

EDIT:

I am controlling the pins on my raspberry pi with a python script as following:

import board
import busio
import adafruit_mcp230xx
from time import sleep

i2c = busio.I2C(board.SCL, board.SDA)
mcp_addresses = [0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27]
mcps = []
for address in mcp_addresses:
    mcps.append(adafruit_mcp230xx.mcp23017.MCP23017(i2c,address = address))  # MCP23017

pins = []
for iter,mcp in enumerate(mcps):
    for i in range(0,15):
        pin = mcp.get_pin(i)
        pins.append(pin)

pins[0].value=True
sleep(0.01)
pins[0].value=False

I am also open to implementing this in c for performance reasons.

11
  • 1
    As a matter of interest what software are you currently using to control the MCP23017?
    – joan
    Commented Sep 1, 2022 at 14:08
  • 3
    "I would also be able to control the timing of when a pin is set with an accuracy of ~0.001s" -> Have you been able to do this with just one GPIO, directly (no expander)? How about just one pin on one expander? If so, you should go into detail about how you are doing this, namely, in what language with what library, and include a snippet of that code. If not, then your question is quite a bit out in front of itself.
    – goldilocks
    Commented Sep 1, 2022 at 14:08
  • @goldilocks I have edited my question accordingly
    – Yes
    Commented Sep 1, 2022 at 15:23
  • 1
    So, that code does nothing to measure precision or timing, meaning my final point was correct: You don't even know if this can be done with one pin. If you cannot do that, then there is no point in asking about how to do it with 200 pins on expanders!
    – goldilocks
    Commented Sep 1, 2022 at 16:50
  • The code you added does not change the pin state, all it seems to do is read the current state into a list and then change the value of the first entry in the list. It does not send that (single) state change back to the pin.
    – joan
    Commented Sep 1, 2022 at 17:24

2 Answers 2

1

I2C is fundamentally slow, as Milliways has explained. There are SPI IO expanders which can achieve much better timing. E.g. here a chip from DIGI which offers 32 IO pins which can be configured as digital inputs/outputs/PWM, accessible over up to 40 MHz SPI channel.

0

The maximum you can get out of a MCP23017 would be similar to the following:-

MCP23017 16-Bit I/O Expander rate test
MCP23017 bit          1620 toggles per second
MCP23017 port         3962 toggles per second
MCP23017 dev          3106 toggles per second

This is based on a c program using the kernel driver with an I²C clock of 400kHz. The I²C clock is the fundamental limit so c or python would give similar results.

Just to be clear the "bit" test is the result of changing a single bit on a 8 bit port (requiring a port read, mask & port write).
The "port" is writing an 8 bit word to a port.
The "dev" is writing an 16 bit dword to the device.

A toggle is 2 writes, so maximum write speed with 16 bit writes would be ~0.167 mS.

4
  • What do you mean by bit, port, dev? Is that the toggle of 1, 8, and 16 bits respectively?
    – joan
    Commented Sep 4, 2022 at 9:41
  • @joan Yes. bit should be obvious; the MCP23017 datasheet mentions "The GPIO module is a general purpose, 16-bit wide, bidirectional port that is functionally split into two 8-bit wide ports" and port is a common usage; I invented dev as a term for the 16-bit wide device ports.
    – Milliways
    Commented Sep 4, 2022 at 11:50
  • I don't understand why you can toggle 1 bit at 1620 per second but you can toggle 16 bits at 3106 per second. Surely that means you can also toggle 1-15 bits at 3106 per second.
    – joan
    Commented Sep 4, 2022 at 12:05
  • @joan the MCP23017 natively transfers data as 8bit words (confusingly called Byte in kernel documentation) but allows 16bit dword transfer (confusingly called Word in kernel documentation) just as the Pi SoC transfers 32 bit. There are no bit operations. To transfer 1 bit requires 2 transactions (a read & write OR a mask write & data write) although internal state storage could speed this up. The fastest transfer is 16 bit in a i2c_smbus_write_word_data
    – Milliways
    Commented Sep 4, 2022 at 12:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.