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
