RF Transceiver using the MRF49XA

Transceiver breakout boards

I’m just finishing up my last class ever!!!  (for credit, anyway.)  It was a really fun, mostly because I decided to have fun with my last class, and make it a 4/5 (undergrad/graduate).  This meant that it was much easier and less theory-heavy than those that I’m used to.  Anyway, as a grad student, I was expected to do something extra, and I decided to make a RF transceiver module.  I looked around for a little while, and I settled on the Microchip MRF49XA.  In general, it’s a nice chip.  It has about the same capability as the Micrel MICRF6xx modules that I’ve used in the past.  The Micrel modules cost $20/ea. and the Microchip IC is around $3.  I was able to make the whole breakout board for the MRF49XA for less than the Micrel module alone.  One of these days, I should dig out my notes from the Micrel project and post them, but I digress.  I noticed a distinct lack of programming information using the MRF49XA, so I’m posting not only my schematic and PCB, but the software library I wrote for the Atmel AVRmega.

Breakout board schematic

The schematic I created for the breakout board is, in large part, copied from the reference schematic from the datasheet.  There are a few parts that make up the complete schematic, including power regulation, microcontroller interface, RF balun, and the IC.  Everything but the balun is really easy to understand, the power input can be +5 volts or more (probably up to about 14 volts) while using the LM317, or if the LM317 is omitted, +3.3 volts.  The MRF49XA is really a 3.3 volt part, and expects 3.3 volt I/0.  Originally, and on the PCB I had made, I had 2 ground pins.  Since then I realized it would be useful to output regulated 3.3 volt output, so I changed one of the grounds for that.  The only other major portion of the circuit is the balun.  This is used to transform the balanced RF input/output from the IC to the unbalanced antenna connector.  This circuit also provides power to the RF power amplifier inside the IC.

Breakout board top

Breakout board top

The PCB designed from the schematic is also fairly straight-forward.  A few things are worth noting, however.  I’ve added some silkscreen between 2 of the pins on the LM317.  These are to indicate where you could jumper if the breakout board is supplied with regulated power.  I used a 0805 0-ohm resistor (see the image below).  The voltage output of the LM317 is selected with R1 and R2.  I decided not to include the “stop” layer on this image so as to not clutter it, but near the word “Fence” there is a strip without solder mask over the vias.  If you wanted to isolate the RF from the outside, you could build a fence out of copper foil (or something).  The Antenna connector is a end-launch SMA, though it could also be raw coax if you want to save some money.

Close up

The only pins necessary for complete functioning of the device are the standard SPI set (MOSI, MISO, SCK, !CS), and IRO (interrupt request out).  The IRO pin is not strictly necessary, but HIGHLY recommended.  The MRF module uses the IRO pin to notify the microcontroller of a few time-sensitive events, such as FIFO full/empty conditions.

MCU interface while transmitting (click to enlarge)

The diagram included above (from the MRF49XA datasheet) provides a useful overview for the transmitting process.  The take-away message is that you first send the “Transmit data enable” (TXDEN) command, which loads 0xAAAA into the transmit FIFO.  You can either leave this in there, as a preamble, or replace it with data of your choice.  Then, you send the “Transmit carrier enable” (TXCEN) command.  At this point, the PLL starts, and the PA turns on, then transmission starts.  When the first full byte leaves the FIFO the IRO asserts.  Hopefully this forces the MCU to jump to your interrupt service routine.  Once there, if the CS pin is held low, the MRF will bring SDO (MISO) high.  This is a clear signal that the FIFO needs attention.  You can continue this process for as long as you have data.  Once you’re done, you need to load a “dummy byte” into the FIFO so your last data byte makes it out.  Finally, on the next interrupt, shut down the transmitter by sending TXCEN and TXDEN = 0.

Receiving FIFO usage (click to enlarge)

This diagram illustrates another detail of the interface to the MRF.  Also borrowed from the datasheet, it describes the way to access the receive FIFO using SPI.  Typically, SPI interfaces don’t have a notion of registers quite like I2C does, but the MRF does.  To get access to the receive FIFO you initiate an SPI transfer to the MRF module with the contents equal to the address of the receive FIFO.  In this case it’s 0xA000.  Once the first byte of the transfer is complete, the MRF begins outputting the FIFO value on the SDO pin.  It is also possible to gain access to the receive FIFO using FSEL (FIFO Select, called “Data” on the schematic) pin.

Baseband Bandwidth calculation (click for full size)

