CC2530 light_switch分析


一些關鍵字:

CCM  - Counter with CBC-MAC (mode ofoperation)

HAL   - HardwareAbstraction Layer      (硬件抽象層)

PAN   - PersonalArea Network          (個人局域網)

RF    - RadioFrequency                (射頻)

RSSI  - Received SignalStrength Indicator  (接收信號強度指示)

 

本實現講解的主要內容有分三部分:

1、工程文件介紹

2、Basic RF layer介紹及其工作過程

3、light_switch.c代碼詳解

 

一、工程文件介紹

  文件夾結構大至如下,僅列出CC2530 BasicRF目錄一些相關的的文件夾:每個文件夾里面放着什么東西,如果缺少其中某些,我們的燈還是否可以點亮呢?我們來一一探討:

 

圖1. 文件夾結構圖

Ø docs文件夾:

  打開文件夾里面僅有一個名為CC2530_Software_ExamplesPDF文檔,文檔的主要內容是介紹BasicRF的特點、結構及使用,如果讀者有TI的開發板的話閱讀這個文檔就可以做Basic RF里面的實驗了,從中我們可以知道,里面Basic RF包含三個實驗例程:無線點燈、傳輸質量檢測、譜分析應用。下面講解的內容中也有部分內容是從這個文檔中翻譯所得,是一份相當有價值的參考資料。

Ø Ide文件夾:

  打開文件夾后會有三個文件夾,及一個cc2530_sw_examples.eww工程,其中這個工程是上面提及的三個實驗例程工程的集合,當然也包含了我們無線點燈的實驗工程!在IAR環境中打開,在workspace看到

  • Ide\Settings文件夾:

是在每個基礎實驗的文件夾里面都會有的,它主要保存有讀者自己的IAR環境里面的設置。

  •  Ide\srf05_CC2530文件夾:

里面放有三個工程,light_switch.ewwper_test.ewwspectrum_analyzer.eww  如果讀者不習慣幾個工程集合在一起看,也可以在這里直接打開你想要用的實驗工程。

Ø source文件夾:

打開文件夾里面有apps文件夾和components文件夾

  • Source\apps文件夾:

存放BasicRF三個實驗的應用實現的源代碼

  • Source\components文件夾:

包含着BasicRF的應用程序使用不同組件的源代碼

 

打開實驗工程:

  打開文件夾Zigbee CC2530BasicRF\ide\srf05_cc2530\iar路徑里面的工程light_switch.eww(無線點燈)。我們的實驗就是對它進行修改的。並點擊applicationlight_switch.c用戶的應用程序就是在里面的了 

 

 

圖2. BasicRF工程路徑

二、Basic RF layer介紹及其工作過程

在介紹Basic RF之前,來看看這個實驗例程設計的大體結構,如3所示Basic RF例程的軟件設計框圖就如一座建築物。

圖3. 軟件設計框圖

Ø Hardwarelayer——對應物理實體

  放在最底,肯定是你實現數據傳輸的基礎了。

Ø HardwareAbstraction layer——對應hal_rf.c

  它提供了一種接口來訪問TIMERGPIOUARTADC等。這些接口都通過相應的函數進行實現。

Ø Basic RF layer——對應basic_rf.c

  為雙向無線傳輸提供一種簡單的協議

Ø Application layer——對應light_switch.c

  是用戶應用層,它相當於用戶使用Basic RF層和HAL的接口,也就是說我們通過在Application layer就可以使用到封裝好的Basic RFHAL的函數。

  本例程的要求就是讀者理解掌握Basic RF

1.Basic RF layer簡介

  BasicRFTI公司提供,它包含了IEEE 802.15.4標准的數據包的收發功能但並沒有使用到協議棧,它僅僅是是讓兩個結點進行簡單的通信,也就是說Basic RF僅僅是包含着IEEE 802.15.4標准的一小部分而已。其主要特點有:

a)不提供多跳設備掃描Beacon

b)不提供不同種的網絡設備,如協調器、路由器等。所有節點同級,只實現點對點傳輸。

c)傳輸時會等待信道空閑,但不按802.15.4 CSMA-CA要求進行兩次CCA檢測。

d)不重傳數據

 

BasicRF layer為雙向無線通信提供了一個簡單的協議,通過這個協議能夠進行數據的發送和接收。Basic RF還提供了安全通信所使用的CCM-64身份驗證和數據加密,它的安全性讀者可以通過在工程文件里面定義SECURITY_CCMProject->Option里面就可以選擇

