Driving FEMs usually require some logic signals, which can be driven from software, but generally that's not fast enough (e.g. for CSMA/CA): It needs some hardware based signals.
Since EFR32 is an integrated radio MCU, this is not handled in the radio module, but a more general purpose module: Peripheral Reflex System, or PRS for short.
EFR32s has 12 PRS channels, each of these channels can be driven by numerous signals from various sources, and even some logic operations can be done between them. These channels can be connected to PRS consumers, so it can trigger a timer start for example.
You can also wire PRS channels to some GPIOs, and this is the feature we'll use. For more information of PRS, see the reference manual.
Hardware configurator does provide an "External LNA" module, but it's not supported in RAIL (as of Flex SDK 2.0). Also, it only supports one TX/RX and one sleep pin, which is probably not enough for more complicated FEMs.
This article is stack independent, although some stacks might provide an integrated way, like the External LNA module.
The prs signals
Generally a FEM requires the following:
The bypass signals are usually either compile time or application time settings, so they can be driven like any other GPIO from software, or exactly the same as sleep and rx/tx, therefore it's not part of this article.
The following PRS signals are useful for driving a FEM:
Note that all signals can be inverted
RAC_ACTIVE can be used to drive the sleep pin, but using (RAC_LNAEN or RAC_PAEN) is more energy efficient.
GPIOs usable for PRS
See the Alternate Functionality Pinout chapter or device pinout table in the datasheet. PRS channel pins has PRS_CHx functionality on it.
The hardware configurator can be also used as a dynamic pinout diagram in Simplicity Studio (in all Flex SDK examples)
Keep in mind that you need independent PRS channels for each signal, you can't configure a PRS channel to two locations.
Also, logical operations limited to channels next to each other:
This limits the usable pins, e.g. it's recommended to drive a sleep pin as (RAC_LNAEN or RAC_PAEN), which means whatever channel you plan to use for sleep, the previous one MUST be set up to either RAC_LNAEN or RAC_PAEN. However, you can set up the same signal to multiple channels, and you don't have to drive a gpio with a channel.
Configuring GPIOs in RAILTest
Generally, RAILTest is recommended to test the radio's setup and performance, so energy efficiency is not a goal here. Therefor, it would be enough to just drive pins low and high, although it's a bit simpler to use the tx/rx switch from PRS.
RAILTest only supports a few GPIOs:
PC10, PC11, PF2, PF3, PF4, PF5, PC9, PD9, PD10, PD11, PD12
A number of PRS signals are supported, unfortunately drive low/high and RAC_ACTIVE is not amongst them.
The following commands can be used
E.g. to drive the LNA on the MGM12P module (sleep pin on PD10, tx/rx on PD11)
setDebugSignal PD10 LNAEN setDebugSignal PD11 CUSTOM_PRS 81 0
Configuring GPIOs in application source
To use PRS channels, you first need to enable GPIO and PRS clock:
CMU_ClockEnable(cmuClock_PRS, true); CMU_ClockEnable(cmuClock_GPIO, true);
Set the GPIO we use to pushpull mode:
GPIO_PinModeSet(port, pin, gpioModePushPullAlternate, 0);
Enable PRS signal:
PRS->CH[x].CTRL = PRS_signal | PRS_CH_CTRL_ASYNC;
It's recommended to use async mode, since this is needed to have a working signal even in sleep states.
Set location of the PRS signal pin:
PRS_ROUTELOCy |= PRS_ROUTELOCy_CHxLOC_LOCz
(the 12 PRS channels are divided into 3 ROUTELOC registers, so channel 0-3 is in ROUTELOC0, etc)
Enable the pin:
BUS_RegBitWrite(&PRS->ROUTEPEN, _PRS_ROUTEPEN_CHxPEN_SHIFT, 1);
E.g. to drive the LNA on the MGM12P module (sleep pin on PD10, tx/rx on PD11):
CMU_ClockEnable(cmuClock_GPIO, true); CMU_ClockEnable(cmuClock_PRS, true); GPIO_PinModeSet(gpioPortD, 10, gpioModePushPullAlternate, 0); GPIO_PinModeSet(gpioPortD, 11, gpioModePushPullAlternate, 0); PRS->CH.CTRL = PRS_RAC_ACTIVE | PRS_CH_CTRL_ASYNC; PRS->CH.CTRL = PRS_RAC_LNAEN | PRS_CH_CTRL_ASYNC; PRS->ROUTELOC1 |= PRS_ROUTELOC1_CH5LOC_LOC0 | PRS_ROUTELOC1_CH6LOC_LOC13; BUS_RegBitWrite(&PRS->ROUTEPEN, _PRS_ROUTEPEN_CH6PEN_SHIFT, 1); BUS_RegBitWrite(&PRS->ROUTEPEN, _PRS_ROUTEPEN_CH5PEN_SHIFT, 1);
Invert a signal
Inverting a signal is very simple. To create an inverted sleep signal for example (high in sleep):
PRS->CH[x].CTRL = PRS_RAC_ACTIVE | PRS_CH_CTRL_ASYNC | PRS_CH_CTRL_INV;
Logical operation between signals
As above mentioned:
If you meet this limitation, the setup is pretty simple, just use PRS_CH_CTRL_ORPREV and PRS_CH_CTRL_ANDNEXT.
For example the energy efficient (RAC_LNAEN | RAC_PAEN) way to drive the LNA on MGM12P:
PRS->CH.CTRL = PRS_RAC_LNAEN | PRS_CH_CTRL_ASYNC; PRS->CH.CTRL = PRS_RAC_PAEN | PRS_CH_CTRL_ASYNC | PRS_CH_CTRL_ORPREV;