PS:這篇文章記錄筆者TI ble協議棧的學習串口的筆記。歡迎各位評論討論,也希望有大牛解答文末的問題。
前些日子CC2540模塊沒到,就搗鼓CC2530。深深的感受到,ZigBee就是個坑啊。然后果斷玩BLE了。BLE,TI的棧給的文檔,project這回看起來好點,然后找了其他一些資料。這里介紹《藍牙4.0BLE開發完全手冊-物聯網開發技術實戰》。看起來還行吧。不過我照着他的串口例子做,沒看到效果(可能是協議棧版本問題)。於是,一怒之下,自己啃。
最后再說一下,CC2530和CC2540除了RF不同外,51內核是一樣的。因為棧中使用的HAL是一樣的。
說下自己的裝備。如下圖。手焊跳線o(∩_∩)o。主要可以用的IO有串口0,定時器1的通道0~3,定時器的IO映射到了位置2。和兩個按鍵。
先來看看HAL都給了啥。我個人認為key、led、lcd就是多余的。這個硬件相關性實在是太強了。所以先從串口開始看起,我想各位看官的串口也都在位置1吧。

HAL里面默認串口使用的DMA。這個就不管它了。我建議先簡單的看下APP Group下面的幾個c文件。和OSAL.C中的osal_init_system和osal_run_system兩個函數,其它最好也瀏覽一下。在開始寫我們自己的串口函數前先看看項目的配置情況。請注意下,函數中用很多預編譯,看的時候仔細點。

