RFM22 PIC32MX library and code example
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*)&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.