Individální projekty MPOA

Mikroprocesory s architekturou ARM

Uživatelské nástroje

Nástroje pro tento web


2015:p2p-rfm12b

Point-to-point spoj s RFM12B

Zadání

Pomocí dvojice transceiverů RFM12B připojených ke kitům FRDM-KL25Z realizujte experimentální rádiový spoj v pásmu 868MHz. Vytvořte firmware pro testování spojení - měření chybovosti, rychlosti přenosu, RTT apod. Proveďte měření základních parametrů v závislosti na vzdálenosti modulů.

Hardware

RFM12B

Jedná se o univerzální poloduplexní transceiver pracující v ISM pásmu. Nosný kmitočet lze nastavit do pásem 433 MHz, 868 MHz a 915 MHz. Komunikační rychlost by dle katalogového listu měla dosahovat rychlosti až 115,2 kb/s při použití digitálního filtru a až 256 kb/s při použití analogového filtru. Modul ke komunikaci využívá FSK modulaci, přičemž kmitočtový zdvih lze nastavovat v rozmezí 15 kHz – 240 KHz. Maximální výstupní výkon při použití antény se vstupní impedancí 50 Ω je 5 dBm a lze jej snižovat s krokem 2,5 dB až na hodnotu -12,5 dBm. Modul je vybaven SPI rozhraním, který slouží k jeho řízení a k přenosu dat z/do připojeného kontroléru.

FRDM KL25Z

K řízení modulů a ke komunikaci s počítačem byl využit vývojový kit FDRM KL25Z, na kterém je osazen 32-bitový mikrokontrolér Kinetis MKL25Z128 od firmy Freescale Semiconductor (respektive NXP). Maximální takt tohoto mikrokontroléru je 48 MHz. Pro programování procesoru je na desce integrováno rozhraní OpenSDA, které zároveň realizuje virtuální COM port, který je v projektu využit k řízení komunikace.

Řešení

Zapojení

Modul byl pomocí vodičů připojen k vývojové desce. Ke komunikaci mezi moduly je vyžadováno 5 vodičů, napájení bylo také vyvedeno z vývojové desky. Zapojení komunikačních vodičů je následující:

RFM12B……KL25Z
SCK………PTD1 (SPI CLK)
SDI……….PTD2 (SPI MOSI)
SDO………PTD3 (SPI MISO)
nSEL……..PTD0 (SPI CS)
nIRQ……..PTD5 (interupt request)
Toto zapojení bylo zvoleno kvůli původnímu záměru využít hardwarové SPI rozhraní, přestože nakonec bylo SPI implementováno softwarově, zapojení bylo ponecháno.
Jako anténa sloužil při testování obyčejný drát délky přibližně 8,6 cm což zhruba odpovídá λ/4 pro kmitočet 868 MHz.
Moduly mají ještě několik dalších komunikačních pinů, ty však v tomto projektu nebyly využity

Vývojové prostředí.

Původním záměrem bylo řešit projekt pomocí online vývojového prostředí mbed.org, protože jsou zde k dispozici knihovny pro použitý modul. Bohužel ani s jednou ze dvou testovaných knihoven se mi nepodařilo zprovoznit ani základní komunikaci. Důvod nefunkčnosti zřejmě nebyl v knihovnách, protože obě byly relativně dost používané, ale někde v mém kódu, chybu se však nepodařilo najít. Proto jsem přistoupil k použití vývojového prostředí Kinetis Design Studio s rozšířením Procesor Expert. Pro projekt jsem vytvořil vlastní knihovnu funkcí, sestavenou dle katalogového listu RFM12B. Některé funkce pak byly částečně inspirovány knihovnou dostupnou na mbed.org.

Inicializace procesoru

