78 lines
4.2 KiB
C++
78 lines
4.2 KiB
C++
#pragma once
|
|
// The GT911 of the Elecrow CrowPanel 7.0" V3.0 hangs on a PCA9557 I/O expander (I2C 0x18): IO0 is its reset, IO1 its
|
|
// interrupt line, and it stays in reset until something drives them (firmware 0.2.82). This is Elecrow's own sequence
|
|
// from their V3.0 ESPHome example, byte for byte: every expander pin an output and low (the reset held, the interrupt
|
|
// line low so the chip takes address 0x5D), 20 ms, the reset released, 100 ms for the chip to come up, then the
|
|
// interrupt line back to an input. It runs after the I2C bus is up and before the GT911 driver looks for the chip
|
|
// (setup priority 700, between the bus's 1000 and the driver's 600), so the driver finds it at 0x5D. A V2.0 board
|
|
// has no expander: the writes fail, the log says so, and the GT911 boots by its pull-up as before.
|
|
//
|
|
// probe() and reset() are the touch_probe and touch_reset actions of the board file (firmware 0.2.83): the expander's
|
|
// registers and whether a GT911 answers at 0x5D or 0x14 (its product id, "911"), and the same sequence with values of
|
|
// one's own, for a diagnosis over the API without a new build.
|
|
//
|
|
// Only the Elecrow's board file builds this (`smart_display: elecrow_touch_reset:`, which sets the flag), so the
|
|
// header adds nothing to a CYD, which has no I2C at all.
|
|
#if defined(ESP_SCREENS_ELECROW_TOUCH_RESET) && ESP_SCREENS_ELECROW_TOUCH_RESET
|
|
#include "esphome/components/i2c/i2c.h"
|
|
#include "esphome/core/component.h"
|
|
#include "esphome/core/hal.h"
|
|
#include "esphome/core/log.h"
|
|
|
|
namespace elecrow {
|
|
class TouchReset : public esphome::Component, public esphome::i2c::I2CDevice {
|
|
public:
|
|
float get_setup_priority() const override { return 700.0f; }
|
|
void setup() override { this->reset(0x00, 0x00, 0x01, 20, 100, 0x02); }
|
|
|
|
// Elecrow's sequence with its values: `config` the expander's configuration register during the reset (0 = every
|
|
// pin an output), `low` its output register while the reset is held, `high` after the release, `final_config`
|
|
// afterwards; then the probe.
|
|
void reset(int config, int low, int high, int hold_ms, int wait_ms, int final_config) {
|
|
bool ok = this->write_reg_(0x03, config);
|
|
ok = this->write_reg_(0x01, low) && ok;
|
|
esphome::delay(hold_ms);
|
|
ok = this->write_reg_(0x01, high) && ok;
|
|
esphome::delay(wait_ms);
|
|
ok = this->write_reg_(0x03, final_config) && ok;
|
|
if (ok) {
|
|
ESP_LOGI("touch", "GT911 reset through the PCA9557 at 0x%02X: config 0x%02X, out 0x%02X -> 0x%02X, %d + %d ms, config 0x%02X",
|
|
this->address_, config, low, high, hold_ms, wait_ms, final_config);
|
|
} else {
|
|
ESP_LOGW("touch", "No PCA9557 at 0x%02X: the GT911 was not reset (a V2.0 board boots it by its pull-up)", this->address_);
|
|
}
|
|
this->probe();
|
|
}
|
|
|
|
// The expander's four registers and the GT911's answer at both addresses.
|
|
void probe() {
|
|
uint8_t regs[4] = {0, 0, 0, 0};
|
|
bool ok = true;
|
|
for (uint8_t reg = 0; reg < 4; ++reg) {
|
|
ok = this->write(®, 1) == esphome::i2c::ERROR_OK && this->read(®s[reg], 1) == esphome::i2c::ERROR_OK && ok;
|
|
}
|
|
ESP_LOGI("touch", "PCA9557 at 0x%02X: %s input 0x%02X output 0x%02X polarity 0x%02X config 0x%02X", this->address_,
|
|
ok ? "answers," : "no answer;", regs[0], regs[1], regs[2], regs[3]);
|
|
for (uint8_t address : {0x5D, 0x14}) {
|
|
esphome::i2c::I2CDevice chip;
|
|
chip.set_i2c_bus(this->bus_);
|
|
chip.set_i2c_address(address);
|
|
const uint8_t product_id[2] = {0x81, 0x40};
|
|
uint8_t id[4] = {0, 0, 0, 0};
|
|
const bool written = chip.write(product_id, sizeof(product_id)) == esphome::i2c::ERROR_OK;
|
|
const bool read = written && chip.read(id, sizeof(id)) == esphome::i2c::ERROR_OK;
|
|
ESP_LOGI("touch", "GT911 at 0x%02X: %s%s product id %02X %02X %02X %02X (\"%c%c%c\")", address,
|
|
written ? "write ok, " : "no write ack, ", read ? "read ok," : "no read,", id[0], id[1], id[2], id[3],
|
|
id[0] >= 32 && id[0] < 127 ? id[0] : '.', id[1] >= 32 && id[1] < 127 ? id[1] : '.', id[2] >= 32 && id[2] < 127 ? id[2] : '.');
|
|
}
|
|
}
|
|
|
|
protected:
|
|
bool write_reg_(uint8_t reg, int value) {
|
|
const uint8_t data[2] = {reg, static_cast<uint8_t>(value)};
|
|
return this->write(data, sizeof(data)) == esphome::i2c::ERROR_OK;
|
|
}
|
|
};
|
|
} // namespace elecrow
|
|
#endif
|