zigbee2007協議棧,串口使用,流程簡要分析


PS,從本篇開始,改變寫作風格,盡量少打字,多用圖。事半功倍。

=========================================

協議棧中,串口使用,按照順序,前后經歷:配置、初始化、執行、調用,這樣幾個階段,下面具體來說。

一、串口配置

zgb中,串口使用dma或者isr中斷模式,系統默認的具體配置是dma模式,下面具體來看,打開

HAL\Target\CC2530EB\Config\ hal_board_cfg.h  這個文件

可知此文件主要進行HAL的一些基礎配置 以及宏定義聲明等,從中我們找到如下部分,

 1 /* Set to TRUE enable UART usage, FALSE disable it */
 2 #ifndef HAL_UART
 3 #if (defined ZAPP_P1) || (defined ZAPP_P2) || (defined ZTOOL_P1) || (defined ZTOOL_P2)
 4 #define HAL_UART TRUE
 5 #else
 6 #define HAL_UART FALSE
 7 #endif
 8 #endif
 9 
10 #if HAL_UART
11 // Always prefer to use DMA over ISR.
12   #if HAL_DMA
13     #ifndef HAL_UART_DMA
14       #if (defined ZAPP_P1) || (defined ZTOOL_P1)
15           #define HAL_UART_DMA  1
16       #elif (defined ZAPP_P2) || (defined ZTOOL_P2)
17           #define HAL_UART_DMA  2
18           #else
19           #define HAL_UART_DMA  1
20       #endif
21     #endif
22     #define HAL_UART_ISR  0
23     #else
24       #ifndef HAL_UART_ISR
25       #if (defined ZAPP_P1) || (defined ZTOOL_P1)
26           #define HAL_UART_ISR  1
27       #elif (defined ZAPP_P2) || (defined ZTOOL_P2)
28           #define HAL_UART_ISR  2
29       #else
30       #define HAL_UART_ISR  1
31       #endif
32       #endif
33     #define HAL_UART_DMA  0
34 #endif
35 
36 // Used to set P2 priority - USART0 over USART1 if both are defined.
37 #if ((HAL_UART_DMA == 1) || (HAL_UART_ISR == 1))
38 #define HAL_UART_PRIPO             0x00
39 #else
40 #define HAL_UART_PRIPO             0x40
41 #endif
42 
43 #else
44 #define HAL_UART_DMA  0
45 #define HAL_UART_ISR  0
46 #endif

 也就是說,

1、如果之前沒有定義uart那么檢查(defined ZAPP_P1) || (defined ZAPP_P2) || (defined ZTOOL_P1) || (defined ZTOOL_P2)是否成立,

一旦使用了這四種中的一種,那么就 #define HAL_UART TRUE,也就是說 啟用uart ——此處解決的是是否啟用uart的選擇

2、之后,由其中這一部分可知

 1 #if HAL_UART
 2 // Always prefer to use DMA over ISR.
 3   #if HAL_DMA
 4     #ifndef HAL_UART_DMA
 5       #if (defined ZAPP_P1) || (defined ZTOOL_P1)
 6           #define HAL_UART_DMA  1
 7       #elif (defined ZAPP_P2) || (defined ZTOOL_P2)
 8           #define HAL_UART_DMA  2
 9           #else
10           #define HAL_UART_DMA  1
11       #endif
12     #endif
13     #define HAL_UART_ISR  0

 

如果開啟了uart,如果開啟了hal_dma,如果未定義haluartdma,則如果定義了上面提到到過的四種宏定義之一ZAPP P1(2)\ZTOOL P1(2),則分別定義dma為1或2 同時令ISR為0,簡單說就是,如果采取了上面四種宏定義之一,則默認開啟為dma模式 並 關閉isr模式。         反之,如果發現hal_dma不成立,這對應開啟hal_uart_isr並關閉HAL_UART_DMA

