I am migrating to the Fanstel BT840XE module with nRF52840 and a Skyworks PA/LNA. I need some assistance in getting my PA/LNA working in a multiprotocol setup.
I have successfully implemented and tested the Softdevice portion of PA/LNA control for BLE, and tested it on my custom hardware platform. I confirmed operation using a BLE-only example from the SDK and confirmed the transmit power is indeed higher when the PA/LNA flag is set -- I think this demonstrates my code is correct and hardware is working.
My actual multiprotocol project implements the same BLE PA/LNA setup tested above (code snippets below). I have *not yet* implemented any PA/LNA control for Thread.
1. At this point, I would expect my BLE advertisements would be send with higher power with the PA/LNA enabled, but they are not. Is the multiprotocol usage somehow defeating the PA/LNA initialization I am doing?
2. What is the recommended approach to PA/LNA in a BLE/Thread multiprotocol concurrent project?
DETAILS:
My project uses concurrent BLE/Thread Multiprotocol with nRF5_SDK_for_Thread_and_Zigbee_3.1.0_c7c4730 and S140 6.1.1.
My custom board file includes the following section for PA/LNA and transmit power:
#define APP_PA_LNA // This board has a Power Amplifier and LNA "APP_PA_LNA" will trigger main to initialize it. #ifdef APP_PA_LNA #define APP_PA_PIN 17 #define APP_LNA_PIN 19 #define APP_CHL_PIN 8 #define APP_CPS_PIN 6 #endif //APP_PA_LNA #define MAX_BLE_XMIT_PWR_DBM 0 //Fanstel BT840XE max transmit power from radio IC is +2dBm. PA adds 21dBm! Use with 0dBm gain antenna max for FCC/IC. #define THREAD_POWER 0 // <i> Transmit Power (in dBm) used by Thread.
In main, after softdevice init, I configure the PA/LNA:
ble_stack_init();
// set up PA/LNA if present - NOTE: must be done after softdevice is enabled.
#ifdef APP_PA_LNA
nrf_gpio_cfg_output(APP_CPS_PIN);
nrf_gpio_cfg_output(APP_CHL_PIN);
nrf_gpio_pin_set(APP_CHL_PIN);
nrf_gpio_pin_clear(APP_CPS_PIN); //enable
pa_lna_init(APP_PA_PIN,APP_LNA_PIN);
#endif
Here is my PA/LNA init code:
#include "fanstel_pa_lna.h"
#include "nrf_assert.h"
#include "nrf_log.h"
#include "ble.h"
#include "app_error.h"
#include "nrf_drv_gpiote.h"
#include "nrf_drv_ppi.h"
void pa_lna_init(uint32_t gpio_pa_pin, uint32_t gpio_lna_pin)
{
ble_opt_t opt;
uint32_t gpiote_ch = NULL;
ret_code_t err_code;
memset(&opt, 0, sizeof(ble_opt_t));
err_code = nrf_drv_gpiote_init();
if(err_code != NRF_ERROR_INVALID_STATE)
APP_ERROR_CHECK(err_code);
err_code = nrf_drv_ppi_init();
//if(err_code != MODULE_ALREADY_INITIALIZED)
APP_ERROR_CHECK(err_code);
nrf_ppi_channel_t ppi_set_ch;
nrf_ppi_channel_t ppi_clr_ch;
err_code = nrf_drv_ppi_channel_alloc(&ppi_set_ch);
APP_ERROR_CHECK(err_code);
err_code = nrf_drv_ppi_channel_alloc(&ppi_clr_ch);
APP_ERROR_CHECK(err_code);
nrf_drv_gpiote_out_config_t config = GPIOTE_CONFIG_OUT_TASK_TOGGLE(false);
if((gpio_pa_pin == NULL) && (gpio_lna_pin == NULL))
{
err_code = NRF_ERROR_INVALID_PARAM;
APP_ERROR_CHECK(err_code);
}
if(gpio_pa_pin != NULL)
{
if(gpiote_ch == NULL)
{
err_code = nrf_drv_gpiote_out_init(gpio_pa_pin, &config);
APP_ERROR_CHECK(err_code);
gpiote_ch = nrf_drv_gpiote_out_task_addr_get(gpio_pa_pin);
}
// PA config
opt.common_opt.pa_lna.pa_cfg.active_high = 1; // Set the pin to be active high
opt.common_opt.pa_lna.pa_cfg.enable = 1; // Enable toggling
opt.common_opt.pa_lna.pa_cfg.gpio_pin = gpio_pa_pin; // The GPIO pin to toggle tx
}
if(gpio_lna_pin != NULL)
{
if(gpiote_ch == NULL)
{
err_code = nrf_drv_gpiote_out_init(gpio_lna_pin, &config);
APP_ERROR_CHECK(err_code);
gpiote_ch = nrf_drv_gpiote_out_task_addr_get(gpio_lna_pin);
}
// LNA config
opt.common_opt.pa_lna.lna_cfg.active_high = 1; // Set the pin to be active high
opt.common_opt.pa_lna.lna_cfg.enable = 1; // Enable toggling
opt.common_opt.pa_lna.lna_cfg.gpio_pin = gpio_lna_pin; // The GPIO pin to toggle rx
}
// Common PA/LNA config
opt.common_opt.pa_lna.gpiote_ch_id = (gpiote_ch - NRF_GPIOTE_BASE) >> 2; // GPIOTE channel used for radio pin toggling
opt.common_opt.pa_lna.ppi_ch_id_clr = ppi_clr_ch; // PPI channel used for radio pin clearing
opt.common_opt.pa_lna.ppi_ch_id_set = ppi_set_ch; // PPI channel used for radio pin setting
err_code = sd_ble_opt_set(BLE_COMMON_OPT_PA_LNA, &opt);
APP_ERROR_CHECK(err_code);
if (err_code == NRF_SUCCESS)
{
NRF_LOG_INFO("PA/LNA Successfully Enabled");
}
return;
}
When I start advertising, I set the radio transmit power using the power level defined in the board file:
#define BLE_GAP_ADV_POWER MAX_BLE_XMIT_PWR_DBM /**< BLE Transmit power to use for advertising, in dBm. */
/**@brief Function for starting advertising.
*/
static void advertising_start(void)
{
ret_code_t err_code;
// NORDIC: SET TX POWER FOR ADVERTISING
err_code = sd_ble_gap_tx_power_set(BLE_GAP_TX_POWER_ROLE_ADV, m_advertising.adv_handle, BLE_GAP_ADV_POWER);
APP_ERROR_CHECK(err_code);
err_code = ble_advertising_start(&m_advertising, BLE_ADV_MODE_FAST);
// We don't have a good way of knowing current adv state, so ignore the error
if ((err_code != NRF_SUCCESS) ) //&& (err_code != NRF_ERROR_INVALID_STATE))
{
APP_ERROR_CHECK(err_code);
}
NRF_LOG_INFO("advertising is started");
}