文章目录
前言
TP是TouchPad触摸屏的意思,触控屏(Touchpanel)又称为触控面板,是个可接收触头等输入讯号的感应式液晶显示装置。
根据传感器的类型,触摸屏大致被分为红外线式、电阻式、表面声波式和电容式触摸屏四种
- 红外线式触摸屏:红外线式触摸屏在显示器的前面安装一个电路板外框,电路板在屏幕四边排布红外发射管和红外接收管,一一对应形成横竖交叉的红外线矩阵。用户在触摸屏幕时,手指就会挡住经过该位置的横竖两条红外线,因而可以判断出触摸点在屏幕的位置。任何触摸物体都可改变触点上的红外线而实现触摸屏操作。
- 电阻式触摸屏:电阻屏最外层一般使用的是软屏,用指尖或任何物体按压外层,通过按压让内两层氧化铟锡(IndiumTinOxides,ITO)相碰导电从而定位到按压点的坐标来实现操控。一般是不能多点触控,即只能支持单点,若同时按压两个或两个以上的触点,是不能被识别和找到精确坐标的。
- 表面声波式触摸屏:表面声波是超声波的一种,它是在介质(例如玻璃)表面进行浅层传播的机械能量波。表面声波性能稳定、易于分析,并且在横波传递过程中具有非常尖锐的频率特性。玻璃屏的左上角和右下角各固定了竖直和水平方向的超声波发射换能器, 右上角则固定了两个相应的超声波接收换能器,玻璃屏的四边刻有由疏到密间隔非常精密的45度角反射条纹。在没有触摸的时候,接收信号的波形与参照波形完全一样。当手指触摸屏幕时,手指吸收了一部分声波能量,控制器侦测到接收信号在某一时刻的衰减,由此可以计算出触摸点的位置。
- 电容式触摸屏:这种触摸屏是利用人体的电流感应进行工作的,在玻璃表面贴上一层透明的特殊金属导电物质,当有导电物体触碰时,就会改变触点的电容,从而可以探测出触摸的位置,但是使用绝缘物体触摸的时候没有反应,因为没有电流。
本文主要针对电容式触摸屏(Y89415)进行说明。
硬件构成
TP类型的结构层次,如下图所示:
TP原理
用户触摸电容屏时,由人体电场,手指和工作面形成一个耦合电容,手指吸收走从屏的四个角上的电极中流出的电流,位于触摸屏幕后的控制器便会计算电流的比例及强弱,准确算出触摸点的位置。可以达到99%的精确度,具备小于3ms的响应速度。
主要硬件组成
主板(UM9117芯片),TP屏Y89415(CHSC6540芯片),IIC连接线(SCL, SDA), 中断连接线INT等
通讯流程
具体的通讯流程如下图所示
引脚配置
从硬件部或者硬件资料上,确认芯片对应的INT引脚和RST引脚,进行硬件引脚的配置。
// 在pinmap_cfg.c 里面增加中断引脚和电源引脚模式配置
// 15引脚(CTP INT) 默认高电平 输入模式 边沿触发
{15, 1, PM_INPUT, PM_FALLING_EDGE},
// 34引脚(CTP RST) 默认低电平 输出模式 无触发
{34, 0, PM_OUTPUT, PM_INVALID_INT},
// 在gpio_cfg.c 里面增加中断引脚和电源引脚触发配置 默认低电平
// ID号 类型baseband 有效值低电平 引脚号 回调函数
{GPIO_PROD_TP_INT_ID, GPIO_PROD_TYPE_BB0, GPIO_PROD_LOW_LEVEL, 15, PNULL},
{GPIO_PROD_TP_WAKE_ID, GPIO_PROD_TYPE_BB0, GPIO_PROD_LOW_LEVEL, 34, PNULL},
通讯方式(IIC)
关于IIC的具体介绍可以查考该文章 IIC总线详解
根据具体的硬件接线,选择I2C1作为通讯接口进行参数配置如下:
struct SPRD_I2C sprd_i2c[I2C_DEVICE_MAX] = {
{
/*i2c0*/
CTL_I2C0_BASE,
400000,
0,
12,
1,
TB_I2C0_INT,
REG_AP_CLK_CORE_CGM_I2C0_CFG,
I2C_26M,
REG_AP_APB_APB_EB,
BIT_AP_APB_I2C0_EB,
REG_AP_APB_APB_RST,
BIT_AP_APB_I2C0_SOFT_RST,
1,
DMA_I2C0_TX,
DMA_I2C0_RX,
},
{
CTL_I2C1_BASE,
400000, // freq 标准速度(最高速度100kHZ),快速(最高400kHZ)
1, // phy_id 物理ID号
12, // full_thd
1, // empty_thd
TB_I2C1_INT, // irq 中断引脚
REG_AP_CLK_CORE_CGM_I2C1_CFG, // clk_base 注册时钟
I2C_26M, // clk 时钟晶振
REG_AP_APB_APB_EB, // i2c_apb_base
BIT_AP_APB_I2C1_EB, // eb
REG_AP_APB_APB_RST, // i2c_apb_base2
BIT_AP_APB_I2C1_SOFT_RST, // eb2
1, // dma_enable 开启DMA
DMA_I2C1_TX, // tx_chn_rq DMA传输通道
DMA_I2C1_RX, // rx_chn_rq DMA传输通道
},
};
// i2c_phy.c i2c初始化
void sprd_i2c_hw_init(void)
{
int num;
for (num = 0; num < I2C_DEVICE_USED_MAX; num++) {
sprd_i2c_init(num);
SCI_TraceLow("i2c%d init !\n", num);
}
}
// 对上面设置的参数 一一进行初始化
LOCAL int sprd_i2c_init(int phy_id)
{
struct SPRD_I2C *pi2c = &sprd_i2c[phy_id];
struct i2c_adapter *adap = &i2c_adapter[phy_id];
struct i2cDev *i2c = &(adap->i2c);
adap->sprd_i2c = pi2c;
i2c->index = phy_id;
i2c->algo = &sprd_i2c_algo;
sprd_i2c_ahb_enable(pi2c);
sprd_i2c_set_clk(pi2c);
sprd_i2c_set_scl(pi2c, pi2c->freq);
sprd_i2c_reg_init(pi2c);
sprd_i2c_reset_fifo(pi2c);
tx_mutex_create(&i2c->mutex, "I2C SYNC MUTEX", SCI_INHERIT);
#ifndef HX_DRV_IIC_USEPOLL
tx_queue_create(&(pi2c->i2c_queue), "i2c_wait", TX_1_ULONG, &pi2c->ret,
sizeof(pi2c->ret));
ISR_RegHandler(pi2c->irq, sprd_i2c_irq_handle);
CHIPDRV_EnableIRQINT(pi2c->irq);
#endif
CHIP_REG_OR(pi2c->base + I2C_CTL, I2C_EN);
//备份寄存器,存储I2C的控制器信息,方便休眠的时候快速恢复
i2c_reg_backup[phy_id][0] = REG32(pi2c->base + I2C_CTL);
i2c_reg_backup[phy_id][1] = REG32(pi2c->base + ADDR_DVD0);
i2c_reg_backup[phy_id][2] = REG32(pi2c->base + ADDR_DVD1);
i2c_reg_backup[phy_id][3] = REG32(pi2c->base + ADDR_STA0_DVD);
sprd_i2c_ahb_disable(pi2c);
return 0;
}
中断初始化
配置中断触发函数,关联到GPIO_PROD_TP_INT_ID,此时如果有触摸TP屏幕,就会产生中断,
执行回调函数_TPM_IntHandler
// 在tp_multi_drv.c 里面增加中断引脚触发函数配置
LOCAL BOOLEAN _TPM_MountGPIOHandler(void)
{
if (GPIO_PROD_RegGpio(
GPIO_PROD_TP_INT_ID, // id
SCI_FALSE, // direction SCI_FALSE: Input dir; SCI_TRUE: output dir
SCI_FALSE, // default_value
SCI_FALSE, // shaking_en
0, // shaking_interval
(GPIO_PROD_CALLBACK)_TPM_IntHandler)) { // gpio_callback_fun
GPIO_TPIRQCtrl(SCI_TRUE); // 开中断
return TRUE;
}
return FALSE;
}
来到_TPM_IntHandler函数,当产生中断的时候,会执行该函数,在这个地方,当触发中断的时候,会发送消息队列,通知P_TPP线程,有中断到来,该去执行对应操作。
// 在tp_multi_drv.c 中断函数处理
LOCAL void _TPM_IntHandler(uint32 int_id, uint32 int_state)
{
TPD_TO_TPSVR_MSG_T *tp_to_tpsvr_sig_ptr = SCI_NULL;
tp_to_tpsvr_sig_ptr =
(TPD_TO_TPSVR_MSG_T *)SCI_ALLOCA(sizeof(TPD_TO_TPSVR_MSG_T));
SCI_ASSERT(SCI_NULL != tp_to_tpsvr_sig_ptr);
tp_to_tpsvr_sig_ptr->SignalSize = sizeof(TPD_TO_TPSVR_MSG_T);
tp_to_tpsvr_sig_ptr->Sender = P_TPP;
//发送信息到P_TPP线程进行处理
tp_to_tpsvr_sig_ptr->SignalCode = TP_ENTER_INTO_GPIO_HANDLER;
tp_to_tpsvr_sig_ptr->tpd_msg_valu = TP_ENTER_INTO_GPIO_HANDLER;
SCI_SendSignal((xSignalHeader)tp_to_tpsvr_sig_ptr, P_TPP);
}
TP屏幕逻辑主线程
中断触发之后,来到P_TPP主线程这里, 可以看到,该线程一直持续运行,运行过程中,不断去读取信号量的值,然后通过标志位的值来判断是否进入线程执行对应操作。
// tp_multi_srv.c里 P_TPP线程持续运行
THREAD_ENTRY(P_TPP)
{
TPD_TO_TPSVR_MSG_T *tp_from_tpd_sig_ptr = SCI_NULL;
SCI_CreateClientList(TP_SERVICE, (TP_MSG_MAX_NUM & 0x0ff),
(REG_CALLBACK)TOUCHPANEL_default);
TPC_Init();
DIAG_RegisterCmdRoutine(DIAG_TP_F, _TP_Hit);
while (1) {
//根据不同信息 进行事件处理
tp_from_tpd_sig_ptr = (TPD_TO_TPSVR_MSG_T *)SCI_GetSignal(P_TPP);
if ((tp_from_tpd_sig_ptr->tpd_msg_valu == TP_ENTER_INTO_GPIO_HANDLER) &&
(tp_from_tpd_sig_ptr->SignalCode == TP_ENTER_INTO_GPIO_HANDLER)) {
HandleTPMessage();
} else if ((tp_from_tpd_sig_ptr->tpd_msg_valu == TP_ENTER_ENABLE) &&
(tp_from_tpd_sig_ptr->SignalCode == TP_ENTER_ENABLE)) {
//灭屏休眠时候恢复
TPC_Resume();
} else if ((tp_from_tpd_sig_ptr->tpd_msg_valu == TP_ENTER_DISABLE) &&
(tp_from_tpd_sig_ptr->SignalCode == TP_ENTER_DISABLE)) {
TPC_Suspend();
} else if ((tp_from_tpd_sig_ptr->tpd_msg_valu == TP_ENTER_CMD) &&
(tp_from_tpd_sig_ptr->SignalCode == TP_ENTER_CMD)) {
TPC_Ioctl(tp_from_tpd_sig_ptr->cmd, tp_from_tpd_sig_ptr->arg);
}
SCI_FREE(tp_from_tpd_sig_ptr);
}
}
// tp_multi_drv.c里 初始化TP
PUBLIC uint32 TPC_Init(void)
{
uint8 rc = TP_MULTI_ERROR;
//如果当前设备为空
if (PNULL == s_tpc_operations) {
int32 i;
TPC_OPERATION_T **tpc_ops = PNULL;
//加载TP设备驱动表
tpc_ops = (TPC_OPERATION_T **)TPC_GetOpsTab();
//通过初始化是否成功,自动匹配TP设备
for (i = 0; i < (int32)(TPC_GetOpsTabLen() - 1); i++) {
if (PNULL != tpc_ops[i]) {
if (PNULL != (tpc_ops[i]->init)) {
if (TP_MULTI_SUCCESS == (tpc_ops[i]->init())) {
s_tpc_operations = tpc_ops[i];
break;
}
}
}
}
// 如果没有一个匹配的 就报错
if (i >= (int32)(TPC_GetOpsTabLen() - 1)) {
rc = TP_MULTI_ERROR;
}
} else {
//如果是已经加载完设备 直接进行初始化
if (s_tpc_operations->init &&
(TP_MULTI_SUCCESS == s_tpc_operations->init())) {
rc = TP_MULTI_SUCCESS;
} else {
rc = TP_MULTI_SUCCESS;
}
}
//中断使能 _TPM_MountGPIOHandler 就是在这里进行初始化
_TPM_Enable_Interrupt();
return 0;
}
// tp_multi_drv.c里 具体初始化实现
LOCAL void _TPM_Enable_Interrupt(void)
{
GPIO_TPIRQCtrl(SCI_FALSE);
pm_level_valid = GPIO_TPGetInterruptStatus();
GPIO_ClearIntStatus((uint32)GPIO_TPInterruptPin());
GPIO_TPInterruptSense(pm_level_valid);
_TPM_MountGPIOHandler();
}
// tp_multi_cfg.c里 配置对应的操作设备
LOCAL const TPC_OPERATION_T *TPC_OPS_TAB[] = {
#if defined(CAP_TP_SUPPORT_TLSC6X)
&tpc_tlsc6x_ops, //增加tlsc6x 这款设备匹配
#endif
PNULL};
// tp_multi_cfg.c里 通过驱动表的方式来实现对不同款TP设备的支持
PUBLIC TPC_OPERATION_T **TPC_GetOpsTab(void)
{
return (TPC_OPERATION_T **)TPC_OPS_TAB;
}
TP触屏信息进行记录和上报
该函数里面实现对已经读取到的TP触屏信息进行记录和上报
// tp_multi_srv.c里 处理消息
void HandleTPMessage(void)
{
uint32 app_queue_avilable = 0;
GPIO_TPIRQCtrl(SCI_FALSE); //关掉TP中断 防止处理事件的过程中 中断来临
if (_TPM_Get_LastStatus() == TP_UP_MSG) {
// Disable APB sleep
SCI_TPC_EnableDeepSleep(DISABLE_APB_SLEEP);
}
TPC_Read(&tpc_total_data); //读取TP寄存器的消息, X, Y DOWN UP MOVE等信息
if (tpc_total_data.SignalCode == TP_DOWN_MSG) {
_TPM_EnSet_Gesture();
}
if ((tpc_total_data.gesture_type > TG_NO_DETECT) &&
(tpc_total_data.gesture_type < TG_UNKNOWN_STATE)) {
if ((_TPM_Get_LastStatus() != TP_GESTURE_MSG)) {
tpc_total_data.SignalCode = TP_GESTURE_MSG;
_TPM_DisSet_Gesture();
} else {
tpc_total_data.SignalCode = TP_MOVE_MSG;
_TPM_Set_LastStatus(tpc_total_data.SignalCode);
}
}
// 确认APP线程有空闲 上报消息通知TP_SERVICE处理
app_queue_avilable = get_app_queue_available();
if (app_queue_avilable > 12) {
if (tpc_total_data.SignalCode == TP_MOVE_MSG) {
if ((IsMove()) && (_TPM_Get_GestureStatus())) {
_TPM_Set_LastStatus(tpc_total_data.SignalCode);
SCI_SendEventToClient(TP_SERVICE, tpc_total_data.SignalCode,
(void *)&tpc_total_data);
}
} else if (((_TPM_Get_LastStatus() != TP_UP_MSG) &&
(tpc_total_data.SignalCode == TP_UP_MSG)) ||
(tpc_total_data.SignalCode != TP_UP_MSG)) {
_TPM_Set_LastStatus(tpc_total_data.SignalCode);
SCI_SendEventToClient(TP_SERVICE, tpc_total_data.SignalCode,
(void *)&tpc_total_data);
}
}
// make sure the up signal must be sent out, meanwhile, repeated up signal
// should be discard;
else if ((_TPM_Get_LastStatus() != TP_UP_MSG) &&
(tpc_total_data.SignalCode == TP_UP_MSG)) {
_TPM_Set_LastStatus(tpc_total_data.SignalCode);
SCI_SendEventToClient(TP_SERVICE, tpc_total_data.SignalCode,
(void *)&tpc_total_data);
}
while (get_app_queue_available() < 12) {
SCI_Sleep(50);
}
if (tpc_total_data.SignalCode == TP_UP_MSG) {
SCI_TPC_EnableDeepSleep(ENABLE_APB_SLEEP);
}
GPIO_TPIRQCtrl(SCI_TRUE); //处理完成后 恢复中断
}
读取TP触屏信息并解析
当收到中断通知的时候,芯片可以通过读取寄存器的值,来获取对应的X, Y坐标以及手势操作(按下,保持,抬起)
根据对应的操作和坐标来对应的进行LCD屏幕上面的效果显示。
Y89415TP这款设备, 对应的寄存器说明如下图所示
当手指触摸在屏幕外的虚拟按键时候,显示的坐标如下所示
/* 按照TP键盘位置排列 X= Y=900 始终不变 */
LOCAL const uint8 xy_keymap[] = {
NULL, /* 按照TP键盘位置排列 */
SCI_VK_MENU_SELECT, SCI_VK_UP, SCI_VK_MENU_CANCEL,
SCI_VK_LEFT, SCI_VK_WEB, SCI_VK_RIGHT,
SCI_VK_CALL, SCI_VK_DOWN, SCI_VK_POWER,
SCI_VK_1, SCI_VK_2, SCI_VK_3,
SCI_VK_4, SCI_VK_5, SCI_VK_6,
SCI_VK_7, SCI_VK_8, SCI_VK_9,
SCI_VK_STAR, SCI_VK_0, SCI_VK_POUND,
};
当手指触摸在屏幕内的时候,会显示对应的坐标
因为该LCD屏幕比较小,设置成只支持单点触碰的模式,那么只需要读取一个点击操作即可(双点16个数据)
读取坐标的方式是,先去往寄存器地址0x5C写入00,然后再去读取0X5D的地址,连续读取8个数据长度
根据上图可以得出算法如下:
/* 坐标的计算方式 */
#define XY_Coordinate_Transform(_data, _high_data, _low_data) \
do { \
_data = ((_high_data & 0xf) << 8) | _low_data; \
} while (0)
LOCAL uint32 TLSC6X_XY_Data(void)
{
uint8 Reg_Data[8] = {0};
if (TLSC6X_I2C_Write_Read(&tlsc6x_touch, 0x5c5d, Reg_Data, 1, Reg_Data, 8) <
0) {
return TP_MULTI_ERROR;
}
memset(XY_Coordinate, 0, sizeof(XY_Coordinate));
memset(event_count, 0, sizeof(event_count));
XY_Coordinate_Transform(XY_Coordinate[0].x_position, Reg_Data[3],
Reg_Data[4]);
XY_Coordinate_Transform(XY_Coordinate[0].y_position, Reg_Data[5],
Reg_Data[6]);
XY_Coordinate[0].finger_id =
(Reg_Data[5] & 0xf0) >> 4; /*low byte is Touch ID*/
switch ((Reg_Data[3] & 0xF0)) {
case 0x80: // pen move
XY_Coordinate[0].finger_id |= ((uint16)TP_PEN_MOVE) << 8;
event_count[1]++;
break;
case 0x40: // pen up
XY_Coordinate[0].finger_id |= ((uint16)TP_PEN_UP) << 8;
event_count[2]++;
break;
case 0x00: // pen down
XY_Coordinate[0].finger_id |= ((uint16)TP_PEN_DOWN) << 8;
event_count[0]++;
break;
default:
XY_Coordinate[0].finger_id |= ((uint16)TP_PEN_NONE) << 8;
break;
}
}
LOCAL uint32 TLSC6X_Read(TPDSVR_SIG_T *data)
{
TLSC6X_XY_Data();
data->x_key = XY_Coordinate[0].x_position;
data->y_key = XY_Coordinate[0].y_position;
data->cur_index = XY_Coordinate[0].finger_id;
data->gesture_type = TG_NO_DETECT;
data->num_of_point = Finger_Num;
//对读到的手势信息 进行分析
switch (_Get_Cal_Msg()) {
case TP_PEN_DOWN:
data->SignalCode = TP_DOWN_MSG;
break;
case TP_PEN_UP:
data->SignalCode = TP_UP_MSG;
TLSC6X_get_chip_info(&tlsc6x_touch);
break;
case TP_PEN_MOVE:
data->SignalCode = TP_MOVE_MSG;
break;
default:
data->SignalCode = TP_MAX_MSG;
break;
}
return TP_MULTI_SUCCESS;
}
TP触屏信息进行分析处理
在TPCallBackFunc回调函数里面去处理HandleTPMessage上报的信息, 判断按键的信息是在屏幕内还是屏幕外,并且对坐标进行适当的校准。
//初始化TP_SERVICE
LOCAL void APP_RegisterRefService(void)
{
// Register TP event
SCI_RegisterMsg(TP_SERVICE, (uint8)TP_DOWN, (uint8)(TP_MSG_MAX - 1),
TPCallBackFunc);
}
//后续的执行都放在回调函数里面进行
LOCAL void TPCallBackFunc(BLOCK_ID id, uint32 argc, void *argv)
{
#if defined(TOUCH_PANEL_SUPPORT) || defined(TOUCH_PANEL_CTRL_SUPPORT)
TPDSVR_SIG_T *tp_ptr = PNULL;
MmiTPPress *sendSignal = PNULL;
uint16 single_code = (uint16)(argc & 0xFFFF);
uint32 state = 0;
#ifndef TOUCH_PANEL_HWICON_SUPPORT_NONE
GUI_POINT_T up_point = {0};
GUI_RECT_T lcd_rect = {0};
LCD_INFO_T lcd_info = {0};
uint16 hw_icon_num = 0;
uint32 i;
uint32 key_code = 0;
uint16 key_single_code = 0;
uint16 keyup_single_code = 0;
MmiKeyPress *KeysendSignal = PNULL;
#endif
//这里获取到即 tpc_total_data 上报的参数
tp_ptr = (TPDSVR_SIG_T *)argv;
if (PNULL == tp_ptr) {
return;
}
if (TP_DOWN == single_code) {
state = MMI_TP_DOWN;
#ifndef TOUCH_PANEL_HWICON_SUPPORT_NONE
//记录TP_DOWN起始点
s_tp_down_point.x = tp_ptr->x_key;
s_tp_down_point.y = tp_ptr->y_key;
#endif
} else if (TP_UP == single_code) {
state = MMI_TP_UP;
#ifndef TOUCH_PANEL_HWICON_SUPPORT_NONE
//记录up点
up_point.x = tp_ptr->x_key;
up_point.y = tp_ptr->y_key;
#endif
} else if (TP_MOVE == single_code) {
state = MMI_TP_MOVE;
} else if (TP_GESURE == single_code) {
state = MMI_TP_GESTURE;
} else {
return;
}
// check the TP message
#ifdef TOUCH_PANEL_HWICON_SUPPORT_NONE
if (MMK_CheckTPSignale((uint16)state, tp_ptr->x_key, tp_ptr->y_key)) {
MmiCreateSignal((uint16)state, sizeof(MmiTPPress),
(MmiSignalS **)&sendSignal);
sendSignal->x = tp_ptr->x_key;
sendSignal->y = tp_ptr->y_key;
sendSignal->gesture_type = tp_ptr->gesture_type;
sendSignal->Sender = P_TPP;
MmiSendSignal(TASK_FL_ID, (MmiSignalS *)sendSignal);
} else {
// SCI_TRACE_LOW:"TPCallBackFunc: the TP message is too much!"
SCI_TRACE_ID(TRACE_TOOL_CONVERT, MMIMAIN_666_112_2_18_2_0_28_265,
(uint8 *)"");
}
#else
//为实现TP虚拟物理按键,需要考虑如下情形:
// 1.down point在LCD区域内,up point在LCD区域内, 正常发tp消息;
// 2.down point在LCD区域内,up point在LCD区域外,
// 需矫正poit到LCD区域内后发tp消息 3.down point在LCD区域外,up
// point在LCD区域外, 转成物理按键消息 4.down point在LCD区域外,up
// point在LCD区域内, 丢弃
if (MMK_IsCoordinatingTp()) {
//校准触摸屏时,无需特殊处理
MmiCreateSignal((uint16)state, sizeof(MmiTPPress),
(MmiSignalS **)&sendSignal);
sendSignal->x = tp_ptr->x_key;
sendSignal->y = tp_ptr->y_key;
sendSignal->gesture_type = tp_ptr->gesture_type;
sendSignal->Sender = P_TPP;
MmiSendSignal(TASK_FL_ID, (MmiSignalS *)sendSignal);
return;
}
GUILCD_GetInfo(0, &lcd_info);
lcd_rect.left = 0;
lcd_rect.top = 0;
lcd_rect.right = lcd_info.lcd_width - 1;
lcd_rect.bottom = lcd_info.lcd_height - 1;
if (GUI_PointIsInRect(s_tp_down_point, lcd_rect)) {
// 处理LCD屏幕里面的触屏信息
if (MMK_CheckTPSignale((uint16)state, tp_ptr->x_key, tp_ptr->y_key)) {
if (MMI_TP_UP != state) {
MmiCreateSignal((uint16)state, sizeof(MmiTPPress),
(MmiSignalS **)&sendSignal);
sendSignal->x = tp_ptr->x_key;
sendSignal->y = tp_ptr->y_key;
sendSignal->gesture_type = tp_ptr->gesture_type;
sendSignal->Sender = P_TPP;
MmiSendSignal(TASK_FL_ID, (MmiSignalS *)sendSignal);
} else {
if (GUI_PointIsInRect(up_point, lcd_rect)) {
MmiCreateSignal((uint16)state, sizeof(MmiTPPress),
(MmiSignalS **)&sendSignal);
sendSignal->x = tp_ptr->x_key;
sendSignal->y = tp_ptr->y_key;
sendSignal->gesture_type = tp_ptr->gesture_type;
sendSignal->Sender = P_TPP;
MmiSendSignal(TASK_FL_ID, (MmiSignalS *)sendSignal);
} else {
if (up_point.x > lcd_rect.right) {
up_point.x = lcd_rect.right;
}
if (up_point.y > lcd_rect.bottom) {
up_point.y = lcd_rect.bottom;
}
MmiCreateSignal((uint16)state, sizeof(MmiTPPress),
(MmiSignalS **)&sendSignal);
sendSignal->x = up_point.x;
sendSignal->y = up_point.y;
sendSignal->gesture_type = tp_ptr->gesture_type;
sendSignal->Sender = P_TPP;
MmiSendSignal(TASK_FL_ID, (MmiSignalS *)sendSignal);
}
}
} else {
// SCI_TRACE_LOW:"TPCallBackFunc: the TP message is too much!"
SCI_TRACE_ID(TRACE_TOOL_CONVERT, MMIMAIN_752_112_2_18_2_0_28_266,
(uint8 *)"");
}
} else {
// 处理屏幕外的信息
if (!GUI_PointIsInRect(up_point, lcd_rect) && MMI_TP_UP == state) {
// down point在lcd_rect外 up point在lcd_rect外:
// 匹配硬图标rect并映射为指定的物理按键消息
hw_icon_num = MMK_GetShortcutIconNum();
for (i = 0; i < hw_icon_num; i++) {
if (MMK_IsPointInShortcutIconRect(up_point, i)) {
key_single_code = KPD_DOWN;
keyup_single_code = KPD_UP;
//获取待虚拟的物理键值, mmiidle_hwicon.c内配置
// MMK_GetShortcutIconVirtualKeycode
// 里面设置LCD屏幕外虚拟按键的坐标信息
if (MMK_GetShortcutIconVirtualKeycode(i, &key_code) &&
(0 != key_code)) {
//虚拟物理按键消息,KEY_DOWN/KEY_UP要成对
MmiCreateSignal(key_single_code, sizeof(MmiKeyPress),
(MmiSignalS **)&KeysendSignal);
KeysendSignal->keyCode = key_code;
KeysendSignal->Sender = KPDSVR;
MmiSendSignal(TASK_FL_ID, (MmiSignalS *)KeysendSignal);
// key up msg
MmiCreateSignal(keyup_single_code, sizeof(MmiKeyPress),
(MmiSignalS **)&KeysendSignal);
KeysendSignal->keyCode = key_code;
KeysendSignal->Sender = KPDSVR;
MmiSendSignal(TASK_FL_ID, (MmiSignalS *)KeysendSignal);
}
}
}
} else {
// SCI_TRACE_LOW:"TPCallBackFunc: the TP message is invalid!"
SCI_TRACE_ID(TRACE_TOOL_CONVERT, MMIMAIN_791_112_2_18_2_0_28_267,
(uint8 *)"");
}
}
#endif
#endif
}
附录:遇到的问题
TP屏幕运行过程中概率出现死机的情况
原因: 暂不确定(一般这种由于线程调度等原因导致)
最终解决方案:
通过代码逐步排查屏蔽排查,最终锁定在位置TLSC6X_Read函数里面,函数里面调用点亮按键灯和LCD灯的两个函数导致了出现死机问题,最后使用别的方式代替,解决该问题.
TP屏幕按键触发频率太高,导致消息队列满了
原因: 当手指触碰屏幕的时候,比如长按屏幕的时候,一直触发中断,而在中断里面,一直往TP处理线程发送消息,导致消息队列占满,目前经过实际测试,发现当手指触碰一次马上移开,会触发5次中断。
解决思路:
-
- 降低中断触发的频率
- 让厂商把TP程序触发中断频率降低,改成3次或者1次再验证是否能够正常运行,待验证
-
- 关闭中断,打开中断
- 通过这个办法,缩小使用范围,但是依旧会小概率出现按下去的操作丢失问题。
-
- 提高TP处理线程优先级
- 提高TP线程的线程优先级,但是可能会影响到整个工程的整体性能,不能随便修改,暂不考虑。
-
- 增加消息队列空间大小
- 增加空间,防止消息队列满的情况,空间要适度,如果太大也不行,浪费不必要的空间。即使增加空间也不能够很好的解决这个问题,长时间按下依旧会出现消息队列满的情况。
最终解决方案:
还是应该使用关闭中断,打开中断的方式来处,因为消息队列大小是有限的,处理的过程中不应该再产生中断。否则一直触发,会导致满了,
至于出现响应慢的问题,应该用别的方式去进行处理。
TP屏幕按键响应慢(卡顿)
测试获取到的波形如下图所示:
原因: 因为i2c使用中断处理方式导致通信的过程中会被释放,以至于出现了上述波形的情况,而上述的情况就是导致TP信息丢失以及TP卡顿等等情况。
最终解决方案:
i2c使用轮询模式等待I2C_STATUS的STU_INT位等不到就不要释放CPU资源,防止数据丢失,保证TP屏幕能够读取完整的一个帧数据
//i2c_phy.c
#ifdef HX_DRV_IIC_USEPOLL
LOCAL int sprd_i2c_polling_int_status(struct SPRD_I2C *pi2c)
{
int ret = 0;
uint32 wait_counter = 0x00;
uint32 timetick = TIMER_GetSystemCounterReg();
while (!(CHIP_REG_GET(pi2c->base + I2C_STATUS) & STU_INT)) {
for (wait_counter = 0x00; wait_counter < 0xff; wait_counter++) {
wait_counter++;
}
// s_i2c_int_timeout 2ms超时时间
if (TIMER_GetSystemCounterReg() - timetick >= s_i2c_int_timeout) {
ret = -1;
break;
}
}
return ret;
}
LOCAL int sprd_i2c_polling_handler(struct SPRD_I2C *pi2c)
{
int ret = 0;
uint32 ack = 0;
ret = sprd_i2c_polling_int_status(pi2c);
if (!ret) {
if (!sprd_i2c_get_opt_mode(pi2c)) {
//如果是读模式 没接收完成 返回失败
ack = CHIP_REG_GET(pi2c->base + I2C_STATUS) & STU_RX_ACK;
if (ack) {
ret = -1;
}
}
}
if (ret) {
sprd_i2c_dump_reg(pi2c);
}
sprd_i2c_clear_ack(pi2c);
sprd_i2c_clear_irq(pi2c);
sprd_i2c_clear_start(pi2c);
sprd_i2c_send_stop(pi2c, 1);
sprd_i2c_enable_dma(pi2c, 0);
return ret;
}
#endif
改完后,解决问题,波形如下图:
IIC传输的波形异常
蓝色SDA
黄色SCL
数据传输的波形存在明显的异常,判断应该是硬件电路上存在问题,具体波形图如下:
原因:查看硬件设计的原理图发现,因为SDA和SCL没有接上拉电阻(一般100K)导致的
最终解决方案
接上上拉电阻,解决该问题,正确波形如下: