This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Status and approach for PA/LNA Support in BLE/Thread Concurrent Multiprotocol

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

Parents
  • You may have discovered a bug here.

    The softdevice does the cleanup of settings on few peripherals before ends its timeslot and gives the control to different Radio timeslot. Based on your description and code, it is possible that it might have cleaned incorrectly. 

    Since I cannot see anything wrong with your code snippets, could you help me reproduce this on my desk by giving me a minimalistic project? Trust me, if I can reproduce it here, it goes much faster than me trying to guess what could be wrong.

Reply
  • You may have discovered a bug here.

    The softdevice does the cleanup of settings on few peripherals before ends its timeslot and gives the control to different Radio timeslot. Based on your description and code, it is possible that it might have cleaned incorrectly. 

    Since I cannot see anything wrong with your code snippets, could you help me reproduce this on my desk by giving me a minimalistic project? Trust me, if I can reproduce it here, it goes much faster than me trying to guess what could be wrong.

Children
No Data
Related