2014. december 9., kedd

RFM73 adóvevő rádiómodul

      Az RFM73 adóvevő rádiómodul (RFM - Radio Frequency Module) a HopeRF cég egyik terméke. Az RFM01 és RFM02-től elsősorban abban különbözik, hogy az ISM (Industrial, Scientific and Medical) sáv 2.4GHz – 2.4835GHz tartományában üzemel és GFSK (Gaussian Frequency-Shift Keying) modulációt használ. Adó és vevő funkciókra egyaránt képes valamint tartalmaz egy megfelelően illesztett antennát is. Következésképp a vevőnek beállított modul képes nyugtázni a vett üzeneteket és az adó képes újraküldeni az üzenetet, ha az nem ért megfelelően célba. Az adás és vétel nem egyszerre történik, hanem váltakozva (TDD – Time Division Duplex). A maximális sebesség elérheti a 2Mbps értéket miközben az alacsony fogyasztás megmarad (max. 25mA azaz kb. mint egy LED). Az üzemfeszültség 1.9V és 3.6V között van, az ajánlott feszültség 3.3V. Éppen ezért vigyázni kell, hogy olyan mikrokontrollerrel vezéreljük, ami tud működni 3.3V-ról is. Az 5V-os tépfeszültség még nem teszi tönkre a rádiómodult, tehát a kontroller felprogramozása során nem történik baj, azonban ha 5V-os logikai feszültségszintekkel történik a vezérlés akkor a modul hibásan értelmezheti a parancsokat és hibás értékeket téríthet vissza regisztereiből.


További tulajdonságok

Az adó maximális teljesítménye: 3dBm (2mW)
A vevő maximális érzékenysége: -97dBm
Adatsebesség: 250Kbps, 1Mbps, 2Mbps
Adatcsomagok hossza: 1 és 32 byte között
Adat-pipe száma: 6, azaz egy 6 modulból álló hálózatot lehet készíteni
Maximális vezérlőfrekvencia: 8MHz
Áramfogyasztás: Kikapcsolt állapotban 2.5μA, Standby-I üzemmódban 50 μA, Standby-II üzemmódban 330μA
Vezérlés típusa: SPI
A regiszterek bankokba vannak csoportosítva, akár a PIC mikrovezérlőknél.

A küldendő adatokat nem egy kivezetésen kapja, hanem konfigurációs parancsként (FIFO vermek)

Felépítés

A DIP és SMD változat között annyi a különbség, hogy a DIP tokozatra rá van forrasztva egy 1x8-as tüskesor. Az áramkör 12.8mm széles és 16.8mm hosszú és vastagsága alkatrészestől nem haladja meg a 2mm-t. Az antennának nincs kivezetés, a panelre maratott antenna készen rá van hangolva az RFM modul impedanciájára, ami a vonalvastagságból ítélve nem lehet több 80 ohmnál.

GND - GrouND: negatív tápfeszültség
VDD - Voltage Drain Drain: pozitív tápveszültség
CE - Chip Enable: ezzel lehet bekapcsolni az adást vagy a vételt
CSN - Chip Select Not: ezzel jelezhető, hogy az SPI interfész figyeljen oda a lábakra érkező adatokra
SCK (SPI ClocK): SPI órajel bemenet a szoftveres vezérléshez
MOSI - Master Out Slave In: itt fogadja a konfigurációs parancsokat
MISO - Master In Slave Out: innen lehet leolvasni a parancsok nyugtáját, a lekérdezések válaszait

A panelon lévő leragasztott IC az RF73 adóvevő IC-t rejti, a többi ellenállás, tekercs, kondenzátor, antenna a hangolására és az IC helyes üzembe helyezésére szolgál, a kristály pedig az rendszerórajelt adja.


