Zstack杂乱笔记1

    我看的代码条理不清,东一棒槌,西一榔头。惭愧。

       1. DMA方式与ISR方式的UART传输,有什么区别?分别如何实现?

  2. ZDO_CB_MSG与AF_INCOMING_MSG_CMD等事件的产生机制?
  3. 如何完成“多对一”或者“一对多”的通信?
  4. 绑定表的存储位置与生命周期?


一个设备可以有240个端点,每个端点必须有一个端点描述符endPointDesc,端点描述符里包括一个简单描述符。在看Zstack的例子时应该详细看一下应用层,3个文件,SerialApp.h SerialApp.c OSAL_SerialApp.c

端点描述符结构:

typedef struct
{
  byte endPoint;
  byte *task_id;  // Pointer to location of the Application task ID.
  SimpleDescriptionFormat_t *simpleDesc;
  afNetworkLatencyReq_t latencyReq;  //延时请求
} endPointDesc_t;

//初始化后的一个端点描述符结构

const endPointDesc_t SerialApp_epDesc =
{
  SERIALAPP_ENDPOINT,
 &SerialApp_TaskID,
  (SimpleDescriptionFormat_t *)&SerialApp_SimpleDesc,
  noLatencyReqs
};

简单描述符的结构

typedef struct
{
  byte          EndPoint;     //端点号1-240
  uint16        AppProfId;     //支持的Profile ID
  uint16        AppDeviceId; //支持的设备ID
  byte          AppDevVer:4; //执行的设备描述的版本
  byte          Reserved:4;             // AF_V1_SUPPORT uses for AppFlags:4.
  byte          AppNumInClusters; //终端支持的输入簇数目
  cId_t         *pAppInClusterList; //指向输入Cluster ID列表的指针
  byte          AppNumOutClusters;//终端支持的输出簇数目
  cId_t         *pAppOutClusterList;//指向输出Cluster ID列表的指针
} SimpleDescriptionFormat_t;

//初始化后的一个简单描述符结构

const SimpleDescriptionFormat_t SerialApp_SimpleDesc =
{
  SERIALAPP_ENDPOINT,              //  int   Endpoint;
  SERIALAPP_PROFID,                //  uint16 AppProfId[2];
  SERIALAPP_DEVICEID,              //  uint16 AppDeviceId[2];
  SERIALAPP_DEVICE_VERSION,        //  int   AppDevVer:4;
  SERIALAPP_FLAGS,                 //  int   AppFlags:4;
  SERIALAPP_MAX_CLUSTERS,          //  byte  AppNumInClusters;
  (cId_t *)SerialApp_ClusterList,  //  byte *pAppInClusterList;
  SERIALAPP_MAX_CLUSTERS,          //  byte  AppNumOutClusters;
  (cId_t *)SerialApp_ClusterList   //  byte *pAppOutClusterList;
};

AF.c 中的afStatus_t afRegister( endPointDesc_t *epDesc ),//在AF层注册该端口描述符。

只有分别在两个节点的简单描述结构体中,同时注册了相同的命令标示符并且方向相反才能建立绑定。

SerialApp_TxBuf[0]存放的是发送的序列号SerialApp_TxSeq,即通过串口发送的数据SerialApp_TxBuf,是从SerialApp_TxBuf[1]开始的,同样接收数组SerialApp_RxBuf[0]存放的是接收的序列号SerialApp_RxSeq,为什么会这样?

其实这里加了个保险措施,其实就是发送设备将发送序列号加在数据中一块发送,接收设备接收到数据后,将发送序列号回传给发送设备,发送设备将该序列号和发送时的序列号比较一下,如果相等则说明数据发送成功。

通过上面的分析可知,接收设备收到数据后,将接数据有关的状态以及接收序列号(其实就是发送设备的发送序列号)等信息放在一个4字节的数组里面,然后将该数组发送给发送设备。

发送设备接收到接收设备回传的响应信息后,将发送序列号和接收序列号比较,如果相等,则说明原来的数据发送成功了。

参考http://www.feibit.com/forum.php?mod=viewthread&tid=2928

代码分析:

