Posts Tagged ‘programming’

RFM22 PIC32MX library and code example

October 12, 2010 6 comments

Here you go with the library :
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
// 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
 // config wait-states & cache

int main()
 AD1PCFG = 0xffff;
 // Turn on the interrupts

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);

_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 :

 if (!RFM22_nIRQ)

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)
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()
	// check POR condition
	if (rfm22.isr2.b.IPOR)
	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.rx_state = RFM22_RX_COMPLETE;
		if (rfm22.isr1.b.ICRC)
			// CRC-error occured - restart receiving
		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

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.

RFM22 Programming tutorial – PART #2

October 12, 2010 64 comments

RFM22 internal registers
When you look into datasheets, you may feel a little bit lost 🙂
Yeah, there are near 127 registers in this little module, but many of them are set-and-forget ones.
Moreover, there are MSExcel spreadsheet calculators, providing you with set of calculated values for
your RFM22 module. You only have to enter desired transmittion parameters.
Look at : Calculator for Vx silicon revision
Calculator for A0 silicon revision
Calculator for B1 silicon revision

To determine your RFM22 chip’s silicon revision, read register at 0x01.

More important registers are (these you should become familiar with, these needs frequently changes during operation):

  • 0x02 – Device status register
  • 0x03 – Interrupt flags register #1
  • 0x04 – Interrupt flags register #2
  • 0x05 – Interrupt enable register #1
  • 0x06 – Interrupt enable register #2
  • 0x07 – Mode of operation register #1
  • 0x08 – Mode of operation register #2
  • 0x72 – Frequency deviation / AFC limiter
  • 0x02 – Device status register

  • ffovfl – FIFO overflow, you’ll see this, when you try to upload more than 64 bytes into TX FIFO, or module receives more than 64 bytes or you didn’t downloaded RX FIFO in right time.
  • ffunfl – FIFO underflow, set, when module is trying to send more data than is in its TX FIFO (eg. you’ve set longer packet than you uploaded into TX FIFO)
  • rxffem – RX FIFO empty – module should awake with this bit set, self-explanatory, I think.
  • headerr – Header error – indicates corrupted header of recently received packet
  • freqerr – Frequency Set error – you’ve tried to tune RFM outside its operating range (240-930 MHz).
  • lockdet – Synthesizer Lock detected – RF synthesizer is working ok and locked to specified frequency… rather informative/diagnostic than useful in everyday work 🙂
  • cps[1:0] – Chip Power State – indicates whether module is IDLE (00), RX (01) or TX (10)
  • Notes: Normally, there is a little need to read this register in FIFO Packet Handler mode. Frankly, I don’t pay attention to this register at all, using FIFO+PH.

    0x03 – Interrupt flags register #1
    0x04 – Interrupt flags register #2
    These registers are quite good described in the datasheet, all flags are more or less self-explanatory.
    Reading these registers clears indicated interrupt request and releases nIRQ line of RFM22.
    You should read them after nIRQ goes LOW.
    if (!nIRQ)

    0x05 – Interrupt enable register #1
    0x06 – Interrupt enable register #2
    When specific bit in these registers is SET, then asserting particular flag in 0x03/0x04 also drives nIRQ LOW.
    You should set ONLY these interrupts enabled, you’re interested in during current mode of operation, e.g. you should enable PKTSENT interrupt before entering TX mode – when nIRQ goes LOW you would know that packet has been sent, and you don’t need to check all interrupt flags. That approach would simplify your code to maximum extent.

    0x07 – Mode of operation register #1

    This is the most important register of the module.

  • swres – soft reset request – writing 1 to this bit would reset RFM22 to default state. You should do this after powerup of your system.
  • txon – TX mode ON – manual start of the transmittion. You should set this bit, after filling TX FIFO with data you want to send. Module will start transmittion and asserts ipktsent flag after sending whole payload. After transmittion module will fall into IDLE mode.
  • rxon – RX mode ON – manual start of reception. Setting this bit to 1 simply starts reception. RFM22 would wait for packet, receives it, checks its integrity and checksum and asserts corresponding flags (see 0x03,0x04 registers) during and after reception :
    ipreaval – valid preamble detected – asserted during reception of valid preamble. THIS IS THE MOMENT when you should save RSSI variable (content of 0x26 register) – that would indicates signal strength of transmittion.
    iswdet – synch word detected & match – module would assert this bit, when valid synch words are detected during reception of current packet, rather informative…
    icrerror – CRC error – received packet is corrupted – CRC calculated & received don’t match. You should respond to this IRQ with restarting RX mode (reset RX FIFO & set RX mode ON).
    ipkvalid – Valid packet received – this is the most wanted flag during reception – indicates that RX FIFO contains healthy data, just as you sent them on the TX side.
  • pllon – PLL ON during IDLE – leaves PLL working during IDLE mode – results in faster transitions between IDLE->RX and IDLE->TX modes. (PLL is tuning up for about 1 ms). With xton set would indicate TUNE-IDLE mode (higher current consumption ~9.5mA)
  • xton – XTAL ON – setting only this bit in this register would indicate READY-IDLE mode (low current consumption <1mA)
  • 0x08 – Mode of operation register #2

    This register controls mainly RX/TX FIFO buffers. Other settings are less important.

  • rxmpk – RX multiple packet mode – Once rxon set, the reception would continue, and RX FIFO would be filled subsequently after each valid packet reception. You can see ffovfl flag here. If rxmpk is cleared, RFM22 would fall into IDLE mode after reception of packet (either valid or invalid !!!)
  • autotx – Automatic TX mode – when set, module would enter transmittion mode each time TX FIFO is filled up to almost-full-condition. With this bit cleared, RFM22 would wait for txon bit set in 0x07 reg.
  • ffclrrx – RX FIFO Clear – to reset RX FIFO, you should write 1 and subsequent 0 to this bit. See code : send(0x08,0x02); send(0x08,0x00);
  • ffclrtx – TX FIFO Clear – accordingly, writing 1 and then 0 to this location would reset TX FIFO buffer and pointer.
  • 0x72 – Frequency deviation / AFC limiter
    Why this magic register is important? It depends on silicon revision of your RFM22 chip. When it is V2 (register 0x01 == 0x02) it has double function. In RX MODE it acts as Automatic Frequency Control Limiter – you should set this reg BEFORE entering RX MODE, according to calculated value. In TX MODE this register acts as Frequency Deviation register. You should write desired frequency deviation value BEFORE entering TX MODE.

    RFM22/23 Programming tutorial – PART #1

    October 11, 2010 1 comment

    Because there is a serious lack of information over the internet about programming RFM22 modules, here you’ve a quick tutorial.

    Here you go with the connections :

    Fig.1. Schematic of RFM22 connections

    Description of Fig.1. :

  • SDN pin of RFM22 is tied LOW to GND – forget about very-low-power shutdown mode, if you need it – modify the schematic (connect it to pin of your MCU)
  • GPIO_0 is programmed to drive RX_ANT input – you can save 1 pin of your MCU here
  • GPIO_1 is programmed to drive TX_ANT input – another 1 pin saved
  • GPIO_2 is CLK_OUT at power on condition of RFM22, you can use it as clock source to your MCU
  • There are some bypass caps – it is nice to solder them, anyway 🙂
  • PAY ATTENTION to SPI wiring… (SDI/SDO lines must be crossed 🙂 )
  • Power the whole thing from 3.3V rail
  • I’m using PIC32MX MCU, but any should work. If your MCU is 5V powered, you must provide some level shifters on SPI lines, but nowadays I believe that it is easier to do the whole thing on 3V3 MCU & stuff than wasting mcu pins & time & pcb space with 74lvc4245…

    Philosophy of operation
    RFM22 may be used in many different modes. The one I would describe is FIFO mode with Packet Handling. It is the easiest mode to implement, RFM22 is doing the most of the job, MCU has hands free most of the time.
    FIFO mode with Packet Handling means, that during transmition, MCU is uploading data into radio’s FIFO, RFM is encapsulating user’s data into packet structure and sends it over RF using correct timing, defined in setup step.
    During reception, RFM is detecting preamble, synch words, packet headers (optional), receiving subsequent data bytes into Rx FIFO and doing CRC check over the packet contents (data only or whole packet). If CRC is OK, then RFM is asserting an interrupt request.

    Packet structure of RFM22 :

    Preamble consists of 1-512 bytes (user configurable count) of 0xAA (10101010b)
    Synch word usually equates to 0x2DD4, but may be configured to desired values
    Header consists of 0-4 bytes (user configurable count & content), consider it as a IP address of packet – receiving side may compare it with predefined variables to determine destination of received packet.
    PktLen is 1 byte if variable packet lengths are in option, it may be ommitted when RFMs are configured to deal with fixed length packets.
    CRC is checksum calculated over data or whole packet structure, usign one of four predefined polynominals (all in hardware of RFM22).

    SPI interface
    SPI interface is using 8 bit transfers. First transfer of each command contains address of RFM22 register and operation bit (Read/Write). Subsequent transfers contains data to be written into register(s), or readings from register(s).
    Each transfer may consist of single write/read command, or may be a burst write/read.

    1. Single write command :
    nSEL=0 | [1][REG_ADDR 0..0x7F] | [DATA_BYTE]sdo | nSEL=1

    2. Single read command :
    nSEL=0 | [0][REG_ADDR 0..0x7F] | [REG_VAL]sdi | nSEL=1

    3. Burst write command :
    nSEL=0 | [1][REG_ADDR 0..0x7F] | [DATA_BYTE0]sdo | [DATA_BYTE1]sdo | [DATA_BYTEn]sdo |nSEL=1
    NOTE : Consecutive bytes are written to addresses starting at REG_ADDR, incrementing after each byte, unless REG_ADDR is 0x7F.
    If REG_ADDR is 0x7F (FIFO gateway), incrementing is disabled, and whole transfer is going through 0x7F register – it allows to burst read/write FIFO in single block.

    4. Burst read command :
    nSEL=0 | [0][REG_ADDR 0..0x7F] | [REG_VAL0]sdi | [REG_VAL1]sdi | [REG_VALn]sdi |nSEL=1
    The same notes as for burst write applies to burst read command.

    Example of setting READY mode of operation :
    nSEL=0 | 0x87 | 0x01 | nSEL=1
    It means, that write (MSB bit (0x80) is set in address) is performed to 0x07 register (mode of operation), with value 0x01 (IDLE, READY).

    RFM22 internal registers
    When you look into datasheets, you may feel a little bit lost 🙂
    Yeah, there are near 127 registers in this little module, but many of them are set-and-forget ones.
    Moreover, there are MSExcel spreadsheet calculators, providing you with set of calculated values for
    your RFM22 module. You only have to enter desired transmittion parameters.

    More important registers are :

  • 0x02 – Device status register
  • 0x03 – Interrupt flags register 1
  • 0x04 – Interrupt flags register 2
  • 0x05 – Interrupt enable register 1
  • 0x06 – Interrupt enable register 2
  • 0x07 – Mode of operation register
  • 0x08 – FIFO control register
  • more coming soon… need some sleep 🙂


    RFM22 interface library v.0.0.1 pre-alpha version 🙂
    Tested with PIC32MXs, 20kbauds configured, 868MHz operation, interrupt operation (INT2) currently switched off.

    RFM12 programming

    October 7, 2010 12 comments

    I know that I wouldn’t rediscover a wheel here, but there are many problems with these modules. Generally, problems arises from lacks in documentations.

    Here is short tutorial, describing basics of SPI transmittion & RFM12 module behavior.

      SPI transmition

    SPI comes from Serial Peripheral Interface. In hardware appears as 4 main lines :
    SDO – data coming out from host (microcontroller) to module
    SDI – data coming in from module into host
    SCK – data clock, timing the transfer
    and more or less optional :
    SS – chip select, indicates that transmittion is going to occur on the lines

    The definition here is host-centric, but any will do when you remember to connect host’s SDO to module’s SDI and host’s SDI to module’s SDO.
    In interface IDLE state – when you don’t want to transmit anything – SS should be LOGIC HIGH. When you want to start the transmittion pull SS down to logic LOW, and keep it low during the transmittion.

    There are two different kind of SPI operation – Master and Slave mode, assuming host-centric (the clock generation is provided by host) interface. Connecting RFM12 we will consider only Master mode – that is, the microcontroller is providing SCK signal and is responsible for helding SS line at appropriate level, short – master is controlling the transmittion.

    Writing is started by pulling SS low. SCK should be low when inactive. Rising edge on SCK is defining moment when module is “reading” its SDI line (the host’s SDO line). Simultaneously, module is sending data through its SDO (host’s SDI line) !!! This is the most important thing, considering SPI interface.
    Of course, during the host writing data, module is sending previously requested data, or default register defined in hardware as a feature (eg. RFM12 is sending its STATUS register).

    Example 1. Writing data to RFM12 module :
    should be executed as follows :
    1) pull down SS to select RFM12 module
    2) pump out bits of data (x8209) through hosts’ SDO line, clocking the SCK simultaneously (high indicates data ready at SDO)
    3) pull up SS to deselect RFM12 module

    In the example above, we’re discarding the information RFM12 is sending back during SCK clocking.

    Example 2. Reading STATUS register of RFM12 :

    status = WriteCMD(0x0000);

    this code should be performed as follows :
    1) pull down SS
    2) pull down host’s SDO (we’re sendind all zeroes)
    3) clock 16 times the SCK, listening host’s SDI before SCK is going high. This is feature of RFM12 module – it is sending its STATUS register MSB bit (FFIT) before first SCK pulse!
    4) pull up SS to indicate end of transmittion

    Accumulating SDI bits in a variable, we will obtain RFM12 status register value after 16 clocks on SCK line.

    int cnt = 16;
    int result = 0;
    while (cnt--)
    result = result | SDI;
    result = result << 1;
    SCK = 1;
    SCK = 0;

    Example 3. Reading from FIFO register of RFM12

     unsigned char data = WriteCMD(0xB000); 

    That code should be executed in this way :
    1) SS low
    2) clocking out the command 0xB000 (16 bits), simultaneously accumulating SDI into result variable
    RFM12 will accept the 0xB0 command during the first 8 clocks (Read FIFO command) and will spit out the FIFO register during the last 8 clocks, so the FIFO contents will be held into lower byte of result variable.
    3) variable ‘data’ will be filled with only 8 lower bits of ‘result’.
    4) SS high

    RFM12 SPI Interface
    There is nothing strange here, except 16-bit mode of interface. It means that each transfer is 16 bit wide, consists of 16 clock changes on SCK line.

    RFM12 vs RFM12B
    RFM12 is 5V tolerant device – it can be powered with +5V and accepts +5V as HIGH level on SPI lines.
    RFM12B is 3.3V device ONLY !!! That means, you should provide +3.3V as power to RFM12B module, and you should translate (if necessary) levels at SPI interface to 3V3 level on the side of RFM12B.

    RFM12 Interface
    RFM12 Interface consists of 4 SPI lines (SDI,SDO,SCK,SS) and optional connections (eg. nIRQ, nRES, etc).
    OPTIMAL way is to connect SPI & nIRQ line.
    nIRQ is an Interrupt ReQuest line, informing host (microcontroller (MCU)), about an event occured in the RFM12 module.
    It is strongly recomended to connect this line to your MCU, especially to some kind of External Interrupt pin (INT0, INT1, and so on…).

    nRES is a hardware RESET signal. It can be connected to provide HW reset ability. To reset RFM12 module, you should held nRES LOW for about 5ms, and then release it HIGH and waits for about 50ms.

    nIRQ is logic HIGH, when there is no event to respond (module is calm and quiet). RFM12 is pulling nIRQ LOW, when it wants to say something, eg. the data is received and ready at FIFO register to be read, the module is ready to transmit next byte…

    Power-Up condition
    After applying power to your circuit, RFM12 will assert nIRQ line LOW because of POR (Power-On-Reset) interrupt. You should read status register (WriteCMD(0x0000)) of RFM12 to CLEAR this condition. After reading STATUS, RFM12 will release nIRQ HIGH -> idle state.
    In readed status, you will find POR bit set (0x4000). That indicates your SPI connections is running smooth, and RFM12 is working correctly.

    RFM12 Initial configuration
    According to many sources in the Internet, esp. RFM12 Datasheet, RF12 Datasheet, RF12 Programming Guide, the first thing you’d do with the RFM12 is sending initial configuration setup. It would sets the internal RFM12′ registers to desired values, eg. frequency of operation, speed of transmittion, modulation deviation, etc…

    Consider this code :

        WriteCMD(0x80D8); //EL,EF,433band,12.0pF
        WriteCMD(0x8201); // Sleep
        WriteCMD(0xC611); // Speed of transmittion = 19200 bauds
        WriteCMD(0x9460); // frequency

    This commands should be sent to RFM12 after applying power to the device. For detailed information about each command I’ll send you to datasheet. After all, the first 4 commands are the most important.

    After this step, your RFM12 is at SLEEP state (it is not receving nor transmitting anything), and is awaiting your further commands.

    RFM12 – Transmitting data
    RFM12 data transmition is all about FIFO and STATUS register.
    FIFO holds data to be sent when your RFM12 is in TX mode, and holds data received in RX mode.

    BEFORE entering TX mode, RFM12 should be at IDLE (0x8209) or SLEEP (0x8201) mode. nIRQ line should be high – there can not be any unhandled IRQ left.

    RFM12 is designed to provide interrupting transmittion, it means, TX mode should be entered only when you want to transmit data, and over immediately after the transmittion completes.
    RX mode can be switched ON all the time.

    RFM12 is designed to send packet of data, consisting of :
    1) PREAMBLE (one or more 0xAA or 0x55 bytes)
    2) SYNC WORD ( default 0x2DD4 )
    3) user packet data…

    Preamble is required to synchronize the other RFM12 receiver circuit clock with the transmitting signal, the longer the better, but usually 2 bytes of 0xAA (alternating 1010101….) is sufficient.
    Sync word is required to indicate start of transmittion. Data sent AFTER SYNC WORD would be pumped into FIFO register.

    Transmittion should be performed with these steps :
    1) Assume the RFM12 is IDLE (you should provide in your code simple state machine, describing current mode of RFM12)
    2) Fill the FIFO with 2 bytes of PREAMBLE ( 0xB8AA, 0xB8AA )
    3) Enter TX mode ( 0x8239 )
    4) Watch nIRQ line while it is HIGH (when it falls low, that would indicate FIFO is ready to accept next byte)
    5) Send next byte of transmittion ( sync word 0xB82D, 0xB8D4, then your own data … 0xB800 | data )
    6) go to 4) while there is something to send
    7) Leave TX mode by entering IDLE (0x8209) or RX (0x82D9) ….

    RFM12 – Reception of data
    Reception starts when you command the RFM12 to enter RX mode (0x82D9). After or before that command you should clear FIFO ( subsequent 0xCA83, 0xCA81).

    Receiving should be performed like this :
    1) Enter RX mode (0x82D9)
    2) Waits for nIRQ LOW -> it means that RFM12 received preamble , syncword and 1st byte of transmitted user data. FIFO would now contain 1st byte of our data.
    3) Read FIFO (0xB000) and store lower byte of result as in your RX buffer.
    The Read FIFO command would restore HIGH on nIRQ.
    4) Go to 2) while you want to receive more data
    5) Clear FIFO

    The packet transmitted over the aether should looks like :
    0xAA, 0xAA, 0x2D, 0xD4, data_len, chksum, data[0], data[1] …. data[n]
    so : preamble, syncword, data length to be received, checksum of data, subsequent data bytes.
    That would ease the reception, and allow to control correctness of received data.

    I hope that this short brief would help you in your fun with RFM12.

    Categories: microcontrollers Tags: , ,