|
本帖最后由 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
}
今天就先说到这里,时间也不早了。欲知后事如何,且听下回分解,多谢列位捧场! |
|