Touch with Waveshare CST816s driver on Zephyr not swiping properly

Description

I am having touch issues with Waveshare 1.69inch Touch LCD Module and am trying to determine the cause. I created a simple app to swipe up, down, left, and right to see if it works. It seemed to detect the touch but it did not always respond (would have to swipe two or three times) and when it did it did not consistently identify the right motion (i.e. the wrong screen is displayed).

Additionally, when one of the 4 screens appears, the button on it does not work.

I never seem to get an event 1 either. This is three presses (with INPUT_CALLBACK_DEFINE(NULL, on_input_subsys_callback) commented out):

[00:00:07.470,092] <dbg> cst816s: cst816s_process: pressed event: 0, row: 65, col: 128
[00:00:07.470,642] <dbg> cst816s: cst816s_process: pressed event: 0, row: 65, col: 128
[00:00:07.481,384] <dbg> cst816s: cst816s_process: pressed event: 2, row: 65, col: 128
[00:00:07.481,903] <dbg> cst816s: cst816s_process: pressed event: 2, row: 65, col: 128
[00:00:07.492,675] <dbg> cst816s: cst816s_process: pressed event: 2, row: 65, col: 128
[00:00:07.493,194] <dbg> cst816s: cst816s_process: pressed event: 2, row: 65, col: 128
[00:00:07.503,967] <dbg> cst816s: cst816s_process: pressed event: 2, row: 65, col: 128
[00:00:07.504,486] <dbg> cst816s: cst816s_process: pressed event: 2, row: 65, col: 128
[00:00:07.515,258] <dbg> cst816s: cst816s_process: pressed event: 2, row: 65, col: 128
[00:00:07.515,747] <dbg> cst816s: cst816s_process: pressed event: 2, row: 65, col: 128
[00:00:09.308,990] <dbg> cst816s: cst816s_process: pressed event: 0, row: 75, col: 134
[00:00:09.309,509] <dbg> cst816s: cst816s_process: pressed event: 0, row: 75, col: 134
[00:00:09.320,251] <dbg> cst816s: cst816s_process: pressed event: 2, row: 75, col: 134
[00:00:09.320,770] <dbg> cst816s: cst816s_process: pressed event: 2, row: 75, col: 134
[00:00:09.331,542] <dbg> cst816s: cst816s_process: pressed event: 2, row: 75, col: 134
[00:00:09.332,031] <dbg> cst816s: cst816s_process: pressed event: 2, row: 75, col: 134
[00:00:09.342,803] <dbg> cst816s: cst816s_process: pressed event: 2, row: 75, col: 134
[00:00:09.343,322] <dbg> cst816s: cst816s_process: pressed event: 2, row: 75, col: 134
[00:00:09.354,095] <dbg> cst816s: cst816s_process: pressed event: 2, row: 75, col: 134
[00:00:09.354,614] <dbg> cst816s: cst816s_process: pressed event: 2, row: 75, col: 134
[00:00:10.995,452] <dbg> cst816s: cst816s_process: pressed event: 0, row: 72, col: 131
[00:00:10.995,971] <dbg> cst816s: cst816s_process: pressed event: 0, row: 72, col: 131
[00:00:11.006,744] <dbg> cst816s: cst816s_process: pressed event: 2, row: 72, col: 131
[00:00:11.007,263] <dbg> cst816s: cst816s_process: pressed event: 2, row: 72, col: 131
[00:00:11.018,005] <dbg> cst816s: cst816s_process: pressed event: 2, row: 72, col: 131
[00:00:11.018,493] <dbg> cst816s: cst816s_process: pressed event: 2, row: 72, col: 131
[00:00:11.029,266] <dbg> cst816s: cst816s_process: pressed event: 2, row: 72, col: 131
[00:00:11.029,785] <dbg> cst816s: cst816s_process: pressed event: 2, row: 72, col: 131
[00:00:11.040,557] <dbg> cst816s: cst816s_process: pressed event: 2, row: 72, col: 131
[00:00:11.041,076] <dbg> cst816s: cst816s_process: pressed event: 2, row: 72, col: 131