左上角,選擇CC2540,關掉POWER_SAVING,在defined symbols中的POWER_SAVING前面加個x就可以了。關於POWER_SAVING,在函數osal_run_system中可以看到,這里我們x掉,不然編譯會出現一個警告,說什么串口和key的中斷被重復的啥啥啥的。至於LCD,因為我的板是沒用LCD的干脆也x了。
現在開始來看看怎么來串口打印出信息來。找到SimpleBLEPeripheral.c中的SimpleBLEPeripheral_Init函數,添加如下代碼,注意{}中才是我添加的。另外要添加hal_uart.h這個頭文件。
1 void SimpleBLEPeripheral_Init( uint8 task_id ) 2 { 3 simpleBLEPeripheral_TaskID = task_id; 4 5 { //add 6 halUARTCfg_t halUARTCfg; 7 halUARTCfg.configured = TRUE; 8 halUARTCfg.baudRate = HAL_UART_BR_115200; 9 halUARTCfg.flowControl = HAL_UART_FLOW_OFF; 10 11 HalUARTOpen(HAL_UART_PORT_0, &halUARTCfg); 12 HalUARTWrite(HAL_UART_PORT_0, "Hello CC2540!\n", 14); 13 }
來看看串口配置這個結構體。注意到,這個結構體是沒有關於配置停止位,校驗位的。也就是全是默認。其他沒用到的先不理會是用來干嘛的。
1 typedef struct 2 { 3 bool configured; 4 uint8 baudRate; 5 bool flowControl; 6 uint16 flowControlThreshold; 7 uint8 idleTimeout; 8 halUARTBufControl_t rx; 9 halUARTBufControl_t tx; 10 bool intEnable; 11 uint32 rxChRvdTime; 12 halUARTCBack_t callBackFunc; //回調函數 13 }halUARTCfg_t;
順便吐槽一下,TI給的HAL api文檔里面關於串口的宏定義有錯,所以還是看源碼靠譜點。
到這里,編譯燒錄,應該就可以看到串口出來的信息了。附圖,注意一下右邊的配置情況。

華麗的分割線
上面講了一下,如何使用Hal給的API來調用串口。也實現了串口信息打印功能。但這並不具有實際意義。在OSAL中,會對系統中的各種事件進行掃描,若事件發生,則會回調相應的函數去執行對應的功能。比如串口接收到信息。筆者嘗試着halUARTCfg_t結構體中的callBackFunc成員賦予回調函數。並使用HAL的串口API來實現這個函數——發送串口接收到的內容。
1 //類似於這樣子 2 3 halUARTCfg_t a 4 5 a.callBackFunc = funcA 6 7 8 static void funcA (uint8 port, uint8 event) 9 { 10 HalUARTRead(, , ); 11 HalUARTWrite(, ,); 12 }
上面是省略了的形式,但有個很怪的問題。就是串口接收一次數據后,這個回調函數就一直被調用,一直打印數據出來。經過各種嘗試之后也沒找出個所以然。可能是對整個協議棧的了解太少了。折騰了一個下午后,使用了npi.c提供的API函數。npi是Network Processor Interface的簡稱。
來看看npi里面到底都是些什么
1 //初始化串口配置 2 void NPI_InitTransport( npiCBack_t npiCBack ) 3 { 4 halUARTCfg_t uartConfig; 5 6 // configure UART 7 uartConfig.configured = TRUE; 8 uartConfig.baudRate = NPI_UART_BR; 9 uartConfig.flowControl = NPI_UART_FC; 10 uartConfig.flowControlThreshold = NPI_UART_FC_THRESHOLD; 11 uartConfig.rx.maxBufSize = NPI_UART_RX_BUF_SIZE; 12 uartConfig.tx.maxBufSize = NPI_UART_TX_BUF_SIZE; 13 uartConfig.idleTimeout = NPI_UART_IDLE_TIMEOUT; 14 uartConfig.intEnable = NPI_UART_INT_ENABLE; 15 uartConfig.callBackFunc = (halUARTCBack_t)npiCBack; 16 17 // start UART 18 // Note: Assumes no issue opening UART port. 19 (void)HalUARTOpen( NPI_UART_PORT, &uartConfig ); 20 21 return; 22 } 23 24 //讀數據 25 uint16 NPI_ReadTransport( uint8 *buf, uint16 len ) 26 { 27 return( HalUARTRead( NPI_UART_PORT, buf, len ) ); 28 } 29 30 //寫數據 31 uint16 NPI_WriteTransport( uint8 *buf, uint16 len ) 32 { 33 return( HalUARTWrite( NPI_UART_PORT, buf, len ) ); 34 } 35 36 //長度R 37 uint16 NPI_RxBufLen( void ) 38 { 39 return( Hal_UART_RxBufLen( NPI_UART_PORT ) ); 40 } 41 42 //R緩沖區大小 43 uint16 NPI_GetMaxRxBufSize( void ) 44 { 45 return( NPI_UART_RX_BUF_SIZE ); 46 } 47 48 //T緩沖區大小 49 uint16 NPI_GetMaxTxBufSize( void ) 50 { 51 return( NPI_UART_TX_BUF_SIZE ); 52 }
1 #ifndef NPI_H 2 #define NPI_H 3 4 #ifdef __cplusplus 5 extern "C" 6 { 7 #endif 8 9 /******************************************************************************* 10 * INCLUDES 11 */ 12 13 #include "hal_types.h" 14 #include "hal_board.h" 15 #include "hal_uart.h" 16 17 /******************************************************************************* 18 * MACROS 19 */ 20 21 /******************************************************************************* 22 * CONSTANTS 23 */ 24 25 /* UART port */ 26 #if !defined NPI_UART_PORT 27 #if ((defined HAL_UART_SPI) && (HAL_UART_SPI != 0)) 28 #define NPI_UART_PORT HAL_UART_PORT_1 29 #else 30 #define NPI_UART_PORT HAL_UART_PORT_0 31 #endif 32 #endif 33 34 #if !defined( NPI_UART_FC ) 35 #define NPI_UART_FC FALSE //add ->true 36 #endif // !NPI_UART_FC 37 38 #define NPI_UART_FC_THRESHOLD 48 39 #define NPI_UART_RX_BUF_SIZE 128 40 #define NPI_UART_TX_BUF_SIZE 128 41 #define NPI_UART_IDLE_TIMEOUT 6 42 #define NPI_UART_INT_ENABLE TRUE 43 44 #if !defined( NPI_UART_BR ) 45 #define NPI_UART_BR HAL_UART_BR_115200 46 #endif // !NPI_UART_BR 47 48 /******************************************************************************* 49 * TYPEDEFS 50 */ 51 52 typedef void (*npiCBack_t) ( uint8 port, uint8 event ); 53 54 // 55 // Network Processor Interface APIs 56 // 57 58 extern void NPI_InitTransport( npiCBack_t npiCBack ); 59 extern uint16 NPI_ReadTransport( uint8 *buf, uint16 len ); 60 extern uint16 NPI_WriteTransport( uint8 *, uint16 ); 61 extern uint16 NPI_RxBufLen( void ); 62 extern uint16 NPI_GetMaxRxBufSize( void ); 63 extern uint16 NPI_GetMaxTxBufSize( void ); 64 65 /******************************************************************************* 66 */ 67 68 #ifdef __cplusplus 69 } 70 #endif 71 72 #endif /* NPI_H */
可以看出來,npi就是對Hal中的串口API做了一層封裝。
之后SimpleBLEPeripheral_Init函數
1 void SimpleBLEPeripheral_Init( uint8 task_id ) 2 { 3 simpleBLEPeripheral_TaskID = task_id; 4 5 { //add 6 NPI_InitTransport(UartEventChange); //Network Processor Interface 初始化 7 } 8 ………… //省略 9 }
添加UartEventChange回調函數
1 static void UartEventChange(uint8 port, uint8 event) //add 2 { 3 VOID port; 4 uint8 temp; 5 6 if(event & HAL_UART_RX_TIMEOUT) //接收完成事件 7 { 8 temp = NPI_RxBufLen(); 9 if(temp) 10 { 11 NPI_ReadTransport(uartbuf, temp); 12 NPI_WriteTransport(uartbuf, temp); 13 osal_memset(uartbuf, 0, sizeof(uartbuf)); 14 } 15 } 16 }
對SimpleBLEPeripheral.c文件做上面的修改,然后編譯、燒錄,就可以看到想要的現象了。CC2540在接收完串口信息后,返回接收到的內容。僅返回一次。而不像使用Hal的串口API那樣,接收到之后就一直發送不停。
注意要關閉串口的流控制和power saving

盡管通過npi實現了串口的收發。但還是有些問題沒搞懂。
1 /* 2 * 3 *1、為何使用Hal中的串口api就無法產生上述效果。是我那些地方沒有注意 4 *到了嗎?? 5 * 6 *2、系統是如何知道串口接收完畢的。在調試過程中發現,串口事件有是有 7 *定時器的。真的嗎? 8 * 9 *3、默認使用了DMA,那這個緩沖區在那里,文件中並沒有聲明出來這個數 10 *組,只聲明了DMA的大小。東西到底存哪里去了。 11 * 12 *4、系統的事件到底是怎么運作的? 13 * 14 *…… 15 * 16 */
先記錄下這些問題。等熟悉了BLE棧之后在慢慢找答案。一開始就糾結這些問題的話,可能到頭來就啥都沒搞懂了。
