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"); }