Az RF73 tartalmaz egy beépített időosztásos duplexszel működő RF adóvevőt, mely jelei frekvenciában vannak modulálva. A küldendő jelek modulálás előtt még átmennek egy Gauss szűrőn, ami csökkenti a jel spektrális szélességét. Tehát a modulátorba érkező jelek nem bitek lesznek, hanem a Gauss függvényhez hasonlító impulzusok (nullából nem ugrik fel hirtelen a jelszint a tápfeszültségre, hanem csak fokozatosan és ezáltal keskenyebb impulzusok alakulnak ki, melyek spektruma is sokkal kisebb tartományban ingadozik). A vett adatok demodulálása után a „Data Slicer” szétválasztja a csomagokat, majd a „Packet Processing” feldolgozza a fejléceket és a tiszta adatot bepakolja az Rx FIFO-ba. A regiszterek, melyek a beállítások, állapotok, küldendő/fogadott adatok memorizálására szolgálnak, bankokra vannak osztva, hogy a memória minél hatékonyabban ki legyen használva. Olcsóbb és egyszerűbb megoldás a gyártó szempontjából, minthogy további címbuszokat használjon (hiszen csak a Bank0 bankkal kell dolgozni, a Bank1-et csak egyszer, a bekapcsoláskor való beállításnál vesszük igénybe). Az SPI interfész arra szolgál hogy a regiszterekbe be és ki lehessen tologatni (clockkolni) a biteket.

Működés

Három alapvető passzív üzemmód van:
-    Power Down: mikor a modul sem adni sem venni nem képes, de nyitva tartja az SPI interfészt hogy lekérdezhetőek legyenek az állapotregiszterek és fel lehessen ébreszteni ha szükséges.
-    Standby-I: mikor a modul befejezte az adást vagy a vételt ebbe az üzemmódba kapcsol, hogy csökkentse a fogyasztást
-    Standby-II: mikor adó módban az adatok már elhagyták a modul antennáját, de már töltődik be az újabb adag küldendő információ.

Az adó és vevő aktív üzemmódok beállítása az adatlap alapján rengeteg féleképpen történhet:
-    A vevő nyugtázhatja vagy nem a vett információt
-    Az adó újraküldheti az adatokat, ha nem kapott nyugtát (max 15-ször). Beállítható, hogy nyugta nélkül is újraküldje a csomagokat néhányszor.
-    Ha az RX verem megtelt, de újabb adat jön, ürítse ki a vermet hogy fogadhassa azt
-    A vett adatok címei változtathatóak 3,4,5 bájt hosszúságig. Ha minden pipe más címet kap, akkor a vevő képes 6 különböző adótól kapott információt külön-külön tárolni. Hogy a vevő a megfelelő adónak küldje vissza a nyugtát, a neki lefoglalt vételi címet adási címként használja, éppen ezért kötelező, hogy a nyugtát váró adó Rx címe is találjon ezzel. Az adók és a vevő nyilván ugyanazon a frekvencián működnek és a sok adó jele zavarhatja egymást. Ennek kiküszöbölésére szolgál az automatikus újraküldés késésének állíthatósága. Ha minden adónál más ez az érték, akkor különböző intervallumokban fognak adni és a vevő tisztán veheti mindeniket. Csupán bekapcsoláskor fog mindenik egyszerre sugározni. A vevő egyszerre 3 adó nyugtáját képes eltárolni (Az Rx és a Tx FIFO verem is 3 szintű).
-    A csomag fejlécében szerepelhet a csomag hosszúsága (dinamikus), a csomagazonosító (új csomag vagy újraküldött, és ha újraküldött akkor hányadik) és a nyugtázást kérő flag.
-    A csomaghosszúság lehet statikus és dinamikus is minden pipe-nak külön külön. A fejléc hozzáfűzése a csomaghoz az adónál és annak feldolgozása a vevőnél automatikusan történik.
-    Engedélyezni lehet a hibakeresést (CRC), ami az adatlapban megadott polinomot alkalmazza a csomag címére, az eredmény hossza 1 vagy 2 byte lehet.
-    A frekvenciasávot 1MHz-es felbontással állíthatjuk 2400MHz-től 2483.5MHz-ig
-    Az adó teljesítménye és a vevő érzékenysége állítható

Forráskód


      A HopeRF honlapjáról letölthető a forráskód, ami egy egyszerű adás-vételt valósít meg két modul között. A forráskód PIC-nek volt szánva, ezért átírtam, hogy használható legyen AVR-el.


