查看: 61345|回复: 68

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

[复制链接]
wwh199169 发表于 2011-11-6 01:54:25 | 显示全部楼层 |阅读模式
又是周六,该挤出点时间继续我的开发历程分享了。
     上篇帖子基本介绍完了测温节点的运行过程,现在轮到系网络中的主角——显示节点了。先来回顾一下显示节点所要完成的任务1,收集两个测温节点发送过来的温度数据,分别显示在液晶屏的第二、第三行;
2,当某个节点传送过来的温度数据超过预设的报警界限时,发出报警信息(文字显示+蜂鸣器报警);
3,当有病人通过测温节点按键发出“求助”信号时,发出提示信息(文字显示+蜂鸣器报警);
4,以上两种报警信息分别可通过OK和Cancel按键取消报警;
5,通过串口将两个测温节点的IEEE地址和温度数据上传至电脑(通过串口调试助手软件)。
      以上就是显示节点在本系统中所要完成的任务了。和测温节点一样,先来看看显示节点的初始化函数
void LocDongle_Init( uint8 task_id )
{
  LocDongle_TaskID = task_id;
  LocDongle_TransID = 0;

  // Register the endpoint/interface description with the AF
  afRegister( (endPointDesc_t *)&epDesc );
  RegisterForKeys( LocDongle_TaskID );//注册按键事件以实现按键功能
}

      呵呵,很简单,就这么四行,没啥可说的。初始化的完成应该也和测温节点一样,是在网络成功建立后才进行的。完成初始化后,它就在操作系统的控制下不断的轮询是否有事件发生。这里说的“事件”应该包括两类事件:1,是否有节点请求加入网络;2,节点加入网络后,是又有数据消息传送进来。第一个事件跟咱关系不大,都是协议栈预设好了的,可以自动完成事件的处理。其实我在调试过程中也一直没捕捉到这个事件的处理过程,我想一方面是由于这个处理过程只在节点加入网络时处理一次,不容易捕捉;另一方面这个过程可能是封装起来了的。不知道到底这个过程是怎样的,网上好像也没看到过此类分析,不过这对咱初学者来说也没多大意义,此处就暂且按下不表。
      从上一帖中可知,测温节点在加入网络后即开始采集温度数据并主动发送消息给显示节点。于是,在成功接受测温节点加入网络后不久,显示节点上就会发生一个属于SYS_EVENT_MSGAF_INCOMING_MSG_CMD事件,接着,操作系统就会根据事件的task_id进入到显示节点的事件处理函数中,如下:
