Zigbee学习-添加一个简单应用到OSAL中
2014-06-25 23:00
260 查看
Zstack是Zigbee协议的代码实现,本次总结我在学习TI在51内核上开发的Zstack,由于zigbee栈实现起来非常复杂而困难,因此在本Zstack中引入了操作系统-OSAL,用来协调、调度各个任务,本篇将总结SampleApp在OSAL中的初始化及任务轮训的实现机制。当系统上电后,开始执行Zmian中的main函数,如下:
其中红色标注osal_init_system()及osal_start_system()需要特别注意,其中osal_start_system()为启动操作系统(后面有介绍),而osal_init_system()为操作系统的初始化操作,其源代码如下:
红色标注部分是我们必须要写的,其中taskID为任务编号,升序排列,越大优先级越低,当初始化完任务后,操作系统启动后会不停查询任务事件,并根据taskID进行基于优先级的区别处理,在本次应用中其代码如下:
uint8 task_id, uint16 events );
操作系统的启动由osal_start_system()函数负责,该函数为整个上电启动过程最后的操作,进入该函数后会不停查询处理任务事件,该函数原型如下:
暂歇一会,明天继续,,,,,,
int main( void ) { // Turn off interrupts osal_int_disable( INTS_ALL ); //关闭所有中断 // Initialization for board related stuff such as LEDs HAL_BOARD_INIT(); //初始化系统时钟 // Make sure supply voltage is high enough to run zmain_vdd_check(); //检查芯片电压是否正常 // Initialize board I/O InitBoard( OB_COLD ); //初始化I/O ,LED 、Timer 等 // Initialze HAL drivers HalDriverInit(); //初始化芯片各硬件模块 // Initialize NV System osal_nv_init( NULL ); //初始化Flash 存储器 // Initialize the MAC ZMacInit(); //初始化MAC 层 // Determine the extended address zmain_ext_addr(); //确定IEEE 64位地址 // Initialize basic NV items zgInit(); //初始化非易失变量 #ifndef NONWK // Since the AF isn't a task, call it's initialization routine afInit(); #endif // Initialize the operating system <span style="color: rgb(255, 0, 0);">osal_init_system(); //初始化操作系统</span> // Allow interrupts osal_int_enable( INTS_ALL ); //使能全部中断 // Final board initialization InitBoard( OB_READY ); //最终板载初始化 // Display information about this device zmain_dev_info(); //显示设备信息 /* Display the device info on the LCD */ #ifdef LCD_SUPPORTED zmain_lcd_init(); //初始化LCD #endif osalInitTasks(); #ifdef WDT_IN_PM1 /* If WDT is used, this is a good place to enable it. */ WatchDogEnable( WDTIMX ); #endif <span style="color:#ff0000;">osal_start_system(); // No Return from here 执行操作系统,进去后不会返回</span> return 0; // Shouldn't get here. } // main()
其中红色标注osal_init_system()及osal_start_system()需要特别注意,其中osal_start_system()为启动操作系统(后面有介绍),而osal_init_system()为操作系统的初始化操作,其源代码如下:
uint8 osal_init_system( void ) { // Initialize the Memory Allocation System osal_mem_init(); // Initialize the message queue osal_qHead = NULL; // Initialize the timers osalTimerInit(); // Initialize the Power Management System osal_pwrmgr_init(); // Initialize the system tasks. <span style="color:#ff0000;"> osalInitTasks(); </span> // Setup efficient search for the first free block of heap. osal_mem_kick(); return ( SUCCESS ); }如上,操作系统的初始化包含了内存、消息队列、电源管理、任务队列等,其中我们需要特别关注红色标注的osalInitTasks(),此函数负责初始化系统任务,我们所写SampleApp任务在此函数内被初始化,源码如下:
void osalInitTasks( void ) { uint8 taskID = 0; // 分配内存,返回指向缓冲区的指针 tasksEvents = (uint16 *)osal_mem_alloc( sizeof( uint16 ) * tasksCnt); // 设置所分配的内存空间单元值为0 osal_memset( tasksEvents, 0, (sizeof( uint16 ) * tasksCnt)); // 任务优先级由高向低依次排列,高优先级对应taskID 的值反而小 macTaskInit( taskID++ ); //macTaskInit(0) ,用户不需考虑 nwk_init( taskID++ ); //nwk_init(1),用户不需考虑 Hal_Init( taskID++ ); //Hal_Init(2) ,用户需考虑 #if defined( MT_TASK ) MT_TaskInit( taskID++ ); #endif APS_Init( taskID++ ); //APS_Init(3) ,用户不需考虑 #if defined ( ZIGBEE_FRAGMENTATION ) APSF_Init( taskID++ ); #endif ZDApp_Init( taskID++ ); //ZDApp_Init(4) ,用户需考虑 #if defined ( ZIGBEE_FREQ_AGILITY ) || defined ( ZIGBEE_PANID_CONFLICT ) ZDNwkMgr_Init( taskID++ ); #endif //用户创建的任务 <span style="color:#ff0000;">SampleApp_Init( taskID ); </span> // SampleApp_Init _Init(5) ,用户需考虑 }
红色标注部分是我们必须要写的,其中taskID为任务编号,升序排列,越大优先级越低,当初始化完任务后,操作系统启动后会不停查询任务事件,并根据taskID进行基于优先级的区别处理,在本次应用中其代码如下:
void SampleApp_Init( uint8 task_id ) { SampleApp_TaskID = task_id; //osal分配的任务ID随着用户添加任务的增多而改变 SampleApp_NwkState = DEV_INIT;//设备状态设定为ZDO层中定义的初始化状态 SampleApp_TransID = 0; //消息发送ID(多消息时有顺序之分) // Device hardware initialization can be added here or in main() (Zmain.c). // If the hardware is application specific - add it here. // If the hardware is other parts of the device add it in main(). #if defined ( BUILD_ALL_DEVICES ) // The "Demo" target is setup to have BUILD_ALL_DEVICES and HOLD_AUTO_START // We are looking at a jumper (defined in SampleAppHw.c) to be jumpered // together - if they are - we will start up a coordinator. Otherwise, // the device will start as a router. if ( readCoordinatorJumper() ) zgDeviceLogicalType = ZG_DEVICETYPE_COORDINATOR; else zgDeviceLogicalType = ZG_DEVICETYPE_ROUTER; #endif // BUILD_ALL_DEVICES //该段的意思是,如果设置了HOLD_AUTO_START宏定义,将会在启动芯片的时候会暂停启动 //流程,只有外部触发以后才会启动芯片。其实就是需要一个按钮触发它的启动流程。 #if defined ( HOLD_AUTO_START ) // HOLD_AUTO_START is a compile option that will surpress ZDApp // from starting the device and wait for the application to // start the device. ZDOInitDevice(0); #endif // Setup for the periodic message's destination address 设置发送数据的方式和目的地址寻址模式 // Broadcast to everyone 发送模式:广播发送 SampleApp_Periodic_DstAddr.addrMode = (afAddrMode_t)AddrBroadcast;//广播 SampleApp_Periodic_DstAddr.endPoint = SAMPLEAPP_ENDPOINT; //指定端点号:20 SampleApp_Periodic_DstAddr.addr.shortAddr = 0xFFFF;//指定目的网络地址为广播地址 // Setup for the flash command's destination address - Group 1 组播发送 SampleApp_Flash_DstAddr.addrMode = (afAddrMode_t)afAddrGroup; //组寻址 SampleApp_Flash_DstAddr.endPoint = SAMPLEAPP_ENDPOINT; //指定端点号:20,1~240 SampleApp_Flash_DstAddr.addr.shortAddr = SAMPLEAPP_FLASH_GROUP;//组号0x0001 // Fill out the endpoint description. 定义本设备用来通信的APS层端点描述符 SampleApp_epDesc.endPoint = SAMPLEAPP_ENDPOINT; //指定端点号:20 SampleApp_epDesc.task_id = &SampleApp_TaskID; //SampleApp 描述符的任务ID SampleApp_epDesc.simpleDesc = (SimpleDescriptionFormat_t *)&SampleApp_SimpleDesc;//SampleApp简单描述符 SampleApp_epDesc.latencyReq = noLatencyReqs; //延时策略 <span style="color:#ff0000;"> // Register the endpoint description with the AF afRegister( &SampleApp_epDesc ); //向AF层登记描述符 // Register for all key events - This app will handle all key events RegisterForKeys( SampleApp_TaskID ); // 登记所有的按键事件</span> // By default, all devices start out in Group 1 SampleApp_Group.ID = 0x0001;//组号 osal_memcpy( SampleApp_Group.name, "Group 1", 7 );//设定组名 aps_AddGroup( SAMPLEAPP_ENDPOINT, &SampleApp_Group );//把该组登记添加到APS中 #if defined ( LCD_SUPPORTED ) HalLcdWriteString( "SampleApp", HAL_LCD_LINE_1 ); //如果支持LCD,显示提示信息 #endif }如上,红色标注部分依然是我们需要特别注意的,afRegister( &SampleApp_epDesc )该函数将endpoint注册至AF(即Aapplication Frame)层,该层负责管理端点进行数据传输;而RegisterForKeys( SampleApp_TaskID )是将所有的按键事件注册绑定到我们SampleApp中,注册后当有按键事件发生时,可以在系统的任务轮询中进入SampleApp的时间相应函数中,在本应用中该函数为SampleApp_ProcessEvent(
uint8 task_id, uint16 events );
操作系统的启动由osal_start_system()函数负责,该函数为整个上电启动过程最后的操作,进入该函数后会不停查询处理任务事件,该函数原型如下:
void osal_start_system( void ) { #if !defined ( ZBIT ) && !defined ( UBIT ) for(;;) // Forever Loop #endif { uint8 idx = 0; osalTimeUpdate(); //扫描哪个事件被触发了,然后置相应的标志位 Hal_ProcessPoll(); //轮询TIMER与UART This replaces MT_SerialPoll() and osal_check_timer(). do { if (tasksEvents[idx]) // Task is highest priority that is ready. { break; //得到待处理的最高优先级任务索引号 idx } } while (++idx < tasksCnt); if (idx < tasksCnt) { uint16 events; halIntState_t intState; HAL_ENTER_CRITICAL_SECTION(intState);// 进入临界区,保护 events = tasksEvents[idx]; //提取需要处理的任务中的事件 tasksEvents[idx] = 0; //清除本次任务的事件 HAL_EXIT_CRITICAL_SECTION(intState); // 退出临界区 <span style="color:#ff0000;"> events = (tasksArr[idx])( idx, events )</span>;//通过指针调用任务处理函数,关键,其中sampleAPP的处理函数即从此处调用 HAL_ENTER_CRITICAL_SECTION(intState); //进入临界区 tasksEvents[idx] |= events; // 保存未处理的事件 Add back unprocessed events to the current task. HAL_EXIT_CRITICAL_SECTION(intState); // 退出临界区 } #if defined( POWER_SAVING ) else // Complete pass through all task events with no activity? { osal_pwrmgr_powerconserve(); // Put the processor/system into sleep } #endif } }如上,该函数为不断轮训任务时间,并根据任务ID进行处理(红色标注部分),跟踪tasksArr[idx]发现其被赋值如下:
const pTaskEventHandlerFn tasksArr[] = { macEventLoop, nwk_event_loop, Hal_ProcessEvent, #if defined( MT_TASK ) MT_ProcessEvent, #endif APS_event_loop, #if defined ( ZIGBEE_FRAGMENTATION ) APSF_ProcessEvent, #endif ZDApp_event_loop, #if defined ( ZIGBEE_FREQ_AGILITY ) || defined ( ZIGBEE_PANID_CONFLICT ) ZDNwkMgr_event_loop, #endif <span style="color:#ff0000;">SampleApp_ProcessEvent</span> };其中 SampleApp_ProcessEvent为我们自定义应用事件处理函数,函数名可自定义,在本次应用中该函数的原型如下:
uint16 SampleApp_ProcessEvent( uint8 task_id, uint16 events ) { afIncomingMSGPacket_t *MSGpkt; (void)task_id; // Intentionally unreferenced parameter if ( events & SYS_EVENT_MSG ) //接收系统消息再进行判断 { //接收属于本应用任务SampleApp的消息,以SampleApp_TaskID标记 MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( SampleApp_TaskID ); while ( MSGpkt ) { switch ( MSGpkt->hdr.event ) { // Received when a key is pressed case <span style="color:#ff0000;">KEY_CHANGE</span>://按键事件 HalLedBlink(HAL_LED_2, 0, 50, 200);//如果是则Led1间隔200ms闪烁 SampleApp_HandleKeys( ((keyChange_t *)MSGpkt)->state, ((keyChange_t *)MSGpkt)->keys ); break; // Received when a messages is received (OTA) for this endpoint case <span style="color:#ff0000;">AF_INCOMING_MSG_CMD:</span>//接收数据事件,调用函数AF_DataRequest()接收数据 SampleApp_MessageMSGCB( MSGpkt );//调用回调函数对收到的数据进行处理 break; // Received whenever the device changes state in the network case <span style="color:#ff0000;">ZDO_STATE_CHANGE:</span> //只要网络状态发生改变,就通过ZDO_STATE_CHANGE事件通知所有的任务。 //同时完成对协调器,路由器,终端的设置 SampleApp_NwkState = (devStates_t)(MSGpkt->hdr.status); //if ( (SampleApp_NwkState == DEV_ZB_COORD)//实验中协调器只接收数据所以取消发送事件 if ( (SampleApp_NwkState == DEV_ROUTER) || (SampleApp_NwkState == DEV_END_DEVICE) ) { // Start sending the periodic message in a regular interval. //这个定时器只是为发送周期信息开启的,设备启动初始化后从这里开始 //触发第一个周期信息的发送,然后周而复始下去 osal_start_timerEx( SampleApp_TaskID, SAMPLEAPP_SEND_PERIODIC_MSG_EVT, SAMPLEAPP_SEND_PERIODIC_MSG_TIMEOUT ); } else { // Device is no longer in the network } break; default: break; } // Release the memory 事件处理完了,释放消息占用的内存 osal_msg_deallocate( (uint8 *)MSGpkt ); // Next - if one is available 指针指向下一个放在缓冲区的待处理的事件, //返回while ( MSGpkt )重新处理事件,直到缓冲区没有等待处理事件为止 MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( SampleApp_TaskID ); } // return unprocessed events 返回未处理的事件 return (events ^ SYS_EVENT_MSG); }如上,红色标注部分为事件类型,当系统发生该类事件时,会进入该函数,部分事件需要注册,如KEY_CHANGE事件;
暂歇一会,明天继续,,,,,,
相关文章推荐
- WPF学习——制作一个简单的录入界面(1): 添加需要的控件
- ios学习笔记block回调的应用(一个简单的例子)
- 用jsp实现一个简单的购物车web应用系统。实现的添加购物商品,删除购物商品并且显示购物车信息。
- [ZigBee] 15、Zigbee协议栈应用(一)——Zigbee协议栈介绍及简单例子(长文,OSAL及Zigbee入门知识)
- ios学习笔记block回调的应用(一个简单的例子)
- struts2.x学习一(搭建一个简单的Struts2应用)
- 一个简单的storyboard示例,其中关于添加navigation的部分可以学习,此前没用过
- Zigbee学习之创建自己的简单应用
- Hibernate学习笔记1--------一个简单的应用
- 【Android开发学习21】写一个简单的乘法计算器,并添加退出菜单
- U3d学习第二天--创建树和预设体、三种灯光属性及添加耀斑、音频的简单应用
- IOS:学习添加一个简单的UINavigationBar
- 一个简单的客户/服务器应用(socket 学习)
- 【安卓学习】4.碎片(Fragment)实践---一个简单的新闻应用。
- Google Android开发者文档系列-创建有内容分享特性的应用之添加一个简单的共享action
- 给自己做的项目添加开机动画,只是一个简单的添加方法,如果系统的方法太麻烦的画,就试试我这个吧,但是如果是想专业一些的话,还是学习系统的吧!!!
- 构建一个简单的WCF应用——WCF学习笔记(1)
- 【狂人C】学习笔记之一个简单的乘法函数的应用
- SilverLight学习笔记--实际应用(一)(2):手把手建立一个Silverlight应用程序之添加记录