查看: 24703|回复: 30

个人对SENSOR DEMO 的认识

[复制链接]
真诚星君 发表于 2011-8-19 09:46:10 | 显示全部楼层 |阅读模式
由TI公司开发的ZStack2007是一种比较完善的协议标准,但是它半开源的,这就给我们学习带来了很多不便的地方,
           我自己在这一块也是刚入门,所以很多东西我自己也不是很明白,因此不能很系统的对这个进行讲解,
          所以只能通过一个例子来介绍下我所知道的一些东西,大家如果觉得有道理就记下,如果感觉不是很对,可以舍弃我的观点,
          或与我进行商讨。好了,我们进入SENSOR DEMO这个例子的学习吧。。。
        就如很多人介绍的一样,我们从MIAN函数进入。。。。。
        在主函数里我们主要了解以下两个函数:
         osal_init_system();                操作系统初始化
        osal_start_system();                进入操作系统
          这个大家应该都知道的哈,我就粗略介绍了。。
        在 osal_init_system();函数中又有一个函数osalInitTasks();任务初始化函数,在这个函数里
        void osalInitTasks( void )
        {
          uint8 taskID = 0;
       
          tasksEvents = (uint16 *)osal_mem_alloc( sizeof( uint16 ) * tasksCnt);
          osal_memset( tasksEvents, 0, (sizeof( uint16 ) * tasksCnt));
       
          macTaskInit( taskID++ );
          nwk_init( taskID++ );
          Hal_Init( taskID++ );
        #if defined( MT_TASK )
          MT_TaskInit( taskID++ );
        #endif
          APS_Init( taskID++ );
          ZDApp_Init( taskID++ );
          SAPI_Init( taskID );
        }每个层面都对应了一个ID号,这是操作系统所必须的,方便后面的任务实施。
        这里了解到这个样子就差不多了,如果追求的太深,反倒搞的自己很晕的。
        好,现在我们进入了操作系统了
       
        void osal_start_system( void )
        {
        #if !defined ( ZBIT ) && !defined ( UBIT )
          for(;;)  // Forever Loop
        #endif
          {
            uint8 idx = 0;
       
            osalTimeUpdate();                               时间更新,与那个定时设定时间的函数有联系
            Hal_ProcessPoll();  // This replaces MT_SerialPoll() and osal_check_timer().
            
            do {
              if (tasksEvents[idx])  // Task is highest priority that is ready.
              {
                break;
              }
            } while (++idx < tasksCnt);
        以上都是查询是否有事件未处理

            if (idx < tasksCnt)                       若有事件进入下面操作
            {
              uint16 events;
              halIntState_t intState;
       
              HAL_ENTER_CRITICAL_SECTION(intState);
              events = tasksEvents[idx];
              tasksEvents[idx] = 0;  // Clear the Events for this task.
              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
          }
        }
        操作系统在这里一直查询,是否有事件发生,若有,则进入相应层面的处理函数,
          我通过单步调试,发现第一个事件就是网络层的16号事件,由于被屏蔽了,所以无法追踪到具体函数,我感觉应该是组网事件,
        还有一个事件是可以确定的,那就是HAL层的按键事件,它在一直扫描按键状态 if (events & HAL_KEY_EVENT)     //0x0001
          {
       
        #if (defined HAL_KEY) && (HAL_KEY == TRUE)
            /* Check for keys */
            HalKeyPoll();
       
            /* if interrupt disabled, do next polling */
            if (!Hal_KeyIntEnable)
            {
              osal_start_timerEx( Hal_TaskID, HAL_KEY_EVENT, 100);
            }
        #endif // HAL_KEY
       
            return events ^ HAL_KEY_EVENT;
          }从代码可以看出,这个按键如果采用了中断的话,就是中断触发相应的事件,否则就是由轮询的方式进行检测。
        除了这一点确定以外,虽然知道有些事件发生了,但却无法确定其发生的次序,所以我就不好说那个执行的流程了,
         根据SENSOR DEMO的说明文档,设置协调器是需要一个UP按键操作,好。。我们就去找到它。。我们就直接找到处理函数了,
         至于它是怎么触发的我就不详细介绍了,这个我自己也是一知半解,如果有兴趣的话可以去论坛里面找,有一篇专门讲按键流程的文章,
        我只是稍微了解了下,没去深究,这个的处理函数在
        if ( keys & HAL_KEY_SW_1 )
            {
              if ( appState == APP_INIT  )
              {
                // Key 1 starts device as a coordinator
                logicalType = ZG_DEVICETYPE_COORDINATOR;
zb_WriteConfiguration(ZCD_NV_LOGICAL_TYPE, sizeof(uint8), &logicalType);
                        
                // Reset the device with new configuration
                zb_SystemReset();
      }这就是实现的代码,这个函数主要就是利用了这个函数zb_WriteConfiguration(ZCD_NV_LOGICAL_TYPE, sizeof(uint8), &logicalType);
   这个函数将设备类型写入到NV中去,据网友介绍,就是一块非易失性存储器,保存设备类型的,论坛也有专门的介绍,自己可以去找到了解下的。
   重启以后它就被定性为协调器,以后就靠它组网了,再比较下路由器的操作,可以猜测默认的设备类型是路由器的,
   至于是在哪里进行初始化的我就没去仔细找了,终端节点也是直接默认的。到了这一步设备的类型就设置好了。

        设置好了设备类型了以后,让我们开始数据传输吧。。首先打开协调器,让它组建网络,
         当其稳定下来以后,有个现象就是LED1,LED3常亮,而LED2闪烁,看到了这个现象以后我们就可以开始下一步的操作,
         允许绑定,操作是导航键向右,
         if ( keys & HAL_KEY_SW_2 )
            {
              allowBind ^= 1;
              if (allowBind)
              {
                // Turn ON Allow Bind mode infinitly
                zb_AllowBind( 0xFF );
                HalLedSet( HAL_LED_2, HAL_LED_MODE_ON );
                //This node is the gateway node
                isGateWay = TRUE;
                
                // Update the display
                #if defined ( LCD_SUPPORTED )
                HalLcdWriteString( "Gateway Mode", HAL_LCD_LINE_2 );
                #endif
              }
              else
              {
                // Turn OFF Allow Bind mode infinitly
                zb_AllowBind( 0x00 );
                HalLedSet( HAL_LED_2, HAL_LED_MODE_OFF );
                isGateWay = FALSE;
                
                // Update the display
                #if defined ( LCD_SUPPORTED )
                HalLcdWriteString( "Collector", HAL_LCD_LINE_2 );
                #endif
              }
            }这是这一步的操作代码,操作成功后小液晶会显示
         HalLcdWriteString( "Gateway Mode", HAL_LCD_LINE_2 );
        此时你再打开终端节点,那么就会自动地加入网络,建立绑定,最终稳定后的现象是LED1,LED2.LED3快速闪烁,
           到了这一步的时候,网络就已经形成了,接下来我们开始传输采集到得信息吧,终端节点导航键向下操作,开始发送报告。!
         if ( keys & HAL_KEY_SW_3 )
            {
              // Start reporting
              osal_set_event( sapi_TaskID, MY_REPORT_EVT );
              reportState = TRUE;
            }这是实现功能的代码,设置了事件
        if ( event & MY_REPORT_EVT )
          {
            if ( appState == APP_REPORT )
            {
              sendReport();
              osal_start_timerEx( sapi_TaskID, MY_REPORT_EVT, myReportPeriod );
            }
          }事件处理主要由函数sendReport();实现

        osal_start_timerEx( sapi_TaskID, MY_REPORT_EVT, myReportPeriod );
        这个函数就是设置循环事件,一直都有发送事件需要处理

        说到这里应该不需要再继续讲下去了吧,好了,这还只是终端节点采集了信息,然后发送给了协调器而已,接下来我们看看协调器怎么处理这些信息的。。
          case AF_INCOMING_MSG_CMD:
                  pMSGpkt = (afIncomingMSGPacket_t *) pMsg;
                         SAPI_ReceiveDataIndication( pMSGpkt->srcAddr.addr.shortAddr, pMSGpkt->clusterId,
                                       pMSGpkt->cmd.DataLength, pMSGpkt->cmd.Data);
                  break;
        这是射频模块接受到信息的处理函数。。处理从这里开始
        接下来我们进入SAPI_ReceiveDataIndication()看这个函数干了些什么
        void SAPI_ReceiveDataIndication( uint16 source, uint16 command, uint16 len, uint8 *pData  )
        {
        #if defined ( MT_SAPI_CB_FUNC )
          /* First check if MT has subscribed for this callback. If so , pass it as
          a event to MonitorTest and return control to calling function after that */
          if ( SAPICB_CHECK( SPI_CB_SAPI_RCV_DATA_IND ) )
          {
            zb_MTCallbackReceiveDataIndication( source, command, len, pData  );
          }
          else
        #endif  //MT_SAPI_CB_FUNC
          {
        #if ( SAPI_CB_FUNC )
            zb_ReceiveDataIndication( source, command, len, pData  );
        #endif
          }
        }貌似没看到什么有意义的东西啊。。

        然后我们继续追踪zb_ReceiveDataIndication()看看它的功能
        void zb_ReceiveDataIndication( uint16 source, uint16 command, uint16 len, uint8 *pData  )
        {
          gtwData.parent = BUILD_UINT16(pData[SENSOR_PARENT_OFFSET+ 1], pData[SENSOR_PARENT_OFFSET]);
          gtwData.source=source;
          gtwData.temp=*pData;
          gtwData.voltage=*(pData+1);
          
          // Flash LED 2 once to indicate data reception
          HalLedSet ( HAL_LED_2, HAL_LED_MODE_FLASH );
          
          // Update the display
          #if defined ( LCD_SUPPORTED )
          HalLcdWriteScreen( "Report", "rcvd" );
          #endif
          
          // Send gateway report
          sendGtwReport(&gtwData);
        }这里就开始有一些处理了,首先这个最明显的就是液晶的显示内容在这里我们可以看到了。。HalLcdWriteScreen( "Report", "rcvd" );
        看来我们是追踪对了,,呵呵。。好继续往下查sendGtwReport(&gtwData);

评分

1

查看全部评分

outman 发表于 2011-8-22 14:58:32 | 显示全部楼层
实在抱歉,审核晚了。多谢楼主的共享
purenet1 发表于 2011-8-24 09:21:58 | 显示全部楼层
辛苦了,正好也在看这段
zhengya198902 发表于 2011-8-24 15:58:49 | 显示全部楼层
谢谢楼主的分享
 楼主| 真诚星君 发表于 2011-8-27 09:07:06 | 显示全部楼层
static void sendGtwReport(gtwData_t *gtwData)
        {
          uint8 pFrame[ZB_RECV_LENGTH];
          
          // Start of Frame Delimiter
          pFrame[FRAME_SOF_OFFSET] = CPT_SOP; // Start of Frame Delimiter
          
          // Length
          pFrame[FRAME_LENGTH_OFFSET] = 10;
          
          // Command type
          pFrame[FRAME_CMD0_OFFSET] = LO_UINT16(ZB_RECEIVE_DATA_INDICATION);   
          pFrame[FRAME_CMD1_OFFSET] = HI_UINT16(ZB_RECEIVE_DATA_INDICATION);
          
          // Source address
          pFrame[FRAME_DATA_OFFSET+ ZB_RECV_SRC_OFFSET] = LO_UINT16(gtwData->source);
          pFrame[FRAME_DATA_OFFSET+ ZB_RECV_SRC_OFFSET+ 1] = HI_UINT16(gtwData->source);
          
          // Command ID
          pFrame[FRAME_DATA_OFFSET+ ZB_RECV_CMD_OFFSET] = LO_UINT16(SENSOR_REPORT_CMD_ID);
          pFrame[FRAME_DATA_OFFSET+ ZB_RECV_CMD_OFFSET+ 1] = HI_UINT16(SENSOR_REPORT_CMD_ID);
          
          // Length
          pFrame[FRAME_DATA_OFFSET+ ZB_RECV_LEN_OFFSET] = LO_UINT16(4);
          pFrame[FRAME_DATA_OFFSET+ ZB_RECV_LEN_OFFSET+ 1] = HI_UINT16(4);
          
          // Data
          pFrame[FRAME_DATA_OFFSET+ ZB_RECV_DATA_OFFSET] = gtwData->temp;
          pFrame[FRAME_DATA_OFFSET+ ZB_RECV_DATA_OFFSET+ 1] = gtwData->voltage;
          pFrame[FRAME_DATA_OFFSET+ ZB_RECV_DATA_OFFSET+ 2] = LO_UINT16(gtwData->parent);
          pFrame[FRAME_DATA_OFFSET+ ZB_RECV_DATA_OFFSET+ 3] = HI_UINT16(gtwData->parent);
          
          // Frame Check Sequence
          pFrame[ZB_RECV_LENGTH - 1] = calcFCS(&pFrame[FRAME_LENGTH_OFFSET], (ZB_RECV_LENGTH - 2) );
          
          // Write report to UART
          HalUARTWrite(HAL_UART_PORT_0,pFrame, ZB_RECV_LENGTH);
        }这个代码虽然比较长,但是功能却很简单,将数据发送到串口,你观察这个代码的数组的内容,再比对下你电脑串口接收到得数据,发现很吻合,首先是长度,然后就是一些常量
          // Length
          pFrame[FRAME_DATA_OFFSET+ ZB_RECV_LEN_OFFSET] = LO_UINT16(4);
          pFrame[FRAME_DATA_OFFSET+ ZB_RECV_LEN_OFFSET+ 1] = HI_UINT16(4);
          
        这里很容易看出是取了4的高位和低位,经过一番查证,可以确定这个流程是对的,所谓知己知彼,方能百战不殆,这是我个人喜好。知道了这个例子的运行过程,然后要改动它就显得很简单了,
        我们从采集节点入手,
        static void sendReport(void)
        {
          uint8 pData[SENSOR_REPORT_LENGTH];
          static uint8 reportNr=0;
          uint8 txOptions;
          
          // Read and report temperature value
          pData[SENSOR_TEMP_OFFSET] =  readTemp();
          
          // Read and report voltage value
          pData[SENSOR_VOLTAGE_OFFSET] = readVoltage();
            
          pData[SENSOR_PARENT_OFFSET] =  HI_UINT16(parentShortAddr);
          pData[SENSOR_PARENT_OFFSET + 1] =  LO_UINT16(parentShortAddr);
          
          // Set ACK request on each ACK_INTERVAL report
          // If a report failed, set ACK request on next report
          if ( ++reportNr<ACK_REQ_INTERVAL && reportFailureNr==0 )
          {
            txOptions = AF_TX_OPTIONS_NONE;
          }
          else
          {
            txOptions = AF_MSG_ACK_REQUEST;
            reportNr = 0;
          }
          // Destination address 0xFFFE: Destination address is sent to previously
          // established binding for the commandId.
          zb_SendDataRequest( 0xFFFE, SENSOR_REPORT_CMD_ID, SENSOR_REPORT_LENGTH, pData, 0, txOptions, 0 );
        }这是采集节点的发送函数,红色部分就是采集函数了,我们首要任务就是将采集函数换成自己的,这个就很简单了吧,选择管脚,搭一下电路,。。。。就可以搞定了,呵呵,这个我就不细说了。。我们接下来就根据采集到的信息量来确定数组的大小,也就是pData的大小,原程序是一个字节的温度,一个字节的光照,由于我采集的温湿度都是16位的,所以存储这些信息量就得改成2个字节,上面粉红色的数据长度就得修改了。
        这样数据就由终端节点发出,通过函数zb_SendDataRequest(),传到了协调器节点以后,其实步骤就差不多了,仿照终端节点的方法,进行一些改造,就可以通过串口得到你所采集的数据了。
 楼主| 真诚星君 发表于 2011-8-27 09:07:53 | 显示全部楼层
呼呼。。今天才看到自己的帖子。。以上是没有粘完的东东。。!!
CC2430 发表于 2011-9-23 22:53:38 | 显示全部楼层
楼主分析的很好,使我对Zstack OSAl的理解更清楚了。但我想问问,该怎样去改“我们首要任务就是将采集函数换成自己的,这个就很简单了吧,选择管脚,搭一下电路”,自己是初学,不是很懂,尤其是对硬件电路。谢谢楼主。
wh413941714 发表于 2011-9-24 16:21:27 | 显示全部楼层
楼主,你最后说你做的温湿度,温度我知道,可以用片内的,也可以用片外的,FB2530BB板子上就有一个片外的温度传感器,也就是温敏电阻,同时有一个光敏电阻。可是湿度你是怎么测的?自己加的外设吗?
ocean 发表于 2011-9-24 23:49:43 | 显示全部楼层
楼主抓了重点,感觉挺好的。
 楼主| 真诚星君 发表于 2011-10-7 16:56:08 | 显示全部楼层
回复 6# 真诚星君


    这个由于我有开发板,所以就可以接外设,我接了一个AM2301,然后将原来例子里的采集函数换成了我对自己的传感器的函数,可以进行采集了
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表