作者:吴家华 引文地址:http://nireus./read-htm-tid-12.html 在一些工业测控领域会用到串口与上位机进行通信,以获取实时的数据和控制信息。那么上位机和串口是如何进行通信的呢?本文是基于z-stack-1.4.3-1.2.1的Utilities/SerialApp的例子。OSAL_SerialApp.c是应用于操作系统的接口,先不用看,首先看应用程序SerialApp.c里的外部函数:extern void SerialApp_Init( byte task_id ); extern UINT16 SerialApp_ProcessEvent( byte task_id, UINT16 events );系统主函数就是调用这两个函数来实现串口功能的。按照TI的编程风格,后面带init(initial)的一定是初始化函数,完成应用的一些变量的初始化和一些功能的配置。后面带ProcessEvent则是进程的扫描函数。 串口应用执行的流程:串口接收到数据包或者其他应用程序有串口发送请求 对串口的task对应的event置位-> SerialApp_ProcessEvent( uint8 task_id, UINT16 events )轮训事件 ->SerialApp_ProcessMSGCmd,SerialApp_ProcessZDOMsgs,SerialApp_HandleKey, 这三个函数完成相应函数功能具体调用。 void SerialApp_Init( uint8 task_id ) { halUARTCfg_t uartConfig; SerialApp_MsgID = 0x00; SerialApp_SeqRx = 0xC3; SerialApp_TaskID = task_id; SerialApp_DstAddr.endPoint = 0;// SerialApp_DstAddr.addr.shortAddr = 0;// SerialApp_DstAddr.addrMode = (afAddrMode_t)AddrNotPresent;// SerialApp_RspDstAddr.endPoint = 0;// SerialApp_RspDstAddr.addr.shortAddr = 0;// SerialApp_RspDstAddr.addrMode = (afAddrMode_t)AddrNotPresent;// afRegister( (endPointDesc_t *)&SerialApp_epDesc );//端口初始化,注册端口 RegisterForKeys( task_id );//注册按键时间 uartConfig.configured = TRUE; // 2430 don't care. uartConfig.baudRate = SERIAL_APP_BAUD;//设置串口通信的波特率 uartConfig.flowControl = TRUE; uartConfig.flowControlThreshold = SERIAL_APP_THRESH; uartConfig.rx.maxBufSize = SERIAL_APP_RX_MAX;//最大接收字节 uartConfig.tx.maxBufSize = SERIAL_APP_TX_MAX;//最大发送字节 uartConfig.idleTimeout = SERIAL_APP_IDLE; // 2430 don't care. uartConfig.intEnable = TRUE; // 2430 don't care. #if SERIAL_APP_LOOPBACK uartConfig.callBackFunc = rxCB_Loopback; #else uartConfig.callBackFunc = rxCB; #endif HalUARTOpen (SERIAL_APP_PORT, &uartConfig);//打开串口 #if defined ( LCD_SUPPORTED ) HalLcdWriteString( "SerialApp2", HAL_LCD_LINE_2 ); #endif ZDO_RegisterForZDOMsg( SerialApp_TaskID, End_Device_Bind_rsp ); ZDO_RegisterForZDOMsg( SerialApp_TaskID, Match_Desc_rsp ); } 串口任务的初始化函数,完成功能:初始化TaskID,注册endpoint,对串口进行设置,打开串口。注册设备对象消息。 在来看看UINT16 SerialApp_ProcessEvent( uint8 task_id, UINT16 events )串口进程轮询函数。 { if ( events & SYS_EVENT_MSG ) { afIncomingMSGPacket_t *MSGpkt; while ( (MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( SerialApp_TaskID )) ) { switch ( MSGpkt->hdr.event ) { case ZDO_CB_MSG://zdo层接收到消息 SerialApp_ProcessZDOMsgs( (zdoIncomingMsg_t *)MSGpkt ); break; case KEY_CHANGE: SerialApp_HandleKeys( ((keyChange_t *)MSGpkt)->state, ((keyChange_t *)MSGpkt)->keys ); break; case AF_INCOMING_MSG_CMD//接收到命令,然后执行 这里就要说说了,zigbee协议信息的传递有两种方式:消息和命令,消息长度不限,命令的大小则严格规定 SerialApp_ProcessMSGCmd( MSGpkt );://执行进来消息命令的回调函数 break; default: break; } osal_msg_deallocate( (uint8 *)MSGpkt ); // Release the memory. } // Return unprocessed events return ( events ^ SYS_EVENT_MSG ); }//系统消息事件 if ( events & SERIALAPP_MSG_SEND_EVT ) { SerialApp_SendData( otaBuf, otaLen ); return ( events ^ SERIALAPP_MSG_SEND_EVT ); }//串口发送请求,这里是指串口通过CC2430发送到灵位一个无线设备 if ( events & SERIALAPP_MSG_RTRY_EVT ) { if ( --rtryCnt ) { AF_DataRequest( &SerialApp_DstAddr, (endPointDesc_t *)&SerialApp_epDesc, SERIALAPP_CLUSTERID1, otaLen, otaBuf, &SerialApp_MsgID, 0, AF_DEFAULT_RADIUS ); osal_start_timerEx( SerialApp_TaskID, SERIALAPP_MSG_RTRY_EVT, SERIALAPP_MSG_RTRY_TIMEOUT ); } else { FREE_OTABUF(); } return ( events ^ SERIALAPP_MSG_RTRY_EVT ); }//重发事件,这里搞了很长时间才搞明白是什么意思,如果callback返回没有发送成功的话,cc2430就会重发信息,rtryCnt是重发的次数。 if ( events & SERIALAPP_RSP_RTRY_EVT ) { afStatus_t stat = AF_DataRequest( &SerialApp_RspDstAddr, (endPointDesc_t *)&SerialApp_epDesc, SERIALAPP_CLUSTERID2, SERIAL_APP_RSP_CNT, rspBuf, &SerialApp_MsgID, 0, AF_DEFAULT_RADIUS ); if ( stat != afStatus_SUCCESS ) { osal_start_timerEx( SerialApp_TaskID, SERIALAPP_RSP_RTRY_EVT, SERIALAPP_RSP_RTRY_TIMEOUT ); } return ( events ^ SERIALAPP_RSP_RTRY_EVT ); }//回应重发事件,说实在的这个协议栈搞这么多冬冬挺烦人的,这也是为了防止阻塞丢包嘛!~ #if SERIAL_APP_LOOPBACK if ( events & SERIALAPP_TX_RTRY_EVT ) { if ( rxLen ) { if ( !HalUARTWrite( SERIAL_APP_PORT, rxBuf, rxLen ) ) { osal_start_timerEx( SerialApp_TaskID, SERIALAPP_TX_RTRY_EVT, SERIALAPP_TX_RTRY_TIMEOUT ); } else { rxLen = 0; } } return ( events ^ SERIALAPP_TX_RTRY_EVT ); } #endif return ( 0 ); // Discard unknown events. } 由上面的程序可以看出,在串口Task中定义了五个事件:SYS_EVENT_MSG,SERIALAPP_MSG_SEND_EVT,SERIALAPP_MSG_RTRY_EVT,SERIALAPP_RSP_RTRY_EVT,SERIALAPP_TX_RTRY_EVT,其中,SYS_EVENT_MSG是系统时间,每个任务中都有,它是完成系统任务之间信息的交换。 SERIALAPP_MSG_SEND_EVT //数据发送 SERIALAPP_MSG_RTRY_EVT //数据重发 SERIALAPP_RSP_RTRY_EVT //回应重发 框架程序读懂了,接下来就是实现我的串口功能了。我要实现的串口功能很简单,协调器,也就是采集器采集到的数据通过串口传给上位机,上位机的指令通过串口传给采集器,然后采集器再发给目标终端。 现在要把串口任务加到我们的工程里面去了。 怎么加进去呢?首先,把SerialApp。c和SerialApp。h加到工程的souce文件夹里面,然后 分三步:第一,把初始化函数SerialApp_Init(),加入到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++); SerialApp_Init(taskID); }在这里操作系统会分配任务ID 第二步,把串口轮询函数SerialApp_ProcessEvent()的函数名,加入任务数组 const pTaskEventHandlerFn tasksArr[] = { macEventLoop, nwk_event_loop, Hal_ProcessEvent, #if defined( MT_TASK ) MT_ProcessEvent, #endif APS_event_loop, ZDApp_event_loop, SAPI_ProcessEvent, SerialApp_ProcessEvent }; 第三步:修改执行程序 在修改执行程序时,首先我们要定义好自己的事件,和相应数据、命令、数据结构。再定义相应的功能实现函数。 先定义事件1.、coordinater接收到传感器数据事件,要上传给上位机 事件二2、coordinator接收到上位机发送来的命令 |
|