Az fenti kapcsolás és az alábbi forráskód adónak és vevőnek is egyaránt jó. A main() függvényben kommentbe kell tenni a nem használt Send() vagy Receive() függvényt. A program a lehető legegyszerűbb beállításokat használja (nincs nyugtázás, nincs CRC, egyetlen adat-pipe és nincs újraküldés) valamint egy előre definiált byte-sorozatot ad vagy vesz újra meg újra. Az adónak beállított modulon a piros LED jelzi az adást, a vevőnek beállított modulon pedig a zöld LED jelzi a vételt.

#define F_CPU 1000000UL      //az AVR rendszerórajel frekvenciája
#include <util/delay.h>
#include <avr/pgmspace.h>

#define UINT8 unsigned char
#define UINT32 unsigned long
#define MAX_PACKET_LEN  32   //legfeljebb 32Byte payload a FIFO-ban

#define RED_LED PB5 //Adást jelzõ LED
#define GREEN_LED PB4 //Vételt jelzõ LED
#define CE   PA6 //Chip Enable: a chip adjon-e vagy vegyen
#define CSN   PA5 //Chip Select Not: az SPI figyeljen-e a parancsokra
#define SCK   PA3 //SPI ClocK: órajel a hardveres vezérléshez
#define MISO PA1 //Master In Slave Out: lekérdezés
#define MOSI PA2 //Master Out Slave In: parancsok

#define RED_LED_OUT() DDRB |= (1<<RED_LED)   //adás LED kimenet
#define HI_RED_LED()   PORTB|= (1<<RED_LED)
#define LOW_RED_LED() PORTB&=~(1<<RED_LED)

#define GREEN_LED_OUT() DDRB |= (1<<GREEN_LED) //vétel LED kimenet
#define HI_GREEN_LED()  PORTB|= (1<<GREEN_LED)
#define LOW_GREEN_LED() PORTB&=~(1<<GREEN_LED)

#define CE_OUT() DDRA |= (1<<CE)     //CE kimenet
#define HI_CE()   PORTA|= (1<<CE)
#define LOW_CE() PORTA&=~(1<<CE)

#define CSN_OUT()  DDRA |= (1<<CSN)    //CSN kimenet
#define HI_CSN()   PORTA|= (1<<CSN)
#define LOW_CSN()  PORTA&=~(1<<CSN)

#define SCK_OUT()  DDRA |= (1<<SCK)    //vezérlő órajel kimenet
#define HI_SCK()   PORTA|= (1<<SCK)
#define LOW_SCK()  PORTA&=~(1<<SCK)

#define MISO_IN()   DDRA &= ~(1<<MISO)  //lekérdezés bemenet
#define MISO_HI()   PINA&(1<<MISO)

#define MOSI_OUT()  DDRA |= (1<<MOSI)    //parancs kimenet
#define HI_MOSI()   PORTA|= (1<<MOSI)
#define LOW_MOSI()  PORTA&=~(1<<MOSI)

// fontosabb parancsok
#define READ_REG         0x00 //regiszterbõl olvas: 001xxxxx, ahol x=cím
#define WRITE_REG       0x20 //regiszterbe ír: 001xxxxx, ahol x=cím
#define RD_RX_PLOAD     0x61 //kiolvas 32B adatot az Rx FIFO-ból
#define FLUSH_TX         0xE1 //kiüríti Tx FIFO-t
#define FLUSH_RX         0xE2 //kiüríti Rx FIFO-t
#define W_TX_PAYLOAD_NOACK_CMD 0xb0 //kikapcsolja a nyugtázást
#define ACTIVATE_CMD 0x50 //a feature regisztereket ki/be kapcsolja
#define R_RX_PL_WID_CMD 0x60 //lekérdezi az Rx FIFO hosszát: 01100000

// fontosabb regiszter címek
#define CONFIG           0x00 //CONFIG regiszter címe (Bank0)
#define STATUS           0x07 //STATUS regiszter címe (Bank0)
#define FIFO_STATUS     0x17 //FIFO_STATUS regiszter címe (Bank0)
#define FEATURE 0x1D //FEATURE regiszter címe (Bank0)

