-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Support for Pimoroni board with IO Expanders #7440
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
For your use case, is it necessary that these appear as genuine I would say that the CYW43 pin stuff was done the way it was due to the idiosyncrasies of these pass-through pins and their use cases. I am not sure we would want to expand on that style for I/O expanders in general. If we wanted a more general solution, generalizing either We have had numerous requests for "Pin-alike" or "DigitalInOut-alike" implementations, for things like the MC23017, shift registers, etc. I'm sure @jepler and @tannewt have things to say about this as well. |
Thanks for the quick reply. The main thing that took me down this path was that a few of the expander pins are intended for (slowly) controlling the secondary lines of SPI displays (CS, DC). I managed to cobble some python code together for testing, but had to do it without the usual SPI display classes as they require Pin/DigitalIO objects. These classes could be hacked to support an IOExpander object I suppose, but having them accept a pin as normal seemed far easier and cleaner for the user. There's also the case that one of the pins should be initialised and reset at the C level, for safely, rather than relying on user code, so I would be needing to have some form of C driver to talk to the expanders anyway. The rest of the pins, although it would be nice if they were usable with DigitalIO to simplify the user's understanding, is not strictly required. Perhaps there is even a performance benefit to not doing it this way as pins could then be set and cleared in single Word operations if the user required 🤔 |
I'm ok adding additional pin types like the CYW43 code does. This makes them available to CP when the VM may not be running. If its only needed by user code in the VM, then I'd push more to make it Python-only. I wouldn't add it to |
I've progressed down the CYW43 Pin approach, and am honestly rather happy with the user experience! Having the pins for LEDs and buttons just being accessed using Newest code added to this PR if anyone wants to look. I also tried a technique in Because of both these things, for my use-case I don't think it's even necessary to have an IO Expander object that's exposed. A few tangential questions:
|
Awesome! Sounds like the right approach.
I don't think you need a separate object if all useful pins are accessible through
digitalio defaults to input, not pulled. When they are reset (not in use by digitalio), then the board can have a reset state so pins don't cause LEDs to glow. Most ports reset to input, not pulled but that isn't actually optimal for power if the input buffer is enabled (the floating pin will waste buffer power.) So, when reset, you should do input with a pull of your choice. (For example, ESP is up except if the board overrides it.
reset should be called between code.py and the REPL. If you open the REPL and then do an import of user code, you are still in the same VM and that code should handle the exception. However, you could also add a finaliser to run before an object is about to be deleted. Generally we have it be the same as
and then:
|
Thanks for the explanation. I understand now what you mean, so have removed the pin defines I added to Here's the latest output from
Thank you for answering my other queries too. I haven't had chance to look over them yet, but will do soon. |
Hi @dhalbert. I just wanted to let you know that I had some merge conflicts come up due to your recent Specifically, I needed to change these 3 functions from the I have added that parameter now, but I did have to change the |
That's a good improvement, thanks. I will add this. |
…on, to speed up some I2C communication
@ZodiusInfuser hi - is this board still in process? |
Hi @dhalbert. Yes it's still very much in progress, just taking its time due to the complexity involved and flip-flopping between other duties: https://twitter.com/ZodiusInfuser/status/1673292599552090112?t=IEEiOgdyxdWWXQ05dlDzRw&s=19 |
…or changing multiple IO outputs at once
not exactly but looking at a commit which adds a new module that is split across shared-modules and shared-bindings may be instructive: f5c637d (there's this guide but it's well out of date: https://learn.adafruit.com/extending-circuitpython/inside-the-virtual-machine)
For diagnosis,
|
Thanks for that! I seem to be getting somewhere now (though I'm not specifically sure which thing fixed it). I'll do some tidying up and try committing soon. On an unrelated note. I noticed this include that seems unnecessary in this file: |
@jepler An attempt was made at restructuring the code. Builds locally and passes the test here, but please let me know if there are structural things I should improve. There's probably some imports I can remove. |
Did a bit more tidy up. I think it's ready for me to convert from a draft, but I'll leave it over the weekend in case I think of any last changes to make. |
Thanks @jepler. Have a good break! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for the reorganization. I think it'll help the changes going forwards.
Overall, I don't want to mimic the CYW implementation and pretend to be a microcontroller pin. It isn't. It is much more limited. Ultimately, that'll confuse folks and riddle the core common-hal implementations with special cases.
Instead, I want to mimic the Python-level IO expander libraries that have a chip class and then *-like
classes for the pin functionality. Then, any native code you want to use it (FourWire, bitbangio
) will need to check its inputs to see if they are a microcontroller pin or DigitalInOut-like object.
I've made some inline comments where I think you can start by creating proper object structs for the expander and its digital io.
Snag me on Discord if you have more questions. Thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd rather not change this file. The CYW bits are a hack I'd rather not propagate further. Instead, let's model it more like the existing Python IO expander code. Here is an example. It has two main classes, one for the chip overall and one "DigitalInOut" to act like one via the IO expander. The tca9555
module would have these two classes.
Then board
can reference the DIO objects. I don't want them to be like Pins because they cannot be used like native pins.
extern bool common_hal_tca_gpio_get_input(uint tca_gpio); | ||
extern bool common_hal_tca_gpio_get_output(uint tca_gpio); | ||
extern bool common_hal_tca_gpio_get_config(uint tca_gpio); | ||
extern bool common_hal_tca_gpio_get_polarity(uint tca_gpio); | ||
|
||
extern void common_hal_tca_gpio_set_output(uint tca_gpio, bool value); | ||
extern void common_hal_tca_gpio_set_config(uint tca_gpio, bool output); | ||
extern void common_hal_tca_gpio_set_polarity(uint tca_gpio, bool polarity); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These should be moved to the DigitalInOut-like object because they are for a single IO.
extern uint16_t common_hal_tca_get_input_port(uint tca_index); | ||
extern uint8_t common_hal_tca_get_input_port_low(uint tca_index); | ||
extern uint8_t common_hal_tca_get_input_port_high(uint tca_index); | ||
|
||
extern uint16_t common_hal_tca_get_output_port(uint tca_index); | ||
extern uint8_t common_hal_tca_get_output_port_low(uint tca_index); | ||
extern uint8_t common_hal_tca_get_output_port_high(uint tca_index); | ||
|
||
extern uint16_t common_hal_tca_get_config_port(uint tca_index); | ||
extern uint8_t common_hal_tca_get_config_port_low(uint tca_index); | ||
extern uint8_t common_hal_tca_get_config_port_high(uint tca_index); | ||
|
||
extern uint16_t common_hal_tca_get_polarity_port(uint tca_index); | ||
extern uint8_t common_hal_tca_get_polarity_port_low(uint tca_index); | ||
extern uint8_t common_hal_tca_get_polarity_port_high(uint tca_index); | ||
|
||
extern void common_hal_tca_set_output_port(uint tca_index, uint16_t output_state); | ||
extern void common_hal_tca_set_output_port_low(uint tca_index, uint8_t output_state); | ||
extern void common_hal_tca_set_output_port_high(uint tca_index, uint8_t output_state); | ||
|
||
extern void common_hal_tca_set_config_port(uint tca_index, uint16_t config_state); | ||
extern void common_hal_tca_set_config_port_low(uint tca_index, uint8_t config_state); | ||
extern void common_hal_tca_set_config_port_high(uint tca_index, uint8_t config_state); | ||
|
||
extern void common_hal_tca_set_polarity_port(uint tca_index, uint16_t polarity_state); | ||
extern void common_hal_tca_set_polarity_port_low(uint tca_index, uint8_t polarity_state); | ||
extern void common_hal_tca_set_polarity_port_high(uint tca_index, uint8_t polarity_state); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All of these should be on a TCA9555 object because tca_index
(I think) is which chip you are talking to. The TCA9555 object should take in the I2C bus it is on and also the device address.
Thanks for your review. It seems we have different philosophies when it comes to the implementation of this IO expander. To me the IO expanders are an implementation limitation of the product, due to the RP2040 not having enough native IO for the functions we needed. As such this PR is geared towards abstracting their existence away, like how was done for CYW. Now, I understand that the CYW support may have been hastily added and thus should not have been used as a template, but the resulting user experience is a good one. I do understand your points though, and that having the expander structured as you describe would be more broadly valuable to the CircuitPython community. Sadly though I don't have the remaining dev time to address this before the product launches, given I still have 2 other languages to finish supporting (MicroPython and C++). This is unfortunate, as I have put most of the product's dev time into the CPy support (as observed by this PR being 9 months old), and even considered having Yukon come pre-flashed with it. That could still happen I suppose, if I provide customers with my custom |
I don't think it's a better user experience when one tries to use the pin for something it isn't meant to work with. It only works with digitalio and therefore should just act like digitalio.
To be fair, this feedback is consistent with what I said in January.
You are welcome to use the CircuitPython code but please do not call it CircuitPython or have CircuitPython as part of the name. We reserve that for code checked into the main repo. That way it gets updated and distributed like all other CircuitPython. I'll chat with ladyada and see if I can spend a bit of time on our qualia board with the io expander. That'd give you a model for how to do it. |
So you're suggesting that rather than having a
And indeed I did follow that, by creating a new TCA pin type. Initially I added them to
Okay, I understand. I will avoid that then.
That would help immensely! Thank you |
Yup! Exactly. Any places where the C code uses digitalinout internally, we should support passing in a digitalinout-like object as well.
Yup, totally ok for
Thank you!
👍 |
Unfortunately, I won't be working on this anytime soon. I'm still happy to advise if you find the time to further modify this code. |
Thank you for letting me know. I'll resist this post launch |
@ZodiusInfuser Hi - is this board still on track for CircuitPython? Is it Yukon? |
Hi Dan. It is indeed Yukon, but sadly we are no longer pursuing CircuitPython for this board, and just sticking to our Pirate-brand of MicroPython. I'll close this PR. |
Hi,
We're working on a new RP2040 board at Pimoroni, that makes use of two IO expander chips (TCA9555) to offer more pins than the typical 30 (there's a good reason for this that'll remain a secret for now). The board is a good while away still, but I wanted raise this draft PR now to start discussions with the CPy team about the direction I'm going in.
Inspired by the support added for the Pico W's LED (#6933), I am hoping I can do similar for these IO expanders and abstract them from the user, so they can focus on the other functions of the board. To this end I have made a copy of the
cyw_pin
(calledtca_pin
for now), duplicated all the relevant code sections that were surrounded by#if CIRCUITPY_CYW43
, and set up the board definition. https://github.com/ZodiusInfuser/circuitpython/blob/yukon/ports/raspberrypi/boards/pimoroni_yukon/pins.cI still need to write a driver for the expanders, so the pin objects are currently non-functional, but they seem to show up correctly when I flash a board and interact with the REPL.
Before I progress further, it would be good to get some thoughts on the approach I'm taking. I'm touching a fair few files more than I normally would to add support for a singular RP2040 product, which may be frowned upon. Also, I am unsure of the best practice for writing/including a custom driver to CircuitPython. Then there's the question of how to support there being 2x expanders without hardcoding.
Thanks 🙂