Pro inicializaci procesoru byl využit program Processor Expert, který je integrován ve vývojovém prostředí Kinetis Design Studio. Pomocí tohoto programu byly inicializovány vstupní a výstupní pinu procesoru, UART pro komunikaci s PC, časovač pro měření RTT, rychlost procesoru. Dále byla z PE do projektu vložena komponenta WAIT realizující blokující zpoždění.

SPI

Pro komunikaci bylo s modulem bylo implementováno softwarové SPI rozhraní, protože s využitím hardwarového SPI se nepodařilo komunikaci zprovoznit. Softwarová implementace SPI je zřejmá z následujícího zdrojového kódu:

uint16_t sendCMD(uint16_t cmd)
{
    uint16_t reply=0;	// slaves answer 
    uint8_t i=0;
    SCK_ClrVal();		// SCK=SPI clock
    nSEL_ClrVal();		//nSEL=SPI chip select
 
    for(i=0;i<16;i++)
    {
 
      if(cmd&0x8000)
      {
    	  SDI_SetVal();	//SDI=SPI MOSI
      }
      else
      {
    	  SDI_ClrVal();
      }
      //WAIT1_Waitns(10);
      SCK_SetVal();
      //WAIT1_Waitns(50);
      reply<<=1;
      if(SDO_GetVal())	//SDO=SPI MISO
      {
        reply|=0x0001;
      }
      SCK_ClrVal();
      cmd<<=1;
    }
    SCK_ClrVal();
    nSEL_SetVal();
    return(reply);
}

Nevýhodou softwarového SPI je to, že i při maximální možné rychlosti použitého procesoru dosahuje jeho komunikační rychlosti pouze 250 kb/s.

Inicializace modulu.

Jak již bylo zmíněno výše, k nastavování modulu slouží 14 registrů. Na začátku vykonávání programu jsou do nastavovacích registrů zapsány výchozí hodnoty, z nichž některé lze později měnit prostřednictvím konzole v PC. Výchozí nastavení transceiverů: Kmitočet nosné: 868,3 MHz Bitová rychlost: 4,78 kb/s Mód: přijímač Kmitočtový zdvih: 90 kHz Přerušení: 16 přijatých bitů Datový filtr: digitální Výstupní výkon: 5 dBm

Funkce pro inicializaci modulu:

void init(void)
{
 
	  sendCMD(0x80E7); 		// Config. Setting Cmd:  f=868MHz, Tx reg. EN, Rx FIFO EN,XTAL Cload 12pF
	  sendCMD(0x82D9); 		// Power Management Cmd: rcvr EN,BB EN,TX dis,synth EN,osc EN,low-batt DIS,wake-up DIS,CLK out DIS,
	  sendCMD(0xA67C);		// Freq. Setting Cmd:fc= 860,3MHz
	  sendCMD(0xC688);		// Data Rate Cmd: BR=4,79 kbps
	  sendCMD(0x96A0);		// VDI out - SLOW,BB=134kHz,LNA gain=0dBm,RSSI= -103dBm
	  sendCMD(0xC2AC);		// CR auto mode, digital filter, DQD=6
	  sendCMD(0xCAF1);		// nIRQ goes low after 16 received bits, 2 synchro bytes
	  sendCMD(0xCED4);		// synchro pattern Byte0 =0xD4
	  sendCMD(0xC483); 		// keep f ofset,not range limit,enable AFC calc and reg.
	  sendCMD(0x9850); 		// f deviation = 90kHz, max TX power
	  sendCMD(0xCC77);		// CLKout not used, phase noise =-102 dBc/Hz
	  sendCMD(0xE000);		// no effect - wake-up timer is disable 
	  sendCMD(0xC800);		// no effect wake-up time is set to zero
	  sendCMD(0xC000);		// no effect low batt detec is off, CLK out not used
}

Odesílání dat

K zápisu do registru vysílače slouží speciální příkaz 0xB800 kde první dva nibbly (B8h)slouží k identifikaci příkazu a do druhých dvou se vkládají data. Před odesláním dat do modulu je vždy nutné počkat než se uvolní vysílací registr, což je indikováno úrovní na pinu nIRQ.