I created a repository with the swipe test lvgl_swipe (github.com).

What MCU/Processor/Board and compiler are you using?

nRF5340 Development Kit with the Nordic Add-In for Visual Studio Code running Zephyr.

What LVGL version are you using?

8.3 (currently supported by Nordic port of Zephyr)

What do you want to achieve?

Swipe left, right, up, and down and get the appropriate feedback from the touch sensor.

What have you tried so far?

I added debug statements to the cst618s driver to see the feedback from the driver. Moving my finger from left to right, somewhat slowly, in the middle of the screen produced these logs:

[00:04:05.294,799] <dbg> cst816s: cst816s_process: pressed event: 0, row: 111, col: 19
[00:04:05.294,891] <dbg> app: on_input_subsys_callback_2: input subsys type=3,code=0,value=19
[00:04:05.294,891] <dbg> app: on_input_subsys_callback_2: input subsys type=3,code=1,value=111
[00:04:05.294,952] <dbg> app: on_input_subsys_callback_2: input subsys type=1,code=330,value=1
[00:04:05.295,318] <dbg> cst816s: cst816s_process: pressed event: 0, row: 111, col: 19
[00:04:05.295,379] <dbg> app: on_input_subsys_callback_2: input subsys type=3,code=0,value=19
[00:04:05.295,379] <dbg> app: on_input_subsys_callback_2: input subsys type=3,code=1,value=111
[00:04:05.295,410] <dbg> app: on_input_subsys_callback_2: input subsys type=1,code=330,value=1
[00:04:05.306,030] <dbg> cst816s: cst816s_process: pressed event: 2, row: 111, col: 19
[00:04:05.306,121] <dbg> app: on_input_subsys_callback_2: input subsys type=3,code=0,value=19
[00:04:05.306,152] <dbg> app: on_input_subsys_callback_2: input subsys type=3,code=1,value=111
[00:04:05.306,213] <dbg> app: on_input_subsys_callback_2: input subsys type=1,code=330,value=1
[00:04:05.306,518] <dbg> cst816s: cst816s_process: pressed event: 2, row: 111, col: 19
[00:04:05.306,884] <dbg> app: on_input_subsys_callback_2: input subsys type=3,code=0,value=19
[00:04:05.306,915] <dbg> app: on_input_subsys_callback_2: input subsys type=3,code=1,value=111
[00:04:05.306,945] <dbg> app: on_input_subsys_callback_2: input subsys type=1,code=330,value=1
[00:04:05.647,369] <dbg> cst816s: cst816s_process: pressed event: 0, row: 174, col: 74
[00:04:05.647,460] <dbg> app: on_input_subsys_callback_2: input subsys type=3,code=0,value=74
[00:04:05.647,491] <dbg> app: on_input_subsys_callback_2: input subsys type=3,code=1,value=174
[00:04:05.647,552] <dbg> app: on_input_subsys_callback_2: input subsys type=1,code=330,value=1
[00:04:05.647,918] <dbg> cst816s: cst816s_process: pressed event: 0, row: 174, col: 74
[00:04:05.647,949] <dbg> app: on_input_subsys_callback_2: input subsys type=3,code=0,value=74
[00:04:05.647,949] <dbg> app: on_input_subsys_callback_2: input subsys type=3,code=1,value=174
[00:04:05.647,979] <dbg> app: on_input_subsys_callback_2: input subsys type=1,code=330,value=1
[00:04:05.658,630] <dbg> cst816s: cst816s_process: pressed event: 2, row: 174, col: 74
[00:04:05.658,721] <dbg> app: on_input_subsys_callback_2: input subsys type=3,code=0,value=74
[00:04:05.658,752] <dbg> app: on_input_subsys_callback_2: input subsys type=3,code=1,value=174
[00:04:05.658,813] <dbg> app: on_input_subsys_callback_2: input subsys type=1,code=330,value=1
[00:04:05.659,118] <dbg> cst816s: cst816s_process: pressed event: 2, row: 174, col: 74
[00:04:05.659,484] <dbg> app: on_input_subsys_callback_2: input subsys type=3,code=0,value=74
[00:04:05.659,515] <dbg> app: on_input_subsys_callback_2: input subsys type=3,code=1,value=174
[00:04:05.659,545] <dbg> app: on_input_subsys_callback_2: input subsys type=1,code=330,value=1
[00:04:06.035,186] <dbg> cst816s: cst816s_process: pressed event: 0, row: 162, col: 127
[00:04:06.035,278] <dbg> app: on_input_subsys_callback_2: input subsys type=3,code=0,value=127
[00:04:06.035,278] <dbg> app: on_input_subsys_callback_2: input subsys type=3,code=1,value=162
[00:04:06.035,339] <dbg> app: on_input_subsys_callback_2: input subsys type=1,code=330,value=1
[00:04:06.035,705] <dbg> cst816s: cst816s_process: pressed event: 0, row: 162, col: 127
[00:04:06.035,766] <dbg> app: on_input_subsys_callback_2: input subsys type=3,code=0,value=127
[00:04:06.035,766] <dbg> app: on_input_subsys_callback_2: input subsys type=3,code=1,value=162
[00:04:06.035,797] <dbg> app: on_input_subsys_callback_2: input subsys type=1,code=330,value=1
[00:04:06.046,508] <dbg> cst816s: cst816s_process: pressed event: 2, row: 162, col: 127
[00:04:06.046,569] <dbg> app: on_input_subsys_callback_2: input subsys type=3,code=0,value=127
[00:04:06.046,600] <dbg> app: on_input_subsys_callback_2: input subsys type=3,code=1,value=162
[00:04:06.046,661] <dbg> app: on_input_subsys_callback_2: input subsys type=1,code=330,value=1
[00:04:06.046,997] <dbg> cst816s: cst816s_process: pressed event: 2, row: 162, col: 127
[00:04:06.047,424] <dbg> app: on_input_subsys_callback_2: input subsys type=3,code=0,value=127
[00:04:06.047,454] <dbg> app: on_input_subsys_callback_2: input subsys type=3,code=1,value=162
[00:04:06.047,485] <dbg> app: on_input_subsys_callback_2: input subsys type=1,code=330,value=1
[00:04:06.376,922] <dbg> cst816s: cst816s_process: pressed event: 0, row: 150, col: 239
[00:04:06.377,014] <dbg> app: on_input_subsys_callback_2: input subsys type=3,code=0,value=239
[00:04:06.377,044] <dbg> app: on_input_subsys_callback_2: input subsys type=3,code=1,value=150
[00:04:06.377,105] <dbg> app: on_input_subsys_callback_2: input subsys type=1,code=330,value=1
[00:04:06.377,471] <dbg> cst816s: cst816s_process: pressed event: 0, row: 150, col: 239
[00:04:06.377,502] <dbg> app: on_input_subsys_callback_2: input subsys type=3,code=0,value=239
[00:04:06.377,532] <dbg> app: on_input_subsys_callback_2: input subsys type=3,code=1,value=150
[00:04:06.377,532] <dbg> app: on_input_subsys_callback_2: input subsys type=1,code=330,value=1
[00:04:06.388,305] <dbg> cst816s: cst816s_process: pressed event: 2, row: 150, col: 239
[00:04:06.388,366] <dbg> app: on_input_subsys_callback_2: input subsys type=3,code=0,value=239
[00:04:06.388,397] <dbg> app: on_input_subsys_callback_2: input subsys type=3,code=1,value=150
[00:04:06.388,458] <dbg> app: on_input_subsys_callback_2: input subsys type=1,code=330,value=1
[00:04:06.388,793] <dbg> cst816s: cst816s_process: pressed event: 2, row: 150, col: 239
[00:04:06.389,160] <dbg> app: on_input_subsys_callback_2: input subsys type=3,code=0,value=239
[00:04:06.389,190] <dbg> app: on_input_subsys_callback_2: input subsys type=3,code=1,value=150
[00:04:06.389,221] <dbg> app: on_input_subsys_callback_2: input subsys type=1,code=330,value=1

