Zigbee系统初始化及任务执行(一)-基于TI2530DB

1. 系统初始化

从main函数开始(ZMain.c)

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
  osal_init_system();           //初始化操作系统

  // 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

#ifdef WDT_IN_PM1
  /* If WDT is used, this is a good place to enable it. */
  WatchDogEnable( WDTIMX );
#endif

  osal_start_system(); // No Return from here 执行操作系统,进去后不会返回

  return 0;  // Shouldn't get here.
} // main()

重点关注osal_init_system()和osal_start_system(),一个是操作系统初始化(定义任务),一个是启动操作系统。

2. osal_init_sytem()解析

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.
  osalInitTasks(); //初始化系统任务

  // Setup efficient search for the first free block of heap.
  osal_mem_kick();

  return ( SUCCESS );
}

一共调用了六个函数,我们只关心osalInitTasks()任务初始化

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
  //用户创建的任务
  SampleApp_Init( taskID );  // SampleApp_Init _Init(5) ,用户需考虑
}

其中关注点:

1)Hal_Init()  ,查看hal_drivers.c

只有一行,why?

void Hal_Init( uint8 task_id )
{
  /* Register task ID */
  Hal_TaskID = task_id;
}

只定义赋值了Hal_TaskID, 在哪里调用呢?留待后续研究

其中hal_drivers.c 文件定义了HalDevierInit(), 上面在main.c里调用了这个初始化函数

两者之间的关系? 

2)MT_TASK  MT按API定义应该是串口直接访问各层的接口,可以对比后续的串口应用,看如何运用

3)ZDApp_Init();  这是网络设备启动的函数。

void ZDApp_Init( uint8 task_id )
{
  // Save the task ID
  ZDAppTaskID = task_id;

  // Initialize the ZDO global device short address storage
  ZDAppNwkAddr.addrMode = Addr16Bit;
  ZDAppNwkAddr.addr.shortAddr = INVALID_NODE_ADDR;
  (void)NLME_GetExtAddr();  // Load the saveExtAddr pointer.

  // Check for manual "Hold Auto Start"
  ZDAppCheckForHoldKey();

  // Initialize ZDO items and setup the device - type of device to create.
  ZDO_Init();

  // Register the endpoint description with the AF
  // This task doesn't have a Simple description, but we still need
  // to register the endpoint.
  afRegister( (endPointDesc_t *)&ZDApp_epDesc );

#if defined( ZDO_USERDESC_RESPONSE )
  ZDApp_InitUserDesc();
#endif // ZDO_USERDESC_RESPONSE

  // Start the device?
  if ( devState != DEV_HOLD )
  {
    ZDOInitDevice( 0 );
  }
  else
  {
    // Blink LED to indicate HOLD_START
    HalLedBlink ( HAL_LED_4, 0, 50, 500 );
  }
  
  ZDApp_RegisterCBs();
} /* ZDApp_Init() */

其中HOLD_START宏定义决定了是否跳过启动过程,转由应用启动。

研究协议栈时,还需要深入研究

4)最后一行定义了用户创建的任务的初始化,及分配taskID

SampleApp_Init( taskID ); 

至此,初始化函数osal_init_system()完成

3. 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); // 退出临界区

      events = (tasksArr[idx])( idx, events );//通过指针调用任务处理函数,关键

      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
  }
}

4. 用户任务初始化SampleApp_Init()分析

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; //指定端点号
  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; //指定端点号
  SampleApp_Flash_DstAddr.addr.shortAddr = SAMPLEAPP_FLASH_GROUP;//组号0x0001

  // Fill out the endpoint description. 定义本设备用来通信的APS层端点描述符
  SampleApp_epDesc.endPoint = SAMPLEAPP_ENDPOINT; //指定端点号
  SampleApp_epDesc.task_id = &SampleApp_TaskID;   //SampleApp 描述符的任务ID
  SampleApp_epDesc.simpleDesc
            = (SimpleDescriptionFormat_t *)&SampleApp_SimpleDesc;//SampleApp简单描述符
  SampleApp_epDesc.latencyReq = noLatencyReqs;    //延时策略

  // Register the endpoint description with the AF
  afRegister( &SampleApp_epDesc );    //向AF层登记描述符,告知应用层已经有这个EP开通使用

  // Register for all key events - This app will handle all key events
  RegisterForKeys( SampleApp_TaskID ); // 登记所有的按键事件

  // 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
}