Before going into some details on the implementation of the library, I’d like to talk briefly about the RF frequency, deviation, and bit rate determination.  I’ve attached another snippet from the datasheet (hopefully the last one), and I’m going to go through the math quickly with numbers for my application.  I’m using 434 Mhz band, with 9600 baud, using a 10ppm crystal.  This means that fxerror = 10 * (434000/1000000) = 4.34 Khz.  Then, our deviation must be 9.6 + 2*fxerror + 10 = 28.28; the closest modulation is 30 Khz.  Therefore, BBBW = 30*2 – 10 = 50 Khz.  The closest BBBW is 67 Khz.

Spectrum plot from alternating 1s and 0s

I recently gained access to a spectrum analyzer courtesy of the OSU Robotics Club.  This is a spectrum plot of 0xAA, or alternating 1’s and 0’s.  It’s a little strange that the distance between peaks is about 90 Khz, as it should be more like 60 Khz.  The comb-like appearance on the flanks is probably from the transceiver switching from 1 to 0 across the scan.

Transmitting ‘1’

This plot is while transmitting all 1’s.  It’s obvious this is much cleaner, but very little information is actually being transmitted here.

With respect to the Atmel AVR library that I wrote, it includes a header file devoted entirely to defining the registers and bits as defined in the datasheet.  Each section includes a comment block description, and in the cases where some bit values need to be calculated, the equations used.  On the occasions where a bitfield is used for some integer value, I include a mask to ensure that if the derived values overrun the width of the bitfield they don’t pollute unrelated settings.  Below, I’ve included an example.

 * Convenience definitions for band setting
 * These defines are provided for use configuring the MRF49XA module.
 * Select the frequency band for the given hardware by uncommenting the
 * appropriate line.  Set the crystal load capacitance using the MRF_XTAL_LD_CAP
 * value.
 * The load capacitance is given by the following equation:
 * Cap pF = 8.5 + (LCS / 2)
 * LCS = (10 - 8.5) * 2
 * For 10pF: LCS = (10 - 8.5) * 2 = 1.5 * 2 = 3
#pragma mark General Configuration Register
#define MRF_GENCREG		0x8000		// General config. register
#define MRF_TXDEN		0x0080		// TX Data Register enable bit
#define MRF_FIFOEN		0x0040		// FIFO enable bit
#define MRF_FBS_MASK		0x0030		// Mask for the band selection
#define MRF_LCS_MASK		0x000F		// Load capacitance mask
// 10pF Crystal load capacitance
#define MRF_LCS			3		// Crystal Load capacitance
// Frequency band settings
#define MRF_FBS_434		0x0010		// 434 mHz band
#define MRF_FBS_868		0x0020		// 868 mHz band
#define MRF_FBS_915		0x0030		// 915 mHz band

All of the files in the library depend on a “hardware.h” file that defines the qualities of the hardware.  The hope is that this file is the only place that implementation-specific code lives.  There are some holes still, however.  Finally, the mrf49xa.c and mrf49xa.h files behave the way you would expect.  The module requires a total of 5 pins and one interrupt.  Some of those pins may be shared with other SPI devices.

void MRF_init(void);

uint8_t MRF_is_idle();
uint16_t MRF_statusRead(void);

// Packet structures
// the maximum payload size
#define MRF_PAYLOAD_LEN 40
// Space for preamble, sync, length and dummy

typedef struct {
	uint8_t	length;
	char		payload[MRF_PAYLOAD_LEN];
} MRF_packet_t;

// Packet based functions
void MRF_transmit_packet(MRF_packet_t *packet);
MRF_packet_t* MRF_receive_packet();

I’ve included a snippet of the header file above so I could mention the basic process for using the MRF module.  The MRF_init function expects the SPI bus to be configured, and it performs the basic initialization of the device.  Once it’s started, the interrupts on the AVR must be enabled.  In the main loop (or at least as often as a packet can be transmitted) you should call MRF_receive_packet.  This function will return NULL if no packet was received, and a pointer to a packet structure if it was.  MRF_transmit_packet takes a packet structure and transmits it.  This is an asynchronous operation, and you may use the packet structure (or it’s memory) once it returns.  This is useful if you want to use a packet structure created on the stack.  It is possible to get yourself into trouble with MRF_packet_transmit, as it spin-loops on a lock set in the ISR.  If for whatever reason that lock isn’t unlocked at some point you can hardlock.  I’ve done my best to ensure that it doesn’t happen, but beware.

And, finally, links to the files.  If there seems to be sufficient interest, I’ll open up a SVN (maybe Google Code, who knows) with these files and a main program useful for telemetry and the like.  Post in the comments if you’re interested, or send me a line.







