转载

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);

此处先插个图

zigbee2007协议栈,串口使用,流程简要分析

可以看到,回调函数是一个指针类型,所以这个赋值,赋的值是SerialApp_CallBack对应的指针地址,而被调用的函数名字叫做SerialApp_CallBack又,根据入口参数可见,有port 指uart0或1,和  event,这两个参数

关于event,此处的对应的事件有四种,根据HAL层接口资料可以知道,为

zigbee2007协议栈,串口使用,流程简要分析

也就是说,根据程序设定,对应这些事情,会出发回调。   具体的触发办法,后面再说。

当把参数配置完后,使用  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  }

对于前半部分,简单提下

zigbee2007协议栈,串口使用,流程简要分析

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

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

正文到此结束
Loading...