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

 

转载于:https://www.cnblogs.com/ifner/p/4524401.html

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值