Archive

Posts Tagged ‘code’

RFM22 PIC32MX library and code example

October 12, 2010 6 comments

Here you go with the library :
RFM22.ZIP
Consider it as a very young release, highly uncommented and not optimized.

It works with PIC32MX MCUs. SPI is currently emulated in software, because for unknown reason, the HW code doesn’t work.

To use library, you should #include "rfm22.h" in your code, and add rfm22.c to your project.

You should also look at the beginning of rfm22.h.

#ifdef __PIC32MX
 #define RFM22_nIRQ     		_RD9
 #define RFM22_nRES			_LATB4
 #define RFM22_nSEL			_LATG9
 #define RFM22_SCK			_LATG6
 #define RFM22_SDO			_LATG8
 #define RFM22_SDI			_RG7
 #define RFM22_nIRQ_TRIS     	_TRISD9
 #define RFM22_nRES_TRIS		_TRISB4
 #define RFM22_nSEL_TRIS 	        _TRISG9
 #define RFM22_SCK_TRIS 		_TRISG6
 #define RFM22_SDO_TRIS 		_TRISG8
 #define RFM22_SDI_TRIS 		_TRISG7
 // external interrupt with nIRQ line hooked
 #define RFM22_int_ie		IEC0bits.INT2IE
 #define RFM22_int_flag		IFS0bits.INT2IF
#endif
// define below to use SW emulated SPI
#define USE_SW_SPI

You should tune it to your hardware routing.

It is nice to wait for some time after power-up of your MCU, so after stabilizing your device you should initialize RFM22 :

void init_system()
{
 // disable JTAG port
 DDPCONbits.JTAGEN = 0;
 // config wait-states & cache
 SYSTEMConfig(GetSystemClock(), SYS_CFG_WAIT_STATES | SYS_CFG_PCACHE);
 }

int main()
{
 DelayUs(100000);
 init_system();
 AD1PCFG = 0xffff;
 init_radio();
 // Turn on the interrupts
 INTEnableSystemMultiVectoredInt();
 ...

where init_radio() is defined with :

void init_radio()
{
// FIRST! enable INT2
INTCONbits.INT2EP = 0;    // INT2 edge -> int request at FALLING
IPC2bits.INT2IP = 2;
IPC2bits.INT2IS = 3;
IFS0bits.INT2IF = 0;    // clear flag
IEC0bits.INT2IE = 0;    // enable int | CURRENTLY DISABLED in LIBRARY !!!
// THEN! initialize rfm22 !!!
_TRISB5 = 1;
_TRISD9 = 1;
rfm22_Init(868, GetPeripheralClock(), 5000000);
rfm22_go_rx();
}

_TRISB5 and _TRISD9 routes nIRQ signal in my project, you may ommit this lines.
GetPeripheralClock() is a macro defining PB_CLK of your PIC32 in [Hz]. 868 is a literal indicating working frequency of RFM22 module.
rfm22_Init(…) initializes SPI, performs SW_RESET of RFM22 and sends basic configuration of RFM22 registers.
rfm22_go_rx() switches RFM22 module into RX mode.

In your main loop you should poll nIRQ line status :

while(1)
{
 if (!RFM22_nIRQ)
 rfm22_Handle_IRQ();
 ...

There is nothing wrong with INT2 external interrupt attempt, but be aware of proper timings and interrupt service routine execution length. If your main loop is executed fast enough, there is no need to use an external interrupt handler, and your code would be level-related-IRQ rather than egde-related-IRQ using INT2.

For example, INT2 handler should looks like :

void __ISR(_EXTERNAL_2_VECTOR, ipl2) INT2Handler(void)
{
rfm22_Handle_IRQ();
IFS0bits.INT2IF = 0;
}

Remember to set proper ipl in this ISR function invocation, according to defined interrupt priority level for INT2.

Currently, rfm22_Handle_IRQ() is defined with following code :

void rfm22_Handle_IRQ()
{
	rfm22_ReadStatus();
	
	// check POR condition
	if (rfm22.isr2.b.IPOR)
	{
		rfm22_go_ready();
	}
	
	if (rfm22.state == RFM22_TX)
	{
		if (rfm22.isr1.b.IPKS)
		{
			// packet sent, RF module went back to READY
			rfm22.tx_state = RFM22_TX_COMPLETE;			
		}	
	}
	
	if (rfm22.state == RFM22_RX)
	{
		if (rfm22.isr1.b.IPKV)
		{
			// received valid packet
			// get the RX FIFO
			int i;
			
			int len = rfm22_Read(0x4B);	// get the received packet length
			
			rfm22_StartBurst(0x7f,0);	// read FIFO in burst read mode
			for (i=0;i<len;i++)
			{
				rfm_rx_buffer[i] = rfm22_BurstRead();
			}	
			rfm22_EndBurst();		
			rfm22.rx_state = RFM22_RX_COMPLETE;
		}			
		
		if (rfm22.isr1.b.ICRC)
		{
			// CRC-error occured - restart receiving
			rfm22_ResetRxFifo();
			rfm22_go_rx();	
		}	
		
		if (rfm22.isr2.b.IPVD)
		{
			rfm22_last_rssi = rfm22_Read(0x26);	// get RSSI
		}	
	}			
}	

I think it is understandable with a little effort.
Code asserts specific state machines according to RFM22 interrupts, e.g. sets RFM22_TX_COMPLETE state of library TX state machine, when packet sent interrupt flag was set.
User may poll for this state in main loop.
RFM22_RX_COMPLETE indicates that rfm_rx_buffer[] contains downloaded data from recently received packet. User should poll for this flag, and perform specific action, e.g. :

while (1) {
...
if (rfm22.rx_state == RFM22_RX_COMPLETE)
{
// process incoming data
rfm22.rx_state = RFM22_RX_IDLE;     // indicate, that packet has been processed
ackbuf[0] = 0xAC;
ackbuf[1] = rfm22_last_rssi;    // RSSI
rfm22_tx_buf_block((uchar*)&amp;ackbuf[0],2);
rfm22_go_rx();
}

UPDATE 07.02.2011: Newer version of RFM22 lib is just uploaded and available under link at the top of this post. Same restrictions about using it : IT IS A DRAFT. IT WORKS… SOMETIMES… 🙂 There are many holes in the code and lacks of documentation, but I really don’t have time to clean up this mess. Maybe someday.

Advertisements