3、末尾進行相關優先級的設定

 1 // Used to set P2 priority - USART0 over USART1 if both are defined.
 2 #if ((HAL_UART_DMA == 1) || (HAL_UART_ISR == 1))
 3 #define HAL_UART_PRIPO             0x00
 4 #else
 5 #define HAL_UART_PRIPO             0x40
 6 #endif
 7 
 8 #else
 9 #define HAL_UART_DMA  0
10 #define HAL_UART_ISR  0
11 #endif

4、 SerialApp_Init 中的配置  、回調函數
打開對應函數可以見到如下

 1 halUARTCfg_t uartConfig;
 2   uartConfig.configured           = TRUE;              // 2x30 don't care - see uart driver.
 3   uartConfig.baudRate             = SERIAL_APP_BAUD;
 4   uartConfig.flowControl          = FALSE;
 5   uartConfig.flowControlThreshold = SERIAL_APP_THRESH; // 2x30 don't care - see uart driver.
 6   uartConfig.rx.maxBufSize        = SERIAL_APP_RX_SZ;  // 2x30 don't care - see uart driver.
 7   uartConfig.tx.maxBufSize        = SERIAL_APP_TX_SZ;  // 2x30 don't care - see uart driver.
 8   uartConfig.idleTimeout          = SERIAL_APP_IDLE;   // 2x30 don't care - see uart driver.
 9   uartConfig.intEnable            = TRUE;              // 2x30 don't care - see uart driver.
10   uartConfig.callBackFunc         = SerialApp_CallBack;
11   HalUARTOpen (SERIAL_APP_PORT, &uartConfig);

uartConfig_t  注意這個變量類型,等到后面跑初始化程序時候 也會涉及到一個串口配置的變量類型,二者不同,此處先提及一下,后面再說

 

第1行,進行了變量的定義, 2-10 進行了本task中串口相關參數的配置,注意第10行

uartConfig.callBackFunc         =           SerialApp_CallBack;   

這句話是 令 串口的回調函數為 SerialApp_CallBack  (如果不打算使用回調,則此處直接 =NULL 即可)  ,這里簡單說下此處的回調,

typedef void (*halUARTCBack_t) (uint8 port, uint8 event);

此處先插個圖

可以看到,回調函數是一個指針類型,所以這個賦值,賦的值是SerialApp_CallBack對應的指針地址,而被調用的函數名字叫做SerialApp_CallBack 
又,根據入口參數可見,有port 指uart0或1,和  event,這兩個參數

 關於event,此處的對應的事件有四種,根據HAL層接口資料可以知道,為

也就是說,根據程序設定,對應這些事情,會出發回調。   具體的觸發辦法,后面再說。

當把參數配置完后,使用  HalUARTOpen (SERIAL_APP_PORT, &uartConfig);  函數  開啟串口  加載配置,其具體過程在下面執行部分說

 

 

二、初始化

 1 void osalInitTasks( void )
 2 {
 3   uint8 taskID = 0;
 4 
 5   tasksEvents = (uint16 *)osal_mem_alloc( sizeof( uint16 ) * tasksCnt);
 6   osal_memset( tasksEvents, 0, (sizeof( uint16 ) * tasksCnt));
 7 
 8   macTaskInit( taskID++ );
 9   nwk_init( taskID++ );
10   Hal_Init( taskID++ );
11 #if defined( MT_TASK )
12   MT_TaskInit( taskID++ );
13 #endif
14   APS_Init( taskID++ );
15 #if defined ( ZIGBEE_FRAGMENTATION )
16   APSF_Init( taskID++ );
17 #endif
18   ZDApp_Init( taskID++ );
19 #if defined ( ZIGBEE_FREQ_AGILITY ) || defined ( ZIGBEE_PANID_CONFLICT )
20   ZDNwkMgr_Init( taskID++ );
21 #endif
22   SerialApp_Init( taskID );
23 }

 

1、按照流程,首先是  osalInitTasks函數  下面的 Hal_Init( taskID++ ) ,跟蹤進去,看到如下

/**************************************************************************************************
 * @fn      Hal_Init
 *
 * @brief   Hal Initialization function.
 *
 * @param   task_id - Hal TaskId
 *
 * @return  None
 **************************************************************************************************/
void Hal_Init( uint8 task_id )
{
  /* Register task ID */
  Hal_TaskID = task_id;
}

也就是注冊一下task_id

2、之后要初始化的是 SerialApp_Init( taskID ) 函數,上面提到過其參數配置,此處不再說了,下面說一下其具體的初始化串口用的函數

HalUARTOpen (SERIAL_APP_PORT, &uartConfig);

其中,SERIAL_APP_PORT 即為該APP所用的串口port ,而第二個參數 就是加載前面配置的參數,具體來看

 1 uint8 HalUARTOpen(uint8 port, halUARTCfg_t *config)
 2 {
 3 #if (HAL_UART_DMA == 1)
 4   if (port == HAL_UART_PORT_0)  HalUARTOpenDMA(config);
 5 #endif
 6 #if (HAL_UART_DMA == 2)
 7   if (port == HAL_UART_PORT_1)  HalUARTOpenDMA(config);
 8 #endif
 9 #if (HAL_UART_ISR == 1)
10   if (port == HAL_UART_PORT_0)  HalUARTOpenISR(config);
11 #endif
12 #if (HAL_UART_ISR == 2)
13   if (port == HAL_UART_PORT_1)  HalUARTOpenISR(config);
14 #endif
15   
16 #if (HAL_UART_DMA == 0) && (HAL_UART_ISR == 0)
17   // UART is not enabled. Do nothing.
18   (void) port;   // unused argument
19   (void) config; // unused argument
20 #endif
21   
22   return HAL_UART_SUCCESS;
23 }

可以看見 ,此處具體的程序分支 是根據前期配置的結果來進行的,此處我們默認是DMA模式,所以 進到 第一個分支里面,如下

 1 /******************************************************************************
 2  * @fn      HalUARTOpenDMA
 3  *
 4  * @brief   Open a port according tp the configuration specified by parameter.
 5  *
 6  * @param   config - contains configuration information
 7  *
 8  * @return  none
 9  *****************************************************************************/
10 static void HalUARTOpenDMA(halUARTCfg_t *config)
11 {
12   dmaCfg.uartCB = config->callBackFunc;
13   // Only supporting subset of baudrate for code size - other is possible.
14   HAL_UART_ASSERT((config->baudRate == HAL_UART_BR_9600) ||
15                   (config->baudRate == HAL_UART_BR_19200) ||
16                   (config->baudRate == HAL_UART_BR_38400) ||
17                   (config->baudRate == HAL_UART_BR_57600) ||
18                   (config->baudRate == HAL_UART_BR_115200));
19   
20   if (config->baudRate == HAL_UART_BR_57600 ||
21       config->baudRate == HAL_UART_BR_115200)
22   {
23     UxBAUD = 216;
24   }
25   else
26   {
27     UxBAUD = 59;
28   }
29   
30   switch (config->baudRate)
31   {
32     case HAL_UART_BR_9600:
33       UxGCR = 8;
34       dmaCfg.txTick = 35; // (32768Hz / (9600bps / 10 bits))
35                           // 10 bits include start and stop bits.
36       break;
37     case HAL_UART_BR_19200:
38       UxGCR = 9;
39       dmaCfg.txTick = 18;
40       break;
41     case HAL_UART_BR_38400:
42       UxGCR = 10;
43       dmaCfg.txTick = 9;
44       break;
45     case HAL_UART_BR_57600:
46       UxGCR = 10;
47       dmaCfg.txTick = 6;
48       break;
49     default:
50       // HAL_UART_BR_115200
51       UxGCR = 11;
52       dmaCfg.txTick = 3;
53       break;
54   }
55 
56   // 8 bits/char; no parity; 1 stop bit; stop bit hi.
57   if (config->flowControl)
58   {
59     UxUCR = UCR_FLOW | UCR_STOP;
60     PxSEL |= HAL_UART_Px_CTS;
61     // DMA Rx is always on (self-resetting). So flow must be controlled by the S/W polling the Rx
62     // buffer level. Start by allowing flow.
63     PxOUT &= ~HAL_UART_Px_RTS;
64     PxDIR |=  HAL_UART_Px_RTS;
65   }
66   else
67   {
68     UxUCR = UCR_STOP;
69   }
70 
71   dmaCfg.rxBuf[0] = *(volatile uint8 *)DMA_UDBUF;  // Clear the DMA Rx trigger.
72   HAL_DMA_CLEAR_IRQ(HAL_DMA_CH_RX);
73   HAL_DMA_ARM_CH(HAL_DMA_CH_RX);
74   osal_memset(dmaCfg.rxBuf, (DMA_PAD ^ 0xFF), HAL_UART_DMA_RX_MAX*2);
75 
76   UxCSR |= CSR_RE;
77   UxDBUF = 0;  // Prime the DMA-ISR pump.
78   
79   // Initialize that TX DMA is not pending
80   dmaCfg.txDMAPending = FALSE;
81   dmaCfg.txShdwValid = FALSE;
82 }

2.1 根據HalUARTOpenDMA(halUARTCfg_t *config)可知,其代入的參數,正是前面配置時候另的那個配置變量(halUARTCfg_t uartConfig;)
2.2 進入函數的第一件事就是

dmaCfg.uartCB = config->callBackFunc;

回調函數是指針類型,所以此處講config里面的,也即是前面配置的回調函數 (uartConfig.callBackFunc= SerialApp_CallBack)  

即 SerialApp_CallBack 賦給 dmaCfg.uartCB

此處的變量 dmaCfg 也即是前面提到的,與 uartConfig 不同類型的,另一個,串口配置用的變量

==============================================================================================
PS      要說明的是  HalUARTOpenDMA(halUARTCfg_t *config) 以及其他一些相關的串口函數 都在 ( _hal_uart_dma.c)這個文件里面,而

static uartDMACfg_t dmaCfg;

這句定義也是在這個文件中的,故下面的其他一些函數可以直接涉及調用===============================================================================================

2.3  之后是一些基本的串口配置,依據之前的配置進行比較底層的設置,按下不表

 

 

 

三、執行部分

進入void osal_start_system( void ) 這個函數 ,按照順序 分別看這幾個函數

1、  Hal_ProcessPoll(); 對硬件處理進行輪詢,

void Hal_ProcessPoll ()
{

  /* Timer Poll */
#if (defined HAL_TIMER) && (HAL_TIMER == TRUE)
  HalTimerTick();
#endif

  /* UART Poll */
#if (defined HAL_UART) && (HAL_UART == TRUE)
  HalUARTPoll();
#endif

}

對於前半部分,簡單提下

 

下面說后半部分,也是核心部分

void HalUARTPoll(void)
{
#if HAL_UART_DMA
  HalUARTPollDMA();
#endif
#if HAL_UART_ISR
  HalUARTPollISR();
#endif
}