void SerialApp_Init( uint8 task_id )
{
  halUARTCfg_t uartConfig;


  SerialApp_TaskID = task_id;
  SerialApp_RxSeq = 0xC3;//不知为何????
   //在AF层注册该端口描述符
  afRegister( (endPointDesc_t *)&SerialApp_epDesc );

//按键事件交给本任务处理
  RegisterForKeys( task_id );

//串口配置
  uartConfig.configured           = TRUE;              // 2x30 don't care - see uart driver.
  uartConfig.baudRate             = SERIAL_APP_BAUD;
  uartConfig.flowControl          = FALSE;
  uartConfig.flowControlThreshold = SERIAL_APP_THRESH; // 2x30 don't care - see uart driver.
  uartConfig.rx.maxBufSize        = SERIAL_APP_RX_SZ;  // 2x30 don't care - see uart driver.
  uartConfig.tx.maxBufSize        = SERIAL_APP_TX_SZ;  // 2x30 don't care - see uart driver.
  uartConfig.idleTimeout          = SERIAL_APP_IDLE;   // 2x30 don't care - see uart driver.
  uartConfig.intEnable            = TRUE;              // 2x30 don't care - see uart driver.
  uartConfig.callBackFunc         = SerialApp_CallBack;//回调函数
  HalUARTOpen (SERIAL_APP_PORT, &uartConfig);


#if defined ( LCD_SUPPORTED )
  HalLcdWriteString( "SerialApp", HAL_LCD_LINE_2 );
#endif
  //注册节点绑定响应事件和匹配描述符响应事件
  ZDO_RegisterForZDOMsg( SerialApp_TaskID, End_Device_Bind_rsp );
  ZDO_RegisterForZDOMsg( SerialApp_TaskID, Match_Desc_rsp );
#if defined (ZDO_COORDINATOR)//有三种设备不知怎样定义????
  HalUARTWrite(SERIAL_APP_PORT,szWelcomeMsgCoor,sizeof(szWelcomeMsgCoor));
#else
  HalUARTWrite(SERIAL_APP_PORT,szWelcomeMsgRouter,sizeof(szWelcomeMsgRouter));
#endif


}

#define End_Device_Bind_rsp     (End_Device_Bind_req | ZDO_RESPONSE_BIT)

#define End_Device_Bind_req     ((uint16)0x0020)

#define ZDO_RESPONSE_BIT        ((uint16)0x8000)

#define Match_Desc_rsp          (Match_Desc_req | ZDO_RESPONSE_BIT)

#define Match_Desc_req          ((uint16)0x0006)

/*********************************************************************
 * @fn          ZDO_RegisterForZDOMsg
 *
 * @brief       Call this function to register of an incoming over
 *              the air ZDO message - probably a response message
 *              but requests can also be received.
 *              Messages are delivered to the task with ZDO_CB_MSG
 *              as the message ID.
 *
 * @param       taskID - Where you would like the message delivered
 * @param       clusterID - What message?
 *
 * @return      ZSuccess - successful, ZMemError if not
 */
ZStatus_t ZDO_RegisterForZDOMsg( uint8 taskID, uint16 clusterID )
{
  ZDO_MsgCB_t *pList;
  ZDO_MsgCB_t *pLast;
  ZDO_MsgCB_t *pNew;

  // Look for duplicate
  pList = pLast = zdoMsgCBs;
  while ( pList )
  {
    if ( pList->taskID == taskID && pList->clusterID == clusterID )
      return ( ZSuccess );
    pLast = pList;
    pList = (ZDO_MsgCB_t *)pList->next;
  }

  // Add to the list
  pNew = (ZDO_MsgCB_t *)osal_mem_alloc( sizeof ( ZDO_MsgCB_t ) );
  if ( pNew )
  {
    pNew->taskID = taskID;
    pNew->clusterID = clusterID;
    pNew->next = NULL;
    if ( zdoMsgCBs )
    {
      pLast->next = pNew;
    }
    else
      zdoMsgCBs = pNew;
    return ( ZSuccess );
  }
  else
    return ( ZMemError );
}

#define ZDO_CB_MSG                0xD3    // ZDO incoming message callback

什么时候会调用下面这个函数呢???是在UINT16 SerialApp_ProcessEvent( uint8 task_id, UINT16 events )中

/*********************************************************************
 * @fn      SerialApp_ProcessZDOMsgs()
 *
 * @brief   Process response messages
 *
 * @param   none
 *
 * @return  none
 */
static void SerialApp_ProcessZDOMsgs( zdoIncomingMsg_t *inMsg )
{
  switch ( inMsg->clusterID )
  {
    case End_Device_Bind_rsp:
      if ( ZDO_ParseBindRsp( inMsg ) == ZSuccess )
      {
        // Light LED
        HalLedSet( HAL_LED_4, HAL_LED_MODE_ON );
      }
#if defined(BLINK_LEDS)
      else
      {
        // Flash LED to show failure
        HalLedSet ( HAL_LED_4, HAL_LED_MODE_FLASH );
      }
#endif
      break;
      
    case Match_Desc_rsp:
      {
        ZDO_ActiveEndpointRsp_t *pRsp = ZDO_ParseEPListRsp( inMsg );
        if ( pRsp )
        {
          if ( pRsp->status == ZSuccess && pRsp->cnt )
          {
            SerialApp_TxAddr.addrMode = (afAddrMode_t)Addr16Bit;
            SerialApp_TxAddr.addr.shortAddr = pRsp->nwkAddr;
            // Take the first endpoint, Can be changed to search through endpoints
            SerialApp_TxAddr.endPoint = pRsp->epList[0];
            
            // Light LED
            HalLedSet( HAL_LED_4, HAL_LED_MODE_ON );
          }
          osal_mem_free( pRsp );
        }
      }
      break;
  }
}