本次實驗並不是什么高度機密,所以在SECURITY_CCM前面帶X了。

 

 

圖4. 注釋SECURITY_CCM

2.Basic RF的工作過程

Basic RF的工作過程:啟動、發射、接收 (請大家按照代碼走)

Ø 啟動

a)確保外圍器件沒有問題

b)創建一個basicRfCfg_t的數據結構,並初始化其中的成員,在basic_rf.h代碼中可以找到

   typedefstruct {
          uint16 myAddr;          //16位的短地址(就是節點的地址)
          uint16 panId;            //節點的PAN ID
          uint8 channel;          //RF通道(必須在11-26之間)
          uint8 ackRequest;       //目標確認就置true
          #ifdef SECURITY_CCM //是否加密,預定義里取消了加密
            uint8*securityKey;   
            uint8*securityNonce;
            #endif
} basicRfCfg_t;

c)在Application層調用Basic RfbasicRfInit()函數進行協議的初始化,在basic_rf.c代碼中可以找到

uint8basicRfInit(basicRfCfg_t* pRfConfig)

函數功能:對Basic RF的數據結構初始化,設置模塊的傳輸通道,短地址,PAD ID

Basic Rf層實現:調用HAL層函數設置模塊的傳輸通道,短地址,PAD ID。並且調用halRfRxInterruptConfig(basicRfRxFrmDoneIsr)語句動態配制接收中斷的中斷服務函數。

HAL層實現:分別配制FREQCTRLSHORT_ADDR,和PAN_ID寄存器來設置模塊的傳輸通道,短地址,PAD ID。在hal_rf.c中可以找到

 

Ø 發送

a)創建一個buffer,把payload放入其中。Payload最大為103個字節

b)調用basicRfSendPacket()函數發送,並查看其返回值。在basic_rf.c中可以找到

uint8basicRfSendPacket(uint16 destAddr, uint8* pPayload, uint8 length)

destAddr 目的短地址

pPayload 指向發送緩沖區的指針

length 發送數據長度

函數功能:給目的短地址發送指定長度的數據,發送成功剛返回SUCCESS,失敗則返回FAILED

Basic Rf層實現:使能模塊的接收功能(為了接收ACK),發送數據,等待ACK,關接收功能。

HAL層實現:發送數據的HAL層實現,將要發送的數據寫入RFD寄存器,該寄存器自動將數據寫入負責裝發送數據的TXFIFO寄存器。

 

Ø 接收

a)上層通過basicRfPacketIsReady()函數來檢查是否收到一個新數據包

basic_rf.c中可以找到

uint8basicRfPacketIsReady(void)

函數功能:檢查模塊是否已經可以接收下一個數據,如果准備好剛返回TRUE

Basic Rf層實現:讀取rxi.isReady的值並返回。

HAL層實現:在接收中斷中,檢測收到的幀的幀頭,如果是正確的,則把rxi.isReady賦值成TRUE.

b)調用basicRfReceive()函數,把收到的數據復制到buffer中。

代碼可以在basic_rf.c中可以找到

uint8basicRfReceive(uint8* pRxData, uint8 len, int16* pRssi)

函數功能:接收來自Basic RF層的數據包,並為所接收的數據和RSSI值配緩沖區

Basic Rf層實現:把rxi.pPayload中的值賦給pRxData並傳遞給上層函數。從rxi.rssi中得到pRssi。並且把rxi.isReady的值重新設置成FALSE.

HAL層實現:rxi.pPayload中的值是在接收中斷服務函數中,讀取RFD寄存器得到的,讀后,RXFIFO會自動把值寫入RFD寄存器。

接收中斷:在hal_types.h中可以看到如下代碼將中斷重命名。