此處顯然 ,默認dma的情況下,進入第一句

  1 /******************************************************************************
  2  * @fn      HalUARTPollDMA
  3  *
  4  * @brief   Poll a USART module implemented by DMA.
  5  *
  6  * @param   none
  7  *
  8  * @return  none
  9  *****************************************************************************/
 10 static void HalUARTPollDMA(void)
 11 {
 12   uint16 cnt = 0;
 13   uint8 evt = 0;
 14 
 15   if (HAL_UART_DMA_NEW_RX_BYTE(dmaCfg.rxHead))
 16   {
 17     uint16 tail = findTail();
 18 
 19     // If the DMA has transferred in more Rx bytes, reset the Rx idle timer.
 20     if (dmaCfg.rxTail != tail)
 21     {
 22       dmaCfg.rxTail = tail;
 23 
 24       // Re-sync the shadow on any 1st byte(s) received.
 25       if (dmaCfg.rxTick == 0)
 26       {
 27         dmaCfg.rxShdw = ST0;
 28       }
 29       dmaCfg.rxTick = HAL_UART_DMA_IDLE;
 30     }
 31     else if (dmaCfg.rxTick)
 32     {
 33       // Use the LSB of the sleep timer (ST0 must be read first anyway).
 34       uint8 decr = ST0 - dmaCfg.rxShdw;
 35 
 36       if (dmaCfg.rxTick > decr)
 37       {
 38         dmaCfg.rxTick -= decr;
 39         dmaCfg.rxShdw = ST0;
 40       }
 41       else
 42       {
 43         dmaCfg.rxTick = 0;
 44       }
 45     }
 46     cnt = HalUARTRxAvailDMA();
 47   }
 48   else
 49   {
 50     dmaCfg.rxTick = 0;
 51   }
 52 
 53   if (cnt >= HAL_UART_DMA_FULL) //HAL_UART_DMA_FULL=128-16=112
 54   {
 55     evt = HAL_UART_RX_FULL;//HAL_UART_RX_FULL=0x01  ************************
 56   }
 57   else if (cnt >= HAL_UART_DMA_HIGH)
 58   {
 59     evt = HAL_UART_RX_ABOUT_FULL;
 60     PxOUT |= HAL_UART_Px_RTS;
 61   }
 62   else if (cnt && !dmaCfg.rxTick)
 63   {
 64     evt = HAL_UART_RX_TIMEOUT;
 65   }
 66 
 67   if (dmaCfg.txMT)
 68   {
 69     dmaCfg.txMT = FALSE;
 70     evt |= HAL_UART_TX_EMPTY;
 71   }
 72 
 73   if (dmaCfg.txShdwValid)
 74   {
 75     uint8 decr = ST0;
 76     decr -= dmaCfg.txShdw;
 77     if (decr > dmaCfg.txTick)
 78     {
 79       // No protection for txShdwValid is required
 80       // because while the shadow was valid, DMA ISR cannot be triggered
 81       // to cause concurrent access to this variable.
 82       dmaCfg.txShdwValid = FALSE;
 83     }
 84   }
 85   
 86   if (dmaCfg.txDMAPending && !dmaCfg.txShdwValid)
 87   {
 88     // UART TX DMA is expected to be fired and enough time has lapsed since last DMA ISR
 89     // to know that DBUF can be overwritten
 90     halDMADesc_t *ch = HAL_DMA_GET_DESC1234(HAL_DMA_CH_TX);
 91     halIntState_t intState;
 92 
 93     // Clear the DMA pending flag
 94     dmaCfg.txDMAPending = FALSE;
 95     
 96     HAL_DMA_SET_SOURCE(ch, dmaCfg.txBuf[dmaCfg.txSel]);
 97     HAL_DMA_SET_LEN(ch, dmaCfg.txIdx[dmaCfg.txSel]);
 98     dmaCfg.txSel ^= 1;
 99     HAL_ENTER_CRITICAL_SECTION(intState);
100     HAL_DMA_ARM_CH(HAL_DMA_CH_TX);
101     do
102     {
103       asm("NOP");
104     } while (!HAL_DMA_CH_ARMED(HAL_DMA_CH_TX));
105     HAL_DMA_CLEAR_IRQ(HAL_DMA_CH_TX);
106     HAL_DMA_MAN_TRIGGER(HAL_DMA_CH_TX);
107     HAL_EXIT_CRITICAL_SECTION(intState);
108   }
109 
110   if (evt && (dmaCfg.uartCB != NULL))
111   {
112     dmaCfg.uartCB(HAL_UART_DMA-1, evt);
113   }
114 }

縱觀整個函數,首先令 cnt、evt=0 ,中間是觸發規則 (在其他文章中具體說,此文不表),最后判斷

1  if (evt && (dmaCfg.uartCB != NULL))
2   {
3     dmaCfg.uartCB(HAL_UART_DMA-1, evt);
4   }

易知,對於前面設置過回調的程序,這里只要evt非零,條件即可滿足,也就是說,只要 (觸發了evt 且 回調非空),那么就啟動回調函數。

dmaCfg.uartCB(HAL_UART_DMA-1, evt);  因為回調本身是指針,所以通過這樣寫的方式,啟動回調,代入參數為 (HAL_UART_DMA-1, evt)。

