本文是我在寻找zstack一些问题的解决方案中无意发现,该文对于zstack任务流程的分析十分透彻,十分值得参照和收藏,所以将其转载过来。以下开始为转载的全部原文:
很久没写过日志了,一转眼已过了好几过月,再过都几周就得领毕业证了,也就结束了校园生活,这段时间发生了很多事,一直没空写下来,其实说没空,那只是借口,只能说自己赖,最近在做个项目,用到CC2430,从网上搜索了很多别人的资料,从中得到了很多帮助,现在自己也总结一下写出来,希望对有需要的人有用,同时我自已日后忘记了也能看看。
安装IAR 8051 7.30B
运行安装程序EW8051-EV-730B.exe,这里说一下如何快速的查找代码,按下Ctrl+Shift+f 可以在整个项目中查找你想要的关键字,注意选择和你workspace工作空间对应的文件,通常有CC2430DB和CC2430EB两个。把光标放在函数名上,右键
选择Go to definition fo XX就可以跳到该函数定义处,工具栏的Navigate Backward 和Navigate Forward 可以让你来回穿梭,还有很多功能,这里不多说了。
安装ZigBee2006
下载Zigbee协议栈压缩包swrc073d.zip,安装后一般在C盘可以找到Texas Instruments文件夹,把它复制,考到D盘,我的IAR装在D盘,有必要看下Documents里面的文档,如Create New Application For The CC2430DB_F8W-2005-0033_.pdf如何新建项目;其它的就不多说了,下面是按键的简单说明,可以初步了解一下OSAL;例子目录为:
Texas
Instruments\ZStack-1.4.3-1.2.1\Projects\zstack\Samples\SimpleApp\CC2430DB
Workspace 选择 simplecollectorEB ;
我们先从主函数说起,如果不知主函数在哪,可以Ctrl+Shift+f输入int main查找,...........为省略
ZSEG int main( void )
{
// Turn off interrupts
osal_int_disable( INTS_ALL );
………………..
// Initialze HAL drivers
HalDriverInit(); //HalKeyInit();初始化按键
………………..
// Determine the extended address
zmain_ext_addr(); //HalKeyRead();读取按键
…………………..
osal_init_system(); //RegisterForKeys( sapi_TaskID ); 注册按键任务
……………………..
// Final board initialization
InitBoard( OB_READY ); /*HalKeyConfig( OnboardKeyIntEnable, OnBoard_KeyCallback);配置
按键,默认为轮询方式*/
…………………
osal_start_system(); // No Return from here 进入系统大循环
} // main()
从主函数可以看出,里面都是初始化函数init,执行过程HalDriverInit()àHalKeyInit();在HalKeyInit()里基本完成了相应管脚的输入输出配置,然后到zmain_ext_addr();时,判断物理扩展地址是否合法,如果不合法,则LED1一直闪烁,等while ( HAL_KEY_SW_5 != HalKeyRead() )按下把无效的地址初始化为有效地物理地址,然后到
osal_init_system();àosalInitTasks();àSAPI_Init( taskID );à RegisterForKeys( sapi_TaskID );注册按键事件,最后InitBoard( OB_READY );à HalKeyConfig( OnboardKeyIntEnable, OnBoard_KeyCallback);配置按键为中断方式还是轮询方式,从
/* Initialize Key stuff */
OnboardKeyIntEnable = HAL_KEY_INTERRUPT_DISABLE;
HalKeyConfig( OnboardKeyIntEnable, OnBoard_KeyCallback);
可以看出默认是配置为轮询方式的,这就是主函数大致对按键的处理过程,接下来从HalKeyConfig()入手,
void HalKeyConfig (bool interruptEnable, halKeyCBack_t cback)
{
#if (HAL_KEY == TRUE)
/* Enable/Disable Interrupt or */
Hal_KeyIntEnable = interruptEnable;
/* Register the callback fucntion */
pHalKeyProcessFunction = cback; //指向回调函数
/* Determine if interrupt is enable or not */
if (Hal_KeyIntEnable) //如果设为中断方式
{
………………..进行一些中断的相关配置
}
else /* Interrupts NOT enabled */ //否则为轮询方式
{
…………………….
osal_start_timerEx (Hal_TaskID, HAL_KEY_EVENT, HAL_KEY_POLLING_VALUE); /* Kick off polling */
}
……………………..
}
可以看出,配置为轮询方式是时启动osal_start_timerEx()函数,那么这个函数是干什么的呢?这个是系统软定时器,在HAL_KEY_POLLING_VALUE时间(100ms)内会触发系统任务事件,也就是触发uint16 Hal_ProcessEvent( uint8 task_id, uint16 events );触发时会把Hal_TaskID、HAL_KEY_EVENT两个参数传给Hal_ProcessEvent();然后看看Hal_ProcessEvent()里面又做了些什么事,
uint16 Hal_ProcessEvent( uint8 task_id, uint16 events )
{
uint8 *msgPtr;
……………………….
if (events & HAL_KEY_EVENT) //按键处理
{
#if (defined HAL_KEY) && (HAL_KEY == TRUE)
/* Check for keys */
HalKeyPoll(); //查看是哪个键
/* if interrupt disabled, do next polling */
if (!Hal_KeyIntEnable) //如果还是轮询方式,则再次启动osal_start_timerEx();
{
osal_start_timerEx( Hal_TaskID, HAL_KEY_EVENT, 100);
}
#endif // HAL_KEY
return events ^ HAL_KEY_EVENT;
}
……………………..
}
函数里面执行完HalKeyPoll();后,如果还是轮询方式,则再一次启动osal_start_timerEx();如此一来,就会每隔100ms循环进入Hal_ProcessEvent()函数读取按键,也就是说系统每隔100ms扫描一次按键,那么HalKeyPoll()又是干什么的呢?我们继续看看,
void HalKeyPoll (void)
{
………………
#if defined (HAL_KEY_SW_6_ENABLE)
if (!(HAL_KEY_SW_6_PORT & HAL_KEY_SW_6_BIT)) /* Key is active low */
{
keys |= HAL_KEY_SW_6;
}
#endif
#if defined (HAL_KEY_SW_5_ENABLE)
if (HAL_KEY_SW_5_PORT & HAL_KEY_SW_5_BIT) /* Key is active high */
{
keys |= HAL_KEY_SW_5;
}
#endif
………….. 调用HalAdcRead()得出操纵杆的值,是通过AD进来了模拟电压值得出;
/* Invoke Callback if new keys were depressed */
if (keys && (pHalKeyProcessFunction))
{
(pHalKeyProcessFunction) (keys, HAL_KEY_STATE_NORMAL); //回调函数
}
}
该函数读出按键值keys,并执行了回调函数(pHalKeyProcessFunction) (keys, HAL_KEY_STATE_NORMAL); pHalKeyProcessFunction是在
void HalKeyConfig (bool interruptEnable, halKeyCBack_t cback)里面pHalKeyProcessFunction = cback;进行赋函数指针的,这样我们就进入回调函数了,我们来看一下回调函数:
void OnBoard_KeyCallback ( uint8 keys, uint8 state ) //回调函数
{
uint8 shift;
// shift key (S1) is used to generate key interrupt
// applications should not use S1 when key interrupt is enabled
shift = (OnboardKeyIntEnable == HAL_KEY_INTERRUPT_ENABLE) ? false : ((keys & HAL_KEY_SW_6) ? true : false);
if ( OnBoard_SendKeys( keys, shift ) != ZSuccess )//ZFailure,如果不成功则执行下面
{
// Process SW1 here
if ( keys & HAL_KEY_SW_1 ) // Switch 1
{
}
…………………
}
}
回调函数里面又调用了OnBoard_SendKeys( keys, shift );接着看
byte OnBoard_SendKeys( byte keys, byte state )
{
keyChange_t *msgPtr;
if ( registeredKeysTaskID != NO_TASK_ID )//之前是否RegisterForKeys( sapi_TaskID );注册过?
{
// Send the address to the task
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 );
}
else
return ( ZFailure );
}
如果之前注册过按键事件,那么就会调用osal_msg_send( registeredKeysTaskID, (uint8 *)msgPtr );发送系统消息,它又会调用
osal_set_event(registeredKeysTaskID, SYS_EVENT_MSG );设置事件发生标志,
byte osal_set_event( byte task_id, UINT16 event_flag )
{
if ( task_id < tasksCnt )
{
halIntState_t intState;
HAL_ENTER_CRITICAL_SECTION(intState); // Hold off interrupts
tasksEvents[task_id] |= event_flag; // Stuff the event bit(s) 置任务标志,
HAL_EXIT_CRITICAL_SECTION(intState); // Release interrupts
}
else
return ( INVALID_TASK );
return ( ZSUCCESS );
}
然后触发SAPI_ProcessEvent()应用层处理事件,SAPI_ProcessEvent()再调用zb_HandleKeys()函数进行最终的按键处理事件:
void zb_HandleKeys( uint8 shift, uint8 keys )
{
uint8 startOptions;
uint8 logicalType;
if ( keys & HAL_KEY_SW_5 )//我自己加的sw5按键处理
{
P1_0=~P1_0;
}
…………..
}
经过了层层函数,最终到达了zb_HandleKeys()按键处理函数,其中的各种函数关系我们应该理清,这样对整个系统的OSAL编程有一定的了解,其中按键有两种处理方式,轮询和中断方式,系统默认为轮询方式,下面再看一下中断方式的过程:
如果修改InitBoard( OB_READY )里的
OnboardKeyIntEnable = HAL_KEY_INTERRUPT_ ENABLE;//原HAL_KEY_INTERRUPT_DISABLE
HalKeyConfig( OnboardKeyIntEnable, OnBoard_KeyCallback);
那么会把按键配置为中断方式,具体可看上面提到的HalKeyConfig()函数;此时如有按键按下,则会进入中断服务函数:
HAL_ISR_FUNCTION( halKeyPort0Isr, P0INT_VECTOR )
{
/* P0IF is cleared by HW for CHVER < REV_E */
halProcessKeyInterrupt(); //按键的中断处理
if( CHVER >= REV_E )
{
……………………
}
}
在中断函数中会执行halProcessKeyInterrupt()函数,我们看看
void halProcessKeyInterrupt (void)
{
#if (HAL_KEY == TRUE)
bool valid=FALSE;
#if defined (HAL_KEY_SW_6_ENABLE)
if (HAL_KEY_SW_6_PXIFG & HAL_KEY_SW_6_BIT) /* Interrupt Flag has been set */
{
HAL_KEY_SW_6_PXIFG = ~(HAL_KEY_SW_6_BIT); /* Clear Interrupt Flag */
valid = TRUE;
}
#endif
#if defined (HAL_KEY_SW_5_ENABLE)
if (HAL_KEY_SW_5_PXIFG & HAL_KEY_SW_5_BIT) /* Interrupt Flag has been set */
{
HAL_KEY_SW_5_PXIFG = ~(HAL_KEY_SW_5_BIT); /* Clear Interrupt Flag */
valid = TRUE;
}
#endif
if (valid)
{
osal_start_timerEx (Hal_TaskID, HAL_KEY_EVENT, HAL_KEY_DEBOUNCE_VALUE);//25ms
}
#endif /* HAL_KEY */
}
我们终于发现了osal_start_timerEx()函数,在HAL_KEY_DEBOUNCE_VALUE时间(25ms)后再次触发,用于按键去抖,然后osal_start_timerEx()会触发Hal_ProcessEvent()函数,这样就和轮询方式的后半部分是一样的,也就是说中断法和轮询法在前面的不同,一旦遇到Hal_ProcessEvent(),那么后面的也就一样了,这就是整个按键的处理过程,下面是我画的流程图,呵呵,很丑:
原文地址:http://zhilong2382.blog.163.com/blog/static/7480422120104291112517/