zigbee zstack 串口,按键,消息,定时器
生活随笔
收集整理的這篇文章主要介紹了
zigbee zstack 串口,按键,消息,定时器
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
協(xié)議棧中的串口接收流程
串口在底層使用的是中斷,輪詢還是DMA呢??答案從下面這個文件定義的宏可以得知
在C:\Texas Instruments\ZStack-CC2530-2.3.0-1.4.0\Components\hal\target\CC2530EB\hal_board_cfg.h
在某個應(yīng)用的初始化函數(shù)中順序執(zhí)行下面兩個函數(shù),比如在SampleApp_Init函數(shù)中,
? MT_UartInit();
? MT_UartRegisterTaskID(task_id);
MT_UartRegisterTaskID//用于給SampleApp注冊串口,這樣當(dāng)串口在MT_UartProcessZToolData(mt_uart.c)發(fā)數(shù)據(jù)到上層時,會發(fā)到SampleApp。如果多個app都執(zhí)行了MT_UartRegisterTaskID,則最后一個注冊的有效,因為前面的被覆蓋掉了。
void MT_UartRegisterTaskID( byte taskID )
{
? App_TaskID = taskID;
}
比如,在osalInitTasks函數(shù)中
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++ );//會執(zhí)行MT_UartRegisterTaskID函數(shù) #endifAPS_Init( taskID++ ); #if defined ( ZIGBEE_FRAGMENTATION )APSF_Init( taskID++ ); #endifZDApp_Init( taskID++ ); #if defined ( ZIGBEE_FREQ_AGILITY ) || defined ( ZIGBEE_PANID_CONFLICT )ZDNwkMgr_Init( taskID++ ); #endifSampleApp_Init( taskID );//會執(zhí)行MT_UartRegisterTaskID函數(shù),把MT_TaskInit函數(shù)注冊的串口覆蓋掉了。串口有數(shù)據(jù)發(fā)給上層時,SampleApp會收到消息。 }
MT_UartInit將會指定串口有數(shù)據(jù)到來時,調(diào)用哪個callback函數(shù)來處理。如果在option的preprocessor中定義了ZTOOL_P1即串口0(cc2530有2個串口,ZTOOL_P2或ZAPP_P2代表串口1),則調(diào)用的是MT_UartProcessZToolData,見下面 /**************************************************************************************************** @fn MT_UartInit** @brief Initialize MT with UART support** @param None** @return None ***************************************************************************************************/ void MT_UartInit () {halUARTCfg_t uartConfig;/* Initialize APP ID */App_TaskID = 0;/* UART Configuration */uartConfig.configured = TRUE;uartConfig.baudRate = MT_UART_DEFAULT_BAUDRATE;uartConfig.flowControl = MT_UART_DEFAULT_OVERFLOW;uartConfig.flowControlThreshold = MT_UART_DEFAULT_THRESHOLD;uartConfig.rx.maxBufSize = MT_UART_DEFAULT_MAX_RX_BUFF;uartConfig.tx.maxBufSize = MT_UART_DEFAULT_MAX_TX_BUFF;uartConfig.idleTimeout = MT_UART_DEFAULT_IDLE_TIMEOUT;uartConfig.intEnable = TRUE; #if defined (ZTOOL_P1) || defined (ZTOOL_P2)uartConfig.callBackFunc = MT_UartProcessZToolData; #elif defined (ZAPP_P1) || defined (ZAPP_P2)uartConfig.callBackFunc = MT_UartProcessZAppData; #elseuartConfig.callBackFunc = NULL; #endif/* Start UART */ #if defined (MT_UART_DEFAULT_PORT)//默認(rèn)串口,如果定義了ZTOOL_P1則是串口0,ZTOOL_P2則是串口1HalUARTOpen (MT_UART_DEFAULT_PORT, &uartConfig); #else/* Silence IAR compiler warning */(void)uartConfig; #endif/* Initialize for ZApp */ #if defined (ZAPP_P1) || defined (ZAPP_P2)/* Default max bytes that ZAPP can take */MT_UartMaxZAppBufLen = 1;MT_UartZAppRxStatus = MT_UART_ZAPP_RX_READY; #endif}
MT_UartProcessZToolData用于處理串口接收緩沖區(qū)的數(shù)據(jù),pMsg->msg的結(jié)構(gòu)體的數(shù)據(jù)如下
/**************************************************************************************************** @fn MT_UartProcessZToolData** @brief | SOP | Data Length | CMD | Data | FCS |* | 1 | 1 | 2 | 0-Len | 1 |** Parses the data and determine either is SPI or just simply serial data* then send the data to correct place (MT or APP)** @param port - UART port* event - Event that causes the callback*** @return None***************************************************************************************************/ void MT_UartProcessZToolData ( uint8 port, uint8 event ) {uint8 ch;uint8 bytesInRxBuffer;(void)event; // Intentionally unreferenced parameterwhile (Hal_UART_RxBufLen(port)){HalUARTRead (port, &ch, 1);//每次讀一個字節(jié)switch (state){case SOP_STATE://0if (ch == MT_UART_SOF)//?如果讀到的字符時Start-of-frame即0xFE,則進(jìn)入LEN_STAT狀態(tài),進(jìn)行讀LENstate = LEN_STATE;break;case LEN_STATE://1LEN_Token = ch;//消息的長度tempDataLen = 0;/* Allocate memory for the data */pMsg = (mtOSALSerialData_t *)osal_msg_allocate( sizeof ( mtOSALSerialData_t ) +MT_RPC_FRAME_HDR_SZ + LEN_Token );if (pMsg){/* Fill up what we can */pMsg->hdr.event = CMD_SERIAL_MSG;pMsg->msg = (uint8*)(pMsg+1);pMsg->msg[MT_RPC_POS_LEN] = LEN_Token;//pMsg->msg[0]=lenstate = CMD_STATE1;//讀完長度,就進(jìn)入CMD_STATE1狀態(tài),進(jìn)行讀命令}else{state = SOP_STATE;return;}break;case CMD_STATE1:pMsg->msg[MT_RPC_POS_CMD0] = ch;//pMsg->msg[1]=命令低字節(jié)state = CMD_STATE2;break;case CMD_STATE2:pMsg->msg[MT_RPC_POS_CMD1] = ch;//pMsg->msg[2]=命令高字節(jié)/* If there is no data, skip to FCS state */if (LEN_Token){state = DATA_STATE;//讀完命令,如果數(shù)據(jù)長度不為0,就進(jìn)入DATA_STATE狀態(tài),進(jìn)行讀數(shù)據(jù)}else{state = FCS_STATE;//否則進(jìn)入FCS_STATE狀態(tài),進(jìn)行校驗}break;case DATA_STATE:/* Fill in the buffer the first byte of the data */pMsg->msg[MT_RPC_FRAME_HDR_SZ + tempDataLen++] = ch;/* Check number of bytes left in the Rx buffer */bytesInRxBuffer = Hal_UART_RxBufLen(port);/* If the remain of the data is there, read them all, otherwise, just read enough,讀數(shù)量較小的數(shù)據(jù) */if (bytesInRxBuffer <= LEN_Token - tempDataLen)//如果剩余數(shù)據(jù)<=len-已讀長度,全部讀出{HalUARTRead (port, &pMsg->msg[MT_RPC_FRAME_HDR_SZ + tempDataLen], bytesInRxBuffer);tempDataLen += bytesInRxBuffer;}else//否則只讀出(len-已讀長度)的數(shù)據(jù){HalUARTRead (port, &pMsg->msg[MT_RPC_FRAME_HDR_SZ + tempDataLen], LEN_Token - tempDataLen);tempDataLen += (LEN_Token - tempDataLen);}/* If number of bytes read is equal to data length, time to move on to FCS */if ( tempDataLen == LEN_Token )state = FCS_STATE;break;case FCS_STATE:FSC_Token = ch;/* Make sure it's correct */if ((MT_UartCalcFCS ((uint8*)&pMsg->msg[0], MT_RPC_FRAME_HDR_SZ + LEN_Token) == FSC_Token))//計算fcs{osal_msg_send( App_TaskID, (byte *)pMsg );}else{/* deallocate the msg */osal_msg_deallocate ( (uint8 *)pMsg );}/* Reset the state, send or discard the buffers at this point */state = SOP_STATE;break;default:break;}} }
所以如果PC發(fā)數(shù)據(jù)給zigbee串口,需要先發(fā)送FE...或者修改這個MT_UartProcessZToolData函數(shù)以適應(yīng)自己的應(yīng)用
協(xié)議棧中的串口發(fā)送流程
在某個app_init()中初始化和注冊串口(如果此app不需要接收數(shù)據(jù),可以不用注冊)
直接使用如下代碼發(fā)送數(shù)據(jù)即可
?HalUARTWrite(0,"Hello World\n",12);
如果在opention中定義了MT_TASK,則在發(fā)送實際的數(shù)據(jù)之前會發(fā)送一個11字節(jié)的數(shù)據(jù)頭(下行紅色數(shù)據(jù)),在PC接收到如下形式的數(shù)據(jù):
FE 06 41 80 01 02 00 02 03 00 C5?48 65 6C 6C 6F 20 57 6F 72 6C 64 0A?
后面黑色字體的12個字節(jié)才是真正的數(shù)據(jù)
將MT_TASK取消定義則直接發(fā)送真實的數(shù)據(jù)
關(guān)于串口buffer的和應(yīng)用層的串口幀大小的關(guān)系:
cc2530的串口buffer是1字節(jié),可以看出串口幀的大小與串口buffer沒啥關(guān)系,因為幀大小一般總是大于1字節(jié)的。
U0DBUF (0xC1) – USART 0 Receive/Transmit Data Buffer
這也說明串口在最低層發(fā)送的時候,是把上層給的數(shù)據(jù)一個字節(jié)一個字節(jié)的塞進(jìn)buffer里面發(fā)出去的)
但是在協(xié)議棧中只要發(fā)送的串口幀大于128字節(jié),就發(fā)不出去,這是因為單片機內(nèi)存分給幀最大長度是128字節(jié)造成的。位于OnBoard.h。可修改之。
#define MT_UART_TX_BUFF_MAX ?128
#define MT_UART_RX_BUFF_MAX ?128
如果你想要發(fā)送大于128字節(jié)的幀而不愿意修改上面的宏,可以在while里面用HalUARTWrite一個字節(jié)一個字節(jié)的發(fā),每發(fā)出一個字節(jié)執(zhí)行一次HalUARTPoll。(相當(dāng)于flush)。
協(xié)議棧中的按鍵流程
以其為例C:\Texas Instruments\ZStack-CC2530-2.3.0-1.4.0\Projects\zstack\Samples\SampleApp
1.在main函數(shù)中會執(zhí)行InitBoard(),用于初始化led和按鍵
OnBoard.c
void InitBoard( uint8 level ) {if ( level == OB_COLD ){// Interrupts offosal_int_disable( INTS_ALL );// Turn all LEDs offHalLedSet( HAL_LED_ALL, HAL_LED_MODE_OFF );// Check for Brown-Out resetChkReset();}else // !OB_COLD{/* Initialize Key stuff */OnboardKeyIntEnable = HAL_KEY_INTERRUPT_ENABLE;//此處需要使能按鍵中斷HalKeyConfig( OnboardKeyIntEnable, OnBoard_KeyCallback);//配置按鍵終端產(chǎn)生之后,觸發(fā)OnBoard_KeyCallback函數(shù)} }
配置函數(shù)HalKeyConfig,用于配置哪個按鍵(哪個端口)
回調(diào)函數(shù)OnBoard_KeyCallback,此函數(shù)執(zhí)行OnBoard_SendKeys,將按鍵事件發(fā)給上層注冊按鍵的應(yīng)用
RegisterForKeys( SampleApp_TaskID );
如果按鍵已被注冊,則后來注冊的就會失敗。
uint8 RegisterForKeys( uint8 task_id )
{
? // Allow only the first task
? if ( registeredKeysTaskID == NO_TASK_ID )
? {
? ? registeredKeysTaskID = task_id;
? ? return ( true );
? }
? else
? ? return ( false );
}
類似于注冊串口,但串口剛好相反。
不管有沒有使用中斷,對于button1和joystick的按鍵,都會響應(yīng)
如果按鍵沒有啟用中斷,則會每隔100ms輪詢一次按鍵狀態(tài),見下
如果注冊了中斷,則在按鍵中斷發(fā)生時,會觸發(fā)hal_key.c中的中斷處理函數(shù),注意kal_key.c中只注冊了p2和p0口的中斷處理函數(shù),見下
?**************************************************************************************************/
HAL_ISR_FUNCTION( halKeyPort2Isr, P2INT_VECTOR )
{
? if (HAL_KEY_JOY_MOVE_PXIFG & HAL_KEY_JOY_MOVE_BIT)
? {
? ? halProcessKeyInterrupt();
? }
? /*
? ? Clear the CPU interrupt flag for Port_2
? ? PxIFG has to be cleared before PxIF
? ? Notes: P2_1 and P2_2 are debug lines.
? */
? HAL_KEY_JOY_MOVE_PXIFG = 0;
? HAL_KEY_CPU_PORT_2_IF = 0;
}
HAL_ISR_FUNCTION( halKeyPort0Isr, P0INT_VECTOR )
{
? if (HAL_KEY_SW_6_PXIFG & HAL_KEY_SW_6_BIT)
? {
? ? halProcessKeyInterrupt();
? }
? /*
? ? Clear the CPU interrupt flag for Port_0
? ? PxIFG has to be cleared before PxIF
? */
? HAL_KEY_SW_6_PXIFG = 0;
? HAL_KEY_CPU_PORT_0_IF = 0;
}
而halProcessKeyInterrupt 只是中斷標(biāo)志。沒有對中斷做具體處理,同時延時25ms設(shè)置一個HAL_KEY_EVENT事件,讓Hal_TaskID去處理。延時的目的是去抖動。
void halProcessKeyInterrupt (void)
{
? bool valid=FALSE;
? if (HAL_KEY_SW_6_PXIFG & HAL_KEY_SW_6_BIT) ?/* Interrupt Flag has been set */
? {
? ? HAL_KEY_SW_6_PXIFG = ~(HAL_KEY_SW_6_BIT); /* Clear Interrupt Flag */
? ? valid = TRUE;
? }
? if (HAL_KEY_JOY_MOVE_PXIFG & HAL_KEY_JOY_MOVE_BIT) ?/* Interrupt Flag has been set */
? {
? ? HAL_KEY_JOY_MOVE_PXIFG = ~(HAL_KEY_JOY_MOVE_BIT); /* Clear Interrupt Flag */
? ? valid = TRUE;
? }
? if (valid)
? {
? ? osal_start_timerEx (Hal_TaskID, HAL_KEY_EVENT, HAL_KEY_DEBOUNCE_VALUE);//
? }
}
在循環(huán)任務(wù)中有一個Hal_ProcessEvent?(hal_driver.c) ,
? if (events & HAL_KEY_EVENT)
? {
#if (defined HAL_KEY) && (HAL_KEY == TRUE)
? ? /* Check for keys */
? ? HalKeyPoll();//讀取時按個按鍵
? ? /* if interrupt disabled, do next polling */
? ? if (!Hal_KeyIntEnable)//如果沒有使用中斷,就重新延時100ms設(shè)置事件HAL_KEY_EVENT。從此處可以看出,如果按鍵沒有啟用中斷,則會每隔100ms輪詢一次按鍵狀態(tài)
? ? {
? ? ? osal_start_timerEx( Hal_TaskID, HAL_KEY_EVENT, 100);
? ? }
#endif // HAL_KEY
? ? return events ^ HAL_KEY_EVENT;
??}
其調(diào)用了HalKeyPoll(hal_key.c),在這個函數(shù)中
?**************************************************************************************************/
void HalKeyPoll (void)
{
? uint8 keys = 0;
//讀取joystick的狀態(tài)
? if ((HAL_KEY_JOY_MOVE_PORT & HAL_KEY_JOY_MOVE_BIT)) ?/* Key is active HIGH */
? {
? ? keys = halGetJoyKeyInput();//返回joystick的哪個按鍵按下
? }
? /* If interrupts are not enabled, previous key status and current key status
? ?* are compared to find out if a key has changed status.
? ?*/
? if (!Hal_KeyIntEnable)//如果沒有啟動中斷
? {
? ? if (keys == halKeySavedKeys)
? ? {
? ? ? /* Exit - since no keys have changed */
? ? ? return;
? ? }
? ? /* Store the current keys for comparation next time */
? ? halKeySavedKeys = keys;
? }
? else
? {
? ? /* Key interrupt handled here */
? }
//讀取button1狀態(tài),
? if (HAL_PUSH_BUTTON1())//返回button1是否按下
? {
? ? keys |= HAL_KEY_SW_6;
? }
//如果按鍵按下設(shè)置了回調(diào)函數(shù),就調(diào)用回調(diào)函數(shù),如下
? /* Invoke Callback if new keys were depressed */
? if (keys && (pHalKeyProcessFunction))
? {
? ? (pHalKeyProcessFunction) (keys, HAL_KEY_STATE_NORMAL);
? }
}
onboard.c中定義了回調(diào)函數(shù)
?*********************************************************************/
void OnBoard_KeyCallback ( uint8 keys, uint8 state )
{
? uint8 shift;
? (void)state;
? /* Get shift key status */
? shift = ((keys & HAL_KEY_SW_6) ? true : false);
? if ( OnBoard_SendKeys( keys, shift ) != ZSuccess )//如果上傳消息失敗(即應(yīng)用層沒有注冊按鍵事件),就就地處理。
? {
? ? // Process SW1 here
? ? if ( keys & HAL_KEY_SW_1 ) ?// Switch 1
? ? {
? ? }
? ? // Process SW2 here
? ? if ( keys & HAL_KEY_SW_2 ) ?// Switch 2
? ? {
? ? }
? ? // Process SW3 here
? ? if ( keys & HAL_KEY_SW_3 ) ?// Switch 3
? ? {
? ? }
? ? // Process SW4 here
? ? if ( keys & HAL_KEY_SW_4 ) ?// Switch 4
? ? {
? ? }
? ? // Process SW5 here
? ? if ( keys & HAL_KEY_SW_5 ) ?// Switch 5
? ? {
? ? }
? ? // Process SW6 here
? ? if ( keys & HAL_KEY_SW_6 ) ?// Switch 6
? ? {
? ? }
? }
}
由上可知,
/* Switches (keys) */
#define HAL_KEY_SW_1 0x01 ?// Joystick up
#define HAL_KEY_SW_2 0x02 ?// Joystick right
#define HAL_KEY_SW_5 0x04 ?// Joystick center
#define HAL_KEY_SW_4 0x08 ?// Joystick left
#define HAL_KEY_SW_3 0x10 ?// Joystick down
#define HAL_KEY_SW_6 0x20 ?// Button S1 if available
協(xié)議棧中只對這6個按鍵由響應(yīng)。而HalKeyConfig (hal_key.c) 函數(shù)中也只是對這6個按鍵進(jìn)行了配置。
協(xié)議棧中的消息流程
3個不得不知道的結(jié)構(gòu)體
typedef struct {void *next;uint16 len;uint8 dest_id; } osal_msg_hdr_t;typedef struct {uint8 event;uint8 status; } osal_event_hdr_t;typedef void * osal_msg_q_t;
//無線收到數(shù)據(jù)向上層發(fā)送的消息 typedef struct {osal_event_hdr_t hdr; /* OSAL Message header */uint16 groupId; /* Message's group ID - 0 if not set */uint16 clusterId; /* Message's cluster ID */afAddrType_t srcAddr; /* Source Address, if endpoint is STUBAPS_INTER_PAN_EP,it's an InterPAN message */uint16 macDestAddr; /* MAC header destination short address */uint8 endPoint; /* destination endpoint */uint8 wasBroadcast; /* TRUE if network destination was a broadcast address */uint8 LinkQuality; /* The link quality of the received data frame */uint8 correlation; /* The raw correlation value of the received data frame */int8 rssi; /* The received RF power in units dBm */uint8 SecurityUse; /* deprecated */uint32 timestamp; /* receipt timestamp from MAC */afMSGCommandFormat_t cmd; /* Application Data */ } afIncomingMSGPacket_t;//按鍵按下時向上層發(fā)送的消息格式 typedef struct {osal_event_hdr_t hdr;uint8 state; // shiftuint8 keys; // keys } keyChange_t;//串口接到數(shù)據(jù)向上層發(fā)送的消息 typedef struct {osal_event_hdr_t ?hdr;uint8 ? ? ? ? ? ? *msg; } mtOSALSerialData_t;
定時器
cc2530-ZStack-CC2530-2.3.0-1.4.0
Hal_timer.c
?NOTE: The following mapping is done between the logical timer
? ? ? ?names defined in HAL_TIMER.H and the physical HW timer.
? ? ? ?HAL_TIMER_0 --> HW Timer 3 ?(8-bits)
? ? ? ?HAL_TIMER_2 --> HW Timer 4 ?(8-bits)
? ? ? ?HAL_TIMER_3 --> HW Timer 1 ?(16-bits)
?NOTE: The timer code assumes only one channel, CHANNEL 0, is used
? ? ? ?for each timer. ?There is currently no support for other
? ? ? ?channels.
HAL_ISR_FUNCTION( halTimer1Isr, T1_VECTOR )
{
? halProcessTimer1 ();//是真實的硬件定時器1處理函數(shù),默認(rèn)沒有開啟
}
HAL_ISR_FUNCTION( halTimer3Isr, T3_VECTOR )
{
? halProcessTimer3 ();//是真實的硬件定時器3處理函數(shù),默認(rèn)沒有開啟
}
HAL_ISR_FUNCTION( halTimer4Isr, T4_VECTOR )
{
? halProcessTimer4 ();//是真實的硬件定時器4處理函數(shù),默認(rèn)沒有開啟
}
還有一個硬件定時器2,也是mac timer,已被協(xié)議棧占用
mac_mcu.c
/**************************************************************************************************
?* @fn ? ? ? ? ?macMcuTimer2Isr
?*
?* @brief ? ? ? Interrupt service routine for timer2, the MAC timer.
?*
?* @param ? ? ? none
?*
?* @return ? ? ?none
?**************************************************************************************************
?*/
HAL_ISR_FUNCTION( macMcuTimer2Isr, T2_VECTOR )
osal_start_timerEx使用的是mac timer
但是osal_start_timerEx最快可以實現(xiàn)1ms的定時,而我測在mac timer的中斷處理函數(shù)中使用P1_1=!P1_1測得的中斷周期卻大于100ms.?????
是使用的mac tiemr嗎???
如下是說是使用的MAC backoff timer,這個timer又是啥玩意?
/*********************************************************************
?* @fn ? ? ?osalTimeUpdate
?*
?* @brief ? Uses the free running rollover count of the MAC backoff timer;
?* ? ? ? ? ?this timer runs freely with a constant 320 usec interval. ?The
?* ? ? ? ? ?count of 320-usec ticks is converted to msecs and used to update
?* ? ? ? ? ?the OSAL clock and Timers by invoking osalClockUpdate() and
?* ? ? ? ? ?osalTimerUpdate(). ?This function is intended to be invoked?
?* ? ? ? ? ?from the background, not interrupt level.
?*
?* @param ? None.
?*
?* @return ?None.
?*/
void osalTimeUpdate( void )
轉(zhuǎn)載于:https://www.cnblogs.com/-song/archive/2012/12/02/3331839.html
總結(jié)
以上是生活随笔為你收集整理的zigbee zstack 串口,按键,消息,定时器的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 小论接口(interface)和抽象类(
- 下一篇: zigbee ti 附带工具使用方法