关键操作:

1)定义网络设备状态

SampleApp_NwkState = DEV_INIT;

2)描述添加APS端点EP

查看SampleApp_epDesc定义

endPointDesc_t SampleApp_epDesc;

再追溯,发现EP的数据结构在AF.H中定义

// Endpoint Table - this table is the device description
// or application registration.
// There will be one entry in this table for every
// endpoint defined.
typedef struct
{
  byte endPoint;
  byte *task_id;  // Pointer to location of the Application task ID.
  SimpleDescriptionFormat_t *simpleDesc;
  afNetworkLatencyReq_t latencyReq;
} endPointDesc_t;

3)设置数据发送方式:

广播,组播,组号0001,组名Group1,组登记到APS中

4)其中对按键处理的定义,是把所有的按键响应都登记到我们定义的用户程序中,查看相关函数(OnBoard.c)

/*********************************************************************
 * Keyboard Register function
 *
 * The keyboard handler is setup to send all keyboard changes to
 * one task (if a task is registered).
 *
 * If a task registers, it will get all the keys. You can change this
 * to register for individual keys.
 *********************************************************************/
uint8 RegisterForKeys( uint8 task_id )
{
  // Allow only the first task
  if ( registeredKeysTaskID == NO_TASK_ID )
  {
    registeredKeysTaskID = task_id;
    return ( true );
  }
  else
    return ( false );
}
至此,初始化分析完成,下一步要分析用户处理程序,怎么追溯用户程序处理入口呢? 

先看osal_start_system中的轮询表(OSAL.C):

events = (tasksArr[idx])( idx, events );

追溯任务入口定义OSAL_SampleApp.c

/*********************************************************************
 * GLOBAL VARIABLES
 */

// The order in this table must be identical to the task initialization calls below in osalInitTask.
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
  SampleApp_ProcessEvent
};

追溯pTaskEventHandlerFn函数原型定义在OSAL_Tasks.h

/*********************************************************************
 * TYPEDEFS
 */

/*
 * Event handler function prototype
 */
typedef unsigned short (*pTaskEventHandlerFn)( unsigned char task_id, unsigned short event );

/*********************************************************************
 * GLOBAL VARIABLES
 */

extern const pTaskEventHandlerFn tasksArr[];
extern const uint8 tasksCnt;
extern uint16 *tasksEvents;
 

至此,找到应用事件入口SampleApp_ProcessEvent

下一节分析SampleApp_ProcessEvent的执行流程

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
内容涵盖了TI协议栈工具的使用、基础实验、组网演练和项目实战,从入门到进阶再到应用,全面扫盲。可以说是网上唯一涵盖内容最全的ZigBee学习电子书。 基于TI公司的CC2530芯片及其协议栈Z-stack,开发环境是IAR。也是国内资料最全、最流行的方案。所有相关软件和工具使用方法都在 《ZigBee实战演练》中详细讲述。为了让大家更好地了解网蜂的教程和资源,下面我们对pdf文档和实例代码等资源来一个简单的介绍: 1、CC2530集成了增强型C8051内核,可以理解成单片机+无线模块的组合,我们可以使用IAR开发自己的软件代码。为此,我们实验代码分三部分,分别是:基础实验(含点亮第一个LED、按键、外部中断、定时器、串口通讯、AD控制(自带温度计)、睡眠唤醒、看门狗);组网演练(含Zigbee 协议栈简介、无线点灯、信号传输质量检测、协议栈工作原理介绍、协议栈中的串口实验、一小时实现无线数据传输、串口透传,打造无线串口模块)和项目实战(含组网状态实时显示系统、无线IC卡考勤机、串口通讯助手==Zigbee聊天助手、无线互联:ZigBee+GPRS、室内定位系统等),部分内容还在不断完善升级中。其中组网实验全部基于TI自带协议栈实现,其中一小时实现无线数据传输、串口透传,打造无线串口模块更是网蜂团队在Z-stack中提取的精华。务求让大家尽快上手,领略ZigBee魅力。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

海里的鱼2022

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值