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