mirror of
https://github.com/esphome/esphome.git
synced 2026-08-18 03:59:08 +08:00
[st7123] add ST7123 touch controller component (M5Stack Tab5) (#12075)
Co-authored-by: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com>
This commit is contained in:
co-authored by
Clyde Stubbs
parent
06c7ac37d1
commit
792dfbcbbf
@@ -501,6 +501,7 @@ esphome/components/ssd1331_base/* @kbx81
|
||||
esphome/components/ssd1331_spi/* @kbx81
|
||||
esphome/components/ssd1351_base/* @kbx81
|
||||
esphome/components/ssd1351_spi/* @kbx81
|
||||
esphome/components/st7123/* @miniskipper
|
||||
esphome/components/st7567_base/* @latonita
|
||||
esphome/components/st7567_i2c/* @latonita
|
||||
esphome/components/st7567_spi/* @latonita
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
import esphome.codegen as cg
|
||||
|
||||
CODEOWNERS = ["@miniskipper"]
|
||||
DEPENDENCIES = ["i2c"]
|
||||
|
||||
st7123_ns = cg.esphome_ns.namespace("st7123")
|
||||
@@ -0,0 +1,32 @@
|
||||
from esphome import pins
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import i2c, touchscreen
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ID, CONF_INTERRUPT_PIN, CONF_RESET_PIN
|
||||
|
||||
from .. import st7123_ns
|
||||
|
||||
ST7123Touchscreen = st7123_ns.class_(
|
||||
"ST7123Touchscreen",
|
||||
touchscreen.Touchscreen,
|
||||
i2c.I2CDevice,
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = touchscreen.TOUCHSCREEN_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(ST7123Touchscreen),
|
||||
cv.Optional(CONF_INTERRUPT_PIN): pins.internal_gpio_input_pin_schema,
|
||||
cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema,
|
||||
}
|
||||
).extend(i2c.i2c_device_schema(0x55))
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await touchscreen.register_touchscreen(var, config)
|
||||
await i2c.register_i2c_device(var, config)
|
||||
|
||||
if interrupt_pin := config.get(CONF_INTERRUPT_PIN):
|
||||
cg.add(var.set_interrupt_pin(await cg.gpio_pin_expression(interrupt_pin)))
|
||||
if reset_pin := config.get(CONF_RESET_PIN):
|
||||
cg.add(var.set_reset_pin(await cg.gpio_pin_expression(reset_pin)))
|
||||
@@ -0,0 +1,108 @@
|
||||
#include "st7123_touchscreen.h"
|
||||
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome::st7123 {
|
||||
|
||||
static const char *const TAG = "st7123.touchscreen";
|
||||
|
||||
void ST7123Touchscreen::setup() {
|
||||
if (this->reset_pin_ != nullptr) {
|
||||
this->reset_pin_->setup();
|
||||
this->reset_pin_->digital_write(true);
|
||||
delay(5);
|
||||
this->reset_pin_->digital_write(false); // TP_RESX is active low, assert for at least tRSTW (2ms)
|
||||
delay(5);
|
||||
this->reset_pin_->digital_write(true);
|
||||
// The controller needs up to 20ms to initialize after reset before it can be accessed.
|
||||
this->setup_time_ = millis() + 30;
|
||||
}
|
||||
}
|
||||
|
||||
void ST7123Touchscreen::update() {
|
||||
// check if setup is complete
|
||||
if (this->setup_time_ != 0) {
|
||||
if (this->setup_time_ > millis())
|
||||
return;
|
||||
|
||||
uint8_t status;
|
||||
if (this->read_register16(ST7123_REG_STATUS, &status, 1) != i2c::ERROR_OK) {
|
||||
this->mark_failed(LOG_STR("Failed to read status register")); // will stop updates
|
||||
return;
|
||||
}
|
||||
if ((status & 0x0F) == ST7123_STATUS_INIT) {
|
||||
ESP_LOGD(TAG, "Controller still initializing");
|
||||
return;
|
||||
}
|
||||
if (this->interrupt_pin_ != nullptr) {
|
||||
this->interrupt_pin_->setup();
|
||||
// INT is held high when idle and pulses low when touch data is ready.
|
||||
this->attach_interrupt_(this->interrupt_pin_, gpio::INTERRUPT_FALLING_EDGE);
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "Status is %X", status);
|
||||
|
||||
uint8_t data;
|
||||
if (this->read_register16(ST7123_REG_MAX_TOUCHES, &data, 1) == i2c::ERROR_OK && data != 0 &&
|
||||
data <= ST7123_MAX_TOUCHES) {
|
||||
this->max_touches_ = data;
|
||||
}
|
||||
|
||||
// If no calibration was supplied, read the native coordinate resolution from the controller.
|
||||
if (this->x_raw_max_ == this->x_raw_min_ || this->y_raw_max_ == this->y_raw_min_) {
|
||||
uint8_t res[4];
|
||||
if (this->read_register16(ST7123_REG_MAX_X, res, sizeof(res)) == i2c::ERROR_OK) {
|
||||
this->x_raw_max_ = encode_uint16(res[0] & ST7123_COORD_HIGH_MASK, res[1]);
|
||||
this->y_raw_max_ = encode_uint16(res[2] & ST7123_COORD_HIGH_MASK, res[3]);
|
||||
if (this->swap_x_y_)
|
||||
std::swap(this->x_raw_max_, this->y_raw_max_);
|
||||
} else {
|
||||
this->mark_failed(LOG_STR("Failed to read calibration"));
|
||||
return;
|
||||
}
|
||||
ESP_LOGD(TAG, "Read dimensions %d/%d", this->x_raw_max_, this->y_raw_max_);
|
||||
}
|
||||
this->setup_time_ = 0; // flag setup complete
|
||||
}
|
||||
Touchscreen::update();
|
||||
}
|
||||
|
||||
void ST7123Touchscreen::update_touches() {
|
||||
// Read the reporting table from the advanced touch info register through the last touch point.
|
||||
// Reading from this register also clears the INT pin so the controller can report the next frame.
|
||||
uint8_t data[(ST7123_REG_TOUCH_DATA - ST7123_REG_ADV_TOUCH_INFO) + ST7123_MAX_TOUCHES * ST7123_TOUCH_STRIDE];
|
||||
const size_t len = (ST7123_REG_TOUCH_DATA - ST7123_REG_ADV_TOUCH_INFO) + this->max_touches_ * ST7123_TOUCH_STRIDE;
|
||||
if (this->read_register16(ST7123_REG_ADV_TOUCH_INFO, data, len) != i2c::ERROR_OK) {
|
||||
this->skip_update_ = true;
|
||||
this->status_set_warning();
|
||||
return;
|
||||
}
|
||||
this->status_clear_warning();
|
||||
|
||||
const uint8_t *points = data + (ST7123_REG_TOUCH_DATA - ST7123_REG_ADV_TOUCH_INFO);
|
||||
for (uint8_t i = 0; i != this->max_touches_; i++) {
|
||||
const uint8_t *p = points + i * ST7123_TOUCH_STRIDE;
|
||||
if ((p[0] & ST7123_TOUCH_VALID) == 0)
|
||||
continue;
|
||||
uint16_t x = encode_uint16(p[0] & ST7123_COORD_HIGH_MASK, p[1]);
|
||||
uint16_t y = encode_uint16(p[2] & ST7123_COORD_HIGH_MASK, p[3]);
|
||||
uint8_t intensity = p[5];
|
||||
ESP_LOGV(TAG, "Touch %u: x=%u, y=%u, intensity=%u", i, x, y, intensity);
|
||||
this->add_raw_touch_position_(i, x, y, intensity);
|
||||
}
|
||||
}
|
||||
|
||||
void ST7123Touchscreen::dump_config() {
|
||||
ESP_LOGCONFIG(TAG,
|
||||
"ST7123 Touchscreen:\n"
|
||||
" Max touches: %u\n"
|
||||
" X Raw Min: %d, X Raw Max: %d\n"
|
||||
" Y Raw Min: %d, Y Raw Max: %d",
|
||||
this->max_touches_, this->x_raw_min_, this->x_raw_max_, this->y_raw_min_, this->y_raw_max_);
|
||||
LOG_I2C_DEVICE(this);
|
||||
LOG_PIN(" Interrupt Pin: ", this->interrupt_pin_);
|
||||
LOG_PIN(" Reset Pin: ", this->reset_pin_);
|
||||
}
|
||||
|
||||
} // namespace esphome::st7123
|
||||
@@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
#include "esphome/components/touchscreen/touchscreen.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/hal.h"
|
||||
|
||||
namespace esphome::st7123 {
|
||||
|
||||
// Sitronix ST7123 capacitive touch controller.
|
||||
// Registers are addressed with a 16-bit big-endian address (sent MSB first).
|
||||
static constexpr uint16_t ST7123_REG_STATUS = 0x0001; // [7:4] error code, [3:0] device status
|
||||
static constexpr uint16_t ST7123_REG_MAX_X = 0x0005; // 0x0005..0x0006 X resolution, 0x0007..0x0008 Y resolution
|
||||
static constexpr uint16_t ST7123_REG_MAX_TOUCHES = 0x0009;
|
||||
static constexpr uint16_t ST7123_REG_ADV_TOUCH_INFO = 0x0010; // start of the reporting table
|
||||
static constexpr uint16_t ST7123_REG_TOUCH_DATA = 0x0014; // first touch point
|
||||
|
||||
// Device status field of the status register.
|
||||
static constexpr uint8_t ST7123_STATUS_INIT = 0x1;
|
||||
|
||||
// Each touch point occupies 7 bytes: X high, X low, Y high, Y low, area, intensity, reserved.
|
||||
static constexpr uint8_t ST7123_TOUCH_STRIDE = 7;
|
||||
// Bit 7 of the X high byte indicates a valid touch point.
|
||||
static constexpr uint8_t ST7123_TOUCH_VALID = 0x80;
|
||||
// The X and Y high bytes only use the low 6 bits.
|
||||
static constexpr uint8_t ST7123_COORD_HIGH_MASK = 0x3F;
|
||||
// The ST7123 can report at most 10 touch points.
|
||||
static constexpr uint8_t ST7123_MAX_TOUCHES = 10;
|
||||
|
||||
class ST7123Touchscreen : public touchscreen::Touchscreen, public i2c::I2CDevice {
|
||||
public:
|
||||
void setup() override;
|
||||
void update() override;
|
||||
void dump_config() override;
|
||||
|
||||
void set_interrupt_pin(InternalGPIOPin *pin) { this->interrupt_pin_ = pin; }
|
||||
void set_reset_pin(GPIOPin *pin) { this->reset_pin_ = pin; }
|
||||
|
||||
protected:
|
||||
void update_touches() override;
|
||||
|
||||
InternalGPIOPin *interrupt_pin_{nullptr};
|
||||
GPIOPin *reset_pin_{nullptr};
|
||||
uint8_t max_touches_{ST7123_MAX_TOUCHES};
|
||||
uint32_t setup_time_{1};
|
||||
};
|
||||
|
||||
} // namespace esphome::st7123
|
||||
@@ -0,0 +1,18 @@
|
||||
display:
|
||||
- platform: ssd1306_i2c
|
||||
i2c_id: i2c_bus
|
||||
id: st7123_ssd1306_i2c_display
|
||||
model: SSD1306_128X64
|
||||
reset_pin: ${display_reset_pin}
|
||||
pages:
|
||||
- id: st7123_page1
|
||||
lambda: |-
|
||||
it.rectangle(0, 0, it.get_width(), it.get_height());
|
||||
|
||||
touchscreen:
|
||||
- platform: st7123
|
||||
i2c_id: i2c_bus
|
||||
id: st7123_touchscreen
|
||||
display: st7123_ssd1306_i2c_display
|
||||
interrupt_pin: ${interrupt_pin}
|
||||
reset_pin: ${reset_pin}
|
||||
@@ -0,0 +1,9 @@
|
||||
substitutions:
|
||||
display_reset_pin: "10"
|
||||
interrupt_pin: "20"
|
||||
reset_pin: "21"
|
||||
|
||||
packages:
|
||||
i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml
|
||||
|
||||
<<: !include common.yaml
|
||||
Reference in New Issue
Block a user