Code to reproduce

I added this code to the cst618s driver to test the single swipe:

static int cst816s_process(const struct device *dev)
{
	const struct cst816s_config *cfg = dev->config;

	int r;
	uint8_t event;
	uint16_t row, col;
	bool pressed;
	uint16_t x;
	uint16_t y;

	r = i2c_burst_read_dt(&cfg->i2c, CST816S_REG_XPOS_H, (uint8_t *)&x, sizeof(x));
	if (r < 0) {
		LOG_ERR("Could not read x data");
		return r;
	}

	r = i2c_burst_read_dt(&cfg->i2c, CST816S_REG_YPOS_H, (uint8_t *)&y, sizeof(y));
	if (r < 0) {
		LOG_ERR("Could not read y data");
		return r;
	}
	col = sys_be16_to_cpu(x) & 0x0fff;
	row = sys_be16_to_cpu(y) & 0x0fff;

	event = (x & 0xff) >> CST816S_EVENT_BITS_POS;
	pressed = (event == EVENT_PRESS_DOWN) || (event == EVENT_CONTACT);

	if (pressed) {
		LOG_DBG("pressed event: %d, row: %d, col: %d", event, row, col);
		input_report_abs(dev, INPUT_ABS_X, col, false, K_FOREVER);
		input_report_abs(dev, INPUT_ABS_Y, row, false, K_FOREVER);
		input_report_key(dev, INPUT_BTN_TOUCH, 1, true, K_FOREVER);
	} else {
		LOG_DBG("not pressed event: %d, row: %d, col: %d", event, row, col);
		input_report_key(dev, INPUT_BTN_TOUCH, 0, true, K_FOREVER);
	}

	return r;
}