#define HAL_ISR_FUNC_DECLARATION(f,v)   \

    _PRAGMA(vector=v##_VECTOR) __interrupt void f(void)

#define HAL_ISR_FUNC_PROTOTYPE(f,v)     \

    _PRAGMA(vector=v##_VECTOR) __interrupt void f(void)

#define HAL_ISR_FUNCTION(f,v)           \

    HAL_ISR_FUNC_PROTOTYPE(f,v); HAL_ISR_FUNC_DECLARATION(f,v)

找HAL_ISR_FUNCTION(f,v)函數,可以看到在hal.rf.cHAL_ISR_FUNCTION( rfIsr, RF_VECTOR )即為中斷服務函數。而其中下列代碼中函數指針調用的函數即為在basicRfInit函數中配制的basicRfRxFrmDoneIsr函數。

if(pfISR){

            (*pfISR)();                 // Execute the custom ISR

        }

如果能看懂啟動、發射、接收就可以說你基本上能使用這個無線模塊了。

使用Basic RF實現無線傳輸只要學會使用這些函數就可以了,但是具體的實現過程遠沒有那么簡單的,大家可以到….\CC2530 BasicRF\docs里面查看CC2530_Software_Examples中的5.2.4 Basic RF operation這個章節的內容,里面詳細介紹了Basic RF的初始化過程、Basic RF的發射過程、Basic RF的接收過程,具體到每個層的功能函數。Zigbee本來想將這部分的內容也詳細的和讀者們講解清楚,但后來再仔細考慮還是不放上來了。因為它的具體實現過程大家看文檔的那個章節就可以大概明白的了,另一方面,實驗例程的模塊化編程做得很好,大家只需要明白函數的作用,學會使用它就行了,至於它內部是怎么樣一層一層的實現,我們也不用太過關心。

三、light_switch.c代碼詳解

無論你看哪個實驗的代碼,首先要找的就是main函數。

1.main()函數

main函數開始:(部分已經屏蔽的代碼並未貼出,詳細的代碼請看打開工程)

1.       void main(void)
2.       {
3.         uint8 appMode = NONE;             //不設置模塊的模式
4.       // Config basicRF
     basicRfConfig.panId= PAN_ID;            //上面講的Basic RF的啟動中的
5.         basicRfConfig.channel =RF_CHANNEL;  第2步初始化basicRfCfg_t
6.         basicRfConfig.ackRequest =TRUE;       結構體的成員。
7.      
8.         #ifdef SECURITY_CCM             //密鑰安全通信,本例程不加密
9.         basicRfConfig.securityKey =key;
10.     #endif
11.   
12.     // Initalise boardperipherals    初始化外圍設備
13.     halBoardInit();
14.     halJoystickInit();
15.   
16.     // Initalise hal_rf  硬件抽象層的rf進行初始化
17.     if(halRfInit()= =FAILED)
18.     {
19.       HAL_ASSERT(FALSE);
20.     }
21.     
22.     halLedSet(2);            // 關 LED2(P1_1=1)
23.     halLedClear(1);          // 開LED1(P1_0=0)
24.   
25.   
26.    appSwitch();          //節點為按鍵S1        P0_4
27.    appLight();           //節點為指示燈LED1    P1_0
28.     // Role is undefined. This code should not bereached
29.     HAL_ASSERT(FALSE);
30.   }

Ø 第2223行:關閉Zigbee底板的LED2,開LED1。由於Zigbee設計的LED電路是低電平點亮的,與TI不同,更符合以前大家學習單片機的習慣,所以halLedSet()1是使燈熄滅,不過這個沒關系,關鍵是掌握怎么使用就可以了。

Ø 第2627行:選擇其中的一行,並把另外一行屏蔽掉;這兩行重要啦,一個是實現發射按鍵信息的功能,另一個是接收按鍵信息並改變LED狀態的功能。分別為Basic RF發射和接收。不同模塊在燒寫程序時選擇不同功能。

注意:程序會在appSwitch();  或者appLight();里面循環或者等待,不會執行到第29行。

2.appSwitch()函數

接下來看看appSwitch()函數,它是如何實現數據發送的呢?

1.       static void appSwitch()
2.       {
3.         #ifdef ASSY_EXP4618_CC2420
4.         halLcdClearLine(1);
5.         halLcdWriteSymbol(HAL_LCD_SYMBOL_TX,1);
6.         #endif
7.         // InitializeBasicRF
8.         basicRfConfig.myAddr =SWITCH_ADDR;
9.         if(basicRfInit(&basicRfConfig)==FAILED){
10.     HAL_ASSERT(FALSE);
11.     }
12.     pTxData[0] = LIGHT_TOGGLE_CMD;
13.     // Keep Receiver off when not needed to savepower
14.     basicRfReceiveOff();
15.     // Main loop
16.     while (TRUE)  //程序進入死循環
17.     {
18.       if(halButtonPushed()==HAL_BUTTON_1)    //按鍵S1被按下
19.       {
20.       basicRfSendPacket(LIGHT_ADDR,pTxData,APP_PAYLOAD_LENGTH);


21.       // Put MCU to sleep. It will wake up onjoystick interrupt
22.       halIntOff();
23.       halMcuSetLowPowerMode(HAL_MCU_LPM_3); // Will turn on global
24.       // interrupt enable
25.       halIntOn();
26.       }
27.      }
28.   }

Ø 第36行:TI學習板上的液晶模塊的定義,我們不用管他

Ø 第811行:Basic RF啟動中的初始化,就是上面所講的Basic RF啟動的第3

Ø 第12行:Basic RF發射第1步,把要發射的數據或者命令放入一個數據buffer,此處把燈狀態改變的命令LIGHT_TOGGLE_CMD放到pTxData中。

Ø 第14行:由於模塊只需要發射,所以把接收屏蔽掉以降低功耗。

Ø 第18行:if(halButtonPushed()==HAL_BUTTON_1)判斷是否S1按下,函數halButtonPushed()halButton.c里面的,它的功能是:按鍵S1有被按動時,就回返回true,則進入basicRfSendPacket(LIGHT_ADDR,pTxData, APP_PAYLOAD_LENGTH);

Ø 第20行:Basic RF發射第2步,也是發送數據最關鍵的一步,函數功能在前面已經講述。basicRfSendPacket(LIGHT_ADDR, pTxData,APP_PAYLOAD_LENGTH)就是說:將LIGHT_ADDRpTxDataAPP_PAYLOAD_LENGTH的實參寫出來就是basicRfSendPacket(0xBEEF ,pTxData[1] ,1 )把字節長度為1的命令,發送到地址0xBEEF

Ø 第2223行:Zigbee開發板暫時還沒有joystick(多方向按鍵),不用理它先。

Ø 第25行:使能中斷

3.appLight()函數

發送的appSwitch()講解完畢,接下來就到我們的接收appLight()函數了

1.       static void appLight()
2.       {
3.         
7.         #ifdef ASSY_EXP4618_CC2420
8.         halLcdClearLine(1);
9.         halLcdWriteSymbol(HAL_LCD_SYMBOL_RX,1);

10.      #endif
11.    // Initialize BasicRF
12.      basicRfConfig.myAddr = LIGHT_ADDR;
13.      if(basicRfInit(&basicRfConfig)==FAILED) {
14.      HAL_ASSERT(FALSE);
15.      }
16.      basicRfReceiveOn();
17.      // Main loop
18.      while (TRUE)
19.       {
20.         while(!basicRfPacketIsReady());


21.         if(basicRfReceive(pRxData,APP_PAYLOAD_LENGTH, NULL)>0) {

22.         if(pRxData[0] == LIGHT_TOGGLE_CMD)
23.         {
24.          halLedToggle(1);
25.          }
26.        }
27.      }
28.    }

Ø 第710行:LCD內容暫時不用理它

Ø 第1215行:Basic RF啟動中的初始化,上面Basic RF啟動的第3

Ø 第16行:函數basicRfReceiveOn(),開啟無線接收功能,調用這個函數后模塊一直會接收,除非再調用basicRfReceiveOff()使它關閉接收。

Ø 第18行:程序開始進行不斷掃描的循環

Ø 第19行:Basic RF接收的第1步,while(!basicRfPacketIsReady()) 檢查是否接收上層數據,

Ø 第20行:Basic RF接收的第2步,if(basicRfReceive(pRxData,APP_PAYLOAD_LENGTH, NULL)>0)判斷否接收到有數據

Ø 第21行:if(pRxData[0] == LIGHT_TOGGLE_CMD)判斷接收到的數據是否就是發送函數里面的LIGHT_TOGGLE_CMD 如果是,執行第22

Ø 第22行:halLedToggle(1),改變Led1的狀態。

 

實驗操作:

第一步:打開….\CC2530 BasicRF\ide文件夾下面的工程在light_switch.c里面找到main函數,找到下面內容,把appLight(); 注釋掉,下載到發射模塊。

       appSwitch();        //節點為按鍵S1       P0_4
       //appLight();        //節點為指示燈LED1    P1_0

第二步:找到相同位置,這次把appSwitch();注釋掉,下載到接收模塊。     

        //appSwitch();        //節點為按鍵S1       P0_4
        appLight();         //節點為指示燈LED1   P1_0

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM