mirror of
https://github.com/esphome/esphome.git
synced 2025-10-01 01:22:20 +01:00
[pca9554] Migrate to CachedGpioExpander to reduce I2C bus usage (#10571)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
@@ -342,7 +342,7 @@ esphome/components/ota/* @esphome/core
|
||||
esphome/components/output/* @esphome/core
|
||||
esphome/components/packet_transport/* @clydebarrow
|
||||
esphome/components/pca6416a/* @Mat931
|
||||
esphome/components/pca9554/* @clydebarrow @hwstar
|
||||
esphome/components/pca9554/* @bdraco @clydebarrow @hwstar
|
||||
esphome/components/pcf85063/* @brogon
|
||||
esphome/components/pcf8563/* @KoenBreeman
|
||||
esphome/components/pi4ioe5v6408/* @jesserockz
|
||||
|
@@ -11,7 +11,8 @@ from esphome.const import (
|
||||
CONF_OUTPUT,
|
||||
)
|
||||
|
||||
CODEOWNERS = ["@hwstar", "@clydebarrow"]
|
||||
CODEOWNERS = ["@hwstar", "@clydebarrow", "@bdraco"]
|
||||
AUTO_LOAD = ["gpio_expander"]
|
||||
DEPENDENCIES = ["i2c"]
|
||||
MULTI_CONF = True
|
||||
CONF_PIN_COUNT = "pin_count"
|
||||
|
@@ -37,10 +37,9 @@ void PCA9554Component::setup() {
|
||||
}
|
||||
|
||||
void PCA9554Component::loop() {
|
||||
// The read_inputs_() method will cache the input values from the chip.
|
||||
this->read_inputs_();
|
||||
// Clear all the previously read flags.
|
||||
this->was_previously_read_ = 0x00;
|
||||
// Invalidate the cache at the start of each loop.
|
||||
// The actual read will happen on demand when digital_read() is called
|
||||
this->reset_pin_cache_();
|
||||
}
|
||||
|
||||
void PCA9554Component::dump_config() {
|
||||
@@ -54,21 +53,17 @@ void PCA9554Component::dump_config() {
|
||||
}
|
||||
}
|
||||
|
||||
bool PCA9554Component::digital_read(uint8_t pin) {
|
||||
// Note: We want to try and avoid doing any I2C bus read transactions here
|
||||
// to conserve I2C bus bandwidth. So what we do is check to see if we
|
||||
// have seen a read during the time esphome is running this loop. If we have,
|
||||
// we do an I2C bus transaction to get the latest value. If we haven't
|
||||
// we return a cached value which was read at the time loop() was called.
|
||||
if (this->was_previously_read_ & (1 << pin))
|
||||
this->read_inputs_(); // Force a read of a new value
|
||||
// Indicate we saw a read request for this pin in case a
|
||||
// read happens later in the same loop.
|
||||
this->was_previously_read_ |= (1 << pin);
|
||||
bool PCA9554Component::digital_read_hw(uint8_t pin) {
|
||||
// Read all pins from hardware into input_mask_
|
||||
return this->read_inputs_(); // Return true if I2C read succeeded, false on error
|
||||
}
|
||||
|
||||
bool PCA9554Component::digital_read_cache(uint8_t pin) {
|
||||
// Return the cached pin state from input_mask_
|
||||
return this->input_mask_ & (1 << pin);
|
||||
}
|
||||
|
||||
void PCA9554Component::digital_write(uint8_t pin, bool value) {
|
||||
void PCA9554Component::digital_write_hw(uint8_t pin, bool value) {
|
||||
if (value) {
|
||||
this->output_mask_ |= (1 << pin);
|
||||
} else {
|
||||
@@ -127,8 +122,7 @@ bool PCA9554Component::write_register_(uint8_t reg, uint16_t value) {
|
||||
|
||||
float PCA9554Component::get_setup_priority() const { return setup_priority::IO; }
|
||||
|
||||
// Run our loop() method very early in the loop, so that we cache read values before
|
||||
// before other components call our digital_read() method.
|
||||
// Run our loop() method early to invalidate cache before any other components access the pins
|
||||
float PCA9554Component::get_loop_priority() const { return 9.0f; } // Just after WIFI
|
||||
|
||||
void PCA9554GPIOPin::setup() { pin_mode(flags_); }
|
||||
|
@@ -3,22 +3,21 @@
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
#include "esphome/components/gpio_expander/cached_gpio.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace pca9554 {
|
||||
|
||||
class PCA9554Component : public Component, public i2c::I2CDevice {
|
||||
class PCA9554Component : public Component,
|
||||
public i2c::I2CDevice,
|
||||
public gpio_expander::CachedGpioExpander<uint16_t, 16> {
|
||||
public:
|
||||
PCA9554Component() = default;
|
||||
|
||||
/// Check i2c availability and setup masks
|
||||
void setup() override;
|
||||
/// Poll for input changes periodically
|
||||
/// Invalidate cache at start of each loop
|
||||
void loop() override;
|
||||
/// Helper function to read the value of a pin.
|
||||
bool digital_read(uint8_t pin);
|
||||
/// Helper function to write the value of a pin.
|
||||
void digital_write(uint8_t pin, bool value);
|
||||
/// Helper function to set the pin mode of a pin.
|
||||
void pin_mode(uint8_t pin, gpio::Flags flags);
|
||||
|
||||
@@ -32,9 +31,13 @@ class PCA9554Component : public Component, public i2c::I2CDevice {
|
||||
|
||||
protected:
|
||||
bool read_inputs_();
|
||||
|
||||
bool write_register_(uint8_t reg, uint16_t value);
|
||||
|
||||
// Virtual methods from CachedGpioExpander
|
||||
bool digital_read_hw(uint8_t pin) override;
|
||||
bool digital_read_cache(uint8_t pin) override;
|
||||
void digital_write_hw(uint8_t pin, bool value) override;
|
||||
|
||||
/// number of bits the expander has
|
||||
size_t pin_count_{8};
|
||||
/// width of registers
|
||||
@@ -45,8 +48,6 @@ class PCA9554Component : public Component, public i2c::I2CDevice {
|
||||
uint16_t output_mask_{0x00};
|
||||
/// The state of the actual input pin states - 1 means HIGH, 0 means LOW
|
||||
uint16_t input_mask_{0x00};
|
||||
/// Flags to check if read previously during this loop
|
||||
uint16_t was_previously_read_ = {0x00};
|
||||
/// Storage for last I2C error seen
|
||||
esphome::i2c::ErrorCode last_error_;
|
||||
};
|
||||
|
Reference in New Issue
Block a user