1 /*
2  * Copyright (c) 2024 Antmicro <www.antmicro.com>
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/logging/log.h>
8 #include <zephyr/kernel.h>
9 #include <zephyr/device.h>
10 #include <zephyr/input/input.h>
11 #include <zephyr/drivers/display.h>
12 #include <zephyr/sys/util.h>
13 
14 LOG_MODULE_REGISTER(sample, LOG_LEVEL_INF);
15 
16 #if !DT_NODE_EXISTS(DT_CHOSEN(zephyr_touch))
17 #error "Unsupported board: zephyr,touch is not assigned"
18 #endif
19 
20 #if !DT_NODE_EXISTS(DT_CHOSEN(zephyr_display))
21 #error "Unsupported board: zephyr,display is not assigned"
22 #endif
23 
24 #define WIDTH     (DT_PROP(DT_CHOSEN(zephyr_display), width))
25 #define HEIGHT    (DT_PROP(DT_CHOSEN(zephyr_display), height))
26 #define CROSS_DIM (WIDTH / CONFIG_SCREEN_WIDTH_TO_CROSS_DIM)
27 
28 #define PIXEL_FORMAT (DT_PROP_OR(DT_CHOSEN(zephyr_display), pixel_format, PIXEL_FORMAT_ARGB_8888))
29 #define BPP          ((DISPLAY_BITS_PER_PIXEL(PIXEL_FORMAT)) / BITS_PER_BYTE)
30 
31 #define BUFFER_SIZE  (CROSS_DIM * CROSS_DIM * BPP)
32 #define REFRESH_RATE 100
33 
34 static const struct device *const display_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_display));
35 static const struct device *const touch_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_touch));
36 static struct display_buffer_descriptor buf_desc = {
37 	.buf_size = BUFFER_SIZE, .pitch = CROSS_DIM, .width = CROSS_DIM, .height = CROSS_DIM};
38 
39 static uint8_t buffer_cross[BUFFER_SIZE];
40 static const uint8_t buffer_cross_empty[BUFFER_SIZE];
41 static struct k_sem sync;
42 
43 static struct {
44 	size_t x;
45 	size_t y;
46 	bool pressed;
47 } touch_point, touch_point_drawn;
48 
touch_event_callback(struct input_event * evt,void * user_data)49 static void touch_event_callback(struct input_event *evt, void *user_data)
50 {
51 	if (evt->code == INPUT_ABS_X) {
52 		touch_point.x = evt->value;
53 	}
54 	if (evt->code == INPUT_ABS_Y) {
55 		touch_point.y = evt->value;
56 	}
57 	if (evt->code == INPUT_BTN_TOUCH) {
58 		touch_point.pressed = evt->value;
59 	}
60 	if (evt->sync) {
61 		k_sem_give(&sync);
62 	}
63 }
64 INPUT_CALLBACK_DEFINE(touch_dev, touch_event_callback, NULL);
65 
clear_screen(void)66 static int clear_screen(void)
67 {
68 	int x;
69 	int y;
70 	int ret;
71 
72 	for (x = 0; x < WIDTH; x += CROSS_DIM) {
73 		for (y = 0; y < HEIGHT; y += CROSS_DIM) {
74 			struct display_buffer_descriptor ddesc = buf_desc;
75 			uint16_t rem_w = WIDTH - x;
76 			uint16_t rem_h = HEIGHT - y;
77 
78 			ddesc.width = MIN(buf_desc.width, rem_w);
79 			ddesc.height = MIN(buf_desc.height, rem_h);
80 			ddesc.buf_size = ddesc.width * ddesc.height * BPP;
81 
82 			ret = display_write(display_dev, x, y, &ddesc, buffer_cross_empty);
83 			if (ret < 0) {
84 				LOG_ERR("Failed to write to display (error %d)", ret);
85 				return ret;
86 			}
87 		}
88 	}
89 
90 	return 0;
91 }
92 
fill_cross_buffer(void)93 static void fill_cross_buffer(void)
94 {
95 	int i;
96 	int x;
97 	int y;
98 	int index;
99 
100 	for (i = 0; i < BPP; i++) {
101 		for (x = 0; x < CROSS_DIM; x++) {
102 			index = BPP * (CROSS_DIM / 2 * CROSS_DIM + x);
103 			buffer_cross[index + i] = -1;
104 		}
105 		for (y = 0; y < CROSS_DIM; y++) {
106 			index = BPP * (y * CROSS_DIM + CROSS_DIM / 2);
107 			buffer_cross[index + i] = -1;
108 		}
109 	}
110 }
111 
get_draw_position(int value,int upper_bound)112 static int get_draw_position(int value, int upper_bound)
113 {
114 	if (value < CROSS_DIM / 2) {
115 		return 0;
116 	}
117 
118 	if (value + CROSS_DIM / 2 > upper_bound) {
119 		return upper_bound - CROSS_DIM;
120 	}
121 
122 	return value - CROSS_DIM / 2;
123 }
124 
main(void)125 int main(void)
126 {
127 	int ret;
128 
129 	LOG_INF("Touch sample for touchscreen: %s, dc: %s", touch_dev->name, display_dev->name);
130 
131 	if (!device_is_ready(touch_dev)) {
132 		LOG_ERR("Device %s not found. Aborting sample.", touch_dev->name);
133 		return 0;
134 	}
135 
136 	if (!device_is_ready(display_dev)) {
137 		LOG_ERR("Device %s not found. Aborting sample.", display_dev->name);
138 		return 0;
139 	}
140 
141 	if (BPP == 0 || BPP > 4) {
142 		LOG_ERR("Unsupported BPP=%d", BPP);
143 		return 0;
144 	}
145 	fill_cross_buffer();
146 	ret = display_blanking_off(display_dev);
147 	if (ret < 0 && ret != -ENOSYS) {
148 		LOG_ERR("Failed to turn blanking off (error %d)", ret);
149 		return 0;
150 	}
151 
152 	ret = clear_screen();
153 	if (ret < 0) {
154 		LOG_ERR("Failed to clear the screen");
155 		return 0;
156 	}
157 
158 	touch_point_drawn.x = CROSS_DIM / 2;
159 	touch_point_drawn.y = CROSS_DIM / 2;
160 	touch_point.x = -1;
161 	touch_point.y = -1;
162 
163 	k_sem_init(&sync, 0, 1);
164 
165 	while (1) {
166 		k_msleep(REFRESH_RATE);
167 		k_sem_take(&sync, K_FOREVER);
168 		LOG_INF("TOUCH %s X, Y: (%d, %d)", touch_point.pressed ? "PRESS" : "RELEASE",
169 			touch_point.x, touch_point.y);
170 
171 		ret = display_write(display_dev, get_draw_position(touch_point_drawn.x, WIDTH),
172 				    get_draw_position(touch_point_drawn.y, HEIGHT), &buf_desc,
173 				    buffer_cross_empty);
174 		if (ret < 0) {
175 			LOG_ERR("Failed to write to display (error %d)", ret);
176 			return 0;
177 		}
178 
179 		ret = display_write(display_dev, get_draw_position(touch_point.x, WIDTH),
180 				    get_draw_position(touch_point.y, HEIGHT), &buf_desc,
181 				    buffer_cross);
182 		if (ret < 0) {
183 			LOG_ERR("Failed to write to display (error %d)", ret);
184 			return 0;
185 		}
186 
187 		touch_point_drawn.x = touch_point.x;
188 		touch_point_drawn.y = touch_point.y;
189 	}
190 	return 0;
191 }
192