1 /*
2 * Copyright (c) 2006-2025, RT-Thread Development Team
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date Author Notes
8 * 2025-05-23 godmial Refactor to conform to RT-Thread coding style.
9 */
10
11 #include <board.h>
12 #include <rthw.h>
13 #include <rtthread.h>
14 #include "drv_touch.h"
15 #include "drv_lcd.h"
16
17 #define DRV_DEBUG
18 #define LOG_TAG "drv.touch"
19 #include <drv_log.h>
20
21 _m_tp_dev tp_dev;
22
23 const uint16_t FT5206_TPX_TBL[5] = {FT_TP1_REG, FT_TP2_REG, FT_TP3_REG, FT_TP4_REG, FT_TP5_REG};
24 uint8_t g_gt_tnum = 5; /**< Default number of supported touch points (5-point touch) */
25
26
27 /**
28 * @brief Delay for a specified number of microseconds.
29 *
30 * @note This function uses the SysTick timer to implement microsecond delay by polling.
31 *
32 * @param _nus Number of microseconds to delay.
33 */
delay_us(uint32_t _nus)34 void delay_us(uint32_t _nus)
35 {
36 uint8_t fac_us = 0;
37 uint32_t ticks;
38 uint32_t t_old, t_now, t_cnt = 0;
39 uint32_t reload = SysTick->LOAD;
40
41 fac_us = SystemCoreClock / 1000000;
42 ticks = _nus * fac_us - fac_us * 7 / 20;
43 t_old = SysTick->VAL;
44
45 while (1)
46 {
47 t_now = SysTick->VAL;
48
49 if (t_now < t_old)
50 {
51 t_cnt += t_old - t_now;
52 }
53 else
54 {
55 t_cnt += t_old + (reload - t_now);
56 }
57
58 if (t_cnt >= ticks)
59 {
60 break;
61 }
62
63 t_old = t_now;
64 };
65 }
66
67
68 /**
69 * @brief Delay function wrapper for touch operations.
70 *
71 * @param us Number of microseconds to delay.
72 */
touch_delay_1us(uint16_t us)73 void touch_delay_1us(uint16_t us)
74 {
75 delay_us(us);
76 }
77
78 /**
79 * @brief Delay to control I2C speed.
80 *
81 * @note Used between I2C signal toggles to ensure proper timing.
82 */
CT_Delay(void)83 void CT_Delay(void)
84 {
85 touch_delay_1us(2);
86 }
87
88 /**
89 * @brief Initialize the I2C interface for the capacitive touch chip.
90 *
91 * @note Configures GPIO ports for SCL and SDA lines as outputs with pull-up resistors.
92 */
CT_IIC_Init(void)93 void CT_IIC_Init(void)
94 {
95 /* enable the led clock */
96 rcu_periph_clock_enable(SCL_RCU);
97 /* configure led GPIO port */
98 gpio_mode_set(SCL_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, SCL_PIN);
99 gpio_output_options_set(SCL_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, SCL_PIN);
100
101 rcu_periph_clock_enable(SDA_RCU);
102 gpio_mode_set(SDA_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, SDA_PIN);
103 gpio_output_options_set(SDA_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, SDA_PIN);
104 }
105
106 /**
107 * @brief Generate I2C start condition.
108 *
109 * @note Starts communication by pulling SDA low while SCL is high.
110 */
CT_IIC_Start(void)111 void CT_IIC_Start(void)
112 {
113 CT_SDA_OUT();
114 SDA_ON;
115 SCL_ON;
116 touch_delay_1us(30);
117 SDA_OFF;
118 CT_Delay();
119 SCL_OFF;
120 }
121
122 /**
123 * @brief Generate I2C stop condition.
124 *
125 * @note Ends communication by pulling SDA high while SCL is high.
126 */
CT_IIC_Stop(void)127 void CT_IIC_Stop(void)
128 {
129 CT_SDA_OUT();
130 SCL_ON;
131 touch_delay_1us(30);
132 SDA_OFF;
133 CT_Delay();
134 SDA_ON;
135 }
136
137 /**
138 * @brief Wait for I2C ACK signal.
139 *
140 * @return 0 if ACK received successfully, 1 if timeout occurred.
141 *
142 * @warning If ACK is not received within 250 iterations, a stop condition is generated.
143 */
CT_IIC_Wait_Ack(void)144 uint8_t CT_IIC_Wait_Ack(void)
145 {
146 uint8_t ucErrTime = 0;
147 CT_SDA_IN();
148 SDA_ON;
149 SCL_ON;
150 CT_Delay();
151 while (CT_READ_SDA)
152 {
153 ucErrTime++;
154 if (ucErrTime > 250)
155 {
156 CT_IIC_Stop();
157 return 1;
158 }
159 CT_Delay();
160 }
161 SCL_OFF;
162 return 0;
163 }
164
165 /**
166 * @brief Generate I2C ACK signal.
167 *
168 * @note This is used to acknowledge receipt of a byte in I2C communication.
169 */
CT_IIC_Ack(void)170 void CT_IIC_Ack(void)
171 {
172 SCL_OFF;
173 CT_SDA_OUT();
174 CT_Delay();
175 SDA_OFF;
176 CT_Delay();
177 SCL_ON;
178 CT_Delay();
179 SCL_OFF;
180 }
181
182 /**
183 * @brief Generate I2C NACK signal.
184 *
185 * @note This is used to indicate the end of reading in I2C communication.
186 */
CT_IIC_NAck(void)187 void CT_IIC_NAck(void)
188 {
189 SCL_OFF;
190 CT_SDA_OUT();
191 CT_Delay();
192 SDA_ON;
193 CT_Delay();
194 SCL_ON;
195 CT_Delay();
196 SCL_OFF;
197 }
198
199
200 /**
201 * @brief Send a byte over I2C.
202 *
203 * @param txd Byte to send.
204 *
205 * @note Sends 8 bits starting from the MSB over the SDA line.
206 */
CT_IIC_Send_Byte(uint8_t txd)207 void CT_IIC_Send_Byte(uint8_t txd)
208 {
209 uint8_t t;
210 CT_SDA_OUT();
211 SCL_OFF;
212 CT_Delay();
213 for (t = 0; t < 8; t++)
214 {
215 if ((txd & 0x80) >> 7)
216 {
217 gpio_bit_set(SDA_PORT, SDA_PIN);
218 }
219 else
220 {
221 gpio_bit_reset(SDA_PORT, SDA_PIN);
222 }
223
224 txd <<= 1;
225 SCL_ON;
226 CT_Delay();
227 SCL_OFF;
228 CT_Delay();
229 }
230 }
231
232 /**
233 * @brief Read a byte over I2C.
234 *
235 * @param ack If 1, send ACK after receiving; if 0, send NACK.
236 *
237 * @return The received byte.
238 */
CT_IIC_Read_Byte(unsigned char ack)239 uint8_t CT_IIC_Read_Byte(unsigned char ack)
240 {
241 volatile uint8_t i, receive = 0;
242 CT_SDA_IN();
243 touch_delay_1us(30);
244 for (i = 0; i < 8; i++)
245 {
246 SCL_OFF;
247 CT_Delay();
248 SCL_ON;
249 receive <<= 1;
250
251 if (CT_READ_SDA)
252 receive++;
253 }
254
255 if (!ack)
256 CT_IIC_NAck();
257 else
258 CT_IIC_Ack();
259 return receive;
260 }
261
262 /**
263 * @brief Write data to FT5206 register.
264 *
265 * @param reg Register address.
266 * @param buf Pointer to data buffer.
267 * @param len Length of data to write.
268 *
269 * @return 0 if successful, non-zero if any write fails.
270 */
FT5206_WR_Reg(uint16_t reg,uint8_t * buf,uint8_t len)271 uint8_t FT5206_WR_Reg(uint16_t reg, uint8_t *buf, uint8_t len)
272 {
273 uint8_t i;
274 uint8_t ret = 0;
275 CT_IIC_Start();
276 CT_IIC_Send_Byte(FT_CMD_WR);
277 CT_IIC_Wait_Ack();
278 CT_IIC_Send_Byte(reg & 0XFF);
279 CT_IIC_Wait_Ack();
280 for (i = 0; i < len; i++)
281 {
282 CT_IIC_Send_Byte(buf[i]);
283 ret = CT_IIC_Wait_Ack();
284 if (ret)
285 break;
286 }
287 CT_IIC_Stop();
288 return ret;
289 }
290
291 /**
292 * @brief Read data from FT5206 register.
293 *
294 * @param reg Register address.
295 * @param buf Pointer to buffer to store read data.
296 * @param len Number of bytes to read.
297 */
FT5206_RD_Reg(uint16_t reg,uint8_t * buf,uint8_t len)298 void FT5206_RD_Reg(uint16_t reg, uint8_t *buf, uint8_t len)
299 {
300 uint8_t i;
301 CT_IIC_Start();
302 CT_IIC_Send_Byte(FT_CMD_WR);
303 CT_IIC_Wait_Ack();
304 CT_IIC_Send_Byte(reg & 0XFF);
305 CT_IIC_Wait_Ack();
306 CT_IIC_Start();
307 CT_IIC_Send_Byte(FT_CMD_RD);
308 CT_IIC_Wait_Ack();
309 for (i = 0; i < len; i++)
310 {
311 buf[i] = CT_IIC_Read_Byte(i == (len - 1) ? 0 : 1);
312 }
313 CT_IIC_Stop();
314 }
315
316 /**
317 * @brief Initialize the FT5206 touch screen controller.
318 *
319 * @note Initializes I2C, sets operational modes and touch sensitivity.
320 *
321 * @return 0 if initialization is successful.
322 */
FT5206_Init(void)323 uint8_t FT5206_Init(void)
324 {
325 uint8_t temp[5];
326 /* enable the led clock */
327 rcu_periph_clock_enable(INT_RCU);
328 /* configure led GPIO port */
329 gpio_mode_set(INT_PORT, GPIO_MODE_INPUT, GPIO_PUPD_NONE, INT_PIN);
330
331
332 CT_IIC_Init();
333
334 FT5206_WR_Reg(FT_DEVIDE_MODE, temp, 1);
335 FT5206_WR_Reg(FT_ID_G_MODE, temp, 1);
336 temp[0] = 22;
337 FT5206_WR_Reg(FT_ID_G_THGROUP, temp, 1);
338 temp[0] = 12;
339 FT5206_WR_Reg(FT_ID_G_PERIODACTIVE, temp, 1);
340
341 FT5206_RD_Reg(FT_ID_G_LIB_VERSION, &temp[0], 2);
342 printf("CTP ID:%x\r\n", ((uint16_t)temp[0] << 8) + temp[1]);
343 return 0;
344 }
345
346 /**
347 * @brief Scan the touch screen using polling.
348 *
349 * @param mode 0 for normal scan mode.
350 *
351 * @return 1 if a touch is detected, 0 otherwise.
352 */
FT5206_Scan(uint8_t mode)353 uint8_t FT5206_Scan(uint8_t mode)
354 {
355 uint8_t buf[4];
356 uint8_t i = 0;
357 uint8_t res = 0;
358 uint8_t temp;
359 uint16_t tempsta;
360 static uint8_t t = 0;
361 t++;
362 if ((t % 10) == 0 || t < 10)
363 {
364 FT5206_RD_Reg(FT_REG_NUM_FINGER, &mode, 1);
365 if ((mode & 0XF) && ((mode & 0XF) <= g_gt_tnum))
366 {
367 temp = 0XFF << (mode & 0XF);
368 tempsta = tp_dev.sta;
369 tp_dev.sta = (~temp) | TP_PRES_DOWN | TP_CATH_PRES;
370 tp_dev.x[g_gt_tnum - 1] = tp_dev.x[0];
371 tp_dev.y[g_gt_tnum - 1] = tp_dev.y[0];
372
373 for (i = 0; i < g_gt_tnum; i++)
374 {
375 if (tp_dev.sta & (1 << i))
376 {
377 FT5206_RD_Reg(FT5206_TPX_TBL[i], buf, 4);
378 if (tp_dev.touchtype & 0X01)
379 {
380 tp_dev.y[i] = ((uint16_t)(buf[0] & 0X0F) << 8) + buf[1];
381 tp_dev.x[i] = ((uint16_t)(buf[2] & 0X0F) << 8) + buf[3];
382 }
383 else
384 {
385 tp_dev.x[i] = (((uint16_t)(buf[0] & 0X0F) << 8) + buf[1]);
386 tp_dev.y[i] = ((uint16_t)(buf[2] & 0X0F) << 8) + buf[3];
387 }
388 }
389 }
390 res = 1;
391 if (tp_dev.x[0] > ACTIVE_WIDTH || tp_dev.y[0] > ACTIVE_HEIGHT)
392 {
393 if ((mode & 0XF) > 1)
394 {
395 tp_dev.x[0] = tp_dev.x[1];
396 tp_dev.y[0] = tp_dev.y[1];
397 t = 0;
398 }
399 else
400 {
401 tp_dev.x[0] = tp_dev.x[g_gt_tnum - 1];
402 tp_dev.y[0] = tp_dev.y[g_gt_tnum - 1];
403 mode = 0X80;
404 tp_dev.sta = tempsta;
405 }
406 }
407 else
408 t = 0;
409 }
410 }
411
412 if ((mode & 0X1F) == 0)
413 {
414 if (tp_dev.sta & TP_PRES_DOWN)
415 {
416 tp_dev.sta &= ~TP_PRES_DOWN;
417 }
418 else
419 {
420 tp_dev.x[0] = 0xffff;
421 tp_dev.y[0] = 0xffff;
422 tp_dev.sta &= 0XE0;
423 }
424 }
425
426 if (t > 240)
427 t = 10;
428 return res;
429 }
430
431 /**
432 * @brief Detect touch release by monitoring a specific touch point.
433 *
434 * @param contact Index of touch contact to check.
435 * @param sx Start X coordinate.
436 * @param sy Start Y coordinate.
437 * @param ex End X coordinate.
438 * @param ey End Y coordinate.
439 *
440 * @note Returns when the touch point leaves the specified rectangle.
441 */
letgo_scan(uint16_t contact,uint16_t sx,uint16_t sy,uint16_t ex,uint16_t ey)442 void letgo_scan(uint16_t contact, uint16_t sx, uint16_t sy, uint16_t ex, uint16_t ey)
443 {
444 while (FT5206_Scan(0))
445 {
446 if (tp_dev.x[contact] <= sx || tp_dev.x[contact] >= ex)
447 {
448 return;
449 }
450
451 if (tp_dev.y[contact] <= sy && tp_dev.y[contact] >= ey)
452 {
453 return;
454 }
455 }
456 }
457
458 /**
459 * @brief Example application to test the touch screen functionality.
460 *
461 * @return Always returns 0.
462 *
463 * @warning This function runs an infinite loop and is intended for manual test only.
464 */
touch_test(void)465 int touch_test(void)
466 {
467 int touch_state = 0;
468 FT5206_Init();
469
470 while (1)
471 {
472 touch_state = FT5206_Scan(0);
473
474 if (touch_state == 1)
475 {
476 rt_kprintf("touch\r\n");
477 }
478 rt_thread_mdelay(10);
479 }
480 }
481 MSH_CMD_EXPORT(touch_test, touch test)
482