mirror of
https://github.com/esphome/esphome.git
synced 2026-10-18 04:02:54 +00:00
[i2c] Write 16-bit registers without a heap allocation (#20907)
This commit is contained in:
@@ -3,7 +3,6 @@
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include <memory>
|
||||
|
||||
namespace esphome::i2c {
|
||||
|
||||
@@ -59,11 +58,22 @@ bool I2CDevice::read_bytes_16(uint8_t a_register, uint16_t *data, uint8_t len) {
|
||||
}
|
||||
|
||||
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]};
|
||||
for (size_t i = 0; i < len; i++)
|
||||
temp[i] = htoi2cs(data[i]);
|
||||
return write_register(a_register, reinterpret_cast<const uint8_t *>(temp.get()), len * 2) == ERROR_OK;
|
||||
const size_t size = 1 + len * 2;
|
||||
SmallBufferWithHeapFallback<17> buffer_alloc(size); // Register plus up to 8 words
|
||||
uint8_t *buffer = buffer_alloc.get();
|
||||
|
||||
buffer[0] = a_register;
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
const uint16_t word = data[i];
|
||||
buffer[1 + i * 2] = word >> 8;
|
||||
buffer[2 + i * 2] = word;
|
||||
}
|
||||
return this->bus_->write_readv(this->address_, buffer, size, nullptr, 0) == ERROR_OK;
|
||||
}
|
||||
|
||||
bool I2CDevice::write_byte_16(uint8_t a_register, uint16_t data) const {
|
||||
const uint8_t buffer[3] = {a_register, static_cast<uint8_t>(data >> 8), static_cast<uint8_t>(data)};
|
||||
return this->bus_->write_readv(this->address_, buffer, sizeof(buffer), nullptr, 0) == ERROR_OK;
|
||||
}
|
||||
|
||||
I2CRegister &I2CRegister::operator=(uint8_t value) {
|
||||
|
||||
@@ -264,7 +264,7 @@ class I2CDevice {
|
||||
|
||||
bool write_byte(uint8_t a_register, uint8_t data) const { return write_bytes(a_register, &data, 1); }
|
||||
|
||||
bool write_byte_16(uint8_t a_register, uint16_t data) const { return write_bytes_16(a_register, &data, 1); }
|
||||
bool write_byte_16(uint8_t a_register, uint16_t data) const;
|
||||
|
||||
protected:
|
||||
uint8_t address_{0x00}; ///< store the address of the device on the bus
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
|
||||
namespace esphome::i2c::testing {
|
||||
|
||||
class RecordingBus final : public I2CBus {
|
||||
public:
|
||||
std::vector<uint8_t> written;
|
||||
ErrorCode result{ERROR_OK};
|
||||
|
||||
ErrorCode write_readv(uint8_t address, const uint8_t *data, size_t size, uint8_t *out, size_t out_size) override {
|
||||
this->written.assign(data, data + size);
|
||||
return this->result;
|
||||
}
|
||||
};
|
||||
|
||||
class WriteBytes16Test : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
this->device_.set_i2c_bus(&this->bus_);
|
||||
this->device_.set_i2c_address(0x40);
|
||||
}
|
||||
|
||||
RecordingBus bus_;
|
||||
I2CDevice device_;
|
||||
};
|
||||
|
||||
TEST_F(WriteBytes16Test, WritesRegisterThenWordBigEndian) {
|
||||
EXPECT_TRUE(this->device_.write_byte_16(0x12, 0xABCD));
|
||||
EXPECT_EQ(this->bus_.written, (std::vector<uint8_t>{0x12, 0xAB, 0xCD}));
|
||||
}
|
||||
|
||||
TEST_F(WriteBytes16Test, WritesEveryWordBigEndian) {
|
||||
const uint16_t words[3] = {0x0102, 0x0304, 0xFF00};
|
||||
EXPECT_TRUE(this->device_.write_bytes_16(0x7E, words, 3));
|
||||
EXPECT_EQ(this->bus_.written, (std::vector<uint8_t>{0x7E, 0x01, 0x02, 0x03, 0x04, 0xFF, 0x00}));
|
||||
}
|
||||
|
||||
TEST_F(WriteBytes16Test, MoreWordsThanTheStackBufferHolds) {
|
||||
uint16_t words[12];
|
||||
std::vector<uint8_t> expected{0x01};
|
||||
for (uint16_t i = 0; i < 12; i++) {
|
||||
words[i] = 0x1000 + i;
|
||||
expected.push_back(0x10);
|
||||
expected.push_back(i);
|
||||
}
|
||||
EXPECT_TRUE(this->device_.write_bytes_16(0x01, words, 12));
|
||||
EXPECT_EQ(this->bus_.written, expected);
|
||||
}
|
||||
|
||||
TEST_F(WriteBytes16Test, ReportsBusFailure) {
|
||||
this->bus_.result = ERROR_NOT_ACKNOWLEDGED;
|
||||
EXPECT_FALSE(this->device_.write_byte_16(0x12, 0x0001));
|
||||
}
|
||||
|
||||
} // namespace esphome::i2c::testing
|
||||
Reference in New Issue
Block a user