//fontosabb állapot értékek
#define STATUS_RX_DR 0x40 //ha a STATUS regiszterben az RX_DR bit=1
#define FIFO_STATUS_TX_FULL   0x20 //ha a FIFO_STATUS regiszterben a TX_FULL=1
#define FIFO_STATUS_RX_EMPTY 0x01 //ha a FIFO_STATUS regiszterben az RX_EMPTY=1

const UINT32 Bank1_Reg0_13[]={ //Bank1-ben az értékek sorrendje: MSB byte -> LSB byte
0xE2014B40, //00 = 40.4B.01.E2 - kötelezõ érték
0x00004BC0, //01 = C0.4B.00.00 - kötelezõ érték
0x028CFCD0, //02 = D0.FC.8C.02 - kötelezõ érték
0x41390099, //03 = 99.00.39.41 - kötelezõ érték
0x0B869ED9, //04 = D9.9E.86.0B - kötelezõ érték
0xA67F0624, //05 = 24.06.7F.A6 - kötelezõ érték
0x00000000, //06 = 00.00.00.00 - lefoglalt
0x00000000, //07 = 00.00.00.00 - lefoglalt
0x63000000, //08 = 00.00.00.63 - csak olvasni lehet (chip ID)
0x00000000, //09 = 00.00.00.00 - lefoglalt
0x00000000, //0A = 00.00.00.00 - lefoglalt
0x00000000, //0B = 00.00.00.00 - lefoglalt
0x00127300, //0C = 00.73.12.00 - 120us mód, Dynamic compatible
0x36B48000};//0D = 00.80.B4.36 - kötelezõ érték

const UINT8 Bank1_Reg14[]={ //FF.EF.7D.F2.08.08.20.82.04.10.41 - kötelezõ érték
0x41,0x10,0x04,0x82,0x20,0x08,0x08,0xF2,0x7D,0xEF,0xFF};

const UINT8 Bank0_Reg[][2] PROGMEM={ //Bank0-ban az értékek sorrendje: LSB byte -> MSB byte
{0,0x03},  //00-CONFIG:    00000011 = PRX, bekapcs, nincs CRC
{1,0x00},  //01-EN_AA:     00000000 = nem nyugtáz semmit sem
{2,0x01},  //02-EN_RXADDR: 00000001 = csak az adatpipe0 címet használjuk
{3,0x03},  //03-SETUP_AW:  00000011 = 5 byte cimhossz
{4,0x00},  //04-SETUP_RETR:00000000 = újraküldés kikapcs
{5,0x00},  //05-RF_CH:     00000000 = 2400+0=2400MHz
{6,0x07},  //06-RF_SETUP:  00000111 = 1Mbps; 5dBm sugárzás; magas nyereség
{7,0x00},  //07-STATUS:    00000000 = csak olvasni lehet
{28,0x3F}, //1C-DYNPD:    00111111 = dinamikus payload mindenik pipe-nak
{29,0x07}};//1D-FEATURE:   00000111 = dinamikus payload hossz bekapcs; Payload nyugtával és anélkül 

const UINT8 RX0_Address[]={0xE7,0xE7,0xE7,0xE7,0xE7};//0A - pipe0 vételi címei és a Tx adási címei

void Initialize(void);
void Send(void); 
void Receive(void);
void RFM73_Initialize(void);
void SwitchToTxMode(void);
void SwitchToRxMode(void);
UINT8 SPI_RW(UINT8 value);
void SPI_Write_Reg(UINT8 reg, UINT8 value); 
UINT8 SPI_Read_Reg(UINT8 reg);
void SPI_Read_Buf(UINT8 reg, UINT8 *pBuf, UINT8 length); 
void SPI_Write_Buf(UINT8 reg, UINT8 *pBuf, UINT8 length);
void SwitchCFG(char _cfg);

const UINT8 tx_buf[17] PROGMEM={0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,
0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,0x78}; //küldendõ adat, az utolsó byte az ellenörzõösszeg
UINT8 rx_buf[MAX_PACKET_LEN];

