-
Notifications
You must be signed in to change notification settings - Fork 2k
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
drivers: add PCF857X I2C I/O expander driver #10430
Conversation
@kYc0o Thanks for the hint, I will take a look. |
@gschorcht See also #9054 which needs updating but may have some overlap. I will take a more in-depth look at your PR soon. Regarding compatibility with the extension interface: |
@ZetaR60 Good news, the driver is working with your GPIO extension API without any changes 😄 It is as fast as direct access:
For some suggestions concerning the implementation, I will use your PRs. I will update my PR. As long as module |
@gschorcht Great! I am glad it is working out well on real hardware. I have been looking at the documentation for the ICs supported by this PR and those supported by #9054 and it looks like at least some of the chips are pin-compatible and maybe even register compatible (some of the docs were a bit spotty with the register maps). It seems there is a wide variety of ICs from multiple manufacturers that:
I think that, if possible, we should make a single driver for all of them, with just a few parameters that are changeable depending on the device. I tried to do this with #9054 (see the already long list of devices in |
@ZetaR60 I have already seen your PR #9054, but I didn't realize that all of these I2C I/O expanders are quite similar because their naming is very different.
Although the differences seem to be small, the handling of the devices may be too different to support them with only one driver. The biggest difference between TCA99xx and PCF857x are
Thus, a completely different port handling is required. Since you don't have registers in PCF857x there are no Other differences of the drivers (yours and mine) that I've seen:
So I guess that using one driver for all these different I/O expanders would either require a lot of conditional instructions which produces larger code size and causes less run-time efficiency or a lot of I can offer, if I have time, to change your code to work with PCF857x, just to see if it would be a good idea to merge it at all. |
@ZetaR60 How deep did you test the interrupt handling? I spent a lot of time to test a lot of different cases with single and multiple expanders and had to realize the following problems:
I don't know whether these problems also occur for PCA95xx device, but I would guess yes. |
9393cb9
to
b1c1335
Compare
I missed the register differences when skimming the PCF857x, since it looked like the documentation was just missing. It's weird that they ICs are almost exactly the same in every other way. I agree that they should be a different driver.
I did not test for the sort of problems you mentioned. I do not have a proper break-out for the PCA95xx, and it is a bit awkward to wire TSSOP pins together when probing them. I am glad you caught this! I agree with you expectation that the exact same problems probably exist with PCA95xx.
This seems a bit complex for a workaround. The best solution would be to fix |
@gschorcht Looks like your EDIT: This was a misunderstanding on my part. Still a problem, but not a bug exactly. |
2a4472f
to
00e6f7b
Compare
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. If you want me to ignore this issue, please mark it with the "State: don't stale" label. Thank you for your contributions. |
Rebased onto current master. Compiled and tested again, still works as expected. |
eebcd2c
to
cb7531a
Compare
447db49
to
046c8cb
Compare
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.
Looks good! Just some small nits
drivers/pcf857x/pcf857x.c
Outdated
DEBUG_DEV("pin=%u mode=%u", dev, pin, mode); | ||
|
||
switch (mode) { | ||
#ifndef MCU_CC2538 |
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.
Why special-case CC2538
here?
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.
To fix compilation errors due to this enumeration type 😟
RIOT/cpu/cc2538/include/periph_cpu.h
Lines 150 to 158 in fea525f
typedef enum { | |
GPIO_IN = ((uint8_t)OVERRIDE_DISABLE), /**< input, no pull */ | |
GPIO_IN_ANALOG = ((uint8_t)OVERRIDE_ANALOG), /**< input, analog */ | |
GPIO_IN_PD = ((uint8_t)OVERRIDE_PULLDOWN), /**< input, pull-down */ | |
GPIO_IN_PU = ((uint8_t)OVERRIDE_PULLUP), /**< input, pull-up */ | |
GPIO_OUT = ((uint8_t)OVERRIDE_ENABLE), /**< output */ | |
GPIO_OD = (0xff), /**< not supported */ | |
GPIO_OD_PU = (0xff) /**< not supported */ | |
} gpio_mode_t; |
GPIO_OD
and GPIO_OD_PU
are declared as undefined. I didn't take a look at the implementation to see if these values are used directly as register values. But it wouldn't make any difference, because the special case would still have to be considered.
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.
But is GPIO_OD
the same as GPIO_IN
? Otherwise you can just let this case be handled by default
and not support it.
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.
A bit of reordering to handle everything that is not GPIO_OUT
as default. The question is whether not supported GPIO_IN_OD
should lead to an assert or simply a failure.
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.
As long as gpio_init
returns an error code, you can do so too
dev->out &= ~(1 << pin); /* set output bit to 0 */ | ||
break; | ||
default: dev->modes |= (1 << pin); /* set mode bit to 1 */ | ||
dev->out |= (1 << pin); /* set output bit to 1 */ |
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.
Now this also catches GPIO_IN_PU
and GPIO_OD
/GPIO_OD_PU
, is this correct?
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.
Ups, simultaneous communication.
This I/O expander is a very simple device that doesn't have a direction register. In fact it physically supports only the GPIO modes GPIO_IN_PU
and GPIO_OD_PU
:
- Writing a 1 to an expander pin configures the pin as an input, which is pulled up to HIGH by a weak pull-up. When reading the pin, its value then depends on the actual voltage.
- Writing a 0 to an expander pin configures the pin as an output and actively drives the pin to LOW.
All other modes are only considered logically identical and behave only like GPIO_IN
, GPIO_OD
and GPIO_OUT
.
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.
It's probably worth adding a comment about that - feel free to squash directly
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.
Now this also catches GPIO_IN_PU and GPIO_OD/GPIO_OD_PU, is this correct?
This affects only the initial level of the pin. If GPIO_OUT
is used a 0 is written to the pin which configures the pin as physical output an drives it active LOW. In all other cases a 1 is written to the pin which configures it as physical input an the pin is pulled-up to HIGH by the weak pull-up. It would also be possible to use HIGH as initial level for all modes.
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.
It's probably worth adding a comment about that - feel free to squash directly
It is documented here
RIOT/drivers/include/pcf857x.h
Line 67 in 0b9b8c2
* - INPUT: |
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.
Comment added to source code in commit d02a87f.
Some small changes / clarifications / enhancements in documentation 2ed6c9a. |
Please squash! |
2ed6c9a
to
d5290a2
Compare
d5290a2
to
4d3cc8d
Compare
@benpicco Many thanks for reviewing and merging. |
Contribution description
This PR adds a driver for the widespread PCF857x serial I/O expander modules which extend the number of GPIOs over I2C. The following expander modules are supported by the driver:
pcf8574
pcf8574a
pcf8575
The driver defines the PCF857x expander variants by a pseudomodule for each of supported expander module
The driver interface is kept as compatible as possible with the peripheral GPIO interface. The only difference is that the functions have the prefix
pcf857x_
and require an additional parameter, the pointer to the expansion device of typepcf857x_t
Since PCF857x expander pins are only quasi-bidirectional without having a separate direction control signal, they only support active LOW driven outputs and inputs that are pulled up to HIGH. HIGH outputs are nothing else than pulled-up inputs. The driver maps this behavior to the
GPIO_IN_PU
andGPIO_OD
and additionally to theGPIO_IN
,GPIO_OUT
andGPIO_OD_PU
modes.Optionally, the driver can use the low-active open-drain INT signal of PCF857x expander modules
periph_gpio_irq
pcf857x_gpio_read
, see the benchmarks.The driver supports direct GPIO mapping to SAUL which is compatible to the peripheral GPIO SAUL interface. Thus PCF857X I/O expander pins can be used with SAUL in the same way as peripheral GPIOS.
Update
The driver also supports GPIO extension API as proposed in PR #9582 and implemented in PR #9860 and #9958 .Benchmarks
The performance of the driver strongly depends on using interrupt capabilities or not:
periph_gpio_irq
) @ 400 kHz I2C bus speed:Reference board was an
esp8266-esp-12x
in I2C fast mode.Testing procedure
The test application
tests/driver_pcf857x
can be used to test each PCF857x expander I/O pin with shell commands. Dependent on used PCF857x variant, it has to be compiled with at least one of the pseudomodulespcf8574
,pcf8574a
orpcf8575
, e.g.:Add module
periph_gpio_irq
and override the MCU interrupt pin by parameter PCF857X_PARAM_INT_PIN` to use the PCF857x interrupt signal, e.g.,The number of the first PCF857X expander port used by the test application is defined by the macro
PCF857X_P0
, which is 16 by default.For testing the driver follow the steps:
pcf857x_irq
and suitable interrupt pin as shown above.GPIO_PIN(0,0)
with any pin of the PCF857X expander, e.g. pin 6.Issues/PRs references
UPDATE
The driver is compatible with the GPIO extension API, PR #9582, #9860 and #9958, but it does not depend on it.Compatibility with the GPIO extension API, PR #9582, #9860 and #9958 has been removed for the moment due to the new proposal of an extensible GPIO API in PR#12877#14602. The driver will be adapted later for this GPIO API.