void send(unsigned char data)
{
  while(nIRQ_GetVal()){}        //waiting for previously TX over
  sendCMD(0xB800|data);
}

Při odesílání paketu dat je nutné na začátku odeslat preambuli se synchonizačními byty a na konci 3 prázdné bity.

void sendDATA(unsigned char *data, unsigned char size)
{
    uint8_t i;
    unsigned char send1[size];
    FIFO_rst();				//reset prijimaci pametu RF modulu
 
    RFmode(TX);				//nastaveni RF modulu do vysilaciho modu
    WAIT1_Waitns(200);
    sendCMD(0x0000);
    send(0xAA);				//preambule
    send(0xAA);
    send(0xAA);
    send(0x2D);
    send(0xD4);				//synchro
 
    for(i=0;i<size;i++)			//send data
    {
      send(*data);
      data++;
    }
    send(0x00);
    send(0x00);
    send(0xAA);				//dummy bytes
    send(0xAA);
    send(0xAA);
 
    RFmode(RX);				//set to RX mode
    WAIT1_Waitns(200);
}

Před začátkem vysílání je samozřejmě nutné přepnout modul do vysílacího módu a po skončení zpět do režimu přijímače. K tomu slouží funkce void RFmode(bool mode);.

void RFmode(bool mode)
{
	if(mode)
	{
		sendCMD(0x8239);
		sendCMD(0x0000);
	}
	else
	{
		sendCMD(0x82D9);
		sendCMD(0x0000);
	}
}

Čtení přijatých dat

Přijetí dat je signalizováno nízkou úrovní na pinu nIRQ. V režimu přijímače je tedy v nekonečné smyčce testován stav tohoto pinu a v případě nízké úrovně je volána funkce void readDATA1(unsigned char data[]); nebo funkce void readDATA(unsigned char FIFO_data[],unsigned char size);. První zmíněná funkce může přijímat libovolně dlouhé pakety, ty však musejí být dvěma znaky ‘’\n’’. Druhá funkce pak přijme paket definované délky, přičemž není vyžadován ukončovací znak.

void readDATA1(unsigned char data[])
{
	  uint8_t i=0;
  	  do
      {
  		 data[i++]=Rx_FIFO();
      }
      while(!((data[i-1]=='\n')&&(data[i-2]=='\n')));
      LED_G_PutVal(!LED_G_GetVal());
      FIFO_rst();
      sprintf(data,data);
}
 
void readDATA(unsigned char data[],unsigned char size)
{
  uint8_t i=0;
            for(i=0;i<size;i++)
            {
  		      data[i]=Rx_FIFO();
            }
            LED_G_PutVal(!LED_G_GetVal());
            FIFO_rst();
  sprintf(data,data);
}

Obě funkce pro příjem paketů volají funkci pro čtení jednotlivých bytů Rx_FIFO().

unsigned char Rx_FIFO(void)
{
   unsigned int data_rx, x=100;			//x - number of tests for RX data
   while(x--)						
   {
 
      if((sendCMD(0x0000) & 0x8000))	    	//is byte valid?				
     {
       data_rx =sendCMD(0xB000);			//read the RX byte
       return (data_rx & 0x00FF);		//mask and return		
     }
   }
   return 0;      					//no data
}

Po přijetí datového paketu je nutné resetovat synchronizaci FIFO paměti:

void FIFO_rst(void)
{
	sendCMD(0xCA81);
	sendCMD(0xCA83);
	sendCMD(0x0000);
} 

Měření RTT

Pro měření RTT byl využit 16 – bitový hardwarový časovač. Samotné měření je realizováno tak, že jeden z transceiveru se nastaví tak, že ihned po přijetí paketu dat jej odešle zpět (funkce resender();). Druhý transceiver, který měří RTT vynuluje hodnotu časovače a odešle paket. Jakmile zachytí vrácený paket, je odečtena hodnota časovače a z ní je vypočtena hodnota RTT v milisekundách, pokud se paket nevrátí do 150 ms, je paket považován za ztracený a do konzole je vypsána zpráva RTT>75 ms (funkce RTTmeasure()).

void resender(void)
{
	unsigned char data[128];
 
	while(flags==resend)
	{
		if(!nIRQ_GetVal())
		{
			readDATA(data,64);
			sendDATA(data,64);
		}
	}
}
void RTTmeasure(void)
{
	unsigned char data[128];
	unsigned char RXdata[128];
	sprintf(data,"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz123456789[]\n");
	uint16_t len;
	uint16_t m;
	bool x=1;
	unsigned char c;
	FC1_Reset();
	sendDATA(data,64);
	while(x)
	{
		if(!nIRQ_GetVal())
		{
			readDATA(RXdata,64);
			x=0;
		}
		FC1_GetCounterValue(&m);
		if(m>56250)
		{
			sprintf(data,"RTT > 75 ms\n\n");
			(UART_SendBlock(data,strlen(data),&len));
			x=0;
			return;
		}
	}
	FC1_GetCounterValue(&m);
	while(UART_SendBlock(data,64,&len)!=ERR_OK){}
	float ms=((float)m)/750;			//timer freq is 375 kHz
	m=(ms);
	ms-=m;
	uint16_t n=round(ms*1000);
	sprintf(data,"RTT = %d.%03d ms\n\n",m,n);
	(UART_SendBlock(data,strlen(data),&len));
	return;
}

Měření BER

Měření bitové chybovosti je realizováno pomocí odeslání známe sekvence dat (jeden 64 bytový paket). Po přijetí paketu je paket bit po bitu porovnán s očekávanou sekvencí. V případě chyby je inkrementována příslušná proměnná. BER je pak poměr mezi počtem chyb a celkovým počtem bitů.

float BER(unsigned char *ref, unsigned char *data, uint8_t size)
{
	uint16_t i,err=0;
	unsigned char x;
 
	for(i=0;i<size;i++)
	{
		x=*ref^*data;
		while(x)
		{
			if(x&1) {err++;}
			x=x>>1;
		}
		ref++;
		data++;
	}
	return ((float)err)/((float)(size*8));
}

Nastavování parametrů přenosu dat

Pro nastavování parametrů modulu RFM12B bylo vytvořeno jednoduché menu, které se v PC zobrazuje a ovládá pomocí konzole. V menu lze nastavit rychlost přenosu dat a výstupní výkon vysílače. Dále lze nastavit příslušný transceiver do módu resender, který slouží pro měření RTT pomocí druhého transceiveru. Z konzole lze také odeslat příkaz pro změření RTT nebo prosté odeslání datového paketu, na základě čehož lze změřit BER. Poslední možností v menu je periodické odesílání paketu, to slouží pro testování spoje.

void menu(void)
{
	unsigned char vypis[]={"\n\nMENU\nSend test packet(t)\nSend test packet 200x (n)\nSet data rate (d) \nSet TX power (p)\nSet frequency deviation (f)\nSet as resender (r)\nRTT measure (a)\n\r"};
	uint16_t len;
	erase();
 
	UART_SendBlock(vypis,strlen(vypis),&len);
}
 
 
void dataRATEmenu(void)
{
	erase();
	uint16_t len;
	unsigned char vypis[256];
	sprintf(vypis,"\nSet data rate\n\n  5.4 kbps (1) \n 10.1 kbps (2) \n 20.3 kbps (3) \n 31.3 kbps (4) \n 49.3 kbps (5) \n 68.9 kbps (6) \n 86.2 kbps (7) \n114.9 kbps (8)\n");
    UART_SendBlock(vypis,strlen(vypis),&len);
}
 
