Bluetooth LE Auto PA mode#


The EFR32 families of chips each come equipped with two or three Power Amplifiers (PAs):

  • EFR32xG1x

    • A high-power 2.4 GHz PA (for power 20 dBm and lower)

    • A low-power 2.4 GHz PA (for power 0 dBm and lower)

  • EFR32xG21

    • A high-power 2.4 GHz PA (for power 20 dBm and lower)

    • A medium-power 2.4 GHz PA (for power 10 dBm and lower)

    • A low-power 2.4 GHz PA (for power 0 dBm and lower)

  • EFR32xG22

    • A high-power 2.4 GHz PA (for power 6 dBm and lower)

    • A low-power 2.4 GHz PA (for power 0 dBm and lower)

Each PA maps to different TX output power curves. While using high-power or medium-power PA, TX power under 0 dBm may get a very inaccuracy output. While using low-power PA, TX power cannot set above 0 dBm. In most use cases, the antenna matching network works well in one range only (either above 0 dBm or below 0 dBm), and hence this is not a big problem. In some use cases, however, high accuracy output is needed both above and below 0 dBm. In this case, an automatic switching between the PAs is needed. This article discusses how to achieve this.

Enabling Auto PA Mode#

By default, the used PA can be configured under the configuration of RAIL Utility, PA software components, as shown here:

PA mode configuration

However, this lets you select a fixed PA only, and does not enable auto PA mode. Currently, auto PA mode can be enabled in source code by directly editing the config\sl_bluetooth_config.h in your project. Find the following configuration and change the .pa.pa_mode to SL_BT_BLUETOOTH_PA_AUTOMODE:

#define SL_BT_CONFIG_DEFAULT                                                   \
  {                                                                            \
    .config_flags = SL_BT_CONFIG_FLAGS,                                        \
    .bluetooth.max_connections = SL_BT_CONFIG_MAX_CONNECTIONS_SUM,             \
    .bluetooth.max_advertisers = SL_BT_CONFIG_MAX_ADVERTISERS,                 \
    .bluetooth.max_periodic_sync = SL_BT_CONFIG_MAX_PERIODIC_ADVERTISING_SYNC, \
    .bluetooth.max_buffer_memory = SL_BT_CONFIG_BUFFER_SIZE,                   \
    .scheduler_callback = SL_BT_CONFIG_LL_CALLBACK,                            \
    .stack_schedule_callback = SL_BT_CONFIG_STACK_CALLBACK,                    \
    .gattdb = &gattdb,                                                         \
    .max_timers = SL_BT_CONFIG_MAX_SOFTWARE_TIMERS,                            \
    .rf.tx_gain = SL_BT_CONFIG_RF_PATH_GAIN_TX,                                \
    .rf.rx_gain = SL_BT_CONFIG_RF_PATH_GAIN_RX,                                \
    .rf.tx_min_power = SL_BT_CONFIG_MIN_TX_POWER,                              \
    .rf.tx_max_power = SL_BT_CONFIG_MAX_TX_POWER,                              \
    .pa.config_enable = BT_PA_CONFIG_STATE,                                    \
    .pa.input = BT_PA_POWER_SUPPLY,                                            \
    .pa.pa_mode = SL_BT_BLUETOOTH_PA_AUTOMODE,                                             \

Note that this, in itself, is not enough to switch between PAs automatically. You also have to define the rules of switching, i.e., between what circumstances you want to switch PAs. To define this, RAIL provides the RAILCb_PaAutoModeDecision() callback function, which can be overwritten in your application. For example, if you want to use high power PA above 10 dBm, mid power PA between 0 and 10 dBm and low power PA below 0 dBm, apply the following function:

#include "rail.h"

RAIL_Status_t RAILCb_PaAutoModeDecision(RAIL_Handle_t railHandle,
                                        RAIL_TxPower_t *power,
                                        RAIL_TxPowerMode_t *mode,
                                        const RAIL_ChannelConfigEntry_t *chCfgEntry)
  if(*power < 0) {
    // Use the LP PA when is below 0dBm
  } else if((*power >= 0) && (*power <= 100)) {
    // Use the MP PA when TX power is from 0dBm to 10dBm
  } else {
    // Use the HP PA when TX power is over 10dBm


To verify whether the Auto PA mode is working properly, first check the set_max value returned by the sl_bt_system_set_tx_power() API command. This value tells you the actual (maximum) power level set by the stack, which may differ from the originally requested value because of the limitation of different PAs. The table below shows the requested and the actual set values for different TX power levels and different PA settings, tested on a EFR32xG21 chip. The following code example may help to test the same on your board: