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