Files
AC707N/SDK/apps/common/device/touch_panel/ft3x68/ft3x68.c
T
2025-12-03 11:12:34 +08:00

1262 lines
36 KiB
C

/*
* Privileged & confidential All Rights/Copyright Reserved by FocalTech.
* ** Source code released bellows and hereby must be retained as
* FocalTech's copyright and with the following disclaimer accepted by
* Receiver.
*
* "THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
* EVENT SHALL THE FOCALTECH'S AND ITS AFFILIATES'DIRECTORS AND OFFICERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE."
*/
/*****************************************************************************
*
* The initial work you should do is to implement the i2c communication function based on your platform:
* platform_i2c_write() and platform_i2c_read().
*
* In your system, you should register a interrupt at initialization stage(The interrupt trigger mode is
* falling-edge mode), then interrupt handler will run while host detects the falling-edge of INT port.
*
*
*****************************************************************************/
/*****************************************************************************
* Included header files
*****************************************************************************/
/* #include "touch_panel/ft3x68/ft3x68.h" */
#include "ft3x68.h"
#if TCFG_TP_FT3X68_ENABLE
#define _IIC_USE_HW
#include "iic_api.h"
#define LOG_TAG "[TP]"
#define PRINTF(format, ...) printf(format, ## __VA_ARGS__)
#if 0
#define log_info(format, ...) PRINTF(LOG_TAG "info:" format, ## __VA_ARGS__)
#define log_debug(format, ...) PRINTF(LOG_TAG "debug:" format, ## __VA_ARGS__)
#define log_error(format, ...) PRINTF(LOG_TAG "error:" format, ## __VA_ARGS__)
#else
#define log_info(format, ...) //PRINTF(LOG_TAG "info:" format, ## __VA_ARGS__)
#define log_debug(format, ...)
#define log_error(format, ...) PRINTF(LOG_TAG "error:" format, ## __VA_ARGS__)
#endif
#define abs(x) __builtin_abs(x)//((x)>0?(x):-(x) )
/*****************************************************************************
* Private constant and macro definitions using #define
*****************************************************************************/
#define FTS_CMD_START_DELAY 12
#define FTS_DEV_SLAVE_ADDR (0x38<<1)
/* register address */
#define FTS_REG_WORKMODE 0x00
#define FTS_REG_WORKMODE_FACTORY_VALUE 0x40
#define FTS_REG_WORKMODE_SCAN_VALUE 0xC0
#define FTS_REG_FLOW_WORK_CNT 0x91
#define FTS_REG_POWER_MODE 0xA5
#define FTS_REG_GESTURE_EN 0xD0
#define FTS_REG_GESTURE_ENABLE 0x01
#define FTS_REG_GESTURE_OUTPUT_ADDRESS 0xD3
/*Max point numbers of gesture trace*/
#define MAX_POINTS_GESTURE_TRACE 6
/*Length of gesture information*/
#define MAX_LEN_GESTURE_INFO (MAX_POINTS_GESTURE_TRACE * 4 + 2)
/*Max point numbers of touch trace*/
#define MAX_POINTS_TOUCH_TRACE 2
/*Length of touch information*/
#define MAX_LEN_TOUCH_INFO (MAX_POINTS_TOUCH_TRACE * 6 + 2)
/*Max touch points that touch controller supports*/
#define FTS_MAX_POINTS_SUPPORT 1
#define TP_FPS 60
#define VK_MIN (VK_X < VK_Y ? VK_X : VK_Y)
#define VK_MAX (VK_X > VK_Y ? VK_X : VK_Y)
// 滑屏阈值
/* #define VK_X_MOVE (VK_MIN/5) */
/* #define VK_Y_MOVE (VK_MIN/5) */
/* #define VK_X_Y_DIFF (VK_MIN/10) */
// 惯性阈值
/* #define ENERGY_XDISTANCE 4//(VK_MIN/30) */
/* #define ENERGY_YDISTANCE 4//(VK_MIN/30) */
#define ENERGY_NUM 3 //取最后n次中最大差值
#define ENERGY_T_MS (1000000/TP_FPS/1000)
// reset gpio
#define CST816S_RESET_H() gpio_set_mode(IO_PORT_SPILT(TCFG_TP_RESET_IO), PORT_OUTPUT_HIGH)
#define CST816S_RESET_L() gpio_set_mode(IO_PORT_SPILT(TCFG_TP_RESET_IO), PORT_OUTPUT_LOW)
// iic
#define CST816S_IIC_DELAY 1
#define CST816S_IIC_ADDR 0x46
#define SLAVE_DEV_ADDR (0x15) //固有,不可配置,用于 chipID 读取,固件烧录操作
#define PER_LEN 512
#define CST816S_UPGRADE_ADD (0x6A) /* 7-bit i2c addr */
#define HYN_I2C_ADDR 0x15 /* 7-bit i2c addr */ //0x38
#define I2C_MASTER_CI2C0 0
#define i2c_t uint8_t
typedef enum {
TP_RUN_STEP_STOP = 0,
TP_RUN_STEP_INIT,
TP_RUN_STEP_RUN_IDLE,
TP_RUN_STEP_RUN,
} TP_RUN_STEP_E;
// iic
typedef struct {
u8 init;
/* u16 iic_delay; */
hw_iic_dev iic_hdl;
} ft_param;
// 惯性
struct touch_kinetic_energy {
u16 x;
u16 y;
};
static struct touch_kinetic_energy tke[ENERGY_NUM]; // 惯性
static volatile TP_RUN_STEP_E tp_step = TP_RUN_STEP_STOP;
/* static u8 tp_last_staus = ELM_EVENT_TOUCH_UP; // 状态记录 */
/* static int tp_down_cnt = 0; // 长按计数 */
static bool touch_flag = 0; // 触摸标记
static OS_SEM touch_sem; // 触摸中断post
static ft_param module_param = {0}; // iic
#define __this (&module_param)
static void ctp_delay_us(unsigned int time)
{
udelay(time);
}
static void ctp_delay_ms(unsigned short time)
{
if (time >= 10) {
os_time_dly(time / 10 + 1);
} else {
mdelay(time);
}
}
/*****************************************************************************
* Private variables/functions
*****************************************************************************/
static struct fts_ts_data _fts_data = {
.suspended = 0,
.gesture_support = FTS_GESTURE_EN,
.esd_support = 1,
/* .iic_hdl = 0, */
.iic_delay = 70,
.int_io_init = 0,
};
static int fts_gpio_interrupt_handler(void);
/*****************************************************************************
* Global variable or extern global variabls/functions
*****************************************************************************/
struct fts_ts_data *fts_data = &_fts_data;
void mdelay(u32 ms);
void delay(volatile u32 t);
/*delay, unit: millisecond */
void fts_msleep(unsigned long msec)
{
mdelay(msec);
}
/*****************************************************************************
platform_tp_deal
*****************************************************************************/
void ctp_enter_sleep(void)
{
fts_ts_suspend();
}
void ctp_exit_sleep(void)
{
fts_ts_resume();
}
static u8 tp_last_staus = ELM_EVENT_TOUCH_UP;
static int tp_down_cnt = 0;
#ifndef abs
#define abs(x) ((x)>0?(x):-(x) )
#endif
#undef log_info
#define log_info(fmt, ...)
#define ENERGY_XDISTANCE 15
#define ENERGY_YDISTANCE 15
#define ENERGY_TIME 20 //ms
/* struct touch_kinetic_energy { */
/* int t1; */
/* int x1; */
/* int y1; */
/* int t2; */
/* int x2; */
/* int y2; */
/* }; */
/* static struct touch_kinetic_energy tke = {0}; */
static void touch_info_reset()
{
tp_last_staus = ELM_EVENT_TOUCH_UP;
tp_down_cnt = 0;
}
static void tke_start(int x, int y)
{
memcpy(&tke[0], &tke[1], sizeof(struct touch_kinetic_energy) * (ENERGY_NUM - 1));
tke[ENERGY_NUM - 1].x = x;
tke[ENERGY_NUM - 1].y = y;
log_info("tke start %d, %d\n", x, y);
}
static void tke_stop(int x, int y, struct touch_event *up_t)
{
struct touch_event t = {0};
int tke_x = 0;
int tke_y = 0;
u8 xdir, ydir;
for (int i = 0; i < (ENERGY_NUM - 1); i++) {
if ((tke[i].x == 0xffff) || (tke[i].y == 0xffff)) {
continue;
}
int tmp_x = (int)tke[i + 1].x - (int)tke[i].x;
int tmp_y = (int)tke[i + 1].y - (int)tke[i].y;
if (abs(tke_x) < abs(tmp_x)) {
tke_x = tmp_x;
}
if (abs(tke_y) < abs(tmp_y)) {
tke_y = tmp_y;
}
log_info("[%d], x:0x%x, y:0x%x", i, tke[i].x, tke[i].y);
log_info("stke %d, %d\n", tke_x, tke_y);
}
log_info("stke end %d, %d\n", tke_x, tke_y);
if (((abs(tke_x) >= ENERGY_XDISTANCE) || abs(tke_y) >= ENERGY_YDISTANCE)) {
up_t->has_energy = 1;
ui_touch_msg_post(up_t);
if (abs(tke_x) < ENERGY_XDISTANCE) {
tke_x = 0;
}
if (abs(tke_y) < ENERGY_YDISTANCE) {
tke_y = 0;
}
xdir = (tke_x < 0) ? 1 : 2;
ydir = (tke_y < 0) ? 1 : 2;
/* printf("xdir %d, ydir %d\n", xdir, ydir); */
t.event = ELM_EVENT_TOUCH_ENERGY;
t.x = (tke_x << 16) | (ENERGY_T_MS & 0xffff);
t.y = (tke_y << 16) | (ydir << 8) | (xdir & 0xff);
ui_touch_msg_post(&t);
/* printf("tke out %d, %d, %d, %d, %d\n", (t.x & 0xffff), (t.x >> 16), (t.y >> 16), (t.y & 0xff), (t.y >> 8) & 0xff); */
} else {
//在这里发送up消息,因为没有惯性,所以直接发送
ui_touch_msg_post(up_t);
}
memset(&tke, 0xff, sizeof(tke));
}
struct touch_info {
int y;
int x;
int p;
int id;
int count;
};
#if TCFG_LCD_FLIP_ENABLE
#define Y_MIRROR 0
#define X_MIRROR 0
#else
#define Y_MIRROR 1
#define X_MIRROR 1
#endif
#define SW_X_Y 1
#define VK_Y 466
#define VK_X 466
#define VK_Y_MOVE 100//227
#define VK_X_MOVE 100//227
#define VK_X_Y_DIFF 47
#define X_SINGLE_MOVE 0 //单次滑动距离上限阈值,用于过滤多点触摸时误发L/R_MOVE事件 0 关闭 ;阈值,根据tp实际情况调整
#define Y_SINGLE_MOVE 0 //y方向过滤
static void tpd_down(int raw_x, int raw_y, int x, int y, int p)
{
struct touch_event t;
static int first_x = 0;
static int first_y = 0;
static u8 move_flag = 0;
if (x < 0) {
x = 0;
}
if (x > (VK_X - 1)) {
x = VK_X - 1;
}
if (y < 0) {
y = 0;
}
if (y > (VK_Y - 1)) {
y = VK_Y - 1;
}
#if Y_MIRROR
x = VK_X - x - 1;
#endif
#if X_MIRROR
y = VK_Y - y - 1;
#endif
if ((tp_last_staus == ELM_EVENT_TOUCH_DOWN) && (x == first_x) && (y == first_y)) {
tp_down_cnt++;
if (tp_down_cnt < 30) {
return;
}
tp_last_staus = ELM_EVENT_TOUCH_HOLD;
tp_down_cnt = 0;
t.event = tp_last_staus;
t.x = x;
t.y = y;
ui_touch_msg_post(&t);
return;
}
log_info("D[%4d %4d %4d]\n", x, y, p);
if (tp_last_staus != ELM_EVENT_TOUCH_UP) {
int x_move = abs(x - first_x);
int y_move = abs(y - first_y);
if (!move_flag && (x_move >= VK_X_MOVE || y_move >= VK_Y_MOVE) && (abs(x_move - y_move) >= VK_X_Y_DIFF)) {
if (x_move > y_move) {
if (x > first_x) {
tp_last_staus = ELM_EVENT_TOUCH_R_MOVE;
} else {
tp_last_staus = ELM_EVENT_TOUCH_L_MOVE;
}
} else {
if (y > first_y) {
tp_last_staus = ELM_EVENT_TOUCH_D_MOVE;
} else {
tp_last_staus = ELM_EVENT_TOUCH_U_MOVE;
}
}
move_flag = 1;
} else {
if ((x == first_x) && (y == first_y)) {
return;
}
tp_last_staus = ELM_EVENT_TOUCH_MOVE;
/* return; */
}
/* tp_last_staus = ELM_EVENT_TOUCH_HOLD; */
tke_start(x, y);
} else {
tp_last_staus = ELM_EVENT_TOUCH_DOWN;
first_x = x;
first_y = y;
move_flag = 0;
tke_start(x, y);
}
t.event = tp_last_staus;
t.x = x;
t.y = y;
ui_touch_msg_post(&t);
}
static void tpd_up(int raw_x, int raw_y, int x, int y, int p)
{
struct touch_event t = {0};
if (tp_last_staus == ELM_EVENT_TOUCH_UP) {
return ; // 重复
}
if (x < 0) {
x = 0;
}
if (x > (VK_X - 1)) {
x = VK_X - 1;
}
if (y < 0) {
y = 0;
}
if (y > (VK_Y - 1)) {
y = VK_Y - 1;
}
#if Y_MIRROR
x = VK_X - x - 1;
#endif
#if X_MIRROR
y = VK_Y - y - 1;
#endif
log_info("U[%4d %4d %4d]\n", x, y, 0);
tp_last_staus = ELM_EVENT_TOUCH_UP;
tp_down_cnt = 0;
t.event = tp_last_staus;
t.x = x;
t.y = y;
//不在这里发送up消息,在tke_stop里面!!!
/* ui_touch_msg_post(&t); */
tke_stop(x, y, &t);
}
/*****************************************************************************
* reset chip
*****************************************************************************/
static int fts_hw_reset(uint8_t msec)
{
/*firsty. set reset_pin low, and then delay 10ms*/
gpio_write(TCFG_TP_RESET_IO, 0);
fts_msleep(10);
/*secondly. set reset_pin high, and then delay 200ms*/
gpio_write(TCFG_TP_RESET_IO, 1);
fts_msleep(msec);
return 0;
}
/*****************************************************************************
* Initialize i2c
*****************************************************************************/
static int platform_i2c_init(void)
{
/*Initialize I2C bus, you should implement it based on your platform*/
/* iic_init(fts_data->iic_hdl); */
return 0;
}
/*****************************************************************************
* Initialize reset pin
*****************************************************************************/
static int platform_reset_pin_cfg(void)
{
/*Initialize reset_pin, you should implement it based on your platform*/
/*firstly,set the reset_pin to output mode*/
/*secondly,set the reset_pin to low */
if (TCFG_TP_RESET_IO == NO_CONFIG_PORT) {
return -1;
}
/* gpio_set_hd0(TCFG_TP_RESET_IO, 1); //set_strengh 0x01 */
gpio_hw_set_direction(IO_PORT_SPILT(TCFG_TP_RESET_IO), 0); //set dir_output
return 0;
}
/*****************************************************************************
* Initialize gpio interrupt, and set trigger mode to falling edge.
*****************************************************************************/
static int platform_interrupt_gpio_init(void)
{
/*Initialize gpio interrupt , and the corresponding interrupt function is fts_gpio_interrupt_handler,
you should implement it based on your platform*/
/*firstly,set int_pin to input mode with pull-up*/
/*secondly,and set int_pin's trigger mode to falling edge trigger */
if (TCFG_TP_INT_IO == NO_CONFIG_PORT) {
return -1;
}
if (fts_data->int_io_init == 0) {
/* port_edge_wkup_set_callback_by_index(1, fts_gpio_interrupt_handler); // 序号需要和板级配置中的wk_param对应上 */
fts_data->int_io_init = 1;
} else {
/* power_wakeup_index_enable(1, true);//中断使能 */
}
/* gpio_set_pull_up(TCFG_TP_INT_IO, 1); */
/* gpio_set_die(TCFG_TP_INT_IO, 1); */
return 0;
}
/*****************************************************************************
* TP power on
*****************************************************************************/
static void fts_power_on(void)
{
/*refer to ic datasheet*/
/* if (TCFG_TP_POWER_IO == NO_CONFIG_PORT) { */
/* return ; */
/* } */
printf("fts power on\n");
/* gpio_hw_direction_output(IO_PORT_SPILT(TCFG_TP_POWER_IO), 1); */
}
/*****************************************************************************
* Initialize timer and set to interrupt mode which period is 1 second
*****************************************************************************/
static int platform_interrupt_timer_init(void)
{
/*Initialize timer and set to interrupt mode which period is 1 second,
and the corresponding interrupt function is fts_timer_interrupt_handler,
you should implement it based on your platform*/
return 0;
}
/* ------------------------------------------------------------------------------------*/
//* @brief platform_i2c_write
/* ------------------------------------------------------------------------------------*/
u16 platform_i2c_write(u8 *buf, u16 len)
{
u16 i = 0;
iic_start(__this->iic_hdl);
if (0 == iic_tx_byte(__this->iic_hdl, FTS_DEV_SLAVE_ADDR)) {
iic_stop(__this->iic_hdl);
FTS_ERROR("%s tx slave_addr error\n", __func__);
return -1;
}
for (i = 0; i < len; i++) {
/* delay(fts_data ->iic_delay); */
delay(70);
if (0 == iic_tx_byte(__this->iic_hdl, buf[i])) {
iic_stop(__this->iic_hdl);
FTS_ERROR("%s tx buf error\n", __func__);
return -2;
}
}
iic_stop(__this->iic_hdl);
return i;
}
/* ------------------------------------------------------------------------------------*/
//* @brief platform_i2c_read
/* ------------------------------------------------------------------------------------*/
u16 platform_i2c_read(u8 *buf, u16 len)
{
u16 i = 0;
iic_start(__this->iic_hdl);
if (0 == iic_tx_byte(__this->iic_hdl, FTS_DEV_SLAVE_ADDR + 1)) {
iic_stop(__this->iic_hdl);
FTS_ERROR("%s tx slave_addr error\n", __func__);
return -1;
}
for (i = 0; i < len - 1; i++) {
/* delay(fts_data->iic_delay); */
delay(70);
*buf++ = iic_rx_byte(__this->iic_hdl, 1, NULL);
}
i++;
*buf = iic_rx_byte(__this->iic_hdl, 0, NULL);
iic_stop(__this->iic_hdl);
return i;
}
/*****************************************************************************
* Name: fts_write
* Brief:
* Write function via I2C bus, you should implement it based on your platform.
*
* The code below is only a sample code, a pseudo code. you must implement it
* following standard I2C protocol based on your platform
*
*
* Input: @addr: the command or register address
* @data: the data buffer, data buffer can be NULL for commands without data fields.
* @datalen: length of data buffer
* Output:
* Return:
* return 0 if success, otherwise error code.
*****************************************************************************/
int fts_write(uint8_t addr, uint8_t *data, uint16_t datalen)
{
/*TODO based your platform*/
int ret = 0;
uint8_t txbuf[256] = { 0 };
uint16_t txlen = 0;
int i = 0;
if (datalen >= 256) {
FTS_ERROR("txlen(%d) fails", datalen);
return -1;
}
memcpy(&txbuf[0], &addr, 1);
txlen = 1;
if (data && datalen) {
memcpy(&txbuf[txlen], data, datalen);
txlen += datalen;
}
/*call platform_i2c_write function to transfer I2C package to TP controller
*platform_i2c_write() is different for different platform, based on your platform.
*/
for (i = 0; i < 3; i++) {
ret = platform_i2c_write(txbuf, txlen);
if (ret < 0) {
FTS_ERROR("platform_i2c_write(%d) fails,ret:%d,retry:%d", addr, ret, i);
continue;
} else {
ret = 0;
break;
}
}
return ret;
}
/*****************************************************************************
* Name: fts_read
* Brief:
* Read function via I2C bus, you should implement it based on your platform.
*
* The code below is only a sample code, a pseudo code. you must implement it
* following standard I2C protocol based on your platform
*
*
* Input: @addr: the command or register data
* @datalen: length of data buffer
* Output:
* @data: the data buffer read from TP controller
* Return:
* return 0 if success, otherwise error code.
*****************************************************************************/
int fts_read(uint8_t addr, uint8_t *data, uint16_t datalen)
{
/*TODO based your platform*/
int ret = 0;
int i = 0;
if (!data || !datalen) {
FTS_ERROR("data is null, or datalen is 0");
return -1;
}
for (i = 0; i < 3; i++) {
/*call platform_i2c_write function to transfer I2C package to TP controller
*platform_i2c_write() is different for different platform, based on your platform.
*/
ret = platform_i2c_write(&addr, 1);
/* ret = system_iic_read_nbytes(__this->iic_hdl, HYN_I2C_ADDR, &addr, 1, data, datalen); */
if (ret < 0) {
FTS_ERROR("platform_i2c_write(0x%x) fails,ret:%d,retry:%d", addr, ret, i);
continue;
}
/*call platform_i2c_read function to transfer I2C package to read data from TP controller
*platform_i2c_read() is different for different platform, based on your platform.
*/
ret = platform_i2c_read(data, datalen);
if (ret < 0) {
FTS_ERROR("platform_i2c_read(0x%x) fails,ret:%d,retry:%d", addr, ret, i);
continue;
}
ret = 0;
break;
}
return ret;
}
/*****************************************************************************
* Name: fts_write_reg
* Brief:
* write function via I2C bus, you should implement it based on your platform.
*
* The code below is only a sample code, a pseudo code. you must implement it
* following standard I2C protocol based on your platform
*
*
* Input: @addr: the command or register address
* @val: the data write to TP controller
* Return:
* return 0 if success, otherwise error code.
*****************************************************************************/
int fts_write_reg(uint8_t addr, uint8_t val)
{
return fts_write(addr, &val, 1);
}
/*****************************************************************************
* Name: fts_read_reg
* Brief:
* read function via I2C bus, you should implement it based on your platform.
*
* The code below is only a sample code, a pseudo code. you must implement it
* following standard I2C protocol based on your platform
*
*
* Input: @addr: the command or register address
* Output:
* @val: the data read from TP controller
* Return:
* return 0 if success, otherwise error code.
*****************************************************************************/
int fts_read_reg(uint8_t addr, uint8_t *val)
{
return fts_read(addr, val, 1);
}
/*****************************************************************************
* Name: fts_check_id
* Brief:
* The function is used to check id.
* Input:
* Output:
* Return:
* return 0 if check id successfully, otherwise error code.
*****************************************************************************/
static int fts_check_id(void)
{
int ret = 0;
uint8_t chip_id[2] = { 0 };
/*delay 200ms,wait fw*/
fts_msleep(200);
/*get chip id*/
fts_read_reg(FTS_REG_CHIP_ID, &chip_id[0]);
fts_read_reg(FTS_REG_CHIP_ID2, &chip_id[1]);
if ((FTS_CHIP_IDH == chip_id[0]) && (FTS_CHIP_IDL == chip_id[1])) {
FTS_INFO("get ic information, chip id = 0x%02x%02x", chip_id[0], chip_id[1]);
return 0;
}
/*get boot id*/
FTS_INFO("fw is invalid, need read boot id");
ret = fts_write_reg(0x55, 0xAA);
if (ret < 0) {
FTS_ERROR("start cmd write fail");
return ret;
}
fts_msleep(FTS_CMD_START_DELAY);
ret = fts_read(FTS_CMD_READ_ID, chip_id, 2);
if ((ret == 0) && ((FTS_CHIP_IDH == chip_id[0]) && (FTS_CHIP_IDL == chip_id[1]))) {
FTS_INFO("get ic information, boot id = 0x%02x%02x", chip_id[0], chip_id[1]);
ret = 0;
} else {
FTS_ERROR("read boot id fail,read:0x%02x%02x", chip_id[0], chip_id[1]);
return -1;
}
return ret;
}
/*****************************************************************************
* Name: fts_esdcheck_algorithm
* Brief:
* The function is use to esd check.It should be called in timer interrupt handler.
* Input:
* Output:
* Return:
*****************************************************************************/
static void fts_esdcheck_process(void)
{
uint8_t reg_value = 0;
static uint8_t flow_work_hold_cnt = 0;
static uint8_t flow_work_cnt_last = 0;
/* 1. check power state, if suspend, no need check esd */
if (fts_data->suspended == 1) {
FTS_DEBUG("In suspend, not check esd");
/* because in suspend state, when upgrade FW, will
* active ESD check(active = 1); when resume, don't check ESD again
*/
return ;
}
/* 2. In factory mode, can't check esd */
fts_read_reg(FTS_REG_WORKMODE, &reg_value);
if ((reg_value == FTS_REG_WORKMODE_FACTORY_VALUE) || (reg_value == FTS_REG_WORKMODE_SCAN_VALUE)) {
FTS_DEBUG("in factory mode(%x), no check esd", reg_value);
return ;
}
/* 3. get Flow work cnt: 0x91 If no change for 5 times, then ESD and reset */
fts_read_reg(FTS_REG_FLOW_WORK_CNT, &reg_value);
if (flow_work_cnt_last == reg_value) {
flow_work_hold_cnt++;
} else {
flow_work_hold_cnt = 0;
}
flow_work_cnt_last = reg_value;
/* 4. If need hardware reset, then handle it here */
if (flow_work_hold_cnt >= 5) {
FTS_DEBUG("ESD, Hardware Reset");
flow_work_hold_cnt = 0;
fts_hw_reset(200);
}
}
/*****************************************************************************
* Name: fts_gesture_process
* Brief:
* The function is used to read and parse gesture information. It should be
* called in gpio interrupt handler while system is in suspend state.
* Input:
* Output:
* Return:
* return 0 if getting and parsing gesture successfully,
* return 1 if gesture isn't enabled in FW,
* otherwise error code.
*****************************************************************************/
static int fts_gesture_process(void)
{
int ret = 0;
uint8_t i = 0;
uint8_t base = 0;
uint8_t regaddr = 0;
uint8_t value = 0xFF;
uint8_t buf[MAX_LEN_GESTURE_INFO] = { 0 };
uint8_t gesture_id = 0;
/*Read a byte from register 0xD0 to confirm gesture function in FW is enabled*/
ret = fts_read_reg(FTS_REG_GESTURE_EN, &value);
if ((ret < 0) || (value != FTS_REG_GESTURE_ENABLE)) {
FTS_DEBUG("gesture isn't enable in fw, don't process gesture");
return 1;
}
/*Read 26 bytes from register 0xD3 to get gesture information*/
regaddr = FTS_REG_GESTURE_OUTPUT_ADDRESS;
ret = fts_read(regaddr, buf, MAX_LEN_GESTURE_INFO);
if (ret < 0) {
FTS_DEBUG("read gesture information from reg0xD3 fails");
return ret;
}
/*get gesture_id, and the gestrue_id table provided by our technicians */
gesture_id = buf[0];
/* Now you have parsed the gesture information, you can recognise the gesture type based on gesture id.
* You can do anything you want to do, for example,
* gesture id 0x24, so the gesture type id "Double Tap", now you can inform system to wake up
* from gesture mode.
*/
/*TODO...(report gesture to system)*/
return 0;
}
/*****************************************************************************
* print touch buffer read from register address 0x01
*****************************************************************************/
static void log_touch_buf(uint8_t *buf, uint32_t buflen)
{
int i = 0;
int count = 0;
char tmpbuf[512] = { 0 };
for (i = 0; i < buflen; i++) {
count += snprintf(tmpbuf + count, 1024 - count, "%02X,", buf[i]);
if (count >= 1024) {
break;
}
}
FTS_DEBUG("point buffer:%s", tmpbuf);
}
/*****************************************************************************
* print touch points information of this interrupt
*****************************************************************************/
static void log_touch_info(struct fts_ts_event *events, uint8_t event_nums)
{
uint8_t i = 0;
for (i = 0; i < event_nums; i++) {
FTS_DEBUG("[%d][%d][%d,%d,%d]%d", events[i].id, events[i].type, events[i].x,
events[i].y, events[i].p, events[i].area);
}
}
/*****************************************************************************
* Name: fts_touch_process
* Brief:
* The function is used to read and parse touch points information. It should be
* called in gpio interrupt handler while system is in display(normal) state.
* Input:
* Output:
* Return:
* return 0 if getting and parsing touch points successfully, otherwise error code.
*****************************************************************************/
static int fts_touch_process(void)
{
int ret = 0;
uint8_t i = 0;
uint8_t base = 0;
uint8_t regaddr = 0x01;
uint8_t buf[MAX_LEN_TOUCH_INFO];/*A maximum of two points are supported*/
uint8_t point_num = 0;
uint8_t touch_event_nums = 0;
uint8_t point_id = 0;
struct fts_ts_event events[FTS_MAX_POINTS_SUPPORT]; /* multi-touch */
/*read touch information from reg0x01*/
ret = fts_read(regaddr, buf, MAX_LEN_TOUCH_INFO);
if (ret < 0) {
FTS_DEBUG("Read touch information from reg0x01 fails");
return ret;
}
/*print touch buffer, for debug usage*/
log_touch_buf(buf, MAX_LEN_TOUCH_INFO);
if ((buf[1] == 0xFF) && (buf[2] == 0xFF) && (buf[3] == 0xFF)) {
FTS_INFO("FW initialization, need recovery");
if (fts_data->gesture_support && fts_data->suspended) {
fts_write_reg(FTS_REG_GESTURE_EN, FTS_REG_GESTURE_ENABLE);
}
}
/*parse touch information based on register map*/
memset(events, 0xFF, sizeof(struct fts_ts_event) * FTS_MAX_POINTS_SUPPORT);
point_num = buf[1] & 0x0F;
if (point_num > FTS_MAX_POINTS_SUPPORT) {
FTS_DEBUG("invalid point_num(%d)", point_num);
return -1;
}
for (i = 0; i < MAX_POINTS_TOUCH_TRACE; i++) {
base = 2 + i * 6;
point_id = buf[base + 2] >> 4;
if (point_id >= MAX_POINTS_TOUCH_TRACE) {
break;
}
events[i].x = ((buf[base] & 0x0F) << 8) + buf[base + 1];
events[i].y = ((buf[base + 2] & 0x0F) << 8) + buf[base + 3];
events[i].id = point_id;
events[i].type = (buf[base] >> 6) & 0x03;
events[i].p = buf[base + 4];
events[i].area = buf[base + 5];
if (((events[i].type == 0) || (events[i].type == 2)) & (point_num == 0)) {
FTS_DEBUG("abnormal touch data from fw");
return -2;
}
touch_event_nums++;
}
if (touch_event_nums == 0) {
FTS_DEBUG("no touch point information(%02x)", buf[1]);
return -3;
}
/*print touch information*/
log_touch_info(events, touch_event_nums);
/*Now you have get the touch information, you can report anything(X/Y coordinates...) you want to system*/
/*TODO...(report touch information to system)*/
/*Below sample code is a pseudo code*/
for (i = 0; i < touch_event_nums; i++) {
if ((events[i].type == 0) || (events[i].type == 2)) {
/* The event of point(point id is events[i].id) is down event, the finger of this id stands for is
* pressing on the screen.*/
/*TODO...(down event)*/
#if SW_X_Y
tpd_down(events[i].y, events[i].x, events[i].y, events[i].x, 0);
#else
tpd_down(events[i].x, events[i].y, events[i].x, events[i].y, 0);
#endif
} else {
/*TODO...(up event)*/
#if SW_X_Y
tpd_up(events[i].y, events[i].x, events[i].y, events[i].x, 0);
#else
tpd_up(events[i].x, events[i].y, events[i].x, events[i].y, 0);
#endif
}
}
return 0;
}
/*****************************************************************************
* An interrupt handler, will be called while the voltage of INT Port changes from high to low(falling-edge trigger mode).
* The program reads touch data or gesture data from TP controller, and then sends it into system.
*****************************************************************************/
static int fts_gpio_interrupt_handler(void)
{
int ret = 0;
if (fts_data->gesture_support && fts_data->suspended) {
/*if gesture is enabled, interrupt handler should process gesture at first*/
ret = fts_gesture_process();
if (ret == 0) {
FTS_DEBUG("success to process gesture.");
return 1;
}
}
/*if gesture isn't enabled, the handler should process touch points*/
fts_touch_process();
return 0;
}
/*****************************************************************************
* An interrupt handler, will be called while the timer interrupt trigger , the code based on your platform.
* The program use to check esd.
*****************************************************************************/
static void fts_timer_interrupt_handler(void)
{
/* esd check */
fts_esdcheck_process();
}
/*****************************************************************************
* Name: fts_suspend
* Brief: System suspends and update the suspended state
* Input:
* Output:
* Return:
* return 0 if enter suspend successfully, otherwise error code
*****************************************************************************/
int fts_ts_suspend(void)
{
int ret = 0;
if (fts_data->suspended) {
FTS_INFO("Already in suspend state");
return 0;
}
if (fts_data->gesture_support) {
/*Host writes 0x01 to register address 0xD0 to enable gesture function while system suspends.*/
ret = fts_write_reg(FTS_REG_GESTURE_EN, FTS_REG_GESTURE_ENABLE);
if (ret < 0) {
FTS_ERROR("enable gesture fails.ret:%d", ret);
} else {
FTS_INFO("enable gesture success.");
}
} else {
/*Host writes 0x03 to register address 0xA5 to enter into sleep mode.*/
ret = fts_write_reg(FTS_REG_POWER_MODE, 0x03);
if (ret < 0) {
FTS_ERROR("system enter sleep mode fails.ret:%d", ret);
} else {
FTS_INFO("system enter sleep mode success.");
}
}
fts_data->suspended = 1;
return 0;
}
/*****************************************************************************
* Name: fts_resume
* Brief: System resume and update the suspended state
* Input:
* Output:
* Return:
* return 0 if enter resume successfully, otherwise error code
*****************************************************************************/
int fts_ts_resume(void)
{
if (!fts_data->suspended) {
FTS_INFO("Already in resume state");
return 0;
}
fts_data->suspended = 0;
fts_hw_reset(200);
touch_info_reset();
return 0;
}
/* static void ctp_delay_us(unsigned int time) */
/* { */
/* udelay(time); */
/* } */
/* static void ctp_delay_ms(unsigned short time) */
/* { */
/* if (time >= 10) { */
/* os_time_dly(time / 10 + 1); */
/* } else { */
/* mdelay(time); */
/* } */
/* } */
/*------------------
* tp中断配置
* -----------------*/
static void tp_irq_callback(enum gpio_port port, u32 pin, enum gpio_irq_edge edge)
{
printf("port%d.%d:%d-cb1\n", port, pin, edge);
touch_flag = 1;
os_sem_post(&touch_sem);
}
struct gpio_irq_config_st tp_int_irq_config = {
.pin = NO_CONFIG_PORT,
.irq_edge = PORT_IRQ_EDGE_FALL,
.callback = tp_irq_callback,
.irq_priority = 3,
};
void tp_irq_init(void)
{
u8 port_x = TCFG_TP_INT_IO / IO_GROUP_NUM;
u16 port_pin_x = BIT(TCFG_TP_INT_IO % IO_GROUP_NUM);
tp_int_irq_config.pin = port_pin_x;
log_debug("[%s] port_x:%x, port_pin_x:%x", __func__, port_x, port_pin_x);
gpio_set_mode(IO_PORT_SPILT(TCFG_TP_INT_IO), PORT_INPUT_PULLUP_10K);
gpio_irq_config(port_x, &tp_int_irq_config);
}
void tp_irq_deinit(void)
{
gpio_irq_deinit(IO_PORT_SPILT(TCFG_TP_INT_IO));
gpio_set_mode(IO_PORT_SPILT(TCFG_TP_INT_IO), PORT_HIGHZ);
}
static void fts_ts_hw_init(void)
{
u8 buf[1];
u8 reg_addr[1];
int i;
int ret;
memset(&tke, 0xff, sizeof(tke));
printf("fts3x68 init...\n");
ret = system_iic_init(__this->iic_hdl, get_iic_config(__this->iic_hdl));
printf("[%s] iic_init ret:%d", __func__, ret);
platform_reset_pin_cfg();
fts_power_on();
fts_hw_reset(0);
touch_info_reset();
/* ctp_delay_ms(100); */
fts_check_id();
/* buf[0] = 0; */
/* reg_addr[0] = 0xa7; */
/* system_iic_read_nbytes(__this->iic_hdl, HYN_I2C_ADDR, &reg_addr[0], 1, &buf[0], 1); */
/* printf("tp id : 0x%x\n", buf[0]); */
/* buf[0] = 0; */
/* reg_addr[0] = 0xa9; */
/* system_iic_read_nbytes(__this->iic_hdl, HYN_I2C_ADDR, &reg_addr[0], 1, &buf[0], 1); */
/* printf("tp version : 0x%x\n", buf[0]); */
/* if (!fts_ts_upgrade()) { */
/* log_error("fts3x68 upgrade fail!\n"); */
/* } */
/* fts_hw_reset(2); */
}
static void touch_interupt_task(void *p)
{
int sem_timeout = 0;
fts_ts_hw_init();
os_sem_create(&touch_sem, 0);
tp_irq_init();
while (1) {
tp_step = TP_RUN_STEP_RUN_IDLE;
os_sem_pend(&touch_sem, sem_timeout);
tp_step = TP_RUN_STEP_RUN;
if (touch_flag) {
touch_flag = 0;
/* fts_read(); */
fts_touch_process();
/* fts_esdcheck_process(); */
}
}
}
void fts_ts_wait_init()
{
while (tp_step == TP_RUN_STEP_INIT) {
os_time_dly(1);
}
}
void fts_ts_init(void)
{
tp_step = TP_RUN_STEP_INIT;
task_create(touch_interupt_task, NULL, "touch_task");
}
void fts_ts_deinit()
{
fts_ts_wait_init();
}
static u8 tp_idle_query(void)
{
return !tp_step;
}
REGISTER_LP_TARGET(tp_lp_target) = {
.name = "tp_cst816d",
.is_idle = tp_idle_query,
};
#endif