概述
转自:http://blog.csdn.net/yanwuxufeng/article/details/5808309
在MTK MMI里面有各种event,最常见的跟交互相关的按键event,触摸屏event。跟各种具体事件相关的比如电话event,短信event,电量event,信号量event,timer event等等。MTK都有相应的处理方式,跟交互相关的按键(KeyBrd.c)和触摸屏(TouchScreen.c)各有一套自己的处理机制,timer event已经在MTK time小结3介绍过。还有一套机制就是处理各种其他事件,它主要分为普通事件和interrupt event,interrupt event是主要用于需要弹出的对话框的event。
下面分析常用事件机制。做过MTK开发的童鞋应该知道要接收 MTK L4层(L4层即OSI的网络层)的消息,用一个注册函数 SetProtocolEventHandler,注册一个event的处理函数。比如短信,电话,电池电量,信号量等等都是通过这个函数来注册消息。当L4层,处理完封装事件后,就会把这个event发送到MMI task里来(消息发送可以看MTK task 小结 5 ),然后MMI task通过这个event机制,找到相应的处理函数,进行处理。实现这种机制,也是考虑到灵活性和扩张性。如果都在MMI task里面,用 switch case来处理,那就很疯狂了,长度不说,写个应用,定义个消息,都要去改MMI task,所以这个机制实现虽然比较简单,但是还是很有必要的。
这个机制的 event主要有两种,普通event和intrrupt event(中断事件),这些主要是一些需要中断当前应用的事件(主要是看那些弹出框),比如电话,有些应用需要提前处理这个消息,还要处理这个事件处理完毕后的处理。还是看代码吧,
在MMI task最后,会调用函数ProtocolEventHandler,这个函数是就是找相应的event的相应处理函数(平台不一样,可能实际的函数名字有些区别,但是流程基本上一样的,我这里被 #define 到 mmi_frm_execute_current_protocol_handler)。
// MsgStruct是具体事件的消息体
void mmi_frm_execute_current_protocol_handler(U16 eventID, void *MsgStruct, int mod_src, void *peerBuf)
{
U16 count = 0;
PsExtPeerFuncPtr currFuncPtr = NULL;
U8 interrup_result = MMI_FALSE; /* False not handle interrupt, True will handle */
MMI_BOOL query_result = MMI_FALSE, execute_result = MMI_FALSE;
interrupt_event_hdlr int_func = NULL, post_int_func = NULL;
mmi_frm_int_event_type current_frm_int_event;
{
// 遍历 protocolEventHandler 查找是否有event注册了回调函数
// 虽然这个方法感觉比较笨,就一个数组,实际上对速度没有什么影响
for (count = 0; count < maxProtocolEvent; count++)
{
if (protocolEventHandler[count].eventID == eventID)
{
//找到处理函数
currFuncPtr = (PsExtPeerFuncPtr) protocolEventHandler[count].entryFuncPtr;
// 这个地方时,一个优化处理的事情就是把刚才现在处理的 event id 往前移动一个单位
// 这是考虑到 程序的局部性原理, 这个优化还是很有必要的
if (count > 0)
{
protocolEventHandler[count].eventID = protocolEventHandler[count - 1].eventID;
protocolEventHandler[count].entryFuncPtr = protocolEventHandler[count - 1].entryFuncPtr;
protocolEventHandler[count - 1].eventID = eventID;
protocolEventHandler[count - 1].entryFuncPtr = (PsFuncPtr) currFuncPtr;
}
break;
}
}
}
// 这个就是查找是否是中断事件
current_frm_int_event = mmi_frm_interrupt_event_converter(eventID, MsgStruct);
if (current_frm_int_event > 0)
{
// 查看该中断事件是否有注册-提前处理函数
query_result = mmi_frm_query_interrupt_event_information(current_frm_int_event, &int_func, &post_int_func);
}
// 如果有,调用该函数
if (query_result && int_func)
{
/* New interruption mechanism */
execute_result = (*int_func)(current_frm_int_event);
}
// 根据 event 相应的回调函数 以及 前面处理的结果,来觉得是否处理该事件
// 要注意的是,如果如果 interrput 提前处理函数 返回 true,那么 这里就不会执行
if ((currFuncPtr) && (!interrup_result) && (!execute_result))
{
(*currFuncPtr)(MsgStruct, mod_src, peerBuf);
}
else
{
MMI_TRACE(MMI_FW_TRC_G1_FRM, MMI_FRM_INFO_EVENT_EXECURPTO_NO_HDLR, eventID);
}
// interrupt event 最后处理函数
if (query_result && post_int_func)
{
execute_result = (*post_int_func)(current_frm_int_event);
}
}
看完这个,其实觉得挺简单的,对吧。就是一个数组,数组里关联了 event id 和 对应的处理函数。
还有就是增加了一种 intrrupt event。可以注册这种event的 pre_handler and post_handler。这样如果需要,可以提前做处理或者不响应该事件。
// 这个函数就是简单把L4c(L4层)的interrupt消息,转换成 MMI 层interrupt的消息
static mmi_frm_int_event_type mmi_frm_interrupt_event_converter(U16 event_id, void *msg)
{
mmi_frm_int_event_type frm_interrupt_event = 0;
switch (event_id)
{
// GPIO 消息
case PRT_EQ_GPIO_DETECT_IND:
{
mmi_eq_gpio_detect_ind_struct *gpio_detect_ind = (mmi_eq_gpio_detect_ind_struct *)msg;
switch (gpio_detect_ind->gpio_device)
{
// 翻盖关闭
case EXT_DEV_CLAM_CLOSE:
{
frm_interrupt_event = MMI_FRM_INT_CLAM_CLOSE;
break;
}
// 翻盖打开
case EXT_DEV_CLAM_OPEN:
{
frm_interrupt_event = MMI_FRM_INT_CLAM_OPEN;
break;
}
// 耳机插入拔出
case EXT_DEV_EARPHONE:
{
if (gpio_detect_ind->on_off == 1)
{
frm_interrupt_event = MMI_FRM_INT_EARPHONE_PLUG_IN;
}
else
{
frm_interrupt_event = MMI_FRM_INT_EARPHONE_PLUG_OUT;
}
break;
}
}
break;
}
// 充电信息,
case PRT_BATTERY_STATUS_IND:
{
mmi_eq_battery_status_ind_struct *battery_status_ind = (mmi_eq_battery_status_ind_struct*)msg;
frm_interrupt_event = mmi_frm_get_frm_int_event_macro(
battery_status_ind->battery_status,
mmi_frm_int_event_battery_table,
sizeof(mmi_frm_int_event_battery_table) / sizeof(mmi_frm_int_event_battery_table[0]));
/* For low battery indication */
if (battery_status_ind->battery_status == PMIC_VBAT_STATUS)
{
if (battery_status_ind->battery_voltage == BATTERY_LOW_WARNING)
{
frm_interrupt_event = MMI_FRM_INT_BATTERY_LOW_WARNING;
}
elseif (battery_status_ind->battery_voltage == BATTERY_LOW_TX_PROHIBIT)
{
frm_interrupt_event = MMI_FRM_INT_BATTERY_LOW_TX_PROHIBIT;
}
}
break;
}
case MSG_ID_TIMER_EXPIRY:
case MSG_ID_MMI_EQ_POWER_ON_IND:
case MSG_ID_MMI_EQ_KEYPAD_DETECT_IND:
{
break;
}
default:
{
//其他 interrupt event,主要是弹出框相关,短信报告等等
frm_interrupt_event = mmi_frm_get_frm_int_event_macro(
event_id,
mmi_frm_int_event_convert_table,
sizeof(mmi_frm_int_event_convert_table) / sizeof(mmi_frm_int_event_convert_table[0]));
break;
}
}
return frm_interrupt_event;
}
这个函数就是把 L4C的消息转换到 MMI的消息,然后通过 mmi_frm_query_interrupt_event_information这个函数去查找,是否有相应的注册。这个函数比较简单,就是数组里去比较。
还有一个常用的函数是 SetProtocolEventHandler,也就是注册一个消息的地回调函数。
具体也没有什么可说的,无非就是循环查找是否有这个 event。
1,如果有,看func是否为空,为空表示去掉这个event的注册,不为空,就直接修改这个event的 func
2,没有,就增加一个event。
这里注意,由一个小小的优化,就是判断,event数组的最后是否为无效的 event,如果无效,那么减少总注册event的个数。
这个样可以减少循环查询的次数。
说说key event,MTK的 key envent管理其实跟MTK MMI event 小结 2 说的protocol event机制也差不了多少,简单来说,就是app通过注册某一个key的回调函数,当这个key事件产生是,该机制就去找到相应的回调函数,进行回调。
先说说key的event有哪些:
1 key down 按键按下 KEY_EVENT_DOWN
2 key up 按键弹起 KEY_EVENT_UP
3 key long press 按键长按 KEY_LONG_PRESS。默认好像是长按1.5记不清楚了,可以自己设置,函数Kbd_SetLongPressTime,不过除非必不得已,不要去随便修改,可能会引起其他应用出问题,要修改,也要先获得当前值,退出应用时设置回原来的值。
4 key repeat 按键按下一直不放,发送该消息 KEY_REPEAT。举个简单例子,pc上当输入时,如果长按 a键,那么回不停的增加a,这个就是repeat动作。这个的时间好像是0.5s,也就是按住键 0.5 s 之后,每隔0.5s,就会发送一个repeat消息。设置函数Kbd_SetRepeatTime
还有两个不常用的 KEY_HALF_PRESS_DOWN和KEY_HALF_PRESS_UP,也就是说的 2step 可以。
先讲一下按键的处理流程。当按键下按键之后,MMI task会收到MSG_ID_MMI_EQ_KEYPAD_DETECT_IND这个消息,在系统初始化的时候,已经注册了这个消息的处理函数 mmi_frm_key_handle。也就是说 mmi_frm_key_handle是处理按键过程的函数。这个函数 mmi_frm_key_handle收到这个消息后,回去一个通过该消息带的函数,去取当前发生的按键事件(实际这个函数就是一个按键缓存队列里去第一个,用一个按键缓存队列,可以防止按键大量的丢失,可以起到同步驱动和MMI层,驱动产生按键事件很快而MMI有可能比较慢,可以简单的理解为生产者和消费者的关系,跑题了)。取到当前按键后,看当前是否可以进行操作,比如是否有触摸笔按下等等。
先看执行key event函数 mmi_frm_key_handle
void mmi_frm_key_handle(void *paraBuff)
{
kbd_data k;
U32 msg_count;
mmi_eq_keypad_detect_ind_struct *p;
//判断参数是否为空
//在初始化时,系统就注册了 MSG_ID_MMI_EQ_KEYPAD_DETECT_IND 的回调函数为mmi_frm_key_handle,当收到这个消息是,表示有按键事件要处理。
//这个paraBuff 是 keyTask 传过来的参数,里面的内容是获取按键信息的函数指针
if (paraBuff != NULL)
{
drv_get_key_func new_key_ptr;
p = (mmi_eq_keypad_detect_ind_struct*) paraBuff;
new_key_ptr = (drv_get_key_func) (p->func);
//判断是否是新的函数指针,如果是的话,需要进行一些清理工作
if (new_key_ptr != keypad_ptr)
{
// 新的函数指针,则进行清理,并且判断是否有按键需要处理
if (!mmi_kbd_process_keyptr_change((void *)new_key_ptr))
{
return;
}
//清理 按键 事件缓存buffer
ClearKeyEvents();
keypad_ptr = new_key_ptr;
}
}
//通过 while 1 来不停的从按键缓存里获取按键信息
//当然这里不会无限死循环,后面会进行相应处理
while (1)
{
//1)判读是否是挂起,2)是否key需要处理
if ((g_mmi_suspend_key_flag == MMI_FALSE) && keypad_ptr &&((*(keypad_ptr))(&k) == MMI_TRUE))
{
if (k.Keydata[0] != KEY_INVALID)
{
#if defined(__MMI_TOUCH_SCREEN__) || defined(__MMI_HANDWRITING_PAD__)
kal_bool is_pen_enabled, is_pen_down;
// 获得触摸屏状态
mmi_pen_get_state(&is_pen_enabled, &is_pen_down);
#endif /* defined(__MMI_TOUCH_SCREEN__) || defined(__MMI_HANDWRITING_PAD__) */
#if defined(__MMI_TOUCH_SCREEN__) || defined(__MMI_HANDWRITING_PAD__)
//如果触摸屏按下,那么保存状态
if (is_pen_down && (k.Keyevent == WM_KEYPRESS))
{
U16 KeyMapIndex;
// 不允许 按键处理
is_allow_key_action = MMI_FALSE;
// 把驱动的按键和MMI 按键进行转换
KeyMapIndex = mmi_frm_get_idx_from_device_key_code(k.Keydata[0]);
if (nKeyPadStatus[KeyMapIndex] == KEY_EVENT_UP)
{
KEYBRD_MESSAGE KeyBrdMsg;
KeyBrdMsg.nKeyCode = nKeyPadMap[KeyMapIndex].nMMIKeyCode;
if (mmi_frm_is_2step_keyCode(KeyBrdMsg.nKeyCode))
{
nKeyPadStatus[KeyMapIndex] = KEY_HALF_PRESS_DOWN;
pressKey = HALF_DOWN_STATUS;
key_is_pressing_count++;
}
else
{
nKeyPadStatus[KeyMapIndex] = KEY_EVENT_DOWN;
pressKey = FULL_DOWN_STATUS;
key_is_pressing_count++;
}
}
}
else
#endif /* defined(__MMI_TOUCH_SCREEN__) || defined(__MMI_HANDWRITING_PAD__) */
//如果按键是弹起,那么还原原来按键状态,也就是弹起状态
//
if ((k.Keyevent == WM_KEYRELEASE) && (is_allow_key_action == MMI_FALSE))
{
#if defined(__MMI_TOUCH_SCREEN__) || defined(__MMI_HANDWRITING_PAD__)
/* Reset allow key flag and Update key status even if pen is down*/
U16 KeyMapIndex;
KeyMapIndex = mmi_frm_get_idx_from_device_key_code(k.Keydata[0]);
if ((nKeyPadStatus[KeyMapIndex] == KEY_EVENT_DOWN)
|| (nKeyPadStatus[KeyMapIndex] == KEY_HALF_PRESS_DOWN)
|| (nKeyPadStatus[KeyMapIndex] == KEY_LONG_PRESS)
|| (nKeyPadStatus[KeyMapIndex] == KEY_REPEAT))
{
nKeyPadStatus[KeyMapIndex] = KEY_EVENT_UP;
key_is_pressing_count--;
}
prevKeyMapIndex = prevKeyMapIndex;
#endif /* defined(__MMI_TOUCH_SCREEN__) || defined(__MMI_HANDWRITING_PAD__) */
is_allow_key_action = MMI_TRUE;
}
// 按键是否可以处理
if (is_allow_key_action == MMI_TRUE)
{
if (((k.Keyevent == WM_KEYPRESS) && (mmi_kbd_get_key_is_pressing_count() == 0))
|| k.Keyevent == DRV_WM_KEYLONGPRESS || k.Keyevent == DRV_WM_KEYREPEATED || k.Keyevent == DRV_WM_KEYFULLPRESS)
{
#if defined(__MMI_TOUCH_SCREEN__) || defined(__MMI_HANDWRITING_PAD__)
//关闭触摸屏
mmi_pen_disable();
#endif
}
//真正处理按键事件
mmi_frm_convert_process_key_event(k.Keyevent, k.Keydata[0]);
if ((k.Keyevent == WM_KEYRELEASE) && (mmi_kbd_get_key_is_pressing_count() == 0))
{
#if defined(__MMI_TOUCH_SCREEN__) || defined(__MMI_HANDWRITING_PAD__)
//重新打开触摸屏
mmi_pen_enable();
#endif
}
}
}
// 获得MMI task 消息队列消息个数
msg_get_ext_queue_info(mmi_ext_qid, &msg_count);
//如果外部有消息或者内部消息,则跳出该循环
if (msg_count > 0 || OslNumOfCircularQMsgs() > 0)
{
//表示还有key需要处理,在MMI task 里会直接调用这个函数进行再处理
g_keypad_flag = MMI_TRUE;
break;
}
}
else
{
// 没有key消息要处理
g_keypad_flag = MMI_FALSE;
break;
}
} /* while(1) */
MMI_TRACE(MMI_FW_TRC_G1_FRM, MMI_FRM_KEY_HDLR_END);
}
1)这里key用while (1)来处理,不停的从key消息buffer里取出按键信息,然后处理,这么做可以防止task中不停的发送消息,可以理解为共享内存。同样当有其他消息来时,需要跳出来处理其他消息,处理完毕后还需要再来处理。
2)这里跟触摸屏的事件进行了冲突处理,也就是按键和触摸屏不能同时工作,这两者也没有优先级。
3)实际真正处理按键是在 mmi_frm_convert_process_key_event里面,下次在分析
在MTK MMI event 小结 5 中,提到了event处理函数 mmi_frm_key_handle,这个函数主要作用是判断是否需要处理按键,从按键缓存里面持续的读取按键信息,然后调用 mmi_frm_convert_process_key_event 进行处理。这个函数没有什么可说的,最多是在屏幕旋转的情况下,把导航键转换一下,接着它调用了 ProcessKeyEvent,这个函数主要是对于一些状态的处理,防止key down和up不成对,出现混乱。
void ProcessKeyEvent(U32 MsgType, U16 DeviceKeyCode)
{
MMI_BOOL isKeyPaired;
U16 KeyMapIndex;
// 按键影射,把驱动的按键码,转换成MMI 的 按键消息
KeyMapIndex = mmi_frm_get_idx_from_device_key_code(DeviceKeyCode);
if (KeyMapIndex >= MAX_KEYS)
{
return;
}
// 处理各种按键事件,没有什么可以多说的,
// 主体结构都一样,
// 1 判断状态是否正常
// 2 正常 则调用 KeyEventHandler 处理, 否则忽略该事件
if (MsgType == WM_KEYPRESS)
{
// 这里处理 多案件同时按下的情况。这里需要硬件支持
if ((KeyMapIndex != prevKeyMapIndex) && (g_kbd_concurrent_key_mode == CONCURRENT_KEY_MODE_1_KEY))
{
isKeyPaired = (nKeyPadStatus[prevKeyMapIndex] == KEY_EVENT_UP);
prevKeyMapIndex = KeyMapIndex;
}
//判断案件状态是否正常,防止不匹配
if (nKeyPadStatus[KeyMapIndex] == KEY_EVENT_UP)
{
KEYBRD_MESSAGE KeyBrdMsg;
KeyBrdMsg.nKeyCode = nKeyPadMap[KeyMapIndex].nMMIKeyCode;
if (mmi_frm_is_2step_keyCode(KeyBrdMsg.nKeyCode))
{
nKeyPadStatus[KeyMapIndex] = KEY_HALF_PRESS_DOWN;
key_is_pressing_count++;
KeyBrdMsg.nMsgType = KEY_HALF_PRESS_DOWN;
}
else
{
nKeyPadStatus[KeyMapIndex] = KEY_EVENT_DOWN; /* same with KEY_FULL_PRESS_DOWN */
key_is_pressing_count++;
KeyBrdMsg.nMsgType = KEY_EVENT_DOWN;
}
// 处理按键事件
KeyEventHandler((KEYBRD_MESSAGE*) & KeyBrdMsg);
}
else
{
/* Ignore the event */
}
}
elseif (MsgType == WM_KEYRELEASE)
{
if ((nKeyPadStatus[KeyMapIndex] == KEY_EVENT_DOWN)
|| (nKeyPadStatus[KeyMapIndex] == KEY_LONG_PRESS)
|| (nKeyPadStatus[KeyMapIndex] == KEY_REPEAT) || (nKeyPadStatus[KeyMapIndex] == KEY_HALF_PRESS_DOWN))
{
KEYBRD_MESSAGE KeyBrdMsg;
nKeyPadStatus[KeyMapIndex] = KEY_EVENT_UP;
key_is_pressing_count--;
KeyBrdMsg.nMsgType = KEY_EVENT_UP;
KeyBrdMsg.nKeyCode = nKeyPadMap[KeyMapIndex].nMMIKeyCode;
KeyEventHandler((KEYBRD_MESSAGE*) & KeyBrdMsg);
}
else
{
/* Ignore the event */
}
}
/* ++Robin, modified by Max Chen */
elseif (MsgType == DRV_WM_KEYLONGPRESS)
{
if (nKeyPadStatus[KeyMapIndex] == KEY_EVENT_DOWN)
{
KEYBRD_MESSAGE KeyBrdMsg;
nKeyPadStatus[KeyMapIndex] = KEY_LONG_PRESS;
KeyBrdMsg.nMsgType = KEY_LONG_PRESS;
KeyBrdMsg.nKeyCode = nKeyPadMap[KeyMapIndex].nMMIKeyCode;
KeyEventHandler((KEYBRD_MESSAGE*) & KeyBrdMsg);
}
else
{
/* Ignore the event */
}
}
elseif (MsgType == DRV_WM_KEYREPEATED)
{
if ((nKeyPadStatus[KeyMapIndex] == KEY_LONG_PRESS) || (nKeyPadStatus[KeyMapIndex] == KEY_REPEAT))
{
KEYBRD_MESSAGE KeyBrdMsg;
nKeyPadStatus[KeyMapIndex] = KEY_REPEAT;
KeyBrdMsg.nMsgType = KEY_REPEAT;
KeyBrdMsg.nKeyCode = nKeyPadMap[KeyMapIndex].nMMIKeyCode;
KeyEventHandler((KEYBRD_MESSAGE*) & KeyBrdMsg);
}
else
{
/* Ignore the event */
}
}
elseif (MsgType == DRV_WM_KEYFULLPRESS)
{
/*
* Only in two-stage key will have KEY_FULL_PRESS_DOWN, and it followed after KEY_HALF_PRESS_DOWN
*/
if (nKeyPadStatus[KeyMapIndex] == KEY_HALF_PRESS_DOWN)
{
KEYBRD_MESSAGE KeyBrdMsg;
nKeyPadStatus[KeyMapIndex] = KEY_EVENT_DOWN;
KeyBrdMsg.nMsgType = KEY_EVENT_DOWN;
KeyBrdMsg.nKeyCode = nKeyPadMap[KeyMapIndex].nMMIKeyCode;
KeyEventHandler((struct KEYBRD_MESSAGE*)&KeyBrdMsg);
}
else
{
/* Ignore the event */
}
}
elseif ((MsgType == DRV_WM_ENABLE_TWOKEY_DETECTION) || (MsgType == DRV_WM_ENABLE_THREEKEY_DETECTION) ||
(MsgType == DRV_WM_DISABLE_MULTIKEY_DETECTION))
{
/* Ignore the event */
}
else
{
MMI_TRACE(MMI_FW_TRC_G6_FRM_DETAIL, MMI_FRM_ERROR_PROC_KEYEVENT_HDLR);
MMI_ASSERT(0);
}
}
KeyEventHandler 函数主要判断是否要真的处理该事件,可以看成是一个按键事件的拦截,比如应用切换过程中,需要一个切换动画,而这个动画工程中,需要处理忽略这些按键。就需要特殊的处理。
staticvoid KeyEventHandler(KEYBRD_MESSAGE *eventKey)
{
MMI_BOOL is_hdlr_enabled = MMI_TRUE;
// 主要处理一些特殊相应:屏幕背光,屏幕锁定,按键声音
mmi_kbd_app_key_hdlr(eventKey);
// 判断是否有前置处理函数
if (g_mmi_frm_cntx.kbd_pre_func)
{
is_hdlr_enabled = g_mmi_frm_cntx.kbd_pre_func(eventKey);
}
// 根据前置处理函数结果,判断是否要处理该 key event
if (is_hdlr_enabled)
{
//处理 案件事件
ExecuteCurrKeyHandler((S16) eventKey->nKeyCode, (S16) eventKey->nMsgType);
}
// 是否有后置处理函数,可以进行一些监视
if (g_mmi_frm_cntx.kbd_post_func)
{
g_mmi_frm_cntx.kbd_post_func(eventKey);
}
}
接下来是 ExecuteCurrKeyHandler这个函数就是根据按键事件,获得处理函数,进行处理。
void ExecuteCurrKeyHandler(S16 keyCode, S16 keyType)
{
FuncPtr currFuncPtr = NULL;
// 重新设定 键盘锁和屏保timer
mmi_idle_restart_keypad_lock_timer();
mmi_idle_restart_screensaver_timer();
frm_p->currKeyCode = keyCode;
frm_p->currKeyType = keyType;
// 对电话状态下,挂电话键的特殊处理.
if (frm_p->currKeyType == KEY_EVENT_DOWN &&
isInCall() && !GetWapCallPresent() &&
IsBitReset(g_mmi_frm_cntx.end_key_flag, frm_p->currKeyType) )
{
RegisterEndKeyHandlerInCall();
}
// 获得按键处理函数
currFuncPtr = currKeyFuncPtrs[keyCode][keyType];
// 导航的选择键,默认和左功能键的效果一样。
if (keyCode == KEY_ENTER && currFuncPtr == NULL)
{
if (currKeyFuncPtrs[KEY_ENTER][KEY_EVENT_UP] == NULL &&
currKeyFuncPtrs[KEY_ENTER][KEY_EVENT_DOWN] == NULL &&
currKeyFuncPtrs[KEY_ENTER][KEY_HALF_PRESS_DOWN] == NULL &&
currKeyFuncPtrs[KEY_ENTER][KEY_REPEAT] == NULL && currKeyFuncPtrs[KEY_ENTER][KEY_LONG_PRESS] == NULL)
{
currFuncPtr = currKeyFuncPtrs[KEY_LSK][keyType];
}
}
// 处理按键 消息
if (currFuncPtr)
{
(*currFuncPtr)();
}
}
// 重置状态
if (keyType == KEY_EVENT_UP)
{
frm_p->currKeyCode = KEY_INVALID;
frm_p->currKeyType = MAX_KEY_TYPE;
}
}
从整个按键事件的处理流程来看,也没有什么特殊的地方,就是收到消息后,从按键buffer里取出按键事件,然后处理。如果有有别的事件要处理,那么就break出来,等到处理完这个消息后,在 MMI task 调用 mmi_frm_key_handle继续处理剩下的按键事件。
这里无非多了很多的判断,是否要进行按键处理,MTK的代码函数名字上虽然看不出什么东西,但是一层一层函数,每一层函数的功能还是比较独立的,所以也不是很难看懂。
到这,key event的处理基本上就算完成了。其他几个常用的函数注册key处理函数了。了解了按键event的处理过程之后(其实也是比较简单的),注册函数就很好了解了,就是把相应key和 event的数组里放入一个回调函数的指针。比如
void SetKeyHandler(FuncPtr funcPtr, U16 keyCode, U16 keyType)抛开其他语句,就是一句简单的实现currKeyFuncPtrs[keyCode][keyType] = funcPtr;还有可以设置一组按键的处理函数,
void SetGroupKeyHandler(FuncPtr funcPtr, PU16 keyCodes, U8 len, U16 keyType),这个很好理解,keyCodes就是一个按键数组,len是这个数组的长度。实现也很简单,就是一个for循环,把这些keyCodes的回调函数都写成 funcPtr。这里就不再说了(其实也没有什么可以说得了)
其他还有:
void mmi_frm_kbd_reg_pre_key_hdlr(PsKeyProcessCBPtr func) ; //注册按键预处理函数,MTK MMI event 小结 6 解释过
void mmi_frm_kbd_set_tone_state(mmi_frm_kbd_tone_state_enum state); //设置按键音。需要说明一点,在开发一个应用程序的时候,有些手机状态一般先保存一下进入程序前的状态,在自己的app退出后还原状态。比如这个按键音,背光长亮等等
接下来简单说一下触摸屏事件,其实触摸屏的实现跟key event几乎一样。也是通过pen event缓存队列来获得pen事件。来看几个代码.
先看一下初始化函数:mmi_pen_init
void mmi_pen_init(void)
{
g_pen_initialized = KAL_TRUE;
// 看到这两个函数指针,是不是感觉很熟悉?对,就是跟key 一样,前置处理和后置处理函数
// 这个看多了,不用细看代码就能猜到了。
g_pen_cntx.pre_pen_callback_fp = NULL;
g_pen_cntx.post_pen_callback_fp = NULL;
// 设置 手写输入法的区域,手写输入法在 触摸屏这里需要特殊处理。
mmi_pen_stop_capture_strokes();
// 设置触摸屏采样时间间隔,参数一是在普通状态下,采样时间间隔,参数二是手写输入法情况下采样时间间隔
mmi_pen_config_sampling_period(MMI_PEN_SAMPLING_PERIOD_1, MMI_PEN_SAMPLING_PERIOD_2);
// 设置超时时间间隔
mmi_pen_config_timeout_period(MMI_PEN_LONGTAP_TIME, MMI_PEN_REPEAT_TIME, MMI_PEN_STROKE_LONGTAP_TIME);
// 设置move 的间距,也就是两个点之间多少个pixel 算move 事件。
mmi_pen_config_move_offset(
MMI_PEN_SKIP_MOVE_OFFSET,
MMI_PEN_SKIP_STROKE_MOVE_OFFSET,
MMI_PEN_SKIP_LONGTAP_OFFSET,
MMI_PEN_SKIP_STROKE_LONGTAP_OFFSET);
// 设置drv 的回调函数,也就是当产生事件时,会回调这个函数
touch_panel_cb_registration(mmi_pen_touch_panel_sendilm, NULL);
// 设置MMI 层消息 MSG_ID_TP_EVENT_IND 的处理函数
SetProtocolEventHandler(mmi_pen_touch_panel_event_ind, MSG_ID_TP_EVENT_IND);
}
在驱动drv这一层,触摸屏有一个task-- MOD_TP_TASK,在处理事件,MOD_TP_TASK会回调用 touch_panel_cb_registration注册的函数,有pen event事件产生了,这个去pen event缓存里去,这里注册的是:mmi_pen_touch_panel_sendilm函数,这个函数就是简单的向MMI task发送一个消息 MSG_ID_TP_EVENT_IND,让MMI task来处理。这里这么做的原因也很简单,drv的task优先级是很高的,要处理的东西也很多,所以希望回调函数能很快处理完这个事情(发送一个消息很快),这里可以想一下 pc上的中断,这两者差不多。当然中断还有更多其他原因,比如内核态和用户态等等,需要回调函数尽可能快处理,发送消息到消息队列(扯远了。。。)。
还有一点需要说明的是,现在触摸屏的手机越来越普遍,那么该善触摸屏的体验是非常重要的,可惜的是MTK的又不是多点触摸,处理起来很费劲。这个时候就需要充分利用好这里的几个设置,采样时间间隔,以及move事件距离。需要时,让自己app可以捕获尽可能多的点,然后自己进行计算,来判断用户行为。这对改善体验很有帮助。当然这个也是比较耗电的,不需要时,一定要设置回普通状态。
在MTK MMI event 小结 7 中,说到,init 时候,注册了触摸屏消息MSG_ID_TP_EVENT_IND的处理函数 mmi_pen_touch_panel_event_ind,
staticvoid mmi_pen_touch_panel_event_ind(void *param )
{
// 这个用于控制 只有一个 MSG_ID_TP_EVENT_IND在 消息队列里
g_pen_is_driver_indication_in_queue = 0;
//判断是否启动了timer 在定时读取 pen event 缓存
if (!g_pen_polling_timer_started)
{
//真正的处理函数
mmi_pen_poll_hdlr();
}
}
这个函数其实没有做什么真正的处理,只是简单的判断的了一下是否要处理这个消息。这里需要注意的是,跟按键事件不同的是,这里通过timer来控制处理 pen event 消息缓存里剩余的消息,按键事件是在 MMI task消息循环里,直接调用按键事件的处理函数。从要实现的效果上没有什么太多的区别。
staticvoid mmi_pen_poll_hdlr(void)
{
TouchPanelEventStruct data;
MMI_BOOL is_stroke_move = MMI_FALSE;
MMI_BOOL has_incoming_event = MMI_FALSE;
MMI_BOOL delay_polling_timer = MMI_FALSE;
MMI_BOOL pen_abort_happen = MMI_FALSE;
U16 interval = 0;
g_pen_polling_timer_started = MMI_FALSE;
ResetBit(g_input_msg_in_queue, MMI_DEVICE_PEN);
//判断 pen event 是否开启,按键和pen 是冲突的。
if (!g_pen_cntx.is_enabled)
{
return;
}
//判断是否还有数据要处理
// delay_polling_timer表示是否启动了timer,来稍后处理这些pen event 事件,开始时初始化为 false
while (!delay_polling_timer && mmi_pen_lookahead_buffer_get_event(&data))
{
mmi_pen_point_struct pos;
// 检测背光情况,是否要开启
mmi_idle_key_event_backlight_check();
// 重置 屏幕保护和键盘锁的时间
mmi_idle_restart_screensaver_timer();
mmi_idle_restart_keypad_lock_timer();
// 获取 x,y。坐标点
pos.x = (S16) data.x_adc;
pos.y = (S16) data.y_adc;
has_incoming_event = MMI_TRUE;
#ifdef __MMI_TOUCH_PANEL_SHORTCUT__
if(!mmi_pen_check_tp_shortcut(&pos, data.event))
#endif/* __MMI_TOUCH_PANEL_SHORTCUT__ */
{
// 判断 是否是 stroke move 事件结束
// is_stroke_move 表示前一个pen event事件是否是 stroke move
if (is_stroke_move && (data.event != STROKE_MOVE))
{
is_stroke_move = MMI_FALSE;
// 这个函数眼熟吧
if (g_pen_stroke_post_move)
{
g_pen_stroke_post_move();
}
}
// 保存现在状态
g_pen_cntx.pen_current_pos = pos;
g_pen_cntx.pen_event = data.event;
switch (data.event)
{
case PEN_DOWN:
g_pen_cntx.is_pen_down = 1;
g_pen_cntx.is_in_pen_handler = 1;
// 计算最近两次 点击事件的 事件间隔
// 这里这么判断是当时间溢出 short的时候,需要特殊处理
if (g_pen_cntx.previous_pen_down_time > data.time_stamp)
{
interval = 0XFFFF - g_pen_cntx.previous_pen_down_time + data.time_stamp;
}
else
{
interval = data.time_stamp - g_pen_cntx.previous_pen_down_time;
}
//判断两次pen down 的时间是否足够短,间隔距离足够短,从而判断是否是 双击
if ((interval <= MMI_PEN_DOUBLE_CLICK_THRESHOLD) &&
(mmi_pen_get_distance_square(g_pen_cntx.pen_down_pos, pos) < MMI_PEN_SKIP_DOUBLE_CLICK_OFFSET_SQUARE))
{
if (g_pen_event_table[MMI_PEN_EVENT_DOUBLE_CLICK] && g_pen_cntx.is_enabled)
{
// 前置处理函数
MMI_PEN_EXECUTE_PRE_CALLBACK_FUNC(MMI_PEN_EVENT_DOUBLE_CLICK);
// 回调double click注册函数
(g_pen_event_table[MMI_PEN_EVENT_DOUBLE_CLICK]) (pos);
// 后置处理函数
MMI_PEN_EXECUTE_POST_CALLBACK_FUNC(MMI_PEN_EVENT_DOUBLE_CLICK);
}
g_pen_cntx.previous_pen_down_time = 0;
}
else
{
g_pen_cntx.previous_pen_down_time = data.time_stamp;
}
g_pen_cntx.pen_down_pos = pos;
if (g_pen_event_table[MMI_PEN_EVENT_DOWN] && g_pen_cntx.is_enabled)
{
// 前置处理函数
MMI_PEN_EXECUTE_PRE_CALLBACK_FUNC(MMI_PEN_EVENT_DOWN);
// 回调pen down注册函数
(g_pen_event_table[MMI_PEN_EVENT_DOWN]) (pos);
// 后置处理函数
MMI_PEN_EXECUTE_POST_CALLBACK_FUNC(MMI_PEN_EVENT_DOWN);
}
break;
case PEN_UP:
g_pen_cntx.is_pen_down = 0;
if (g_pen_event_table[MMI_PEN_EVENT_UP] && g_pen_cntx.is_enabled)
{
MMI_PEN_EXECUTE_PRE_CALLBACK_FUNC(MMI_PEN_EVENT_UP);
(g_pen_event_table[MMI_PEN_EVENT_UP]) (pos);
MMI_PEN_EXECUTE_POST_CALLBACK_FUNC(MMI_PEN_EVENT_UP);
}
// 判断是否要进行pen event reset
if (g_pen_cntx.reset_stroke_on_pen_up)
{
// 判读是否有 stroke event
if (g_pen_stroke_max_points > 0)
{
// 结束 stroke event,注意,stroke event 会把所有的点push到一个数组里,供注册的程序使用,当然这个数组大小也是程序设定的。这里设置结束标志。
mmi_pen_end_strokes_of_character();
mmi_pen_reset();
mmi_pen_begin_strokes_of_character();
}
else
{
mmi_pen_reset();
}
}
// 一对 down move up后,即时还有pen事件,通过timer 来取下一组信息
delay_polling_timer = MMI_TRUE;
g_pen_cntx.is_in_pen_handler = 0;
g_pen_cntx.pen_event = TP_UNKNOWN_EVENT;
break;
case PEN_MOVE:
if (g_pen_event_table[MMI_PEN_EVENT_MOVE] && g_pen_cntx.is_enabled)
{
MMI_PEN_EXECUTE_PRE_CALLBACK_FUNC(MMI_PEN_EVENT_MOVE);
(g_pen_event_table[MMI_PEN_EVENT_MOVE]) (pos);
MMI_PEN_EXECUTE_POST_CALLBACK_FUNC(MMI_PEN_EVENT_MOVE);
}
break;
case PEN_LONGTAP:
if (g_pen_event_table[MMI_PEN_EVENT_LONG_TAP] && g_pen_cntx.is_enabled)
{
MMI_PEN_EXECUTE_PRE_CALLBACK_FUNC(MMI_PEN_EVENT_LONG_TAP);
(g_pen_event_table[MMI_PEN_EVENT_LONG_TAP]) (pos);
MMI_PEN_EXECUTE_POST_CALLBACK_FUNC(MMI_PEN_EVENT_LONG_TAP);
}
break;
case PEN_REPEAT:
if (g_pen_event_table[MMI_PEN_EVENT_REPEAT])
{
MMI_PEN_EXECUTE_PRE_CALLBACK_FUNC(MMI_PEN_EVENT_REPEAT);
(g_pen_event_table[MMI_PEN_EVENT_REPEAT]) (pos);
MMI_PEN_EXECUTE_POST_CALLBACK_FUNC(MMI_PEN_EVENT_REPEAT);
}
break;
case PEN_ABORT:
pen_abort_happen = MMI_TRUE;
g_pen_drvq_full_abort = MMI_TRUE;
mmi_pen_reset();
break;
case TP_UNKNOWN_EVENT:
MMI_ASSERT(0);
break;
case STROKE_MOVE:
// 这个主要是用于当只有一个手写区域时使用
if (g_pen_cntx.is_pending_stroke_event)
{
// 判断是第一stroke move点是否符合标准(最小距离)
if (mmi_pen_get_distance_square(g_pen_cntx.pen_down_pos, pos) > g_pen_stroke_min_offset_square)
{
g_pen_cntx.is_pending_stroke_event = 0;
g_pen_stroke_min_offset_square = 0;
g_pen_cntx.reset_stroke_on_pen_up = 0;
g_pen_cntx.pen_event = PEN_ABORT;
// 先产生 pen_abort事件,告诉应用普通的pen 事件被中断,也就是光有pen down 没有pen up事件了
if (g_pen_event_table[MMI_PEN_EVENT_ABORT]&& g_pen_cntx.is_enabled)
{
MMI_PEN_EXECUTE_PRE_CALLBACK_FUNC(MMI_PEN_EVENT_ABORT);
(g_pen_event_table[MMI_PEN_EVENT_ABORT]) (g_pen_cntx.pen_down_pos);
MMI_PEN_EXECUTE_POST_CALLBACK_FUNC(MMI_PEN_EVENT_ABORT);
}
g_pen_cntx.pen_event = STROKE_DOWN;
// 把 stroke down时间事件 保存到数组里
mmi_pen_push_stroke_point(g_pen_cntx.pen_down_pos.x, g_pen_cntx.pen_down_pos.y);
// 发送 stroke down事件
if (g_pen_stroke_table[MMI_PEN_STROKE_DOWN] && g_pen_cntx.is_enabled)
{
MMI_PEN_EXECUTE_PRE_CALLBACK_FUNC(MMI_PEN_STROKE_DOWN);
(g_pen_stroke_table[MMI_PEN_STROKE_DOWN]) (g_pen_cntx.pen_down_pos);
MMI_PEN_EXECUTE_POST_CALLBACK_FUNC(MMI_PEN_STROKE_DOWN);
}
g_pen_cntx.pen_event = STROKE_MOVE;
// 预处理
is_stroke_move = MMI_TRUE;
if (g_pen_stroke_pre_move)
{
g_pen_stroke_pre_move();
}
// 发送 stroke move
mmi_pen_push_stroke_point(pos.x, pos.y);
if (g_pen_stroke_table[MMI_PEN_STROKE_MOVE] && g_pen_cntx.is_enabled)
{
MMI_PEN_EXECUTE_PRE_CALLBACK_FUNC(MMI_PEN_STROKE_MOVE);
(g_pen_stroke_table[MMI_PEN_STROKE_MOVE]) (pos);
MMI_PEN_EXECUTE_POST_CALLBACK_FUNC(MMI_PEN_STROKE_MOVE);
}
}
}
else
{
// 判断在哪个区域里面
if (g_pen_num_stroke_area > 1)
{
mmi_pen_fix_multi_block_pen_position(&pos);
}
// 预处理
if (!is_stroke_move)
{
is_stroke_move = MMI_TRUE;
if (g_pen_stroke_pre_move)
{
g_pen_stroke_pre_move();
}
}
// 保存 stroke move并发送
mmi_pen_push_stroke_point(pos.x, pos.y);
if (g_pen_stroke_table[MMI_PEN_STROKE_MOVE] && g_pen_cntx.is_enabled)
{
MMI_PEN_EXECUTE_PRE_CALLBACK_FUNC(MMI_PEN_STROKE_MOVE);
(g_pen_stroke_table[MMI_PEN_STROKE_MOVE]) (pos);
MMI_PEN_EXECUTE_POST_CALLBACK_FUNC(MMI_PEN_STROKE_MOVE);
}
}
break;
case STROKE_DOWN:
g_pen_cntx.is_stroke_down = 1;
g_pen_cntx.is_pen_down = 1;
g_pen_cntx.pen_down_pos = pos;
g_pen_cntx.stroke_down_block_index = mmi_pen_lookup_handwriting_block(pos.x, pos.y);
// 单个区域特殊处理
if (g_pen_stroke_min_offset_square > 0)
{
g_pen_cntx.is_pending_stroke_event = 1;
g_pen_cntx.pen_event = PEN_DOWN;
if (g_pen_event_table[MMI_PEN_EVENT_DOWN] && g_pen_cntx.is_enabled)
{
MMI_PEN_EXECUTE_PRE_CALLBACK_FUNC(MMI_PEN_EVENT_DOWN);
(g_pen_event_table[MMI_PEN_EVENT_DOWN]) (g_pen_cntx.pen_down_pos);
MMI_PEN_EXECUTE_POST_CALLBACK_FUNC(MMI_PEN_EVENT_DOWN);
}
}
else
{
g_pen_cntx.is_pending_stroke_event = 0;
g_pen_cntx.pen_event = STROKE_DOWN;
if (g_pen_stroke_table[MMI_PEN_STROKE_DOWN] && g_pen_cntx.is_enabled)
{
MMI_PEN_EXECUTE_PRE_CALLBACK_FUNC(MMI_PEN_STROKE_DOWN);
(g_pen_stroke_table[MMI_PEN_STROKE_DOWN]) (pos);
MMI_PEN_EXECUTE_POST_CALLBACK_FUNC(MMI_PEN_STROKE_DOWN);
}
if (g_pen_cntx.is_enabled && g_pen_cntx.is_pen_down && g_pen_stroke_max_points > 0)
{
mmi_pen_push_stroke_point(pos.x, pos.y);
}
}
break;
case STROKE_UP:
MMI_DBG_ASSERT(g_pen_stroke_max_points > 0);
g_pen_cntx.is_stroke_down = 0;
g_pen_cntx.is_pen_down = 0;
if (g_pen_cntx.is_pending_stroke_event)
{
g_pen_cntx.is_pending_stroke_event = 0;
g_pen_cntx.reset_stroke_on_pen_up = 1;
g_pen_cntx.pen_event = PEN_UP;
if (g_pen_event_table[MMI_PEN_EVENT_UP] && g_pen_cntx.is_enabled)
{
MMI_PEN_EXECUTE_PRE_CALLBACK_FUNC(MMI_PEN_EVENT_UP);
(g_pen_event_table[MMI_PEN_EVENT_UP]) (g_pen_cntx.pen_down_pos);
MMI_PEN_EXECUTE_POST_CALLBACK_FUNC(MMI_PEN_EVENT_UP);
}
delay_polling_timer = MMI_TRUE;
}
else
{
g_pen_cntx.pen_event = STROKE_UP;
if (g_pen_num_stroke_area > 1)
{
mmi_pen_fix_multi_block_pen_position(&pos);
}
mmi_pen_push_stroke_end();
if (g_pen_stroke_table[MMI_PEN_STROKE_UP] && g_pen_cntx.is_enabled)
{
MMI_PEN_EXECUTE_PRE_CALLBACK_FUNC(MMI_PEN_STROKE_UP);
(g_pen_stroke_table[MMI_PEN_STROKE_UP]) (pos);
MMI_PEN_EXECUTE_POST_CALLBACK_FUNC(MMI_PEN_STROKE_UP);
}
}
if (g_pen_cntx.reset_stroke_on_pen_up)
{
if (g_pen_stroke_max_points > 0)
{
mmi_pen_end_strokes_of_character();
mmi_pen_reset();
mmi_pen_begin_strokes_of_character();
}
else
{
mmi_pen_reset();
}
}
/* leave pen handler procedure */
g_pen_cntx.is_in_pen_handler = 0;
g_pen_cntx.pen_event = TP_UNKNOWN_EVENT;
break;
default:
MMI_ASSERT(0);
}
} /* if(!mmi_pen_check_tp_shortcut(&pos, data.event)) */
// 把外部消息放入到内部循环消息队列
mmi_frm_fetch_msg_from_extQ_to_circularQ();
OslCircularDump();
// 如果内部消息积压大于 MMI_PENDING_MSG_THRESHOLD,就跳出循环,进行处理
if (OslNumOfCircularQMsgs() > MMI_PENDING_MSG_THRESHOLD)
{
break;
}
}
if (is_stroke_move)
{
if (g_pen_stroke_post_move)
{
g_pen_stroke_post_move();
}
}
if (has_incoming_event)
{
if (!mmi_shutdown_is_in_power_off_period())
{
mmi_idle_key_event_backlight_check();
}
}
if (g_pen_cntx.is_in_pen_handler == 0)
{
g_pen_cntx.pen_event = TP_UNKNOWN_EVENT;
}
//启动定时期
if (delay_polling_timer)
{
StartTimer(PEN_POLLING_TIMER, MMI_PEN_DEBOUNCE_POLLING_DELAY, mmi_pen_poll_hdlr);
g_pen_polling_timer_started = MMI_TRUE;
SetBit(g_input_msg_in_queue, MMI_DEVICE_PEN);
}
elseif (g_pen_cntx.is_pen_down || pen_abort_happen)
{
StartTimer(PEN_POLLING_TIMER, MMI_PEN_POLLING_PERIOD, mmi_pen_poll_hdlr);
g_pen_polling_timer_started = MMI_TRUE;
SetBit(g_input_msg_in_queue, MMI_DEVICE_PEN);
if(pen_abort_happen)
{
pen_abort_happen = MMI_FALSE;
}
}
}
这里其实也是比较简单:
1:取pen event事件
2:根据时间类型处理事件
3:是否有额外消息要处理,是退出,否继续 1
这里有点点复杂的是手写输入法,这个事件比较乱,全局变量一堆,分析起来比较头疼。
到这里 pen event其实也就差不多了,说一下几个常用的函数:
mmi_pen_enable /mmi_pen_disable //打开/关闭 pen 事件
mmi_pen_set_calibration_data / mmi_pen_read_calibration_data //设置和读取校准数据,校准数据总共四个值 x = x*x_scale + x_offset, y = y*y_scale + y_offset
mmi_pen_start_calibration //开始校准
mmi_pen_start_capture_strokes / mmi_pen_stop_capture_strokes //在设定区域里开始和关闭 捕捉输入法轨迹(stroke)点。具体可以参见 EntryEmPenTest里面用法,有机会在分析MTK手写输入法
mmi_pen_config_move_offset / mmi_pen_config_timeout_period / mmi_pen_config_sampling_period //几个配置函数,对于写一个好的触摸屏应用,还是很有必要的,相关介绍看MTK MMI event 小结 7
mmi_pen_register_move(up down repeat …)_handler //注册pen事件处理函数。
对于触摸屏校准,做一个简单的说明:
当调用 void mmi_pen_start_calibration(kal_uint16 num, const mmi_pen_point_struct *points)函数时,就开始进入触摸屏校准状态,一旦调用这个函数,那么收到点将都是没有校准过的,直到校准完成。num表示校准的个数,MTK是3个,points分别就是理想点的位置(可能不好理解,就是屏幕上显示需要点击的3个点)
真正执行校准的是在函数 touch_excute_cali里面
void touch_excute_cali(kal_int16 x_adc, kal_int16 y_adc)
{
ilm_struct *tp_ilm;
tp_cali_done_struct *local_para;
kal_bool cali_result;
module_type owner=0;
// 校准是否被激活,调用 mmi_pen_start_calibration 后,就为true
if(tp_cali_mode==KAL_FALSE)
return;
// 校准第一个点
if(tp_cali_cnt==0)
{
tp_cali_cnt++;
// 记录获取的adc点(实际物理点坐标)
cali_point_adc[0].x=x_adc;
cali_point_adc[0].y=y_adc;
}
// 第二个点
elseif(tp_cali_cnt==1)
{
tp_cali_cnt++;
cali_point_adc[1].x=x_adc;
cali_point_adc[1].y=y_adc;
}
// 第三个点
elseif(tp_cali_cnt==2)
{
cali_point_adc[2].x=x_adc;
cali_point_adc[2].y=y_adc;
tp_cali_mode=KAL_FALSE;
tp_cali_cnt=0;
// 对校准进行计算
cali_result=touch_panel_check_cali();
if(cali_result==KAL_TRUE)
{
// 校准ok ,分别计算 x 和 y 的 slope 和 offset
touch_panel_tuning(cali_point[0].x, cali_point_adc[0].x,
cali_point[1].x, cali_point_adc[1].x,
&TPCali.x_slope, &TPCali.x_offset);
touch_panel_tuning(cali_point[0].y, cali_point_adc[0].y,
cali_point[1].y, cali_point_adc[1].y,
&TPCali.y_slope, &TPCali.y_offset);
}
// 发送消息告诉MMI , 校准的结果.
// 如果失败,MMI 需要重新校准
local_para = (tp_cali_done_struct *) construct_local_para(sizeof(tp_cali_done_struct),TD_UL);
local_para->result=cali_result;
owner=MOD_MMI;
DRV_BuildPrimitive(tp_ilm,
MOD_TP_TASK,
owner,
MSG_ID_TP_CALI_DONE,
local_para);
msg_send_ext_queue(tp_ilm);
}
}
其实校准是否正确还是主要看 touch_panel_check_cali这个函数的结果。这个函数主要里有 3个校准测试,只有都符合条件才符合。
stage 1 比例对比,也就是保证测试点1和点 2 倾斜一定的角度。不会与x轴或者y轴平行
stage 2 保证第1点和第 2点 在第3点的两端
stage 3 根据stage 1和 stage 2得出的slope和 offset,比较计算获得的第3点和希望获得第3点误差小于设定值。
这几个函数就是几个数学公式,自己研究研究就Ok了。
最后
以上就是小巧冰棍为你收集整理的MTK MMI event的全部内容,希望文章能够帮你解决MTK MMI event所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复