mirror of
https://github.com/esphome/esphome.git
synced 2026-10-20 04:56:48 +00:00
[i2c] Add I2CDevice::update_register for read-modify-write of register bits
This commit is contained in:
@@ -58,6 +58,13 @@ bool I2CDevice::read_bytes_16(uint8_t a_register, uint16_t *data, uint8_t len) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool I2CDevice::update_register(uint8_t a_register, uint8_t mask, uint8_t value) {
|
||||
uint8_t data;
|
||||
if (!this->read_byte(a_register, &data))
|
||||
return false;
|
||||
return this->write_byte(a_register, (data & ~mask) | (value & mask));
|
||||
}
|
||||
|
||||
bool I2CDevice::write_bytes_16(uint8_t a_register, const uint16_t *data, uint8_t len) const {
|
||||
// we have to copy in order to be able to change byte order
|
||||
std::unique_ptr<uint16_t[]> temp{new uint16_t[len]};
|
||||
|
||||
@@ -266,6 +266,11 @@ class I2CDevice {
|
||||
|
||||
bool write_byte_16(uint8_t a_register, uint16_t data) const { return write_bytes_16(a_register, &data, 1); }
|
||||
|
||||
/// Read a register, replace the bits in @p mask with those of @p value and write it back.
|
||||
/// The write always happens; bits of @p value outside @p mask are ignored.
|
||||
/// @return false if the read or the write failed (nothing is written after a failed read)
|
||||
bool update_register(uint8_t a_register, uint8_t mask, uint8_t value);
|
||||
|
||||
protected:
|
||||
uint8_t address_{0x00}; ///< store the address of the device on the bus
|
||||
I2CBus *bus_{nullptr}; ///< pointer to I2CBus instance
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <array>
|
||||
#include <vector>
|
||||
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
|
||||
namespace esphome::i2c::testing {
|
||||
|
||||
static constexpr uint8_t ADDRESS = 0x40;
|
||||
static constexpr uint8_t REG = 0x12;
|
||||
|
||||
class RegisterBus final : public I2CBus {
|
||||
public:
|
||||
std::array<uint8_t, 256> regs{};
|
||||
std::vector<uint8_t> writes;
|
||||
bool fail_read{false};
|
||||
bool fail_write{false};
|
||||
|
||||
ErrorCode write_readv(uint8_t address, const uint8_t *out, size_t out_size, uint8_t *in, size_t in_size) override {
|
||||
EXPECT_EQ(address, ADDRESS);
|
||||
if (out_size == 0)
|
||||
return ERROR_TIMEOUT;
|
||||
const uint8_t reg = out[0];
|
||||
if (in_size > 0) {
|
||||
if (this->fail_read)
|
||||
return ERROR_TIMEOUT;
|
||||
for (size_t i = 0; i < in_size; i++)
|
||||
in[i] = this->regs[reg + i];
|
||||
return ERROR_OK;
|
||||
}
|
||||
if (this->fail_write)
|
||||
return ERROR_TIMEOUT;
|
||||
this->writes.push_back(reg);
|
||||
for (size_t i = 1; i < out_size; i++)
|
||||
this->regs[reg + i - 1] = out[i];
|
||||
return ERROR_OK;
|
||||
}
|
||||
};
|
||||
|
||||
class UpdateRegisterTest : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
this->device_.set_i2c_bus(&this->bus_);
|
||||
this->device_.set_i2c_address(ADDRESS);
|
||||
}
|
||||
|
||||
RegisterBus bus_;
|
||||
I2CDevice device_;
|
||||
};
|
||||
|
||||
TEST_F(UpdateRegisterTest, ReplacesOnlyMaskedBits) {
|
||||
this->bus_.regs[REG] = 0b1010'0101;
|
||||
EXPECT_TRUE(this->device_.update_register(REG, 0b0000'1111, 0b0000'0011));
|
||||
EXPECT_EQ(this->bus_.regs[REG], 0b1010'0011);
|
||||
}
|
||||
|
||||
TEST_F(UpdateRegisterTest, IgnoresValueBitsOutsideMask) {
|
||||
this->bus_.regs[REG] = 0x00;
|
||||
EXPECT_TRUE(this->device_.update_register(REG, 0x0F, 0xFF));
|
||||
EXPECT_EQ(this->bus_.regs[REG], 0x0F);
|
||||
}
|
||||
|
||||
TEST_F(UpdateRegisterTest, WritesEvenWhenUnchanged) {
|
||||
this->bus_.regs[REG] = 0x3C;
|
||||
EXPECT_TRUE(this->device_.update_register(REG, 0x0C, 0x0C));
|
||||
EXPECT_EQ(this->bus_.regs[REG], 0x3C);
|
||||
EXPECT_EQ(this->bus_.writes, std::vector<uint8_t>{REG});
|
||||
}
|
||||
|
||||
TEST_F(UpdateRegisterTest, FailedReadWritesNothing) {
|
||||
this->bus_.fail_read = true;
|
||||
EXPECT_FALSE(this->device_.update_register(REG, 0xFF, 0x01));
|
||||
EXPECT_TRUE(this->bus_.writes.empty());
|
||||
}
|
||||
|
||||
TEST_F(UpdateRegisterTest, FailedWriteReturnsFalse) {
|
||||
this->bus_.fail_write = true;
|
||||
EXPECT_FALSE(this->device_.update_register(REG, 0xFF, 0x01));
|
||||
}
|
||||
|
||||
} // namespace esphome::i2c::testing
|
||||
Reference in New Issue
Block a user