Also, the PCB is available as a public design from BatchPCB.  I’ll include a bill of materials if necessary, though the components are clearly printed on the silkscreen.

Oh!  before you ask: No, I don’t know what the range is.  The longest I’ve tried is about 20 feet, and there weren’t any errors, but it was only about 100 bytes.  The chip is rated to 7dBm, I think, so go from there. 🙂


Here is a simple bill of materials, I used the Eagle export feature, and attempted to place digikey part numbers for each part.  I’m not saying they’re all right, you may want to make sure you’re getting what you think is correct.


Also, here are the eagle files.  They aren’t necessarily finalized


, , , , , , , ,

  1. #1 by Emil on February 24, 2014 - 1:49 am

    Hi. Yep am using unix.
    I was thinking on using your firmware files and to update them for my needs.

  2. #2 by Alexandre on May 21, 2014 - 10:28 am

    I am trying to use the MRF49XA and I am facing the following problem.
    I followed the steps described on the IC datasheet to configure the device to work on 915MHz and to the internal data FIFO as enabled.
    So that, I understand from the datasheet that pin 7 (RCLKOUT/FCAP/ FINT) was supposed to rise to HIGH when the FIFO became full filed with the bit numbers seted on FFBC ( @ register FIFORSTREG ).

    The fact is that, I do have a spectrum analyzer and, sens to me that the transmit section is working good. I do have too boards very close each other and no matter how much data I send with one of then, the pin 7 (FINT) does not goes UP on the other one !

    Does any one faced this situation before ?

  3. #3 by hpux735 on May 21, 2014 - 1:48 pm

    I have seen that before, but I have a nasty habit of forgetting almost everything about the device when I’m done looking at it.

    What controller are you using? If it’s atmel-based take a look at my code and see if you can get it working with it. https://github.com/hpux735/MRF49XA-Dongle

  4. #4 by Vincent on August 2, 2014 - 12:23 pm

    Hello William, nice job !

    I have a problem, it seems I can’t even send a byte.

    After calling the Init function, and then the MRF_transmit_alternating function, the IRO pin never goes low.
    I tried reading the Status reg, it gives me 0x0000 ..
    So I tried to send 1 additionnal byte, then the status reg says I have an overflow ( 0x0200 )

    Any clue ?

    I wonder if this is something related to my 10MHz quartz, which has a 18pF load capacitance .. this value is out of LCS possible values.

    Thanks for help.


  5. #5 by Vincent on August 3, 2014 - 6:42 am

    @ Vincent

    Got it fixed .. was a quartz soldering issue.

    Now I manage to send and receive some bytes, but I always have some shifted bits ..

    For example, when I send 0xAA
    Sometimes I receive 0xAA
    Sometimes I receive 0x55
    Sometimes I receive 0x05 50

    How to synchronize the 1st bit ??

    Thanks for help

  6. #6 by hpux735 on August 3, 2014 - 7:32 am

    Are you using the built-in synchronizer, or are you making your own serializer/deserializer using the DIO pin? I’ve never seen the transceiver give shifted bytes using the built-in synchronizer. The transceiver sends 0xAAAA to help to synchronize the clocks then 0x2DD4 to synchronize the alignment. Assuming you’re using the built-in synchronizer, are you using the byte or word-length synch word? If you’re using the byte version, try going to word. I’ve only ever used word.

  7. #7 by Vincent on August 3, 2014 - 7:54 am

    @ hpux735

    Thank you for your answer.
    I’m using your code, excepting some tunings for 868MHz communications.

    When I use the 2 bytes long sync word, the communication never syncs … (maybe because of this bit shift).
    Therefore, I removed the sync words to see what the receiver sees by adding the MRF_FFSC flag.
    When the transmitter is turned off, I see a lot of incoherent values, which I guess is the ambiant noise.
    When I turn-on the transmitter, I can see the bytes I send, but with that strange shift..

    When I send 0x01, sometime I receive 0x02, sometimes 0x04, sometimes 0x10, etc…

    I have the feeling that the receiver synchronizes the first bit with the noise.. that would explain the random shift.

    Note that this shift is fixed, until I restart the transmitter.

  8. #8 by Vincent on August 3, 2014 - 12:50 pm

    ok, I got it working.

    There was multiple things to consider :
    1. One of the issues was that I was not using IRO falling edge, I was just polling its value. Since my CPU goes much more faster than IRO switches, I really needed to use IRO falling edge interrupt.
    2. The MRF_FFSC flag is really useless. It is a mess, it just reads and synchronises with the noise and then un-synchronises the bits. That’s why I had a bit shift.
    3. Combining the MRF_FFSC flag + polling on IRO (instead of falling edge interrupt) is the worst thing ever, because once configured, the IRO pins immediatly goes low thanks to the noise .. then you can’t get out of the mess.

    Therefore, I highly recommend to the readers to do the following :
    – use MRF_FFSC flag ONLY in DEBUG transmitter mode, just to check that “something” goes over the air
    – use falling edge interrupts + remove FFSC flag for expecting to read the tranmistted data

    In other words .. just clearly copy/paste William’s code, just adapt the ISR to your CPU/µC.

  9. #9 by hpux735 on August 3, 2014 - 12:58 pm

    @ Vincent
    Well said Vincent.

    The only way I got this damn thing working was by porting the Microchip example code from the PIC to Atmel. This chip really is a mess, as you say. Many of the “features” just don’t work as advertised.

  10. #10 by Vincent on August 3, 2014 - 1:17 pm

    I’m not used to raw radio chips, this is the first one I’ve used.

    But it’ve been using electronic components and datasheets for over 10 years, and this one is the worst I’ve ever seen.
    The datasheet is just impossible to understand.

    The problem is that the datasheet does not clearly explain how the chip works in the selected modes.

    Anyway .. after 2 days, I finally have it working 🙂

  11. #11 by Dizzwold on August 6, 2014 - 5:14 am


    What kind of range can be achieved, indoors and out-doors?
    Would be interested to know.

    Looking to make a couple of RF transceivers, with a range of about 1.5 miles, but don’t know where to start. I have some nrf24l01+ but could do with a greater range.

    Would like to build my own.

    Any input welcome.

  12. #12 by hpux735 on August 6, 2014 - 9:52 am

    If you want a precise answer, it depends on many factors. These include output power, input noise figure, antenna system (rx and tx) gain, path loss, and bit rate (to name only a few). First, let’s compute path loss (https://en.wikipedia.org/wiki/Free-space_path_loss) because it’s the easiest. Starting with 1.5 miles (rounded to 3km), FSPL(dB) = 20log10(3000) + 20log10(433) – 27.55 = -95dB (I made it negative because it’s loss). Now, let’s assume that you have a pair of 6dBi omni=directional antennas = 12dB. The transmitter has a maximum transmit power of +7dBm. Adding these up, we have a total power received at the input to the chip (ignoring losses in the baluns) of -76dB.

    Microchip doesn’t provide the noise figure spec and ROC (receiver operating characteristics, https://en.wikipedia.org/wiki/Receiver_operating_characteristic). Normally these could be used to determine an expected bit-error rate for a given input power level and bit rate. However, the datasheet does provide bit-error curves for a given bit rate and power level directly, so we can use those (page 84). You’ll see that -78dBm is off-the-scale, indicating that it should be a good link.

    HOWEVER! This is a very basic exploration, with many assumptions. I’m assuming that the baluns are perfect. I’m assuming that you’re really getting 12dB from your antennas. I’m assuming that there’s nothing but free space between the transceivers (no ground, no trees, nothing). In practice, I’ve flown one of the transceivers around in my R/C airplane and got probably a 1/2 mile away. It mostly seemed to work, but I did get some bit errors.

    In doors, your guess is as good as mine. It all depends on walls.

  13. #13 by Vincent on September 9, 2014 - 8:01 am

    Hi Dillon,

    I’ve written a Linux driver for the MRF49XA.
    Some parts of the code is based on your MRF49XA driver.

    Will you allow me to publish the Linux driver ?
    I’ve added this to the header files :
    Based on Microchip MRF49XA controller driver written by
    William Dillon, Copyright 2010 Oregon State University. All rights reserved.

    Best regards,

  14. #14 by hpux735 on September 9, 2014 - 8:13 am

    Hell yah! That sounds awesome. How does it work? Is it limited to linux running on SoCs with GPIO? Here’s hoping it gets merged into the mainline kernel. 🙂

  15. #15 by Vincent on September 9, 2014 - 10:54 am

    @ hpux735
    “Is it limited to linux running on SoCs with GPIO?”
    At first, my implementation was for any linux platform with a full-duplex SPI + a GPIO IRQ available.

    After some tests, I was facing issues because of Linux SPI stack latency and the lack of RX buffer inside MRF49XA.
    Typically, a 2.4kbps baud rate is not possible.

    Therefore I also wrote an architecture-dependant wrapper to avoid Linux SPI stack latency, then you can achieve higher baud rates.

    I’ll post the code on a google code project, I hope some people will help to improve this driver.

    In the end, my idea is to create a higher level driver, to give a network interface to Linux userspace … this would create like a PAN (Private Area Network), usable for IoT projects or so..

  16. #16 by Vincent on September 10, 2014 - 7:01 am

  17. #17 by LDaniel on September 15, 2014 - 11:07 am

    I’m sorry to do that, but I want to use your blog I order to find people which can help me.

    Yes, It’s been 3 months that I’m trying to build a simple remote control using PIC16F628A and MRF49XA, but actually I don’t succeed to build the code (I build a source code, but I think it is not complete).

    So, if someone wants to help me, I will explain better my project.

    Thank you.

  18. #18 by hpux735 on September 15, 2014 - 11:11 am

    I haven’t tried using this module with a PIC micro controller, but the code that I used from Microchip was intended to be used with a PIC. My advice is to find the sample code from Microchip and start from that.

  19. #19 by LDaniel on September 15, 2014 - 11:39 am

    My biggest problem is not to use the PIC but to really understand the functionning of the MRF’9XA. I read the MRF49XA Datasheet, I read lots of source code from differents projects and from sample Microchip code, but they were not so clear for me to understand.
    What I need is an explaination, step by step, like a tutorial, in order for me to understand and to developp my code… but I am searching… without find it.
    I also write a topic on microchip forum.

    Well, developp project with MRF49XA is not simple…

  20. #20 by hpux735 on September 15, 2014 - 11:41 am

    @ Vincent

    Yep, that’s true. It’s a quirky chip. As Vincent says, it’s not something that’s easily understood. The best course of action is to use working sample code and slowly, methodically, change it for your needs. Good luck.

  21. #21 by LDaniel on September 15, 2014 - 11:56 am

    Thanks, I’m desperate… Today, I consult a electronic development company in order to write for me the source code for my project… and the answer was : “I’am sorry but I’m not sure we are the best solution for your project…”
    So effectively I will take more time to try to finish (or begin) my project..
    Thanks again.

  22. #22 by ALEXANDRE PANOSSO VIEIRA on September 17, 2014 - 9:44 am

    @ Vincent

    Hi there Vincent !
    . .. i totally agree with you about the datasheet quality ! I am trying to get this microchip IC to work with a simple board we made here in Brazil and it was 2 week and no success until now !

    I am following the steps on the item 3.3 of the manual ( 3.3 Initialization ) and “The device transmit sequence” that is on page 68 . . .also, on another board, I am trying to use the sequence 3.18 RX FIFO Buffered Data Read using the pooling method as described on item “3.18.2 POLLING MODE” and nothing seems to make any sense !

    Did you found any other way to make it to work, instead the methods described on this itens ?
    There were any insight you have got that makes the things to work ?

    Please, in the name of the Lord, if some one can help me . . . this problems is getting me crazy ! hehehehehhe !

    best regards

  23. #23 by LDaniel on October 6, 2014 - 12:03 am

    Hello !
    I am here again… and I finally succeed to build and work my board(s).
    Actually I’d like to work with a frequency of 433MHz (more or less) but I have 438MHz.
    I try to change this frequency to 430MHz but I never 433…
    What is the parameter which control the frequency ?

    Thank you.

  24. #24 by hpux735 on October 6, 2014 - 2:35 pm

    @ LDaniel

    The best source for the information about setting the center frequency will always be the data sheet, but I have some of that information in the MRF49XA_definitions header file.

    * Convenience definitions for center frequency
    * These defines are provided for use configuring the MRF49XA module.
    * Select the center frequency using the following equation:
    * Fo = 10 * FA1 * (FA0 + Fval/4000)
    * Fval = Decimal value of FREQB, 96 < Fval < 3903 * * Choose FA1 anf FA0 using the following table according to band: * * Range FA1 FA0 * 434 1 43 * 868 2 43 * 915 3 30 * * Fo = 10 * (43 + Fval/4000) = 10*43 + 10*(Fval/4000) = 430 + Fval/400 * * Fval = (Fo - 430) * 400 = (432.10 - 430) * 400 = 2.1 * 400 = 840 * ******************************************************************************/ #pragma mark Center Frequency Value Set Register #define MRF_CFSREG 0xA000 // Center Frequency value register address #define MRF_FREQB_MASK 0x0FFF // Center Frequency value (see datasheet) // Frequency setting for 432.10 mHz #define MRF_FREQB 840

(will not be published)

Please complete this capcha. I get almost 1000 spam comments a day! * Time limit is exhausted. Please reload CAPTCHA.