I also added this to main.c

...
INPUT_CALLBACK_DEFINE(NULL, on_input_subsys_callback_2);
...
static void on_input_subsys_callback_2(struct input_event *evt)
{
	LOG_DBG("input subsys type=%d,code=%d,value=%d", evt->type, evt->code, evt->value);
	lv_color_t c0;
    lv_color_t c1;

    c0.full = 0;
    c1.full = 1;
	
	//LOG_DBG("input subsys type,code,value,%d,%d,%d", evt->type, evt->code, evt->value);
	if (evt->code == INPUT_ABS_X) {
		last_x = evt->value;
	}
	if (evt->code == INPUT_ABS_Y) {
		last_y = evt->value;
	}
	if (last_x != -1 && last_y != -1) {
		//lv_canvas_set_px_color(canvas, last_x, last_y, c0);
		last_x = -1;
		last_y = -1;
	}
}

Screenshot and/or video

I have a video but can’t seem to share it.

Following up years later — I hit this exact symptom (event: 0 then repeated event: 2, never event: 1) on different hardware (a custom nRF54L board), and eventually traced it to the CST816S’s IRQ_CTL register (0xFA). Bit 5 (IRQ_EN_CHANGE) is what the chip uses to signal a touched→released state transition — if anything (board bring-up code, a vendor init sequence, etc.) clears that bit, the driver keeps getting interrupts for press/contact but never gets one for release. In my case it was our own bring-up code accidentally clearing it while trying to fix an unrelated idle-interrupt flood. Zephyr’s stock input_cst816s.c driver doesn’t touch this register at all (leaves both IRQ_EN_TOUCH and IRQ_EN_CHANGE enabled from chip reset), so if you’re on an unmodified driver + a clean board file, it’s probably not this exact cause — but it’s a good thing to check via a raw I2C read of 0xFA if you’re still stuck. Worth confirming whether that bit is actually set on your hardware when releases go missing.