void powerMenu(void)
{
	erase();
	uint16_t len;
	unsigned char vypis[256];
	sprintf(vypis,"\nSet data rate\n\n  5 dBm (1) \n 2.5 dBm (2) \n 0 dBm (3) \n -2.5 dBm (4) \n -5 dBm (5) \n -7.5 dBm (6) \n -10 dBm (7) \n-12.5 dbm (8)\n");
    UART_SendBlock(vypis,strlen(vypis),&len);
}
 
void deviationMenu(void)
{
	erase();
	uint16_t len;
	unsigned char vypis[256];
	sprintf(vypis,"\nSet frequency deviation\n\n  15 kHz (0) \n 30 kHz (1) \n 45 kHz (2) \n 60 kHz (3) \n 75 kHz (4) \n 90 kHz (5) \n105 kHz (6) \n120 kHz (7)\n135 kHz (8)\n150 kHz (9)\n165 kHz (A)\n180 kHz (B)\n195 kHz (C)\n210 kHz (D)\n225 kHz (E)\n240 kHz (F)\n");
    UART_SendBlock(vypis,strlen(vypis),&len);
}
 
void erase(void)
{
	  uint16_t len;
	  uint8_t i,j;
	  int8_t k,l;;
	  unsigned char block[128];
  	   i=(uint16_t)(DR);
  	   j=((DR-i)*10);
  	   k=((int8_t)(TXpower));
  	   l=abs(((TXpower-k)*10));
	  sprintf(block,"\e[2J\e[H ***Experimentalni radiovy spoj v pasmu 868 MHz***\n\nfc=868.3 MHz\ndelta f=%d\nData rate =%d.%1d [kbps]\nTX power =%d.%1d\n",fdev,i,j,k,l);
	  UART_SendBlock(block,strlen(block),&len);
}

Výše uvedené funkce vypíší různé úrovně menu pro nastavování modulů samotné nastavování pak zajišťují následující funkce, prostřednictvím odeslání příkazu:

void setDataRate(unsigned char c,uint8_t *flags)
{
 
	switch(c)
	{
		case '1': sendCMD((0xC600|63)); DR=5.4;  break;
		case '2': sendCMD((0xC600|33)); DR=10.1; break;
		case '3': sendCMD((0xC600|16)); DR=20.3; break;
		case '4': sendCMD((0xC600|10)); DR=31.3; break;
		case '5': sendCMD((0xC600|6));  DR=49.3; break;
		case '6': sendCMD((0xC600|4));  DR=68.9; break;
		case '7': sendCMD((0xC600|3));  DR=86.2; break;
		case '8': sendCMD((0xC600|2));  DR=114.9; break;
	}
	*flags=0;
	sendCMD(0x0000);
	UART_ClearTxBuf();
	menu();
	return;
}
 
void setPower(unsigned char c,uint8_t *flags)
{
	TXconf&=0xFFF0;
	switch(c)
	{
		case '1': TXconf|=0; TXpower=5;     break;
		case '2': TXconf|=1; TXpower=2.5;   break;
		case '3': TXconf|=2; TXpower=0;     break;
		case '4': TXconf|=3; TXpower=-2.5;  break;
		case '5': TXconf|=4; TXpower=-5;    break;
		case '6': TXconf|=5; TXpower=-7.5;  break;
		case '7': TXconf|=6; TXpower=-10;   break;
		case '8': TXconf|=7; TXpower=-12.5; break;
		default: return;
	}
	sendCMD(TXconf);
	*flags=0;
	sendCMD(0x0000);
	UART_ClearTxBuf();
	menu();
	return;
}
 
