mirror of
https://github.com/esphome/esphome.git
synced 2025-09-25 14:42:21 +01:00
73 lines
2.9 KiB
C++
73 lines
2.9 KiB
C++
#pragma once
|
|
|
|
#include <array>
|
|
#include <cstdint>
|
|
#include <cstring>
|
|
#include <limits>
|
|
#include "esphome/core/hal.h"
|
|
|
|
namespace esphome::gpio_expander {
|
|
|
|
/// @brief A class to cache the read state of a GPIO expander.
|
|
/// This class caches reads between GPIO Pins which are on the same bank.
|
|
/// This means that for reading whole Port (ex. 8 pins) component needs only one
|
|
/// I2C/SPI read per main loop call. It assumes, that one bit in byte identifies one GPIO pin
|
|
/// Template parameters:
|
|
/// T - Type which represents internal register. Could be uint8_t or uint16_t. Adjust to
|
|
/// match size of your internal GPIO bank register.
|
|
/// N - Number of pins
|
|
template<typename T, uint8_t N> class CachedGpioExpander {
|
|
public:
|
|
/// @brief Read the state of the given pin. This will invalidate the cache for the given pin number.
|
|
/// @param pin Pin number to read
|
|
/// @return Pin state
|
|
bool digital_read(uint8_t pin) {
|
|
const uint8_t bank = pin / BANK_SIZE;
|
|
const T pin_mask = (1 << (pin % BANK_SIZE));
|
|
// Check if specific pin cache is valid
|
|
if (this->read_cache_valid_[bank] & pin_mask) {
|
|
// Invalidate pin
|
|
this->read_cache_valid_[bank] &= ~pin_mask;
|
|
} else {
|
|
// Read whole bank from hardware
|
|
if (!this->digital_read_hw(pin))
|
|
return false;
|
|
// Mark bank cache as valid except the pin that is being returned now
|
|
this->read_cache_valid_[bank] = std::numeric_limits<T>::max() & ~pin_mask;
|
|
}
|
|
return this->digital_read_cache(pin);
|
|
}
|
|
|
|
void digital_write(uint8_t pin, bool value) { this->digital_write_hw(pin, value); }
|
|
|
|
protected:
|
|
/// @brief Read GPIO bank from hardware into internal state
|
|
/// @param pin Pin number (used to determine which bank to read)
|
|
/// @return true if read succeeded, false on communication error
|
|
/// @note This does NOT return the pin state. It returns whether the read operation succeeded.
|
|
/// The actual pin state should be returned by digital_read_cache().
|
|
virtual bool digital_read_hw(uint8_t pin) = 0;
|
|
|
|
/// @brief Get cached pin value from internal state
|
|
/// @param pin Pin number to read
|
|
/// @return Pin state (true = HIGH, false = LOW)
|
|
virtual bool digital_read_cache(uint8_t pin) = 0;
|
|
|
|
/// @brief Write GPIO state to hardware
|
|
/// @param pin Pin number to write
|
|
/// @param value Pin state to write (true = HIGH, false = LOW)
|
|
virtual void digital_write_hw(uint8_t pin, bool value) = 0;
|
|
|
|
/// @brief Invalidate cache. This function should be called in component loop().
|
|
void reset_pin_cache_() { memset(this->read_cache_valid_, 0x00, CACHE_SIZE_BYTES); }
|
|
|
|
static constexpr uint8_t BITS_PER_BYTE = 8;
|
|
static constexpr uint8_t BANK_SIZE = sizeof(T) * BITS_PER_BYTE;
|
|
static constexpr size_t BANKS = N / BANK_SIZE;
|
|
static constexpr size_t CACHE_SIZE_BYTES = BANKS * sizeof(T);
|
|
|
|
T read_cache_valid_[BANKS]{0};
|
|
};
|
|
|
|
} // namespace esphome::gpio_expander
|