这里主要想说的是按键事件发生后,app层是怎么收到消息的一些想法。
main.c:
执行InitBoard( OB_COLD ) // OB_COLD宏定义为0
{
if ( level == OB_COLD )
{
*(uint8 *)0x0 = 0;
osal_int_disable( INTS_ALL )
}
}
这个函数执行完毕系统关禁止中断
然后执行HalDriverInit (void) //头文件有#define HAL_KEY TRUE
{
......
#if (defined HAL_KEY) && (HAL_KEY == TRUE)
HalKeyInit();
#endif
.....
}
———————————————————————————————————————
hal_key.c:
执行HalKeyInit( void ) //这是关于按键初始化的配置,比如回调函数,按键储存变量
{
halKeySavedKeys = 0;
.......
pHalKeyProcessFunction = NULL;//回调函数为空
HalKeyConfigured = FALSE; //按键还没有配置
}
执行完返回main.c
———————————————————————————————————————
顺着main函数的函数体往下执行,再次遇到
InitBoard( OB_READY ); // #define OB_READY 2
{
......
else
{
HalKeyConfig(HAL_KEY_INTERRUPT_DISABLE, OnBoard_KeyCallback);
}
}
hal_key.c:
// #define HAL_KEY_INTERRUPT_DISABLE 0x00
执行 HalKeyConfig ( bool interruptEnable , halKeyCBack_t cback )
{
Hal_KeyIntEnable = interruptEnable; //将0x00赋值给Hal_KeyIntEnable
pHalKeyProcessFunction = cback;
/*回调函数赋值给 pHalKeyProcessFunction,往上查定义得到
static halKeyCBack_t pHalKeyProcessFunction,再往上查得到
typedef void (*halKeyCBack_t) (uint8 keys, uint8 state);
说白了pHalKeyProcessFunction就是个函数指针*/
......
else
{
HAL_KEY_SW_6_ICTL &= ~(HAL_KEY_SW_6_ICTLBIT); /* don't generate interrupt */
HAL_KEY_SW_6_IEN &= ~(HAL_KEY_SW_6_IENBIT); /* Clear interrupt enable bit */
osal_start_timerEx (Hal_TaskID, HAL_KEY_EVENT, HAL_KEY_POLLING_VALUE);
// #define HAL_KEY_EVENT 0x0001
HAL_KEY_POLLING_VALUE定义为500ms,开始计时任务,每100ms触发一次。意思就是HAL_KEY_EVENT这个事件100ms发生一次
}
HalKeyConfigured = TRUE;
}
——————————————————————————————————————
到此为止,按键相关的初始化都搞完了。接下来就是等第一个100ms
现在第一个100ms来了。
main.c:
osal_start_system()
for(;;)
{ osal_run_system();}
________________________________________________________________________________________________
执行 osal_run_system();
{
osalTimeUpdate();
Hal_ProcessPoll();
//上面两个函数实现了计时和事件置位
......
events = tasksEvents[idx];
tasksEvents[idx] = 0;
events = (tasksArr[idx])( idx, events );
}
主循环里面检测到Hal_TaskID这个任务被置位(至于osal_start_timerEx是怎么让任务置位这个问题我还没弄懂,暂且当做已知…),保存在event,然后把任务清0。进入事件处理函数(这里为什么叫事件处理函数而不是叫任务处理函数呢?我想是因为一个任务可以接受处理很多个事件,这个函数只是个入口,函数体里面是会具体区分什么事件的)。
———————————————————————————————————————
hal_drivers.c
Hal_ProcessEvent( uint8 task_id, uint16 events )
{
if (events & HAL_KEY_EVENT)
{
#if (defined HAL_KEY) && (HAL_KEY == TRUE)
HalKeyPoll();
if (!Hal_KeyIntEnable)
{
osal_start_timerEx( Hal_TaskID, HAL_KEY_EVENT, 100);
}
#endif // HAL_KEY
return events ^ HAL_KEY_EVENT;
}
}
这里想说,osal_start_timerEx (Hal_TaskID, HAL_KEY_EVENT, HAL_KEY_POLLING_VALUE)传递给Hal_TaskID这个任务的事件是HAL_KEY_EVENT。(events & HAL_KEY_EVENT)的结果就是1,进入if判断,HalKeyPoll()获得是哪个按键按下了。然后继续osal_start_timerEx( Hal_TaskID, HAL_KEY_EVENT, 100),为什么要这样做呢?因为在osal_run_system()里面有tasksEvents[idx] = 0 意味着这个事件被处理过了,但是谁知道什么时候有人要按键呢,所以只能不停的查询。
——————————————————————————————————————————
看到这里,好像还是没说怎么通知app层啊?仔细找找,原来在HalKeyPoll();里hal_key.c:
HalKeyPoll()
{
if (!Hal_KeyIntEnable)
{
if (keys == halKeySavedKeys)
{
return;
}
halKeySavedKeys = keys;
}
else
{
/* Key interrupt handled here */
}
if (keys && (pHalKeyProcessFunction))
{
(pHalKeyProcessFunction) (keys, HAL_KEY_STATE_NORMAL);
}
创建halKeySavedKeys变量是为了下一次触发事件的时候和keys进行比较,如果相同则说明
这一次轮询没有按键,就return回去(橙色代码)。如果keys有值并且回调函数已经定义了的话就调用回调函数。
#define HAL_KEY_STATE_NORMAL 0x00
———————————————————————————————————————
Onboard.c:
回调函数前面说了是个函数指针,实际上调用:
OnBoard_KeyCallback ( uint8 keys, uint8 state )
{
......
if ( OnBoard_SendKeys( keys, shift ) != ZSuccess )
.......
}
OnBoard_SendKeys( uint8 keys, uint8 state )
{
//在这里正式填充消息结构体(不是那种无线消息),而是“keyChange_t”,我觉得是这个层次向app层发消息的特定的结构体,是不是别的层向app层发送的话有别的结构体样式?
if ( registeredKeysTaskID != NO_TASK_ID )
{
msgPtr = (keyChange_t *)osal_msg_allocate( sizeof(keyChange_t) );
if ( msgPtr )
{
msgPtr->hdr.event = KEY_CHANGE;
msgPtr->state = state;
msgPtr->keys = keys;
osal_msg_send( registeredKeysTaskID, (uint8 *)msgPtr );
}
return ( ZSuccess );
}
在文件开头有static uint8 registeredKeysTaskID = NO_TASK_ID;
在这个文件里有函数RegisterForKeys( uint8 task_id ) 。函数实现了registeredKeysTaskID = task_id。 那么到底谁调用了这个函数呢?全局查找之后发现在sampleapp.c里面调用了它,这里就明白了很多教程说使用按键就一定要先调用按键注册函数,因为如果不这样做的话if循环根本进不去….
define KEY_CHANGE 0xC0 这是一个事件的编号,猜想应该还有很多事件的宏定义散落在不同的文件里,但是好像记得说过事件是独热码编码的,那怎么会有0xc0这种东西,暂时保留疑问….
———————————————————————————————————————————
OSAL.c:
osal_msg_send(destination_task, (uint8 *)msgPtr )
{
......
osal_msg_enqueue( &osal_qHead, msg_ptr );
osal_set_event( destination_task, SYS_EVENT_MSG );
......
}
// destination_task就是app层的id
//这里就正式将消息传给destination_task,也就是SampleApp_TaskID。其实我们口中说的将消息传给谁,程序中的实现是这样的:将这个消息加入消息队列(所有任务都能访问的一个队列),然后那个消息传给了哪个任务,就将那个任务事件标志位置1(main循环就能轮询到),那个任务对应的事件处理函数里面就会去取属于自己的消息(函数是不知道属于自己的消息放在哪里的,所以它一定要将整个消息队列遍历一次,这个说法在osal_msg_receive( SampleApp_TaskID )的函数体中可以证实)
————————————————————————————————————————————
OSAL.c:
osal_set_event( uint8 task_id, uint16 event_flag )
{
.......
tasksEvents[task_id] |= event_flag;
......
}
这句话牛逼啦,event_flag就是传递的参数SYS_EVENT_MSG,也就是0x8000.
task¬_id下标对应的tasksevents就变成了0x8000。为什么要这样?理由 1.统一规定一个强制的参数作为事件发生的标志,清晰明了 2.后面的事件处理函数好判断
_ ____________________________________________________________________
sampleapp.c:
现在任务已经被置位,并进入了事件处理函数
SampleApp_ProcessEvent( uint8 task_id, uint16 events )
{
......
if ( events & SYS_EVENT_MSG ) //没有上面的event_flag,这个if是进不去的。
{
........
while ( MSGpkt )
{
switch ( MSGpkt->hdr.event )
{
......
case KEY_CHANGE:
SampleApp_HandleKeys( ((keyChange_t *)MSGpkt)->state, ((keyChange_t *)MSGpkt)->keys );
break;
}
......
}
}
SYS_EVENT_MSG告诉你有事件发生了,发生的事什么事件通过(MSGpkt->hdr.event)来判断,通过switch语句跳转到不同的处理函数上。
———————————————————————————————————————
关于消息从产生到传递到处理暂时只能理解这个份上了….