//********************************************************************************************************
int main(void)
{
_delay_ms(1000); //bekapcsolási késés 1mp
  Initialize(); //portok, regiszterek inicializálása
while(1)
{
Send();
//Receive();
}
}

//--------------------------------------------------------------------------------------------------------
void Initialize(void) //inicializáló függvény
{
CE_OUT(); //adás/vétel inicializáló kimenet
CSN_OUT(); //SPI inicializáló kimenet
SCK_OUT(); //órajel kimenet
MISO_IN(); //nyugta bemenet
MOSI_OUT(); //parancs kimenet
RED_LED_OUT(); //adó LED kimenet
GREEN_LED_OUT(); //vevõ LED kimenet
LOW_CE(); //nincs se adás se vétel
HI_CSN(); //nincs SPI kommunikáció
LOW_SCK(); //nincs órajel
LOW_MOSI(); //nincs parancs
LOW_RED_LED(); //adó LEd kikapcs
LOW_GREEN_LED(); //vevõ LED kikapcs
RFM73_Initialize();     //adatbankok feltöltése, vevõ üzemmód
}

//--------------------------------------------------------------------------------------------------------
void Send(void) //adó függvény
{
      UINT8 i,fifo_sta;
      UINT8 temp_buf[32];

      for(i=0;i<17;i++)
            temp_buf[i]=pgm_read_byte(&tx_buf[i]); //a küldendõ 17 byte eltárolása
     
      SwitchToTxMode(); //adó üzemmód
      fifo_sta=SPI_Read_Reg(FIFO_STATUS);   //FIFO állapotának lekérdezése
      if((fifo_sta&FIFO_STATUS_TX_FULL)==0) //ha van még hely a TX_FIFO-ban
      {
            HI_RED_LED(); //akkor bkapcsol a piros LED 30 miliszekundumig
            SPI_Write_Buf(W_TX_PAYLOAD_NOACK_CMD, temp_buf, 17); //adatok FIFO-ba írása
            _delay_ms(30);
            LOW_RED_LED();
            _delay_ms(30);
      }
}

//--------------------------------------------------------------------------------------------------------
void Receive(void) //vevõ függvény
{
      UINT8 len,sta,fifo_sta,chksum;
      UINT8 rx_buf[MAX_PACKET_LEN];

      sta=SPI_Read_Reg(STATUS);       //állapotregiszter kiolvasása
   
      if((STATUS_RX_DR&sta) == STATUS_RX_DR)  //ha új adat került a FIFO-ba
      {
            do
            {
                  len=SPI_Read_Reg(R_RX_PL_WID_CMD);  //csomaghossz lekérdezése

                  if(len<=MAX_PACKET_LEN)     //ha a csomag mérete megfelelõ
                        SPI_Read_Buf(RD_RX_PLOAD,rx_buf,len); //akkor a FIFO kiolvasása
                  else
                        SPI_Write_Reg(FLUSH_RX,0);          //különben a FIFO kiüríése

                  fifo_sta=SPI_Read_Reg(FIFO_STATUS); //maradt-e még a FIFO-ban valami
                                         
            }while((fifo_sta&FIFO_STATUS_RX_EMPTY)==0); //ismételd míg üres nem lesz
           
            chksum = rx_buf[0]+rx_buf[1]+rx_buf[2]+rx_buf[3]+rx_buf[4]+rx_buf[5]+rx_buf[6]+
                  +rx_buf[7]+rx_buf[8]+rx_buf[9]+rx_buf[10]+rx_buf[11]+rx_buf[12]+rx_buf[13]+
                  +rx_buf[14]+rx_buf[15]; //ellenörzõösszeg kiszámítása
           
            if(chksum==rx_buf[16]&&rx_buf[0]==0x30) //ha az ellenörzõösszeg és az elsõ byte stimmel
            {
                  HI_GREEN_LED();//akkor bekapcsolja a zöld LEDet
                  _delay_ms(30);
                  LOW_GREEN_LED();
                  _delay_ms(30);
            }
            SwitchToRxMode(); //adó üzemmód és egyben a FIFO kiürítése
      }
      SPI_Write_Reg(WRITE_REG|STATUS,sta);//RX_DR,TX_DS,MAX_RT megszakításjelzõ bitek kikapcs
}