PS。 通過這個輪詢函數來決定是否啟用回調函數,注意此時尚未進入taskArr隊列的最后一個,也就是用戶自己的那個程序,比如 SerialApp_ProcessEvent

2、現在具體看回調函數的內容
(本工程為 SerialApp_CallBack )

static void SerialApp_CallBack(uint8 port, uint8 event)
{
  (void)port;

  if ((event & (HAL_UART_RX_FULL | HAL_UART_RX_ABOUT_FULL | HAL_UART_RX_TIMEOUT)) && !SerialApp_TxLen)
  {
    SerialApp_Send();
  }
}

其中的 if 語句,重點關心的是

(event & (HAL_UART_RX_FULL | HAL_UART_RX_ABOUT_FULL | HAL_UART_RX_TIMEOUT))

前文提到過有 4種事件 均可以產生觸發回調的event  ,此處根據需要寫了3種,這個具體可以因項目而異。
如果if條件滿足,則

 1 static void SerialApp_Send(void)
 2 {
 3     if (!SerialApp_TxLen && 
 4         (SerialApp_TxLen = HalUARTRead(SERIAL_APP_PORT, SerialApp_TxBuf+1, SERIAL_APP_TX_MAX)))
 5     {
 6       // Pre-pend sequence number to the Tx message.
 7       SerialApp_TxBuf[0] = ++SerialApp_TxSeq;
 8     }
 9   
10     if (SerialApp_TxLen)
11     {
12       if (afStatus_SUCCESS != AF_DataRequest(&SerialApp_TxAddr,
13                                              (endPointDesc_t *)&SerialApp_epDesc,
14                                               SERIALAPP_CLUSTERID1,
15                                               SerialApp_TxLen+1, SerialApp_TxBuf,
16                                               &SerialApp_MsgID, 0, AF_DEFAULT_RADIUS))
17       {
18         osal_set_event(SerialApp_TaskID, SERIALAPP_SEND_EVT);  // if 發送成功,則,不走此分支
19       }
20     }
21 }

大致內容,此處,首先是對發送隊列的一些檢測,然后調用AF_DataRequest函數發送到空中,如果發送不成功,則設置事情標志,等到后面在osal中重發(稍后提及)
————至此,一個數據從串口進來,到轉發到空中的過程便完成了。

對於這個回調的作用,必須要說明的是,

A、一般來說,回調只是用來傳遞“收到串口消息”這個信息,也就是說,最純粹的 回調函數里面,在函數核心,應該只有一句 比如

osal_set_event(SerialApp_TaskID, SERIALAPP_SEND_EVT);

至於,set的event是什么名字,又是干什么事的,這個看個人情況處理。

B、如果必須要在回調中直接處理數據,當然也可以在回調之中寫程序處理事情,但是不建議處理的任務太密集,(可能導致溢出等問題)

C、如果采用設置event的方式對數據進行處理,那么需要知道,這個處理不是立馬處理的,是在后面輪到該event的時候才處理 的。

 

3、taskArr最后一項 UINT16 SerialApp_ProcessEvent( uint8 task_id, UINT16 events )

當走到這里的時候,每輪程序已經進入了最后一個大函數,比如在本工程中,這個函數進來后

 1 if ( events & SYS_EVENT_MSG );
 2 
 3 ===========================================
 4 if ( events & SERIALAPP_SEND_EVT )
 5   {
 6     SerialApp_Send();
 7     return ( events ^ SERIALAPP_SEND_EVT );
 8   }
 9 
10 ===========================================
11 if ( events & SERIALAPP_RESP_EVT )
12   {
13     SerialApp_Resp();
14     return ( events ^ SERIALAPP_RESP_EVT );
15   }

有這樣的3個分支,而第二個,(events & SERIALAPP_SEND_EVT),也就是前面回調里面當無線電發送失敗時,set的event,它最終在這里被第二次調用,也就是重新發送一遍,所以換句話,對於實時性不着急的工程,現在回調里面設置好要觸發什么事件,然后事件在此大函數中處理 (比如對外設的控制指令等)。

 

XJ

15:54:44

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM