标签:
废话什么的就不罗嗦了,直接开说
首先找到工程的主文件,即整个工程的入口
ZMain\ZMain.c
进入主函数 int main() 如下
1 int main( void )
2 {
3 // Turn off interrupts
4 osal_int_disable( INTS_ALL );
5
6 // Initialization for board related stuff such as LEDs
7 HAL_BOARD_INIT();
8
9 // Make sure supply voltage is high enough to run
10 zmain_vdd_check();
11
12 // Initialize stack memory
13 zmain_ram_init();
14
15 // Initialize board I/O
16 InitBoard( OB_COLD );
17
18 // Initialze HAL drivers
19 HalDriverInit();
20
21 // Initialize NV System
22 osal_nv_init( NULL );
23
24 // Initialize basic NV items
25 zgInit();
26
27 // Initialize the MAC
28 ZMacInit();
29
30 // Determine the extended address
31 zmain_ext_addr();
32
33 #ifndef NONWK
34 // Since the AF isn‘t a task, call it‘s initialization routine
35 afInit();
36 #endif
37
38 // Initialize the operating system
39 osal_init_system();
40
41 // Allow interrupts
42 osal_int_enable( INTS_ALL );
43
44 // Final board initialization
45 InitBoard( OB_READY );
46
47 // Display information about this device
48 zmain_dev_info();
49
50 /* Display the device info on the LCD */
51 #ifdef LCD_SUPPORTED
52 zmain_lcd_init();
53 #endif
54
55 #ifdef WDT_IN_PM1
56 /* If WDT is used, this is a good place to enable it. */
57 WatchDogEnable( WDTIMX );
58 #endif
59
60 osal_start_system(); // No Return from here
61
62 // Shouldn‘t get here
63 return ( 0 );
64 }
整理上面的程序,容易知道,进入main后,首先是各种检测和初始化,之后是 这里我们比较感兴趣的
osal_init_system(); 以及最后的 osal_start_system()
先说系统初试化
(附 osalInitTasks()位于 OSAL.c 中)
uint8 osal_init_system( void )
{
// Initialize the Memory Allocation System
osal_mem_init();
// Initialize the message queue
osal_qHead = NULL;
#if defined( OSAL_TOTAL_MEM )
osal_msg_cnt = 0;
#endif
// Initialize the timers
osalTimerInit();
// Initialize the Power Management System
osal_pwrmgr_init();
// Initialize the system tasks.
osalInitTasks();
// Setup efficient search for the first free block of heap.
osal_mem_kick();
return ( SUCCESS );
}
照例,先是系统级的初始化,这里我们直接看工程对应的
osalInitTasks(); (位于 OSAL_SerialApp.c)
跟踪并打开如下
1 void osalInitTasks( void )
2 {
3 uint8 taskID = 0;
4
5 tasksEvents = (uint16 *)osal_mem_alloc( sizeof( uint16 ) * tasksCnt);
6 //osal_mem_alloc()为当前OSAL中的各任务分配存储空间(实际上是一个任务数组),函数返回指向任务缓冲
7 //区的指针,因此tasksEvents指向该任务数组(任务队列).注意tasksEvents和后面谈到的tasksArr[]里的顺
8 //序是一一对应的, tasksArr[ ]中的第i个事件处理函数对应于tasksEvents中的第i个任务的事件.
9
10 osal_memset( tasksEvents, 0, (sizeof( uint16 ) * tasksCnt));
11 //申请内存空间,大小为 sizeof( uint16 )乘tasksCnt
12 //sizeof( uint16 )是4个字节,即一个任务的长度(同样是uint16定义)
13 //乘以任务数量tasksCnt
14
15 macTaskInit( taskID++ );
16 nwk_init( taskID++ );
17 Hal_Init( taskID++ );
18 #if defined( MT_TASK )
19 MT_TaskInit( taskID++ );
20 #endif
21 APS_Init( taskID++ );
22 #if defined ( ZIGBEE_FRAGMENTATION )
23 APSF_Init( taskID++ );
24 #endif
25 ZDApp_Init( taskID++ );
26 #if defined ( ZIGBEE_FREQ_AGILITY ) || defined(ZIGBEE_PANID_CONFLICT )
27 ZDNwkMgr_Init( taskID++ );
28 #endif
29 SerialApp_Init( taskID );
30 }
在这部分中
1、主要任务就是对各个系统内的层次进行初始化,其中核心的 SerialApp_Init( taskID ) 处在最后
2、注意此处的taskID,表示任务的优先级,在 ZDProfile.c 中有其定义,如下
1 typedef struct
2 {
3 void *next;
4 uint8 taskID;
5 uint16 clusterID;
6 } ZDO_MsgCB_t;
注意结构体 ZDO_MsgCB_t 后面还会提到,可见这个结构体中包含了 任务ID 和 簇ID
3、现在回头看第一句
1 uint8 taskID = 0;
2 tasksEvents = (uint16 *)osal_mem_alloc( sizeof( uint16 ) * tasksCnt);//此步骤的关键作用在于
3 osal_memset( tasksEvents, 0, (sizeof( uint16 ) * tasksCnt));
1 @fn osal_mem_alloc
2 @brief Implementation of the allocator functionality.
3 @param size - number of bytes to allocate from the heap.
4 @return void * - pointer to the heap allocation; NULL if error or failure.
也就是说,该函数负责 执行分配功能 ,osal_mem_alloc( uint16 size ),其中size就是要分配的字节大小,sizeof( uint16 )是4个字节,即一个任务的长度(同样是uint16定义),乘以任务数量tasksCnt,即全部内存空间。
这里看一下 tsaksCnt,跟踪可知
const uint8 tasksCnt = sizeof( tasksArr ) / sizeof( tasksArr[0] );
uint16 *tasksEvents;
sizeof 表示返回一个对象或者类型所占的内存字节数。
也就是,通过这样 tasksCnt 就可以得到总共的任务个数,tasksArr是任务列表,稍后说。
接上面说,osal_mem_alloc 的返回 :如果成功执行则返回指向一个缓存的指针,一个无类型指针指向被分配的新的缓存区。
1 * @fn osal_memset
2 * @brief Set memory buffer to value.
3 *
4 * @param dest - pointer to buffer
5 * @param value - what to set each uint8 of the message
6 * @param size - how big
7
8 * @return value of next widget, 0 if no widget found
9
10 void *osal_memset( void *dest, uint8 value, int len )
11 {
12 return memset( dest, value, len );
13 }
该函数的作用是给内存设定指定地址、指定内容、指定大小的,话说怎么感觉那么像三归 哈~
osal_memset()把开辟的内存全部设置为0;sizeof( uint16 )是4个字节,即一个任务的长度(同样是uint16定义),乘以任务数量tasksCnt,即全部内存空间
以上,总结一下第3点:
首先令taskID=0,期间通过 tasksCnt的定义 顺带计算osal中总的任务数量 (任务队列数组的总字节数 除以 每个任务的字节数 unit16都是4)
然后tasksEvents = (uint16 )osal_mem_alloc( sizeof( uint16 ) tasksCnt)进行内存空间的分配,成功后将结果 也就是指向 heap allocation 的空指针 赋给 tasksEvents
然后通过 osal_memset()把开辟的内存(tasksEvents指向的区域)全部设置对应的值value为0,大小就是
sizeof( uint16 )是4个字节,即一个任务的长度(同样是uint16定义),乘以任务数量tasksCnt, 即全部内存空间。
=========================================================================================
4、下面开始说 osalInitTasks( void ) 中的 SerialApp_Init( taskID ),跟踪打开如下
1 void SerialApp_Init( uint8 task_id )
2 {
3 halUARTCfg_t uartConfig;
4
5 P0SEL &= 0xDf; //设置P0.5口为普通IO
6 P0DIR |= 0x20; //设置P0.5为输出
7 LAMP_PIN = 1; //高电平继电器断开;低电平继电器吸合
8 P0SEL &= ~0x40; //设置P0.6为普通IO口
9 P0DIR &= ~0x40; //设置P0.6为输入口
10 P0SEL &= 0x7f; //P0_7配置成通用io
11
12 SerialApp_TaskID = task_id;
13 //SerialApp_RxSeq = 0xC3;
14
15 afRegister( (endPointDesc_t *)&SerialApp_epDesc );
16
17 RegisterForKeys( task_id );
18
19 uartConfig.configured = TRUE; // 2x30 don‘t care - see uart driver.
20 uartConfig.baudRate = SERIAL_APP_BAUD;
21 uartConfig.flowControl = FALSE;
22 uartConfig.flowControlThreshold = SERIAL_APP_THRESH; // 2x30 don‘t care - see uart driver.
23 uartConfig.rx.maxBufSize = SERIAL_APP_RX_SZ; // 2x30 don‘t care - see uart driver.
24 uartConfig.tx.maxBufSize = SERIAL_APP_TX_SZ; // 2x30 don‘t care - see uart driver.
25 uartConfig.idleTimeout = SERIAL_APP_IDLE; // 2x30 don‘t care - see uart driver.
26 uartConfig.intEnable = TRUE; // 2x30 don‘t care - see uart driver.
27 uartConfig.callBackFunc = SerialApp_CallBack;
28 HalUARTOpen (UART0, &uartConfig);
29
30 #if defined ( LCD_SUPPORTED )
31 HalLcdWriteString( "SerialApp", HAL_LCD_LINE_2 );
32 #endif
33 //HalUARTWrite(UART0, "Init", 4);
34 //ZDO_RegisterForZDOMsg( SerialApp_TaskID, End_Device_Bind_rsp );
35 //ZDO_RegisterForZDOMsg( SerialApp_TaskID, Match_Desc_rsp );
36 }
此为 串口app任务 的初始化 ,下面进行相关语句分析,没提到的不另做说明
4.1 afRegister( (endPointDesc_t *)&SerialApp_epDesc );
作用:Register an Application’s EndPoint description ,注册一个应用的端点描述符
1 * @fn afRegister
2
3 * @brief Register an Application‘s EndPoint description.
4
5 * @param epDesc - pointer to the Application‘s endpoint descriptor.
6
7 * @return afStatus_SUCCESS - Registered
8 * afStatus_MEM_FAIL - not enough memory to add descriptor
9 * afStatus_INVALID_PARAMETER - duplicate endpoint
10
11 afStatus_t afRegister( endPointDesc_t *epDesc )
12 {
13 epList_t *ep;
14
15 // Look for duplicate endpoint
16 if ( afFindEndPointDescList( epDesc->endPoint ) )
17 return ( afStatus_INVALID_PARAMETER );
18
19 ep = afRegisterExtended( epDesc, NULL );
20
21 return ((ep == NULL) ? afStatus_MEM_FAIL : afStatus_SUCCESS);
22 }
函数类型为 afStatus_t , 跟踪可知 typedef ZStatus_t afStatus_t;又 typedef Status_t ZStatus_t;又 typedef uint8 Status_t;
函数参数为 epDesc - pointer to the Application’s endpoint descriptor,即一个 endPointDesc_t 类型的指针
在 afRegister( (endPointDesc_t *)&SerialApp_epDesc ) 中 可见,该注册函数指向了 SerialApp_epDesc 对应的指针
下面具体看一下这个指针类型
1 typedef struct
2 {
3 byte endPoint;
4 byte *task_id; // Pointer to location of the Application task ID.
5 SimpleDescriptionFormat_t *simpleDesc;
6 afNetworkLatencyReq_t latencyReq;
7 } endPointDesc_t;
这个也就是 端点描述符的定义,这里展看结构体中的 SimpleDescriptionFormat_t 看一下
1 typedef struct
2 { //关于此冒号的用法,见
3 byte EndPoint;
4 uint16 AppProfId;
5 uint16 AppDeviceId; //app设备ID
6 byte AppDevVer:4; //app版本号,冒号4,此处冒号用法为 “定义变量:占位符”表明定义的变量的需要位数
7 byte Reserved:4; //保留,具体是什么可以自己看着办,比如下面的实例中用来表示 程序的版本标识
8 // AF_V1_SUPPORT uses for AppFlags:4.
9 byte AppNumInClusters;
10 cId_t *pAppInClusterList;
11 byte AppNumOutClusters;
12 cId_t *pAppOutClusterList;
13 } SimpleDescriptionFormat_t;
附:变量声明加冒号 C语言变量声明加冒号的用法(占位符)
上面这两个定义,均在 AF.h 中
下面 对照程序中的 SerialApp_epDesc 看一下,下面的函数定位于 SerialApp.c 中
1 const endPointDesc_t SerialApp_epDesc =
2 {
3 SERIALAPP_ENDPOINT,
4 &SerialApp_TaskID,
5 (SimpleDescriptionFormat_t *)&SerialApp_SimpleDesc,
6 noLatencyReqs
7 };
又其中 SerialApp_SimpleDesc 可得
1 const SimpleDescriptionFormat_t SerialApp_SimpleDesc =
2 {
3 SERIALAPP_ENDPOINT, // int Endpoint;
4 SERIALAPP_PROFID, // uint16 AppProfId[2];
5 SERIALAPP_DEVICEID, // uint16 AppDeviceId[2];
6 SERIALAPP_DEVICE_VERSION, // int AppDevVer:4;
7 SERIALAPP_FLAGS, // int AppFlags:4;
8 SERIALAPP_MAX_CLUSTERS, // byte AppNumInClusters;
9 (cId_t *)SerialApp_ClusterList, // byte *pAppInClusterList;
10 SERIALAPP_MAX_CLUSTERS, // byte AppNumOutClusters;
11 (cId_t *)SerialApp_ClusterList // byte *pAppOutClusterList;
12 };
具体来说,看一下这几个参数的情况,在 SerialApp.h 中
#define SERIALAPP_ENDPOINT 11 #define SERIALAPP_PROFID 0x0F05 #define SERIALAPP_DEVICEID 0x0001 #define SERIALAPP_DEVICE_VERSION 0 #define SERIALAPP_FLAGS 0
`故可知,相关参数,在头文件中定义
4.2 下面看一下 RegisterForKeys( task_id )
它完成任务对按键事件的注册工作,这里如果不注册的话在后来的程序里是不会产生KEY_CHANG这个事件。
byte RegisterForKeys( byte task_id )
{
// Allow only the first task
if ( registeredKeysTaskID == NO_TASK_ID )
{
registeredKeysTaskID = task_id;//注意这句话,就是给任务注册。和任务的ID号联系起来
return ( true );
}
else
return ( false );
}
这样就把任务和事件联系在了一起
标签:
原文地址:http://www.cnblogs.com/ifner/p/4438192.html