ST7123 Touch Controller
The St7123Touch class provides an interface to the capacitive touch controller integrated in the Sitronix ST7123 TDDI (Touch and Display Driver Integration) chip.
The ST7123 combines a MIPI-DSI display driver and a multi-touch capacitive controller in a single IC. This driver accesses the touch side over I2C (default address 0x55).
Note
The ST7123’s touch engine is enabled by the LCD_RST pulse issued during
display initialization — do not toggle the TP_RST line used by
standalone controllers such as the GT911, as this can take the touch I2C
endpoint offline.
St7123Touch satisfies the espp::TouchDriverConcept, so it can be wrapped
in the type-erased espp::ITouchDriver interface via
espp::make_touch_driver(). These shared type-erasure helpers now live in the
Touch Interface component and are reused by every espp touch driver and BSP:
espp::TouchDriverConcept— C++23 concept satisfied by any touch driver exposingupdate(),get_touch_point(), andget_home_button_state().espp::ITouchDriver— abstract type-erased interface backed by the concept.espp::TouchDriverAdapter<T>— concept-constrained adapter wrapping any concrete driver behindITouchDriver.espp::make_touch_driver(driver)— convenience factory returning astd::shared_ptr<ITouchDriver>.
API Reference
Header File
Classes
-
class St7123Touch : public espp::BasePeripheral<std::uint16_t>
Driver for the ST7123 integrated touch controller.
The ST7123 is a TDDI (Touch and Display Driver Integration) chip that includes both a MIPI-DSI display driver and a capacitive multi-touch controller. The touch data is accessed via I2C at the chip’s address (default 0x55).
Touch data reading sequence (based on ST7123 TDDI Interface Protocol): 1. Read 1 byte from register 0x0010 (advanced info). Bit 3 = with_coord. 2. If with_coord is set: a. Read 1 byte from register 0x0009 (max touch count). b. Read (max_touches × 7) bytes from register 0x0014 (touch reports). c. For each 7-byte report: bit 7 of byte[0] = valid flag, x = ((byte[0] & 0x3F) << 8) | byte[1], y = (byte[2] << 8) | byte[3].
Example
// make the I2C that we'll use to communicate espp::I2c i2c({ .port = I2C_NUM_0, .sda_io_num = (gpio_num_t)CONFIG_EXAMPLE_I2C_SDA_GPIO, .scl_io_num = (gpio_num_t)CONFIG_EXAMPLE_I2C_SCL_GPIO, .sda_pullup_en = GPIO_PULLUP_ENABLE, .scl_pullup_en = GPIO_PULLUP_ENABLE, .timeout_ms = 100, .clk_speed = 400 * 1000, }); bool has_st7123 = i2c.probe_device(espp::St7123Touch::DEFAULT_ADDRESS); fmt::print("ST7123 touch probe: {}\n", has_st7123); fmt::print(" address: {:#02x}\n", espp::St7123Touch::DEFAULT_ADDRESS); // Create the ST7123 touch driver. It owns a recursive_mutex and atomics, so // it is non-copyable and non-movable — construct it directly inside the // shared_ptr rather than moving a stack instance into it. auto touch = std::make_shared<espp::St7123Touch>(espp::St7123Touch::Config{ // The ST7123 only holds its register pointer within a single I2C // transaction, so register reads must be a repeated-START // write-then-read rather than a separate write + read. .write_then_read = [&i2c](uint8_t addr, const uint8_t *wdata, size_t wlen, uint8_t *rdata, size_t rlen) -> bool { return i2c.write_read(addr, wdata, wlen, rdata, rlen); }, .log_level = espp::Logger::Verbosity::WARN, }); // Wrap the driver in a concept-erased ITouchDriver pointer. // Any type satisfying espp::TouchDriverConcept can be wrapped this way. auto driver = espp::make_touch_driver(touch); // Poll for touch events using the type-erased interface auto task_fn = [&driver](std::mutex &m, std::condition_variable &cv) { std::error_code ec; bool new_data = driver->update(ec); if (ec) { fmt::print("Could not update state\n"); return false; } if (!new_data) { return false; // don't stop the task } uint8_t num_touch_points = 0; uint16_t x = 0, y = 0; driver->get_touch_point(&num_touch_points, &x, &y); fmt::print("num_touch_points: {}, x: {}, y: {}\n", num_touch_points, x, y); // NOTE: sleeping in this way allows the sleep to exit early when the // task is being stopped / destroyed { std::unique_lock<std::mutex> lk(m); cv.wait_for(lk, 50ms); } return false; // don't stop the task }; auto task = espp::Task({.callback = task_fn, .task_config = {.name = "St7123Touch Task"}, .log_level = espp::Logger::Verbosity::WARN}); task.start();
Note
The ST7123’s touch engine is gated by the LCD reset (LCD_RST) line, NOT the TP_RST line used by standalone touch controllers such as the GT911. When used in a system that has a separate TP_RST signal (e.g. M5Stack Tab5), do NOT toggle TP_RST for this chip — doing so may knock the touch I2C endpoint offline.
Public Types
-
typedef std::function<bool(uint8_t)> probe_fn
Function to probe the peripheral
- Param address:
The address to probe
- Return:
True if the peripheral is found at the given address
Public Functions
-
inline explicit St7123Touch(const Config &config)
Constructor for the St7123Touch driver.
- Parameters:
config – The configuration for the driver
-
inline bool update(std::error_code &ec)
Update the touch state by reading from the ST7123 over I2C.
- Parameters:
ec – Error code to set if an I2C error occurs
- Returns:
True when the read succeeded (regardless of whether a finger is actually touching), false on I2C error
-
inline uint8_t get_num_touch_points() const
Get the number of active touch points.
- Returns:
Touch point count as of the last update() call
-
inline void get_touch_point(uint8_t *num_touch_points, uint16_t *x, uint16_t *y) const
Get the primary touch point coordinates.
Note
The values are cached from the last update() call.
- Parameters:
num_touch_points – Output: number of active touch points
x – Output: X coordinate of the first active touch point
y – Output: Y coordinate of the first active touch point
-
inline bool get_home_button_state() const
Get the home-button state.
- Returns:
Always false — the ST7123 does not expose a home button via I2C
-
inline bool probe(std::error_code &ec) const
Probe the peripheral
Note
This function is thread safe
Note
If the probe function is not set, this function will return false and set the error code to operation_not_supported
Note
This function is only available if UseAddress is true
- Parameters:
ec – The error code to set if there is an error
- Returns:
True if the peripheral is found
-
inline void set_address(uint8_t address)
Set the address of the peripheral
Note
This function is thread safe
Note
This function is only available if UseAddress is true
- Parameters:
address – The address of the peripheral
-
inline void set_probe(const probe_fn &probe)
Set the probe function
Note
This function is thread safe
Note
This should rarely be used, as the probe function is usually set in the constructor. If you need to change the probe function, consider using the set_config function instead.
Note
This function is only available if UseAddress is true
- Parameters:
probe – The probe function
-
inline void set_write(const write_fn &write)
Set the write function
Note
This function is thread safe
Note
This should rarely be used, as the write function is usually set in the constructor. If you need to change the write function, consider using the set_config function instead.
- Parameters:
write – The write function
-
inline void set_read(const read_fn &read)
Set the read function
Note
This function is thread safe
Note
This should rarely be used, as the read function is usually set in the constructor. If you need to change the read function, consider using the set_config function instead.
- Parameters:
read – The read function
-
inline void set_read_register(const read_register_fn &read_register)
Set the read register function
Note
This function is thread safe
Note
This should rarely be used, as the read register function is usually set in the constructor. If you need to change the read register function, consider using the set_config function instead.
- Parameters:
read_register – The read register function
-
inline void set_write_then_read(const write_then_read_fn &write_then_read)
Set the write then read function
Note
This function is thread safe
Note
This should rarely be used, as the write then read function is usually set in the constructor. If you need to change the write then
- Parameters:
write_then_read – The write then read function
-
inline void set_separate_write_then_read_delay(const std::chrono::milliseconds &delay)
Set the delay between the write and read operations in write_then_read
Note
This function is thread safe
Note
This should rarely be used, as the delay is usually set in the constructor. If you need to change the delay, consider using the set_config function instead.
Note
This delay is only used if the write_then_read function is not set to a custom function and the write and read functions are separate functions.
- Parameters:
delay – The delay between the write and read operations in write_then_read
-
inline void set_config(const Config &config)
Set the configuration for the peripheral
Note
This function is thread safe
Note
The configuration should normally be set in the constructor, but this function can be used to change the configuration after the peripheral has been created - for instance if the peripheral could be found on different communications buses.
- Parameters:
config – The configuration for the peripheral
-
inline void set_config(Config &&config)
Set the configuration for the peripheral
Note
This function is thread safe
Note
The configuration should normally be set in the constructor, but this function can be used to change the configuration after the peripheral has been created - for instance if the peripheral could be found on different communications buses.
- Parameters:
config – The configuration for the peripheral
-
inline const Config &config() const
Get the configuration for the peripheral
- Returns:
The configuration for the peripheral
-
inline uint8_t address() const
Get the address of the peripheral
- Returns:
The address of the peripheral
-
inline const std::string &get_name() const
Get the name of the component
Note
This is the tag of the logger
- Returns:
A const reference to the name of the component
-
inline void set_log_tag(const std::string_view &tag)
Set the tag for the logger
- Parameters:
tag – The tag to use for the logger
-
inline espp::Logger::Verbosity get_log_level() const
Get the log level for the logger
See also
See also
- Returns:
The verbosity level of the logger
-
inline void set_log_level(espp::Logger::Verbosity level)
Set the log level for the logger
See also
See also
- Parameters:
level – The verbosity level to use for the logger
-
inline void set_log_verbosity(espp::Logger::Verbosity level)
Set the log verbosity for the logger
See also
See also
See also
Note
This is a convenience method that calls set_log_level
- Parameters:
level – The verbosity level to use for the logger
-
inline espp::Logger::Verbosity get_log_verbosity() const
Get the log verbosity for the logger
See also
See also
See also
Note
This is a convenience method that calls get_log_level
- Returns:
The verbosity level of the logger
-
inline void set_log_rate_limit(std::chrono::duration<float> rate_limit)
Set the rate limit for the logger
See also
Note
Only calls to the logger that have _rate_limit suffix will be rate limited
- Parameters:
rate_limit – The rate limit to use for the logger
Public Static Attributes
-
static constexpr uint8_t DEFAULT_ADDRESS = 0x55
Default I2C address for the ST7123 touch interface.
-
struct Config
Configuration for the St7123Touch driver.
Note
Provide EITHER a combined `write_then_read` (a repeated-START write-then-read, no STOP between the register-pointer write and the data read) OR a separate `write` + `read` pair. If `write_then_read` is set it takes precedence; otherwise the driver writes the register pointer and reads the data as two separate transactions. Some boards are happier with the separate form (e.g. when reads run from an interrupt handler and the longer combined transaction is more prone to I/O errors).
Public Members
-
BasePeripheral::write_fn write
Write function (paired with `read` for separate reads)
-
BasePeripheral::read_fn read
Read function (paired with `write` for separate reads)
-
BasePeripheral::write_then_read_fn write_then_read
Combined (repeated-START) write-then-read; takes precedence if set.
-
uint8_t address = DEFAULT_ADDRESS
I2C address of the chip.
-
BasePeripheral::write_fn write
-
typedef std::function<bool(uint8_t)> probe_fn