//--------------------------------------------------------------------------------------------------------
UINT8 SPI_RW(UINT8 value) //parancsadó és választ fogadó függvény
{                                                          
      UINT8 bit_ctr;
     
      for(bit_ctr=0;bit_ctr<8;bit_ctr++)  //a "value" bitenkénti átjárása
      {   //mindig a MSB bitet adjuk
            if(value & 0x80) HI_MOSI(); else LOW_MOSI(); //parancs bitek
            value = (value << 1);           //következõ bit betolása a MSB-be
            HI_SCK();                                 //órajel félciklus
            if(MISO_HI()) value |= 0x01;    //a visszajelzõ bitek betöltése "value"-ba
            LOW_SCK();                           //órajel félciklus
      }
      return(value);                           //a visszajelzés visszatérítése
}

//--------------------------------------------------------------------------------------------------------
void SPI_Write_Reg(UINT8 reg, UINT8 value) //"reg" címû regiszterbe írja a "value"-t
{    //az írás sorrendje: parancs(regisztercím) és parancs(érték)
      LOW_CSN();       //az SPI parancs elején a CSN alacsony kell legyen
      SPI_RW(reg);     //regiszter kiválasztása
      SPI_RW(value);   //az érték beleírása
      HI_CSN();        //az SPI parancs magas CSN-el zárul
 

//--------------------------------------------------------------------------------------------------------
UINT8 SPI_Read_Reg(UINT8 reg) //kiolvassa "reg" regiszter tartalmát és visszatéríti
{                                                          
      UINT8 value;
     
      LOW_CSN();          //SPI parancs eleje
      SPI_RW(reg);        //regiszter kiválasztása
      value = SPI_RW(READ_REG);  //az érték kiolvasása
      HI_CSN();           //SPI parancs vége
      return(value);      //a kiolvasott érték visszatérítése
}

//--------------------------------------------------------------------------------------------------------
void SPI_Read_Buf(UINT8 reg, UINT8 *pBuf, UINT8 length)
{        //"reg" regiszterbõl kiolvassa a "length" hosszúságú "pBuf" tömböt
      UINT8 byte_ctr;                             
 
      LOW_CSN();                    //SPI parancs eleje
      SPI_RW(reg);                  //regiszter kiválasztása
      for(byte_ctr=0;byte_ctr<length;byte_ctr++) //a tömb minden eleme egy-egy byte
            pBuf[byte_ctr] = SPI_RW(READ_REG);  //az érték kiolvasása
      HI_CSN();                     //SPI parancs vége
}

//--------------------------------------------------------------------------------------------------------
void SPI_Write_Buf(UINT8 reg, UINT8 *pBuf, UINT8 length)   
{        //"reg" regiszterbe beleírja a "length" hosszúságú "pBuf" tömböt
      UINT8 byte_ctr;                             
 
      LOW_CSN();                    //SPI parancs eleje
      SPI_RW(reg);                  //regiszter kiválasztása
      for(byte_ctr=0; byte_ctr<length; byte_ctr++) //a tömb minden eleme egy-egy byte
            SPI_RW(*pBuf++);        //az érték beleírása                         
      HI_CSN();                     //SPI parancs vége
}

//--------------------------------------------------------------------------------------------------------
void SwitchToRxMode() //átvált Rx üzemmódba (PRIM_RX=1 és CE=1)
{
      UINT8 value;
     
      SPI_Write_Reg(FLUSH_RX,0);    //Rx FIFO kiürítése
      LOW_CE();                     //standby-I üzemmód
      value=SPI_Read_Reg(CONFIG);   //a konfigurációs bitek
      value=value|0x01;             //melyek közül az elsõt 1-re állítjuk (PRX üzemmód)
      SPI_Write_Reg(WRITE_REG | CONFIG, value); //a megváltoztatott érték visszatöltése
      HI_CE();                      //CE=1
}

//--------------------------------------------------------------------------------------------------------
void SwitchToTxMode() //átvált Tx üzemmódba (PRIM_RX=0 és CE=1)
{
      UINT8 value;
     
      SPI_Write_Reg(FLUSH_TX,0);  //Tx FIFO kiürítése
      LOW_CE();                   //standby-I üzemmód
      value=SPI_Read_Reg(CONFIG); //a konfigurációs bitek
      value=value&0xfe;           //melyek közül az elsõt 0-ra állítjuk (PTX üzemmód)
      SPI_Write_Reg(WRITE_REG | CONFIG, value); //a megváltoztatott érték visszatöltése
      HI_CE();                    //CE=1
}

//--------------------------------------------------------------------------------------------------------
void SwitchCFG(char _cfg) //váltogatás a bankok között: 1=Bank1 0=Bank0
{
      UINT8 Tmp;

      Tmp=SPI_Read_Reg(STATUS);     //állapotregiszter bitjei
      Tmp=Tmp&0x80;                 //melyek közül a hetedik (MSB) jelzi a bankot

      if( ( (Tmp)&&(_cfg==0) )||( ((Tmp)==0)&&(_cfg) ) ) //ha nem ugyanarra történik a váltás
            SPI_Write_Reg(ACTIVATE_CMD,0x53); //akkor váltson át a másikra
}

//--------------------------------------------------------------------------------------------------------
void RFM73_Initialize()
{
      UINT8 i,j;
      UINT8 WriteArr[12];

      _delay_ms(100);
     
      SwitchCFG(0); //-----------------------------Bank0-----------------------------------------

      for(i=0;i<8;i++) // a Bank0_Reg tömb elsõ 8 elemének betöltése a Bank0-ba
            SPI_Write_Reg((WRITE_REG|pgm_read_byte(&Bank0_Reg[i][0])),pgm_read_byte(&Bank0_Reg[i][1]));

      for(j=0;j<5;j++) //Bank0 0A regisztere: az 5 byte-os pipe0 címe
            WriteArr[j]=RX0_Address[j];
      SPI_Write_Buf((WRITE_REG|10),&(WriteArr[0]),5);

      for(j=0;j<5;j++) //Bank0 10 regisztere: a Tx cím ugyanaz kell legyen mint a pipe0 vételi címe
            WriteArr[j]=RX0_Address[j];
      SPI_Write_Buf((WRITE_REG|16),&(WriteArr[0]),5);
     
      i=SPI_Read_Reg(FEATURE);//a FEATURE regisztert kiolvasva
      if(i==0)                       //ha mindenik bitje 0, akkor nem volt aktiválva
            SPI_Write_Reg(ACTIVATE_CMD,0x73); //és aktiválni kell
           
      for(i=9;i>=8;i--) //a FEATURE aktiválása után betöltjük az értékeket
            SPI_Write_Reg((WRITE_REG|pgm_read_byte(&Bank0_Reg[i][0])),pgm_read_byte(&Bank0_Reg[i][1]));
     
      SwitchCFG(1);  //-----------------------------Bank1-----------------------------------------
     
      for(i=0;i<=8;i++)//a Bank1 0-8 regisztereiben a byte-ok fordított sorrendben vannak (MSB->LSB)
      {
            for(j=0;j<4;j++) //4 byte-os regiszterek vannak a Bank1-ben
                  WriteArr[j]=Bank1_Reg0_13[i]>>(8*(j));
            SPI_Write_Buf((WRITE_REG|i),&(WriteArr[0]),4);
      }

      for(i=9;i<=13;i++)//a Bank1 9-13 regisztereiben a sorrend LSB->MSB
      {
            for(j=0;j<4;j++)
                  WriteArr[j]=Bank1_Reg0_13[i]>>(8*(3-j));
            SPI_Write_Buf((WRITE_REG|i),&(WriteArr[0]),4);
      }

      for(j=0;j<11;j++) //Bank1 0E regisztere 11 byte hosszú
            WriteArr[j]=Bank1_Reg14[j];
      SPI_Write_Buf((WRITE_REG|0x0E),&(WriteArr[0]),11);

      _delay_ms(50);
      SwitchCFG(0);
      SwitchToRxMode(); //Alapállás a vevõ üemmód
}

A C programozási nyelvben használt #define direktívákkal érthetőbbé tehető az AVR lábainak neve valamint a rájuk küldött parancsok értéke. Az eredeti forráskódban minden használható címet, értéket, parancsot definiáltak, én azonban csak a létfontosságúakat hagytam meg. A bank1 regisztereit kötelezően be kell állítani, a bank0-ban lévők közül viszont elég azokat, melyek menet közben módosulnak is.

RFM73_Initialize: ez tölti be a tömbökben tárolt beállításokat a regiszterekbe. Egyes regiszterek hosszúsága több Byte hosszú is lehet,  Bank1 regisztereinek értékeit pedig fordított Byte sorrendben kell betologatni. A beállítások elvégeztével a függvény visszavált Bank0-ra és beáll vevő üzemmódba.

SwitchCFG: a bankok közti átváltást valósítja meg. Hogy épp melyik bank aktív, az a STATUS regiszter hetedik bitjében szerepel. Ha nem ugyanaz amire váltani szeretnénk akkor átvált a másikra.

SwitchToTxMode: átvált adó üzemmódra átírván a CONFIG regiszter legelső bitjét.
SwitchToRxMode: átvált vevő üzemmódra átírván a CONFIG regiszter legelső bitjét.

SPI_Write_Buf: beleírja a paraméternek megadott tömböt (buffert) a paraméternek megadott regiszterbe. A Tx FIFO feltöltésére jó.
SPI_Read_Buf: kiolvassa a paraméternek megadott regiszterből az értékeket a paraméternek megadott tömbbe (bufferbe). Az Rx FIFO kiolvasására jó.

SPI_Write_Reg: beírja a paraméternek megadott regiszterbe a paraméternek megadott értéket.
SPI_Read_Reg: kiolvassa a paraméternek megadott regiszter tartalmát és visszatéríti azt.

SPI_RW: az SPI kommunikációt megvalósító függvény. A MOSI lábra tolja a paraméternek megadott parancsot (ami lehet olvasó parancs is) és közben elmenti a MISO lábról érkezett biteket, majd visszatéríti őket.

Receive: a vevő függvény, mely ellenőrzi, hogy került-e új adat a FIFO-ba. Mivel a csomag hossza dinamikus (hogy kisebb tömbököt is lehessen a Tx FIFO-ba rakni a Send függvényben), leellenőrzi, hogy nem-e hoszabb a maximális 32 byte-nál (a fejlécben van ez az információ). Ha a hossza megfelelő akkor belerakja a vett értékeket egy helyi változóba és mindezt addig ismétli míg az üres FIFO-t jelző flag be nem kapcsol. Ezután összeadja a tömb értékeit és megnézi, hogy talál-e az ellenőrző összeggel (az utolsó értékkel). Itt sajnos elemenként kellett összeadni mert for ciklussal nem működött megfelelően. Ha az értékek találnak (és az első érték is jó) akkor 30 miliszekundumig felgyúl a zöld LED. Ezek után a FIFO-t ki kell üríteni, és itt a vevő üzemmódra kapcsolás a legalkalmasabb. A végén kiolvasott állapotérték újbóli visszaírása arra jó, hogy a rádiómodulnak „nyugtázzuk” a feladat végeztét.

Send: az adó függvény, mely elküldi a tx_buf tömbben tárolt értékeket a Tx FIFO-nak. Előbb helyi változóba kimenti az értékeket (optimálisabb helyi változókkal dolgozni mint globálisokkal) majd átvált adó üzemmódba. Ha a FIFO telítetsségét jelző bit nincs bekapcsolva akkor meg lehet pakolni a FIFO-t. Ezt a 30 miliszekundumig világító piros LED jelzi.

Mivel az Attiny26 adatmemóriálya igen csekély és a regiszterek hatalmasak, a feltöltésükre szánt adatok nem férnek el az AVR adatmemóriájában. Szerencsére az avr/pgmspace.h header segítségével alkalmazható a PROGMEM attribútum, melynek segítségével a programmemóriában is tárolhatóak az adatok. A PROGMEM-el tárolt adatok a pgm_read_byte(&adat) függvénnyel olvashatók ki.