查看: 29363|回复: 34

我的ZigBee项目—医疗无线监护系统—开发历程分享(二)

[复制链接]
wwh199169 发表于 2011-11-2 00:16:39 | 显示全部楼层 |阅读模式
本帖最后由 wwh199169 于 2011-11-2 00:20 编辑

列位坛友,时隔三日,在下在经历了上次“字数超标”事件的打击,以及“葵花宝典”文档的辛苦翻译两件事后,今天感觉精力有所恢复,故而继续特来开帖,继续分享开发经历!
      上回说到,应用层的第三个.c文件,即Sensor_18B20.c。从该文件名即可猜出,这是DS18B20传感器的驱动文件,关于这个网上已经有了很多资源,基于各种单片机应用的差异,在这里就不详细介绍了。只是想顺便说一下,我在开发初始遇到的第一个问题就是传感器无法完成初始化,导致程序无法顺利运行。关于此问题的详细解决办法可参考之前分享的帖子:http://www.feibit.com/bbs/viewthread.php?tid=3376&extra=page%3D2

      项目的基本结构就介绍到此,下面就进入程序的整个运行过程!
      众所周知,Z-stack有一个十分神奇的操作系统——os,这个东西对于初学者来说就像一只拦路虎,制服这只老虎才是真正理解ZigBee协议的起点,才能谈及将来的熟练运用。在下作为刚刚入门的中级菜鸟,在此就不献丑了,以免误导大家,呵呵对于这部分的详细解说,outman前辈就有比较精辟的论著,网上也可以搜到一些或零碎或完整的讲解,大家可以参考。下面我就直接以自己的程序为例,讲解它的运行过程。
      由于本系统中有测温节点和显示节点两种设备,故需分为两个子工程分别分析。先讲那个呢?这还真是个问题,不是随意选择的,因为两个节点的工作过程必然存在一个先后关系。比如,当测温节点,也就是协调器,建立好网络,且测温节点成功加入后,是前者先给后者发送了测温指令,还是后者主动测温并向前者发送数据的呢?其实这个问题也无伤大雅,但偏偏我是个有点爱钻牛角尖的人,不弄懂这个,总觉得心里不踏实,呵呵
     经过仔细排查,我终于从程序中找到了答案!
     在测温节点的工程文件RefNode.c中(这个文件就是测温节点所要运行的程序了),其初始化函数RefNode_Init()如下:
void RefNode_Init( byte task_id )
{

  RefNode_TaskID = task_id;
  RegisterForKeys( RefNode_TaskID );

  dstAddr.addrMode = afAddr16Bit;
  dstAddr.addr.shortAddr = 0;
  dstAddr.endPoint = LOCATION_DONGLE_ENDPOINT;

//Initial the send_msg.
  send_msg[0] = 0;
  send_msg[1] = 0;
  osal_memcpy (&send_msg[2], aExtendedAddress, 8);

   //以下是测温节点测温、发送数据等所需用到的参数
  Temperature_Config.T1 = 5; //测温周期
  Temperature_Config.T2 = 10;//发送周期
  Temperature_Config.T3 = 5;//报警周期
  Temperature_Config.deltaT = 1;//温差0.5度
  Temperature_Config.TH = 0x07d0;//报警上限125度
  Temperature_Config.TL = 0;//报警下限0度
  Temperature_Config.TM = 1;//
  Temperature_Config.ER = 0;//
  Temperature_Config.Voltage = 0;//

  SendPeriod = Temperature_Config.T2;
  Voltage_cur = 0;
  Temperature_Cur = 0;
  Temp_samplecnt = 0;
  // Register the endpoint/interface description with the AF.
  afRegister( (endPointDesc_t *)&epDesc );

  Debug_HalUARTWrite( SPI_MGR_DEFAULT_PORT, "refinit!", 8 );

//  osal_start_timerEx( RefNode_TaskID, REF_ABSENCE_EVT,500 );
osal_start_timerEx( RefNode_TaskID, REF_TEMSAMPLE_EVT, (Temperature_Config.T1) * 1000);
/*这一句想必对Z-stack有所了解的人都很熟悉吧,呵呵。这里调用osal_start_timerEx()函数的作用就是每隔(Temperature_Config.T1) * 1000=5*1000ms=5s的时间向操作系统发送测温事件REF_TEMSAMPLE_EVT。接下来,操作系统接收到该事件,就要进入其任务处理函数进行相应的事件处理了,请看下面的任务处理函数RefNode_ProcessEvent()。
*/
//  osal_start_timerEx( RefNode_TaskID, REF_TEMSEND_EVT, SendPeriod * 1000);

}


uint16 RefNode_ProcessEvent( byte task_id, uint16 events )
{
  if ( events & SYS_EVENT_MSG )
  {
    afIncomingMSGPacket_t *MSGpkt;

    while ((MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive(RefNode_TaskID)))
    {
      switch ( MSGpkt->hdr.event )
      {
      case AF_DATA_CONFIRM_CMD:
        break;

      case AF_INCOMING_MSG_CMD:
        Ref_processMSGCmd( MSGpkt );
        break;

      case ZDO_STATE_CHANGE:
//       osal_start_timerEx( RefNode_TaskID, REF_ABSENCE_EVT, ANNCE_DELAY );
        break;

      case KEY_CHANGE:
          HAL_TURN_OFF_LED1();//
          P0DIR|=0x80;
          P0_7=0;//
          send_msg[0] = 0x00;
          send_msg[1] = 0x08;//这两个数据数组元素本来是存储16位温度数值的低8位和高8位的,这里我装入了一个固定的数,原因见后文
          (void)AF_DataRequest( &dstAddr, (endPointDesc_t *)&epDesc,
                           TAS_READ_TEMPERATURE, 10, send_msg,
                          &transId, 0, AF_DEFAULT_RADIUS );//这个就是消息发送函数,所要传递的数据在send_msg数组中,
       break;
/*这一段就是为了发送求助报警信号而添加的按键事件处理程序。这里用了一点小技巧,偷了点懒。本来嘛,既然是通过按键发送的消息,那肯定要把按键检测出来,然后把按键值作为数据保存到数据数组send_msg[0]send_msg[1]中,然后在显示节点的程序里再提取出该数组中的信息,判断是温度数值还是按键数据。这样做不仅繁琐,而且由于温度数据是16位的,而按键数据是8位的,传递起来也不方便。所以,我就想了个”邪门歪道”——反正测温节点只有这么一个按键任务,不会存在冲突的问题,不如不去管到底是哪个按键,只要是有按键被按下,即发生案件事件,就知道是病人要发送求助信息了。这样,就可以用一个固定的数字来代替按键检测,表示求助信号。这样,在显示节点的数据判断程序中,也可以直接在已有的判断温度数据的switch函数里添加对该固定数据的处理,方便多了。于是,我就采用了0x0800这个数据。当然,这也不是随便取的,因为这个数据是和温度数据一起被switch函数判断的,故而不能与其重复,否则程序就会执行错误的代码。因为DS18b20的测温范围上限是125℃,也就是12位方式下的0x07D0,那只要比这个数大,就肯定不会产生冲突了!于是,我就取了个好判断的0x0800,其实我也可以取0x8888的,瞧,多吉利啊,呵呵*/



      default:
        break;
      }

      osal_msg_deallocate( (uint8 *)MSGpkt );
    }

    // Return unprocessed events.
    return ( events ^ SYS_EVENT_MSG );
  }


// Temperature acquisition event
  if ( events & REF_TEMSAMPLE_EVT )//初始化传递进来的事件就在这里开始被处理
  {
    //唤醒函数
//从18b20读取温度  
    Temperature_Cur = read_data_value();
    Temp_samplecnt++;

    if((Temp_Convert(Temperature_Cur) > Temp_Convert(Temperature_Config.TH))
        || (Temp_Convert(Temperature_Cur) < Temp_Convert(Temperature_Config.TL)))
    {
      SendPeriod = Temperature_Config.T3;//报警发送周期
    }
    else
    {
      SendPeriod = Temperature_Config.T2;//普通发送周期
    }

    Debug_HalUARTWrite( SPI_MGR_DEFAULT_PORT, "temp sampled!", 13 );

  //if Temp_samplecnt exceed the time,send out message
    if(Temp_samplecnt >= (SendPeriod/(Temperature_Config.T1)))
    {
      Temp_samplecnt = 0;
      send_msg[0] = Temperature_Cur & 0x00FF;
      send_msg[1] = (Temperature_Cur >> 8) & 0x00FF;



//Enable the rx radio


macRxEnable(1);
      (void)AF_DataRequest( &dstAddr, (endPointDesc_t *)&epDesc,
                           TAS_READ_TEMPERATURE, 10, send_msg,
                          &transId, 0, AF_DEFAULT_RADIUS );//这个就是消息发送函数,所要传递的数据在send_msg数组中,
/*至此,我明白了——测温节点在成功加入网络后,即开始进入其初始化函数RefNode_Init(),其中调用osal_start_timerEx()向操作系统发送了测温事件REF_TEMSAMPLE_EVT,于是操作系统在轮询时发现该事件标志,进而进入对应的事件处理函数RefNode_ProcessEvent()中。这个函数就完成了数据采集,并通过上面这个发送函数将数据发给了显示节点。因此,是测温节点加入网络后主动采集温度数据并发送!*/


//turn off the light so as to save power
        // HAL_TURN_OFF_LED1();
        // HAL_TURN_OFF_LED2();
        // HAL_TURN_OFF_LED3();

// HAL_TURN_OFF_LED4();

//disable the rx radio

macRxHardDisable();


    }


    osal_start_timerEx( RefNode_TaskID, REF_TEMSAMPLE_EVT, (Temperature_Config.T1) * 100);//这一次调用是让测温节点改为每隔5*100ms=0.5s采集温度数据并发送
  //enter power saving mode
  #if 0

  #if defined( POWER_SAVING )

    SET_MAIN_CLOCK_SOURCE(CRYSTAL);
    SET_LOW_CLOCK(CRYSTAL);
    Init_SLEEP_TIMER();
    addToSleepTimer(10);
    SET_POWER_MODE(5);
   // P1_0=0;
    //PowerMode(2);

         //osal_pwrmgr_task_state( RefNode_TaskID, PWRMGR_CONSERVE );
    //   halRestoreSleepLevel( );
       //halSleepSetTimer(0x23);
   //   HAL_ISR_FUNCTION(halSleepTimerIsr, ST_VECTOR);
      NLME_SetPollRate( 0 );
    #endif

#endif
    // Return unprocessed events.
    return ( events ^ REF_TEMSAMPLE_EVT );
  }


  return 0;  // Discard unknown events
}


     今天就先说到这里,时间也不早了。欲知后事如何,且听下回分解,多谢列位捧场!
outman 发表于 2011-11-2 12:44:49 | 显示全部楼层
楼主辛苦了。
能有楼主这样能沉下心来研究,又愿意分享的朋友,是飞比论坛的福,也是大家的福~~
楼主加油
安龙飞 发表于 2011-11-2 17:15:24 | 显示全部楼层
写的很好额   赞一个
cfqz11234 发表于 2011-11-7 16:00:01 | 显示全部楼层
好东西啊,但人气怎么这么少啊,
F117C 发表于 2011-11-14 09:09:41 | 显示全部楼层
这个系列的可以作为入门极好的参考了!
fugushatu 发表于 2011-11-22 14:08:28 | 显示全部楼层
学习了!感谢楼主!
davidzeng 发表于 2011-12-5 16:46:00 | 显示全部楼层
好好学习。。。
more606 发表于 2011-12-14 16:51:45 | 显示全部楼层
好好学习。。。
duanjingbo1 发表于 2011-12-26 23:25:24 | 显示全部楼层
很透彻!强烈支持分享开发经验!
Arlene 发表于 2012-3-2 16:45:04 | 显示全部楼层
十分感谢楼主分享!
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

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