我看的代码条理不清,东一棒槌,西一榔头。惭愧。
1. DMA方式与ISR方式的UART传输,有什么区别?分别如何实现?
2. ZDO_CB_MSG与AF_INCOMING_MSG_CMD等事件的产生机制?
3. 如何完成“多对一”或者“一对多”的通信?
4. 绑定表的存储位置与生命周期?
一个设备可以有240个端点,每个端点必须有一个端点描述符endPointDesc,端点描述符里包括一个简单描述符。在看Zstack的例子时应该详细看一下应用层,3个文件,SerialApp.h SerialApp.c OSAL_SerialApp.c
端点描述符结构:
typedef struct
{
byte endPoint;
byte *task_id; // Pointer to location of the Application task ID.
SimpleDescriptionFormat_t *simpleDesc;
afNetworkLatencyReq_t latencyReq; //延时请求
} endPointDesc_t;
//初始化后的一个端点描述符结构
const endPointDesc_t SerialApp_epDesc =
{
SERIALAPP_ENDPOINT,
&SerialApp_TaskID,
(SimpleDescriptionFormat_t *)&SerialApp_SimpleDesc,
noLatencyReqs
};
简单描述符的结构
typedef struct
{
byte EndPoint; //端点号1-240
uint16 AppProfId; //支持的Profile ID
uint16 AppDeviceId; //支持的设备ID
byte AppDevVer:4; //执行的设备描述的版本
byte Reserved:4; // AF_V1_SUPPORT uses for AppFlags:4.
byte AppNumInClusters; //终端支持的输入簇数目
cId_t *pAppInClusterList; //指向输入Cluster ID列表的指针
byte AppNumOutClusters;//终端支持的输出簇数目
cId_t *pAppOutClusterList;//指向输出Cluster ID列表的指针
} SimpleDescriptionFormat_t;
//初始化后的一个简单描述符结构
const SimpleDescriptionFormat_t SerialApp_SimpleDesc ={
SERIALAPP_ENDPOINT, // int Endpoint;
SERIALAPP_PROFID, // uint16 AppProfId[2];
SERIALAPP_DEVICEID, // uint16 AppDeviceId[2];
SERIALAPP_DEVICE_VERSION, // int AppDevVer:4;
SERIALAPP_FLAGS, // int AppFlags:4;
SERIALAPP_MAX_CLUSTERS, // byte AppNumInClusters;
(cId_t *)SerialApp_ClusterList, // byte *pAppInClusterList;
SERIALAPP_MAX_CLUSTERS, // byte AppNumOutClusters;
(cId_t *)SerialApp_ClusterList // byte *pAppOutClusterList;
};
AF.c 中的afStatus_t afRegister( endPointDesc_t *epDesc ),//在AF层注册该端口描述符。
只有分别在两个节点的简单描述结构体中,同时注册了相同的命令标示符并且方向相反才能建立绑定。
SerialApp_TxBuf[0]存放的是发送的序列号SerialApp_TxSeq,即通过串口发送的数据SerialApp_TxBuf,是从SerialApp_TxBuf[1]开始的,同样接收数组SerialApp_RxBuf[0]存放的是接收的序列号SerialApp_RxSeq,为什么会这样?
其实这里加了个保险措施,其实就是发送设备将发送序列号加在数据中一块发送,接收设备接收到数据后,将发送序列号回传给发送设备,发送设备将该序列号和发送时的序列号比较一下,如果相等则说明数据发送成功。
通过上面的分析可知,接收设备收到数据后,将接数据有关的状态以及接收序列号(其实就是发送设备的发送序列号)等信息放在一个4字节的数组里面,然后将该数组发送给发送设备。
发送设备接收到接收设备回传的响应信息后,将发送序列号和接收序列号比较,如果相等,则说明原来的数据发送成功了。
参考http://www.feibit.com/forum.php?mod=viewthread&tid=2928
代码分析:
void SerialApp_Init( uint8 task_id )
{
halUARTCfg_t uartConfig;
SerialApp_TaskID = task_id;
SerialApp_RxSeq = 0xC3;//不知为何????
//在AF层注册该端口描述符
afRegister( (endPointDesc_t *)&SerialApp_epDesc );
//按键事件交给本任务处理
RegisterForKeys( task_id );
//串口配置
uartConfig.configured = TRUE; // 2x30 don't care - see uart driver.
uartConfig.baudRate = SERIAL_APP_BAUD;
uartConfig.flowControl = FALSE;
uartConfig.flowControlThreshold = SERIAL_APP_THRESH; // 2x30 don't care - see uart driver.
uartConfig.rx.maxBufSize = SERIAL_APP_RX_SZ; // 2x30 don't care - see uart driver.
uartConfig.tx.maxBufSize = SERIAL_APP_TX_SZ; // 2x30 don't care - see uart driver.
uartConfig.idleTimeout = SERIAL_APP_IDLE; // 2x30 don't care - see uart driver.
uartConfig.intEnable = TRUE; // 2x30 don't care - see uart driver.
uartConfig.callBackFunc = SerialApp_CallBack;//回调函数
HalUARTOpen (SERIAL_APP_PORT, &uartConfig);
#if defined ( LCD_SUPPORTED )
HalLcdWriteString( "SerialApp", HAL_LCD_LINE_2 );
#endif
//注册节点绑定响应事件和匹配描述符响应事件
ZDO_RegisterForZDOMsg( SerialApp_TaskID, End_Device_Bind_rsp );
ZDO_RegisterForZDOMsg( SerialApp_TaskID, Match_Desc_rsp );
#if defined (ZDO_COORDINATOR)//有三种设备不知怎样定义????
HalUARTWrite(SERIAL_APP_PORT,szWelcomeMsgCoor,sizeof(szWelcomeMsgCoor));
#else
HalUARTWrite(SERIAL_APP_PORT,szWelcomeMsgRouter,sizeof(szWelcomeMsgRouter));
#endif
}
#define End_Device_Bind_rsp (End_Device_Bind_req | ZDO_RESPONSE_BIT)
#define End_Device_Bind_req ((uint16)0x0020)
#define ZDO_RESPONSE_BIT ((uint16)0x8000)
#define Match_Desc_rsp (Match_Desc_req | ZDO_RESPONSE_BIT)
#define Match_Desc_req ((uint16)0x0006)
/*********************************************************************
* @fn ZDO_RegisterForZDOMsg
*
* @brief Call this function to register of an incoming over
* the air ZDO message - probably a response message
* but requests can also be received.
* Messages are delivered to the task with ZDO_CB_MSG
* as the message ID.
*
* @param taskID - Where you would like the message delivered
* @param clusterID - What message?
*
* @return ZSuccess - successful, ZMemError if not
*/
ZStatus_t ZDO_RegisterForZDOMsg( uint8 taskID, uint16 clusterID )
{
ZDO_MsgCB_t *pList;
ZDO_MsgCB_t *pLast;
ZDO_MsgCB_t *pNew;
// Look for duplicate
pList = pLast = zdoMsgCBs;
while ( pList )
{
if ( pList->taskID == taskID && pList->clusterID == clusterID )
return ( ZSuccess );
pLast = pList;
pList = (ZDO_MsgCB_t *)pList->next;
}
// Add to the list
pNew = (ZDO_MsgCB_t *)osal_mem_alloc( sizeof ( ZDO_MsgCB_t ) );
if ( pNew )
{
pNew->taskID = taskID;
pNew->clusterID = clusterID;
pNew->next = NULL;
if ( zdoMsgCBs )
{
pLast->next = pNew;
}
else
zdoMsgCBs = pNew;
return ( ZSuccess );
}
else
return ( ZMemError );
}
#define ZDO_CB_MSG 0xD3 // ZDO incoming message callback
什么时候会调用下面这个函数呢???是在UINT16 SerialApp_ProcessEvent( uint8 task_id, UINT16 events )中
/*********************************************************************
* @fn SerialApp_ProcessZDOMsgs()
*
* @brief Process response messages
*
* @param none
*
* @return none
*/
static void SerialApp_ProcessZDOMsgs( zdoIncomingMsg_t *inMsg )
{
switch ( inMsg->clusterID )
{
case End_Device_Bind_rsp:
if ( ZDO_ParseBindRsp( inMsg ) == ZSuccess )
{
// Light LED
HalLedSet( HAL_LED_4, HAL_LED_MODE_ON );
}
#if defined(BLINK_LEDS)
else
{
// Flash LED to show failure
HalLedSet ( HAL_LED_4, HAL_LED_MODE_FLASH );
}
#endif
break;
case Match_Desc_rsp:
{
ZDO_ActiveEndpointRsp_t *pRsp = ZDO_ParseEPListRsp( inMsg );
if ( pRsp )
{
if ( pRsp->status == ZSuccess && pRsp->cnt )
{
SerialApp_TxAddr.addrMode = (afAddrMode_t)Addr16Bit;
SerialApp_TxAddr.addr.shortAddr = pRsp->nwkAddr;
// Take the first endpoint, Can be changed to search through endpoints
SerialApp_TxAddr.endPoint = pRsp->epList[0];
// Light LED
HalLedSet( HAL_LED_4, HAL_LED_MODE_ON );
}
osal_mem_free( pRsp );
}
}
break;
}
}
MT这又是哪一层?
开始时所有任务的状态都被初始化为0,代表了当前任务没有需要响应的事件。事件是如何被OSAL捕获的,即taskEvents数组中的元素是什么时候设定为非零的?
参考http://bbs.feibit.com/thread-280-1-1.html
以按键响应为例,处理按键响应的函数是 Hal_ProcessEvent,采用扫描的方式。那么 Hal_ProcessEvent对应的taskEvents是在什么时候被置为非0的呢?
大体思路是这样的:
1、 // Final board initialization
InitBoard( OB_READY );中的 HalKeyConfig(HAL_KEY_INTERRUPT_DISABLE, OnBoard_KeyCallback);
2、在void HalKeyConfig (bool interruptEnable, halKeyCBack_t cback)中最后有 osal_set_event(Hal_TaskID, HAL_KEY_EVENT);将事件HAL_KEY_EVENT交给任务Hal_TaskID处理,即taskEvents中的与Hal_TaskID相对的元素就被置为了非0,从而调用了
uint16 Hal_ProcessEvent( uint8 task_id, uint16 events )
3、在uint16 Hal_ProcessEvent( uint8 task_id, uint16 events )中又调用了 HalKeyPoll();并且启动了osal_start_timerEx( Hal_TaskID, HAL_KEY_EVENT, 100);即每隔100微秒就要调用uint16 Hal_ProcessEvent( uint8 task_id, uint16 events )一次,而在HalKeyPoll();中来扫描判断按键是否变化。HalKeyPoll()中(pHalKeyProcessFunction) (keys, HAL_KEY_STATE_NORMAL);
static halKeyCBack_t pHalKeyProcessFunction;
pHalKeyProcessFunction这个函数指针指向了 void OnBoard_KeyCallback ( uint8 keys, uint8 state ),在这个函数中,又调用了byte OnBoard_SendKeys( byte keys, byte state ),在这个函数中,按键的状态信息被封装到了一个消息结构体中(对于消息,我们稍后再说)。最后有一个极其重要的函数被调用了。osal_msg_send( registeredKeysTaskID, (uint8 *)msgPtr );registeredKeysTaskID所指示的任务正是我们需要响应按键的SerialApp这个任务。在osal_msg_send函数中osal_set_event( destination_task, SYS_EVENT_MSG );
HalKeyPoll()获取当前按键的状态,并且通过调用OnBoard_KeyCallback函数向SerialApp任务发送一个按键消息,并且设置tasksEvents中SerialApp所对应的值为非零。
大神的解答:
第一、pHalKeyProcessFunction这个函数指针为何指向了OnBoard_KeyCallback函数。
在HAL\Commen\ hal_drivers.c这个文件中,我们找到了HalDriverInit这个函数,在这个函数中,按键的初始化函数HalKeyInit被调用。在HalKeyInit中有这样的语句:
{
pHalKeyProcessFunction = NULL;
}
这说明在初始化以后pHalKeyProcessFunction并没有指向任何一个函数。那pHalKeyProcessFunction是什么时候被赋值的呢?
就在HalKeyInit的下方有一个这样的函数HalKeyConfig。其中有这样一条语句:
pHalKeyProcessFunction = cback;
cback是HalKeyConfig所传进来的参数,所以,想要知道它所指向的函数,必须找到其调用的地方。经过简单的搜索我们不难找出答案。在main函数中有这样一个函数调用:InitBoard( OB_READY );此函数中做了如下调用:
{
HalKeyConfig( OnboardKeyIntEnable, OnBoard_KeyCallback);
}
第二、registeredKeysTaskID为什么标识了GenericApp这个任务?
由于OSAL是一个支持多任务的调度机制,所以在同一时间内将会有多个任务同时运行。但是从逻辑上来讲,一个事件只能由一个任务来处理。按键事件也不例外。
那么如何向OSAL声明处理按键事件的任务是GenericApp呢?
在GenericApp_Init(GenericApp的任务初始化函数)中有这么一个语句:
{
RegisterForKeys( GenericApp_TaskID );
}
RegisterForKeys函数向OSAL声明按键事件将由GenericApp任务来处理。在RegisterForKeys函数中:
{
registeredKeysTaskID = task_id;
}
说实话下面这句不懂??是回调函数,就是要向SerialApp任务发送按键消息。
/* Invoke Callback if new keys were depressed */
if (keys && (pHalKeyProcessFunction))
{
(pHalKeyProcessFunction) (keys, HAL_KEY_STATE_NORMAL);
}