void setDeviation(unsigned char c,uint8_t *flags)
{
	TXconf&=0xFF0F;
	switch(c)
	{
	    case '0': TXconf|=0x00;  fdev=15;  break;
		case '1': TXconf|=0x10;  fdev=30;  break;
		case '2': TXconf|=0x20;  fdev=45;  break;
		case '3': TXconf|=0x30;  fdev=60;  break;
		case '4': TXconf|=0x40;  fdev=75;  break;
		case '5': TXconf|=0x50;  fdev=90;  break;
		case '6': TXconf|=0x60;  fdev=105; break;
		case '7': TXconf|=0x70;  fdev=120; break;
		case '8': TXconf|=0x80;  fdev=135; break;
		case '9': TXconf|=0x90;  fdev=150; break;
		case 'A': TXconf|=0xA0;  fdev=165; break;
		case 'B': TXconf|=0xB0;  fdev=180; break;
		case 'C': TXconf|=0xC0;  fdev=195; break;
		case 'D': TXconf|=0xD0;  fdev=210; break;
		case 'E': TXconf|=0xE0;  fdev=225; break;
		case 'F': TXconf|=0xF0;  fdev=240; break;
		default: return;
	}
	sendCMD(TXconf);
	*flags=0;
	sendCMD(0x0000);
	UART_ClearTxBuf();
	menu();
	return;

Poslední zmíněná funkce byla navržena na poslední chvíli a nebyla otestována na hardwaru, je však jednoduchá a neměl by s ní být problém.

Ovládání je řešeno pomocí dedikovaných kláves:

void UART_OnRxChar(void)
{
  /* Write your code here ... */
	char c;
 
	uint8_t i;
	UART_RecvChar(&c);
	UART_SendChar(c);
 
 
	//unsigned char TXdata[16]={"ABCDEFGHIJKLMNO\0"};
   switch(c)
   {
	case('n'):
		sendTEST(1);
		break;
	case('m'):
		menu();
	    break;
	case('r'):
		flags=resend;
		break;
	case('a'):
		RTTmeasure();
		break;
	case('d'):
		dataRATEmenu();
		flags=DRset;
		break;
	case('p'):
		powerMenu();
		flags=Pset;
		break;
/*	case('f'):
		deviationMenu;
		flags=DEVset;
		break;
*/	case('t'):
		sendTEST(0);
	    break;
	case('c'):
		flags=0;
		menu();
		break;
   }
 
   if(c>'0' && c<'9' &&(flags==DRset))                            setDataRate(c,&flags); menu();
   if(c>'0' && c<'9' &&(flags==Pset))                             setDataRate(c,&flags); menu();
  // if(((c>='0' && c<='9')||(c>='A' && c<='F')) &&(flags==DEVset)) setDeviation(c,&flags);menu();
}

Video

Na videu je zachycena funkčnost bezdrátového spoje na vzdálenost několika metrů, pro demonstraci chybovosti při krátké vzdálenosti jsou z modulů odstraněny antény.

h

Závěr

V projektu byla navržena a otestována knihovna pro komunikační moduly RFM12B. Psaní knihovny vyžadovalo nastudování katalogového listu modulů což zabralo značné množství času, a proto jsem nestihl implementovat všechny funkce, které jsem chtěl. Do knihovny by bylo vhodné přidat například funkci, pro vkládání znaků do datových paketů z klávesnice. Dalším nedostatkem, který by bylo vhodné odstranit je to, že nelze dosáhnout maximální rychlosti uváděné výrobcem, to je zřejmě způsobeno tím, že je použito softwarové SPI, které dosahuje rychlosti pouze 250 kbps. Na nižších komunikačních rychlostech (cca do 40 kbps) však spoj funguje bezchybně. Při testování bylo zjištěno, že chybovost je nulová i při vzdálenosti 30m bez připojených antén. Při vyšších vzdálenostech se již začnou vyskytovat chybné bity. S anténou je přenos při komunikační 20,3 kbps bezchybný i na vzdálenost 100m. RTT se při této komunikační rychlosti pohybuje okolo 15 ms při přenosu paketu délky 512 bitů. Celý projekt je dostupný zde.

2015/p2p-rfm12b.txt · Poslední úprava: 2016/01/17 23:56 autor: Petr Prachař