UINT16 LocDongle_ProcessEvent( uint8 task_id, UINT16 events )
{
  afIncomingMSGPacket_t *MSGpkt;

  if ( events & SYS_EVENT_MSG )//发生了一个系统事件
  {
    MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( LocDongle_TaskID );//提取消息中的信息
    while ( MSGpkt )
    {
      switch ( MSGpkt->hdr.event )
      {      
        case SERIALAPP_UART_REV_EVT://received UART command
            LocDongle_Process_UARTMSGCmd();
            Debug_HalUARTWrite( SPI_MGR_DEFAULT_PORT, "uart ok!", 8 );
          break;

       case KEY_CHANGE://按键事件的处理
          HAL_TOGGLE_LED1();//按一次键LED1就转换一次状态
          if (MSGpkt->groupId == 1024)
             cancel_alarm1 =~ cancel_alarm1;//cancel_alarm1对应OK键,用来标志测温节点1的报警或取消,为0xFF(~0)时取消报警
          else
             cancel_alarm2 =~ cancel_alarm2;//cancel_alarm2对应Cancel键,用来标志测温节点2的报警或取消,为0xFF(~0)时取消报警
          break;
/*嘻嘻,这里我又偷了个懒。有过按键事件处理经验的坛友们可能遇到过这样的问题:前面用来提取消息的变量MSGpktafIncomingMSGPacket_t这种格式的,而协议栈中传递按键事件的消息却是keyChange_t格式的。这两种格式定义分别如下:
typedef struct
{
  osal_event_hdr_t hdr;
  byte             state; // shift
  byte             keys;  // keys
} keyChange_t;

typedef struct
{
  osal_event_hdr_t hdr;
  uint16 groupId;
  uint16 clusterId;
  afAddrType_t srcAddr;
  byte endPoint;
  byte wasBroadcast;
  byte LinkQuality;
  byte SecurityUse;
  uint32 timestamp;
  afMSGCommandFormat_t cmd;
} afIncomingMSGPacket_t;

      想必大家都看出来了吧,这两种格式根本不兼容嘛!这样,在这个事件处理函数中就无法直接抽取keyChange_t中的按键信息keys,也就不能判断是哪个键被按下了。我试了几种方法,包括强制类型转换(IAR提示Error)、从afIncomingMSGPacket_t格式的某些参数里抽取数据(根本没有储存按键值的参数),都没能成功,网上一搜,找到的也只是一堆同样的问题,而且都没有得到解决的。我也在这里提出这个疑问,希望高手能给予指点!
      最后,实在没办法了,我只好通过调试来寻求解决。几次一试,还真给我找到窍门了!大家注意afIncomingMSGPacket_t格式中我用蓝色标出的参数groupId,通过对MSGpkt的跟踪Watch,我发现分别按下OK键Cancel键后,这个groupId分别对应的变为10246879。说实话,我到现在也不知道为什么有这种变化,但它就是这么变滴!那咱就先不管,就利用这点区别,足以判断是那个按键被按下了,嘻嘻
     于是,我就用上面的代码分别处理了OK键和Cancel键被按下后的动作,即若groupId为1024,表示OK键被按下,则将cancel_alarm1置位0xFF(~0),取消对节点1的本次报警,否则表示Cancel键被按下,则将cancel_alarm2置位0xFF(~0),取消对节点2的本次报警。之后若再次按下OK键或Cancel键,则将cancel_alarm1或cancel_alarm2复位0(~0xFF),重新开启报警功能,以便之后的报警信号能被正常处理.*/


        case AF_INCOMING_MSG_CMD://接收到节点消息后,进入消息处理函数
          LocDongle_ProcessMSGCmd( MSGpkt );
          break;

        default:
          break;
      }

      osal_msg_deallocate( (uint8 *)MSGpkt );
      MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( LocDongle_TaskID );
    }

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

#if defined ( ZBIT )
  if ( events & DONGLE_TIMER_EVT )
  {
//    processTimerEvt();

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

  // Discard unknown events
  return 0;
}


     下面看看经过在下几天的创作,被改得面目全非的消息处理函数(两个节点的处理过程类似,故仅在节点1的处理过程里详细注释),代码写的有点乱,大家凑和着看吧,呵呵
static void LocDongle_ProcessMSGCmd( afIncomingMSGPacket_t *pkt )
{
  unsigned int Tempvalue;//储存16位的原始温度数值

  unsigned char ch[8];//储存转变后的温度数值(字符串格式,可直接送到液晶显示函数Print()里显示出来)
  unsigned int t;

  UART_SendMsg.start_frame = 0xff;//发送到串口的帧头
  UART_SendMsg.cmd = (uint8)(pkt->clusterId);//串口发送命令代码
if (cancel_alarm1 == 0xFF) num1=0;
  if (cancel_alarm2 == 0xFF) num2=0;
/*这两句作用相同,以第一句为例:若病人通过测温节点的按键发出“求助”信息,根据前一篇帖子中的介绍,消息包中会在原本储存温度数据的数组中替代以一个标志“求助”信息的数值—0x0800,这个数值经过后面的转换程序后会变成12800,也就是说在显示节点程序中标志“求助”的是12800这个数字。在按下OK键取消对节点1的报警后,由于num1的数值仍保持之前的12800,则在后面的条件判断中有会进入报警程序,达不到取消报警的目的。因此,此处将num1置0,以防止再次进入报警程序,而能够进入显示“护理中...”文字提示的程序。*/

    UART_SendMsg.dat[0] = *pkt->cmd.Data++;//抽取温度数值低8位
    UART_SendMsg.dat[1] = *pkt->cmd.Data--;//抽取温度数值高8位
    osal_memcpy (UART_SendMsg.dat, pkt->cmd.Data, 10);
/*温度数值64位的IEEE地址保存到UART_SendMsg.dat数组中,这里有一点要注意,也是很多坛友存在疑惑的:从pkt->cmd.Data数组中的第3个元素(前两个分别存放了温度数值的低高8位)开始到第10个元素这8个,依次存放了64位(8字节)IEEE地址,顺序是从低到高,也就是说第3个元素中存放的是IEEE地址的最后一个字节。这个IEEE地址可以利用TI的一个工具软件SmartRF Flash Programmer自己设定,我为两个节点分别设置了FF FF FF FF FF FF FF 00(节点1)和FF FF FF FF FF FF FF 01(节点2)。这样就可以通过第3个元素来判断该消息是哪个节点发送过来的了:若为00,表示这个消息是节点1的,否则就是节点2的。当然,这是只有两个节点的情况,若多了就还要增加判断条件。 */

评分

1

查看全部评分

 楼主| wwh199169 发表于 2011-11-6 22:11:08 | 显示全部楼层
唉,枉我辛辛苦苦写的帖子,竟然无人问津~~~
 楼主| wwh199169 发表于 2011-11-6 02:03:35 | 显示全部楼层
本帖最后由 wwh199169 于 2011-11-6 02:07 编辑

Tempvalue = UART_SendMsg.dat[1];

Tempvalue = ( Tempvalue << 8 )|UART_SendMsg.dat[0];//16位原始温度数值转存到Tempvalue 中供下面的数据转换成字符串处理

   

  if (UART_SendMsg.dat[2]==00)//若消息是节点1发送过来的,就对num1进行处理,供后面使用

  {if (num1 != 12800)   //num1==12800,则表示病人发出求助信号,保持该值不变进入求助显示程序

    num1=Tempvalue*6.25;//num1 != 12800,则先要进行数据处理,然后判断是否超出报警界线

   

    if(UART_SendMsg.dat[1]&0x80)//负数

      {

      ch[0]='-';             //+0x2d 为变"-"ASCII

      }

      else ch[0]='+';  

   

     if(num1/10000==0)    ch[1]=' ';

     else   ch[1]=num1/1000+0x30;     //+0x30 为变 0~9 ASCII

     if((num1/1000%10==0)&&(num1/10000==0))  ch[2]=' ';

     else   ch[2]=num1/1000%10+0x30;

     ch[3]=num1/100%10+0x30;

     ch[4]='.';

     ch[5]=num1/10%10+0x30;//忽略小数点后1位的数

     ch[6]=num1%10+0x30;

     ch[7]='\0';

  };//以上这一大段就是温度数值num1转变成可直接显示的字符串的处理,下面这一大段类似~~~本来想另做一个函数的,但涉及到诸多参数的传递,还不如复制粘贴来得快,就···

  if (UART_SendMsg.dat[2]==01)//若消息是节点2发送过来的,就对num2进行处理,供后面使用

  {

    if (num2 != 12800)   //num1==12800,则保持该值不变以进入求助显示程序

    num2=Tempvalue*6.25;

  

  if(UART_SendMsg.dat[1]&0x80)//负数

      {

      ch[0]='-';             //+0x2d 为变"-"ASCII

      }

      else ch[0]='+';  

   

     if(num2/10000==0)    ch[1]=' ';

     else   ch[1]=num2/1000+0x30;     //+0x30 为变 0~9 ASCII

     if((num2/1000%10==0)&&(num2/10000==0))  ch[2]=' ';

     else   ch[2]=num2/1000%10+0x30;

     ch[3]=num2/100%10+0x30;

     ch[4]='.';

     ch[5]=num2/10%10+0x30;//忽略小数点后1位的数

     ch[6]=num2%10+0x30;

     ch[7]='\0';

  }

switch(UART_SendMsg.dat[2])//这一句也是判断消息是来自哪个节点的,以分别调用前面的num1num2   

{ case(0):

   

 楼主| wwh199169 发表于 2011-11-6 02:08:32 | 显示全部楼层
本帖最后由 wwh199169 于 2011-11-6 02:12 编辑

if (num1==8500)break;

/*大家可能对这一句莫名其妙,其实也是我偷懒的结果。用过DS18b20的坛友可能也有这方面的经验:有时候传感器第一次给出的数值会是FF,换算后就是我这里的8500。可能还是传感器初始化程序的问题吧,我之前好不容易解决了无法完成初始化的问题,至于这个我实在是无能为力,就只好补上这一句。它的作用就是放弃对温度数值为8500的消息的处理,等待下一个消息包。一般来说第二个消息包的温度就是正常的了。若不添加这一句,系统刚开始就会在下面的判断中进入报警程序而出错。*/


if (cancel_alarm1 == 0)
//cancel_alarm1初值就是0,表示开启报警功能,所以将会对消息包中的num1进行判断   

{     if (num1==12800)//病人发送的求助标志     

    {      

       P0DIR|=0x80;        P0_7=0;//开蜂鸣器报警        

       Print(HAL_LCD_LINE_2,48,"   求助!  ",0);//显示求助信号      

        for(t=0;t<10;t++)         

        MicroWait( 30000 );//延时0.3s      

        P0_7=1;//关蜂鸣器,制造断续报警效果        

        Print(HAL_LCD_LINE_2,48,"          ",1);   //显示空白,制造文字闪烁效果       

        for(t=0;t<10;t++)         

        MicroWait( 10000 );//延时0.1s     };          

if ((num1 >=2500 || num1 <=0) && num1 !=12800)//判断温度是否超出正常范围,这里的两个值分别对应250,是为了方便报警程序调试的,实际应用当然不可能是这个数值的哦,呵   

{              if (cancel_alarm1 == 0)     

        { P0DIR|=0x80;         P0_7=0;         

           Print(HAL_LCD_LINE_2,48," 体温过高!",1);//显示体温异常信息         

for(t=0;t<10;t++)         MicroWait( 30000 );         

P0_7=1;         

Print(HAL_LCD_LINE_2,48,ch,1);   //显示异常的温度值,和上面的体温过高是轮流显示的         Print(HAL_LCD_LINE_2,105,"",1); //显示摄氏度标示         

for(t=0;t<10;t++)         MicroWait( 10000 );       }      

else        { P0_7=1;         Print(HAL_LCD_LINE_2,48," 护理中...",1);//cancel_alarm1 != 0,即报警关闭,表示值班人员接收到了报警,并主动通过按键关闭了报警功能,当然,他必须通知医生或护士前往病人1去进行护理。注意,这一句是针对体温异常的报警。       };     }

 楼主| wwh199169 发表于 2011-11-6 02:13:08 | 显示全部楼层
本帖最后由 wwh199169 于 2011-11-6 02:15 编辑

else

     {

       if(num1 != 12800)

       {

       Print(HAL_LCD_LINE_2,48,ch,1);   //显示温度值

       Print(HAL_LCD_LINE_2,105,"",1); //显示摄氏度标示

       };

     };

   }

   else

   {

     P0_7=1;

     Print(HAL_LCD_LINE_2,48," 护理中...",1);   //同上一句作用相同,但这一句用来取消病人发出的求助报警

   };

     break;

     

     case(1):

     if (num2==8500) break;

   if (cancel_alarm2 == 0)

   {

     if (num2==12800)

     {

        P0DIR|=0x80;

        P0_7=0;

        Print(HAL_LCD_LINE_3,48,"   求助!  ",0);

        for(t=0;t<10;t++)

          MicroWait( 30000 );

        P0_7=1;

        Print(HAL_LCD_LINE_3,48,"          ",1);   //显示温度值

        for(t=0;t<10;t++)

          MicroWait( 10000 );

     };

     

     if ((num2 >=2500 || num2 <=0) && num2 !=12800)

     {

      

       if (cancel_alarm2 == 0)

       { P0DIR|=0x80;

         P0_7=0;

         Print(HAL_LCD_LINE_3,48," 体温过高!",1);

         for(t=0;t<10;t++)

         MicroWait( 30000 );

         P0_7=1;

         Print(HAL_LCD_LINE_3,48,ch,1);   //显示温度值

         Print(HAL_LCD_LINE_3,105,"",1); //显示摄氏度标示

         for(t=0;t<10;t++)

         MicroWait( 10000 );

       }

       else

       { P0_7=1;

         Print(HAL_LCD_LINE_3,48," 护理中...",1);   //显示温度值

         Print(HAL_LCD_LINE_3,105,"",1); //显示摄氏度标示

       };

     }

     else

     {

       if(num2 != 12800)

       {

       Print(HAL_LCD_LINE_3,48,ch,1);   //显示温度值

       Print(HAL_LCD_LINE_3,105,"",1); //显示摄氏度标示

       };

     };

   }

   else

   {

     P0_7=1;

     Print(HAL_LCD_LINE_3,48," 护理中...",1);   //显示温度值

   };

     break;

   };

        

  UART_SendMsg.check = SPIMgr_CalcFCS((uint8*)&UART_SendMsg.cmd, 11); //串口数据检验码

        

  HalUARTWrite(SPI_MGR_DEFAULT_PORT,&UART_SendMsg.start_frame, 13); //发送串口数据
   

        

  Debug_HalUARTWrite( SPI_MGR_DEFAULT_PORT, "node messagereceived!", 22 );//显示接收成功信息

}

 楼主| wwh199169 发表于 2011-11-6 02:15:07 | 显示全部楼层

至此,显示节点的程序就分析完了,整个项目的开发经过主要也就是这三篇帖子中所涉及的内容了。虽然在下十分希望能对和我一样的ZigBee初学者提供一点经验,以期抛砖引玉,但无奈本人水平实在有限,至今写过的代码还不超过100句(从上面的这一堆杂乱的程序中就可见一斑~~~)。所以,万一好心办了坏事儿,反而对您产生误导,还忘诸位多多包涵,哈哈···


    这个项目还没结束,如果能成功申请到国创,还要继续扩展下去,到时还会继续来飞比和大家分享的!虽然很费时间和精力(不是我夸张哦,就这篇帖子,从文本输入到后期美化,我花了整整4个小时(22:00~02:00)),但分享的乐趣还是很值得去体会的,呵呵···

outman 发表于 2011-11-6 22:25:18 | 显示全部楼层
可能周末大家都出去happy了
楼主辛苦了,这么详细的讲解,一定会让大家受益的,加油
 楼主| wwh199169 发表于 2011-11-7 00:51:19 | 显示全部楼层
多谢outman前辈的鼓励!
cddxhy 发表于 2011-11-7 07:17:46 | 显示全部楼层
OUTMAN,加精吧,这么好的贴子
outman 发表于 2011-11-7 10:39:38 | 显示全部楼层
回复 9# cddxhy


    疏忽了,这样的好贴,必须要加精!
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

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