MT这又是哪一层?

开始时所有任务的状态都被初始化为0,代表了当前任务没有需要响应的事件。事件是如何被OSAL捕获的,即taskEvents数组中的元素是什么时候设定为非零的?

参考http://bbs.feibit.com/thread-280-1-1.html

以按键响应为例,处理按键响应的函数是 Hal_ProcessEvent,采用扫描的方式。那么 Hal_ProcessEvent对应的taskEvents是在什么时候被置为非0的呢?

大体思路是这样的:

1、 // Final board initialization
  InitBoard( OB_READY );中的
 HalKeyConfig(HAL_KEY_INTERRUPT_DISABLE, OnBoard_KeyCallback);     


2、在void HalKeyConfig (bool interruptEnable, halKeyCBack_t cback)中最后有 osal_set_event(Hal_TaskID, HAL_KEY_EVENT);将事件HAL_KEY_EVENT交给任务Hal_TaskID处理,即taskEvents中的与Hal_TaskID相对的元素就被置为了非0,从而调用了

uint16 Hal_ProcessEvent( uint8 task_id, uint16 events )

3、在uint16 Hal_ProcessEvent( uint8 task_id, uint16 events )中又调用了   HalKeyPoll();并且启动了osal_start_timerEx( Hal_TaskID, HAL_KEY_EVENT, 100);即每隔100微秒就要调用uint16 Hal_ProcessEvent( uint8 task_id, uint16 events )一次,而在HalKeyPoll();中来扫描判断按键是否变化。HalKeyPoll()中(pHalKeyProcessFunction) (keys, HAL_KEY_STATE_NORMAL);

static halKeyCBack_t pHalKeyProcessFunction;

pHalKeyProcessFunction这个函数指针指向了 void OnBoard_KeyCallback ( uint8 keys, uint8 state )在这个函数中,又调用了byte OnBoard_SendKeys( byte keys, byte state ),在这个函数中,按键的状态信息被封装到了一个消息结构体中(对于消息,我们稍后再说)。最后有一个极其重要的函数被调用了。osal_msg_send( registeredKeysTaskID, (uint8 *)msgPtr );registeredKeysTaskID所指示的任务正是我们需要响应按键的SerialApp这个任务。在osal_msg_send函数中osal_set_event( destination_task, SYS_EVENT_MSG );

HalKeyPoll()获取当前按键的状态,并且通过调用OnBoard_KeyCallback函数向SerialApp任务发送一个按键消息,并且设置tasksEvents中SerialApp所对应的值为非零。

大神的解答:

第一、pHalKeyProcessFunction这个函数指针为何指向了OnBoard_KeyCallback函数。
  在HAL\Commen\ hal_drivers.c这个文件中,我们找到了HalDriverInit这个函数,在这个函数中,按键的初始化函数HalKeyInit被调用。在HalKeyInit中有这样的语句:
  {
        pHalKeyProcessFunction  = NULL;
  }
  这说明在初始化以后pHalKeyProcessFunction并没有指向任何一个函数。那pHalKeyProcessFunction是什么时候被赋值的呢?
  就在HalKeyInit的下方有一个这样的函数HalKeyConfig。其中有这样一条语句:
  pHalKeyProcessFunction = cback;
  cback是HalKeyConfig所传进来的参数,所以,想要知道它所指向的函数,必须找到其调用的地方。经过简单的搜索我们不难找出答案。在main函数中有这样一个函数调用:InitBoard( OB_READY );此函数中做了如下调用:
  {
        HalKeyConfig( OnboardKeyIntEnable, OnBoard_KeyCallback);
  }
  第二、registeredKeysTaskID为什么标识了GenericApp这个任务?
  由于OSAL是一个支持多任务的调度机制,所以在同一时间内将会有多个任务同时运行。但是从逻辑上来讲,一个事件只能由一个任务来处理。按键事件也不例外。
  那么如何向OSAL声明处理按键事件的任务是GenericApp呢?
  在GenericApp_Init(GenericApp的任务初始化函数)中有这么一个语句:
  {
        RegisterForKeys( GenericApp_TaskID );
  }
  RegisterForKeys函数向OSAL声明按键事件将由GenericApp任务来处理。在RegisterForKeys函数中:
  {
        registeredKeysTaskID = task_id;
  }


说实话下面这句不懂??是回调函数,就是要向SerialApp任务发送按键消息。

/* Invoke Callback if new keys were depressed */
  if (keys && (pHalKeyProcessFunction))
  {
    (pHalKeyProcessFunction) (keys, HAL_KEY_STATE_NORMAL);
  }


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值