MTK timer

来源:互联网 发布:手机淘宝中的一元抢拍 编辑:程序博客网 时间:2024/06/05 01:08

小结一: MTK timer 有很多种,从最低层的 KAL(kernel adpat layer)到 MMI,都有 timer 的身影. 先来看一下 KAL 的 timer KAL timer 是非常底层的 timer, 它的实现是直接封装 MTK 的 RTOS(nuleus)的 timer,实现方式是由 HI SR,从而这种 timer 具有很高的优先级, 也就是说,当这个 timer 超时时,就会触发一个 HISR(高级中断 High Level ISR (Interrupt Service Routin e)),这个中断会回调注册的回调函数. 所以这种 timer 使用时,要比较小心,它的优先级很高,在回调函数运行时,其他事件是得不到处理的. kal_timerid kal_create_timer(kal_char* timer_name_ptr); //创建一个 timer, 参数是 timer 的名字 void kal_set_timer(kal_timerid timer_id, kal_timer_func_ptr handler_func_ptr, void* handler_param_ptr, / kal_uint32 delay, kal_uint32 reschedule_time); 回值,handler_func_ptr 是回调函数, //设置 timer 超时时间, timer_id 是 create 返 handler_param_ptr 是回调函数返回的参数(一般回调函数都是这么设置,这样很灵活),delay,是超时时间, 注意这个参数的单位是 ticks 不是 ms.reschedule_time 表示是否循环使用 timer,0 表示 timer 超时一次就停止,1 表示自动循环启动 timer. 第二种 timer 是 stack timer,这种 timer 与 KAL timer 最大的区别是:stack timer 超时后,发送一个超 时消息到 相应 task 的消息队列, 由 task 的消息队列来处理这个消息,而不像 KAL timer,直接在中断状态回调注册函数.从时间的精确性 来说,stack timer 可能没有 KAL timer 精确(发送消息, task 切换等都要花费时间), 但是 stack 更加的安全(在 task 里处理这个消息), 提高并发性(stack timer 到期后 只是发送了一个消息,具体并不处理).当然 stack timer 底层具体实现还是依靠 KAL timer.有一点需要 注意的是,当 stack timer 超时后, 向消息队列发送了消息,但是这个消息还没有来的处理,这个时候取消了这个 timer,需要特别处理,虽然 这种情况发生的概率很小. void stack_init_timer(stack_timer_struct *stack_timer, kal_char *timer_name, module_type module_id);// stac k timer 初始化 module_id 是要接受消息的 mdoule,也就是当 stack timer 超时,会像改 model 发送消息. kal_bool stack_is_time_out_valid(stack_timer_struct *stack_timer); //判断这个消息是否继续有效,也就是是 否需要处理,这个函数就是用于方式上面提到那种情况的发生. void stack_process_time_out(stack_timer_struct *stack_timer); //这个函数与上面的函数成对使用,具体看例 子. void stack_start_timer(stack_timer_struct *stack_timer, kal_uint16 timer_indx, kal_uint32 init_time); //启动 定时期,timer_index timer 索引, 超时时间. 小结二: KAL timer 和 stack timer, 这两种 timer 在平时用的比较少,在驱动开发,或者时间要求特别精确的情 况下,使用 KAL timer, 一般在 task 要管理一组 timer,用 stack timer 加上 event scheduler,后者 就是今天要介绍的第三种 timer. event scheduler 处理的 timer 时间精确性上来说,相对不是那么精确,对于上层 app 应用来说,是必不可 少的.MMI 层的 timer(StartTimer 系列函数) 就是用 event scheduler + stack timer 来实现的. extern event_scheduler *new_evshed(void *timer_id, void (*start_timer)(void *, unsigned int), void (*stop_timer)(void *), kal_uint32 fuzz, malloc_fp_t alloc_fn_p, free_fp_t free_fn_p, kal_uint8 max_delay_ticks); 创建一个 event scheduler , timer_id 是 stack timer 创建的 timer id,一般称为 base timer,start_timer 启 动这个 base timer, stop_timer 停止这个 base timer,fuzz 校正 timer 时间,alloc_fn_p 内存分配函数,fr ee_fn_p 内存释放函数,max_delay_ticks timer 最大可延迟时间,这个表示 timer 的准确度,这个参数的作 用主要是用于节省电池. extern eventid evshed_set_event(event_scheduler *es, kal_timer_func_ptr event_hf, void *event_hf_param, kal_uint32 elapse_time); 设置一个 timer,es 用 new_evshed 创建的,event_hf, timer 超时后的回调函数,event_hf_param 回调函数 传入的参数,elapse_time timer 的时间. extern void evshed_timer_handler(event_scheduler *es); 时间超时后,统一处理超时回调函数.也就是说,当 stack timer 向 相应的 mod 发送 MSG_ID_TIMER_E XPIRY 后,需要调用该函数,该函数会处理相应的回调函数. MTK 的这套机制让人感觉很别扭,像 evshed_timer_handler 这样的函数都要手动去调用,仔细想想,也只 能这么来用,event scheduler 依赖于 stack timer, 而 stack timer 又只能往相应的 mod 里发送消息,而这 个 mod 的消息处理又是自己手动写的,如果不开放 evshed_timer_handler 这个函数,那么超时了,event s cheduler 也不知道. 不过还是别扭阿 下面举了个例子,就上面这么一说,估计也是晕晕的,我当时看了 N 遍代码,才慢慢明白些,也没有个资 料可以参考. 就看 MMI timer 的实现,平时开发应用的时候,这个是用的最多的. 源文件:MMITimer.c 先来看初始化函数: void L4InitTimer(void) { TIMERTABLE *p; TIMERTABLE *pp; // 1 释放 timer table 内存 p = g_timer_table.next; pp = NULL; do { if (p != NULL) { pp = p->next; OslMfree(p); } p = pp; } while (p != NULL); /* reset g_timer_talbe */ memset(&g_timer_table, 0, sizeof(TIMERTABLE)); g_timer_table_size = SIMULTANEOUS_TIMER_NUM; g_timer_table_used = 0; /* Initiate the clock time callback function. */ get_clocktime_callback_func = NULL; set_clocktime_callback_func = NULL; //2 初始化 stack timer 1 /* Initate the no alignment stack timer */ stack_init_timer(&base_timer1, "MMI_Base_Timer1", MOD_MMI); //3 根据 stack timer 1 ,创建 event scheduler 1 /* Create a no alignment timer schedule */ event_scheduler1_ptr = new_evshed( &base_timer1, L4StartBaseTimer, L4StopBaseTimer, 0, kal_evshed_get_mem, kal_evshed_free_mem, 0); //4 初始化 stack timer 2 /* Initate the alignment stack timer */ stack_init_timer(&base_timer2, "MMI_Base_Timer2", MOD_MMI); //5 根据 stack timer 2 创建 event scheduler 2 /* Create an alignment timer schedule */ event_scheduler2_ptr = new_evshed( &base_timer2, L4StartBaseTimer, L4StopBaseTimer, 0, kal_evshed_get_mem, kal_evshed_free_mem, 254); } 说明:MMI 共有两种 timer,一种是 no alignment timer ,一种叫 alignment timer. 两者的区别有两点: 1,前置不会延迟,也就是说相对于后来来说,精确很多,当然后者有可能延迟,也就是用后者创建了一个 100ms timer ,也许过了 150ms 才被回调 ,甚至 300ms. 2,前置在手机休眠时不会被关起,二后会被挂起,也就是如果用后者创建了一个 timer,还没有到期,这 个时候手机休眠了,那么这个 timer 就不会被触发了,知道手机再次唤醒. 在 MMI timer 里面,这两种 timer 分别对应 event_scheduler1_ptr 和 event_scheduler2_ptr. 在 MMI 实现 timer 里面用了一种 table,用来存放每一个 timer 的信息,这种 table 是一种 链表 加上 数 组的实现. 这样实现 可以省去一些内存的频繁申请和释放. 小结三: 上面是最常用的 MMI timer 实现机制的初始化过程.下面要说的是创建一个 timer.MM I 层,启动一个 timer,最终都会调用到 L4StartTimer 这个函数.具体来分析一下这个 函数 // 参数 nTimerId 是要自己定义一个 timer id,在 timerEvents.h 里的 MMI _TIMER_IDS 定义,用来区分 timer // TimerExpiry 超时的回调函数 // funcArg 回调函数回传的参数 // nTimeDuration 超时时间,单位 ms // alignment alignment 或者 non-alignment,在 MTK timer 小结 2 说明过 static void L4StartTimer( unsigned short nTimerId, oslTimerFuncPtr TimerExpiry, void *funcArg, unsigned long nTimeDuration, unsigned char alignment) { /*---------------------------------------------------------------*/ /* Local Variables */ /*---------------------------------------------------------------*/ TIMERTABLE *pTable = NULL; U16 i = 0; U32 temp; /*---------------------------------------------------------------*/ /* Code Body */ /*---------------------------------------------------------------*/ if (TimerExpiry == NULL) { /* If TimerExpiry is NULL, we don't start the timer */ MMI_ASSERT(0); return; } MMI_ASSERT(nTimerId < MAX_TIMERS); if (L4TimerUsePreciseTick(nTimerId)) { alignment = TIMER_IS_NO_ALIGNMENT; } // 把 ticks 转化为 ms // mtk 底层的 timer 的单位都是 ticks if (nTimeDuration == 1000) { temp = KAL_TICKS_1_SEC - 4; } else { temp = (U32)((nTimeDuration / 5) * MMI_TICKS_5_MSEC); } if (temp == 0) { /* Cause by by rounding. If expire immediately, MoDIS boot-up failure because MMI keeps running and block NVRAM task */ temp = (U32)MMI_TICKS_5_MSEC; } //取得存放所有 MMI timer 的 table // 在 MTK timer 小结 2 中提到,这个 table 是 list + array 组合 // 如果只用存数组的话,初始化大小不好确定,而且要 array relloc. 如果考虑内存回收还容易引起数组震荡 // 如果只用 list 的话,一个 timer 如果很频繁的话,会不停的 malloc 和 free pTable = &g_timer_table; //判断 timer 是否已经 满了 // 如果满了,那么需要在 timer table 后面增加一个 timer node if (g_timer_table_used >= g_timer_table_size) { do { if (pTable->next == NULL) { pTable->next = OslMalloc(sizeof(TIMERTABLE)); memset(pTable->next, 0, sizeof(TIMERTABLE)); g_timer_table_size += SIMULTANEOUS_TIMER_NUM; pTable = pTable->next; i = 0; break; } pTable = pTable->next; } while (pTable != NULL); } else { //寻找空的 timer node i = 0; do { if (pTable->tm[i].event_id == NULL) { /* find the empty space */ break; } i++; if (i >= SIMULTANEOUS_TIMER_NUM) { pTable = pTable->next; i = 0; } } while (pTable != NULL); if (pTable == NULL) { /* Can't find the empty space in TIMERTABLE list, asser t!!! */ MMI_ASSERT(0); } } /* if (g_timer_table_used >= g_timer_table_size) */ // 根据 algigment 属性,分别创建一个 event scheduler timer // 把 timer 的信息保存到 timer node 里面 if (alignment == TIMER_IS_NO_ALIGNMENT) { /* MSB(Most Significant Bit) is align_timer_mask */ pTable->tm[i].timer_info = nTimerId | NO_ALIGNMENT_TIMER_MAS K; pTable->tm[i].event_id = evshed_set_event( event_scheduler1_ptr, (kal_timer_func_ptr) L4CallBackTi mer, (void*)&(pTable->tm[i]), temp); pTable->tm[i].arg = funcArg; pTable->tm[i].callback_func = TimerExpiry; g_timer_table_used++; } else if (alignment == TIMER_IS_ALIGNMENT) { /* MSB(Most Significant Bit) is align_timer_mask */ pTable->tm[i].timer_info = nTimerId | ALIGNMENT_TIMER_MASK; pTable->tm[i].event_id = evshed_set_event( event_scheduler2_ptr, (kal_timer_func_ptr) L4CallBackTi mer, (void*)&(pTable->tm[i]), temp); pTable->tm[i].arg = funcArg; pTable->tm[i].callback_func = TimerExpiry; g_timer_table_used++; } } 再说一句 timer table,其实也可以用 timer pool 来实现,把超时或者 stop 的 ti mer node,放入到 free pool 中,需要时再拿出来,也可以避免重复的 malloc 和 free. 在 设置 event scheduler timer 中,设置了一个回调函数 L4CallBackTimer, 这个回调函数就是在 stack timer 超时需要回调的函数. 那具体再哪里回调?在 MTK timer 小结 2 提到, event scheduler 还是依赖与 stack timer,在 MTK timer 小结 1 提到 stack timer 超时后是向相应的 mod 发送消息. 在初始化 L4InitTimer 函数中,stack timer 初始化(stack_init_timer(&base_timer1, "MMI_Base_Timer1", MOD_MMI))时 mod 是 MOD_MMI,那么超时后,就会 像 mmi task 发送超时消息. MMI 层 在 MMI_task 函数中处理所有的消息,while 消息处理中,可以看到 case MSG_ID_TIMER_EXPIRY: { kal_uint16 msg_len; EvshedMMITimerHandler(get_local_para_ptr(Message.oslDataPtr, &msg _len)); } break; 这个就是处理 stack timer 地方.也就是 stack timer 超时后,回回调 EvshedMMITi merHandler 函数. void EvshedMMITimerHandler(void *dataPtr) { /*---------------------------------------------------------------*/ /* Local Variables */ /*---------------------------------------------------------------*/ stack_timer_struct *stack_timer_ptr; stack_timer_ptr = (stack_timer_struct*) dataPtr; /*---------------------------------------------------------------*/ /* Code Body */ /*---------------------------------------------------------------*/ // 判断是哪个 stack timer if (stack_timer_ptr == &base_timer1) { // 这里需要判断 这 stack timer 是否还是有效,也就是否被 stop // 这种情况出现环境在 MTK timer 小结 1 中介绍过 // 如果无效,就不要触发这个 timer if (stack_is_time_out_valid(&base_timer1)) { //调用 这个函数,处罚注册的 event scheduler timer evshed_timer_handler(event_scheduler1_ptr); } stack_process_time_out(&base_timer1); } else if (stack_timer_ptr == &base_timer2) { if (stack_is_time_out_valid(&base_timer2)) { evshed_timer_handler(event_scheduler2_ptr); } stack_process_time_out(&base_timer2); } } 这个函数在 调用 evshed_timer_handler 函数是,就会回调 由 evshed_set_event 注册的 timer 回调函数 L4CallBackTimer. 小结四: 说到回调函数 L4CallBackTimer. static void L4CallBackTimer(void *p) { // 在 evshed_set_event 第三个参数中传得 timer noder // 这里转换这个指针 mmi_frm_timer_type *pTimer = (mmi_frm_timer_type *)p; // 得到 timer id U32 nTimerId = pTimer->timer_info & (~NO_ALIGNMENT_TIMER_MASK); // 得到回调函数 oslTimerFuncPtr pTimerExpiry = pTimer->callback_func; // 得到 回调 函数参数 void * arg = pTimer->arg; // timer 个数减少 g_timer_table_used--; // 清空这个 timer node memset( pTimer, 0, sizeof(mmi_frm_timer_type)); // 回调 timer 的回调函数 // 注意:现在这个回调函数是在 MMI task 环境下执行的 if (pTimerExpiry) { pTimerExpiry((void *)arg); } // 这个的作用是 把 mmi task 外部循环队列的消息 放入到内部消息循环 // 具体在下次 分析 MMI task 时解释 // 这里真么做主要是 让消息及时得到处理 mmi_frm_fetch_msg_from_extQ_to_circularQ(); } 这样 整个 MMI timer 梳理通了,从初始化(小结二),到 set timer(小结三) 到最后的触发 timer. 还有几个需要注意的地方,在 (小结二)说到, MMI timer 有两种,其中 ali gnment timer 在手机 休眠时 会被挂起. 具体函数如下: void mmi_frm_suspend_timers(U32 type) { /*---------------------------------------------------------------*/ /* Local Variables */ /*---------------------------------------------------------------*/ event_scheduler *ev = NULL; // 判断是哪一种 timer,实际中,只使用 alignment switch(type) { case TIMER_IS_NO_ALIGNMENT: ev = event_scheduler1_ptr; break; case TIMER_IS_ALIGNMENT: ev = event_scheduler2_ptr; break; default: /* undefined type */ MMI_ASSERT(0); break; } // 挂起所有 alignment timer evshed_suspend_all_events(ev); } 具体调用改函数的地方在 BacklightTimerHdlr 里面, 这个函数具体实现以后分 析 主要作用就是控制屏不背光灯,当屏幕背光灯关闭时,关闭 alignment timer. 有挂起,就由恢复,函数 mmi_frm_resume_timers 调用地方在 TurnOnBacklig ht 等几个函数里,具体就不在分析