Home > microcontrollers > RFM12 programming

RFM12 programming


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 :
WriteCMD(0x8209);
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;
Delay();
SCK = 0;
Delay();
}

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
    WriteCMD(0xC2AC); 
    WriteCMD(0xCED4); 
    WriteCMD(0xC483);
    WriteCMD(0x9880);
    WriteCMD(0xCC77); 
    WriteCMD(0xE000); 
    WriteCMD(0xC800); 
    WriteCMD(0xC000);

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.

Advertisements
Categories: microcontrollers Tags: , ,
  1. David
    December 6, 2010 at 4:15 pm

    Hi; Thank you a lot, for describing RFM12, in details. Your notes helped me a lot. I had many problem with this module. Thank you again.

  2. grabbo
    March 12, 2011 at 5:37 pm

    Thx for this tutorial. It brought some light. What could be possibly wrong if i receive 0xA100 after reading status command and IRQ never goes up to logic 1.

    • March 12, 2011 at 6:49 pm

      What is your current module operation mode? What have you set in 0x82xx command ?
      Is your module transmitting or receiving or standby ?

  3. grabbo
    March 12, 2011 at 7:23 pm

    I’m transmitter at the moment. I read your tutorial and you mention that after reading status command ,IRQ should be high and POR response 0x4000. I always get 0xA100 and IRQ is low when im debugging and put breakpoint after reading status.

    The clock shouldn’t be a problem here if i use bit banging SPI? I attached 8MHz crystal for MCLK/SCLK.

    My main is following:

    rf12_port_init();
    rf12_init();
    delay(1000);
    data=rf12_wrt_cmd(0x0000);

    Functions:

    void rf12_init() {

    rf12_wrt_cmd(0x80E7); //868Mhz, 12pf, Enable TX reg, Enable RX FIFO buff
    rf12_wrt_cmd(0x8239);//!er,!ebb,ET,ES,EX,!eb,!ew,DC
    rf12_wrt_cmd(0xA640);//860.32
    rf12_wrt_cmd(0xC647);//4.8kbps
    rf12_wrt_cmd(0x94A0);//VDI,FAST,134kHz,0dBm,-103dBm
    rf12_wrt_cmd(0xC2AC);//AL,!ml,DIG,DQD4
    rf12_wrt_cmd(0xCA81);//FIFO8,SYNC,!ff,DR
    rf12_wrt_cmd(0xCED4);//SYNC=2DD4;
    rf12_wrt_cmd(0xC483);//@PWR,NO RSTRIC,!st,!fi,OE,EN
    rf12_wrt_cmd(0x9850);//!mp,9810=30kHz,MAX OUT
    rf12_wrt_cmd(0xCC17);//OB1,OB0,!lpx,!ddy,DDIT,BW0
    rf12_wrt_cmd(0xE000);//NOT USE
    rf12_wrt_cmd(0xC800);//NOT USE
    rf12_wrt_cmd(0xC0E0);//

    }

    uint16_t rf12_wrt_cmd(uint16_t cmd) {
    uint8_t i;
    uint16_t res=0;

    _low(SCK);
    _low(SEL);
    for (i=0;i<16;i++) {
    if (cmd & 0x8000)
    _hi(SDI);
    else
    _low(SDI);
    _hi(SCK);
    res <<= 1;
    if (_is_hi(SDO))
    {
    res |= 1;
    }
    _low(SCK);
    asm("nop"); //delay .2ms
    asm("nop");
    cmd <<= 1;
    }//for
    _low(SCK);
    _hi(SEL);
    return (res);
    }

    void rf12_port_init(void) {

    _output(SEL);
    _output(SDI);
    _output(SCK);
    _input(SDO);
    _input(IRQ);
    _hi(SEL);
    _hi(SDI);
    _low(SCK);

    }

    nRES and DATA pin connections on RFM12b are connected through pull-up resistor to Vcc (3.3V). I dont have a receiver board at the moment, can't try the transmitting out. That's why i wonder if it's really important to get 0x4000 and IRQ high.
    I've checked lot of almost identical codes with RFM12 and nobody checks that status command if they actually have response 0x4000 and IRQ appears only in sending/receiving functions to be waited for logical 0 and then transmit/receive.

    Thx for you response! I appriciate it

  4. hayankata
    May 23, 2011 at 6:52 am

    going mad working with this module.
    i want to make a remote control around 10 channels for my robots.
    can you please give me an idea and necessary code examples.
    I am using Arduino(ATmega168).

  5. Daniel
    September 19, 2011 at 4:58 am

    Hi,

    Thank you a lot for the light about the RFM12. I have a question:
    I’m using a PIC18F2550 and the builtin SPI module.
    I’m trying to make one RFM12 to work and i have started from the very beginning start.
    I power on the module
    I read the status and clear the nIRQ as expected and read 0x4000
    Send command 0xe196
    send command 0x8202
    and after a few miliSeconds i can see nIRQ on low but i can’t restore the nIRQ state to high.
    After that, i can’t restore the nIRQ to high in any form.
    I think that the problem is that in any form, the SPI transaction is blocking de RFM12. Do you have any experience with this situation?

    Thanks a lot!
    Sorry about my english =-S

    • Anonymous
      September 19, 2011 at 9:11 pm

      Sometimes the PIC’s SPI module work in a strange way (different that you may think of). Have you checked the SPI is working properly ?
      Have you tried described initialization command sequence ? what is the result ? nIRQ high ?

  6. Bert Botha
    December 12, 2011 at 12:08 pm

    Thank you very much for your helpfull information. Will you please try and help me get these modules going, I have been at it for about 6 months already!
    nIRQ goes high after reading the status register and sending of parameters are no problem.
    After setting et, nIRQ goes low as can be expected, but RGIT is not on. Reading the status register only gives 0x00. From that point, nIRQ never goes high again.
    #define nRES LATC,2
    #define nSEL LATC,6
    #define nIRQ PORTB,0
    #define SDo LATC,5
    #define SDi PORTC,4
    #define sck ;********************************************************************************
    Send_SPI Macro value ;
    movlw value ;
    call hard ; *
    endm ;
    hard ;
    movwf output ;Moved data from W to OUTPUT reg
    clrf input ;
    bcf SDo ;Ensure that SERIAL pin starts @ 0 *
    movlw 0x08 ;Byte control
    movwf myreg ;escape value
    test btfsc SDi ;Test data bit 7 *
    call sets ;
    btfsc output,7 ;
    bsf SDo ;SET SERIAL pin to 1
    call clock ;Clock the data to shift register *
    bcf SDo ;Reset SERIAL pin for next test
    rlncf output,F
    decfsz myreg,F ;
    goto spi_in
    goto ends
    spi_in rlcf input,f
    goto test
    sets bsf input,0
    return ;
    ends movf input,W
    movwf PORTD ;
    return ;
    ;
    clock ;
    bcf sck ;
    nop ;
    bsf sck ;Clock value HIGH
    nop ;
    bcf sck ;Falling edge *Clock
    return
    ;********************************************************************************
    ; Initialise *
    ;********************************************************************************
    init_rfm ;
    bcf nRES ;Reset device
    call ms10mhz4 ;
    bsf nRES ;Take out of reset condition
    call ms10mhz4 ;
    ;
    ;************************ ; *
    bcf nSEL ;Select device
    call ms10mhz4 ;
    Send_SPI 0x00 ;Read STATUS register command [0x0000] *
    Send_SPI 0x00 ;This sets nIRQ high, RFM12 is ready *
    call ms10mhz4 ;

    AND ALL OTHER PARAMETERS SENT AS ABOVE

    Start ;
    ;********************************************************************************
    Send_SPI 0x80 ;
    Send_SPI 0xd8 ;Switch el on
    call ms10mhz4 ;
    ;********************************************************************************
    Send_SPI 0x82 ;Switch et on
    Send_SPI 0x39 ;Automatically sends first preamble *
    call ms10mhz4 ;and waits for next data write

    NOW nIRQ IS LOW AND NEVER GOES HIGH AGAIN

    Please help me with this!!!!

  7. December 18, 2011 at 8:16 am

    RFM12 is very cheap rf module having very good perfomance. The only trouble is its programming, very hard.
    And do toy have arduino library for these modules.

  8. April 10, 2013 at 4:08 am

    I discovered your “RFM12 programming | Gobotronics&” page and noticed you could have a lot more traffic. I have found that the key to running a website is making sure the visitors you are getting are interested in your subject matter. There is a company that you can get traffic from and they let you try it for free. I managed to get over 300 targetted visitors to day to my website. Check it out here: http://voxseo.com/traffic/

  9. gargoor
    April 11, 2013 at 8:03 am

    thanks alot for this post. any idea if i can use the codes you provided on arduino ? . one more thing , you think i can transfer voice (like speaking in mic) with this transceiver. am thinking of building walkie talkie

  10. Loki
    March 19, 2017 at 5:15 pm

    Very nice, thanks to your blog I understood the correct usage of the module!

  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: