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

Mesh PA / LNA problems

On SDK16.0.0 + Mesh SDK 4.1.0

I am using a Fanstel BT840X module that has an onboard Skyworks SKY66112 PA/LNA.

I followed the Infocenter guide along with an old example (to automatically assign unused PPI and GPIOTE channels). I can see the stronger RSSI and I can provision it fine (nRF Mesh app), but then it times out on 'Requesting Composition Data'.

static ble_opt_t ble_pa_lna_opts;
static mesh_pa_lna_gpiote_params_t m_pa_lna_params;

static void pa_lna_init(uint8_t gpio_pa_pin, uint8_t gpio_lna_pin) {
    nrf_ppi_channel_t m_pa_lna_set_ppi_channel;
    nrf_ppi_channel_t m_pa_lna_clr_ppi_channel;
    uint8_t pa_lna_gpiote_ch = NULL;

    memset(&ble_pa_lna_opts, 0, sizeof(ble_opt_t));
    memset(&m_pa_lna_params, 0, sizeof(mesh_pa_lna_gpiote_params_t));

    // Enable BT840X SKY66112 PA/LNA
    nrf_gpio_cfg_output(BT840X_CPS);
    nrf_gpio_cfg_output(BT840X_CHL);
    nrf_gpio_pin_set(BT840X_CHL);
    nrf_gpio_pin_clear(BT840X_CPS); //low=enable, high=bypass

    //Already initialized by buttons_init()
    //ERROR_CHECK(nrf_drv_gpiote_init());
    
    //Already initialized by saadc_init()
    //ERROR_CHECK(nrf_drv_ppi_init());
    
    ERROR_CHECK(nrf_drv_ppi_channel_alloc(&m_pa_lna_set_ppi_channel));
    ERROR_CHECK(nrf_drv_ppi_channel_alloc(&m_pa_lna_clr_ppi_channel));

    nrf_drv_gpiote_out_config_t config = GPIOTE_CONFIG_OUT_TASK_TOGGLE(false);
    
    if((gpio_pa_pin == NULL) && (gpio_lna_pin == NULL)) {
        ERROR_CHECK(NRF_ERROR_INVALID_PARAM);
    }    

    if(gpio_pa_pin != NULL) {
        if(pa_lna_gpiote_ch == NULL) {
            ERROR_CHECK(nrf_drv_gpiote_out_init(gpio_pa_pin, &config));
            
            pa_lna_gpiote_ch = nrf_drv_gpiote_out_task_addr_get(gpio_pa_pin); 
        }
        
        // BLE PA config
        ble_pa_lna_opts.common_opt.pa_lna.pa_cfg.enable = 1;
        ble_pa_lna_opts.common_opt.pa_lna.pa_cfg.active_high = 1;
        ble_pa_lna_opts.common_opt.pa_lna.pa_cfg.gpio_pin = gpio_pa_pin;
        // MESH PA config
        m_pa_lna_params.pa_cfg.enable = 1;
        m_pa_lna_params.pa_cfg.active_high = 1;
        m_pa_lna_params.pa_cfg.gpio_pin = gpio_pa_pin;
    }
    
    if(gpio_lna_pin != NULL) {
        if(pa_lna_gpiote_ch == NULL) {
            ERROR_CHECK(nrf_drv_gpiote_out_init(gpio_lna_pin, &config));        
            
            pa_lna_gpiote_ch = nrf_drv_gpiote_out_task_addr_get(gpio_lna_pin); 
        }
        
        // LNA config
        ble_pa_lna_opts.common_opt.pa_lna.lna_cfg.enable = 1;
        ble_pa_lna_opts.common_opt.pa_lna.lna_cfg.active_high = 1;
        ble_pa_lna_opts.common_opt.pa_lna.lna_cfg.gpio_pin = gpio_lna_pin;
        // MESH LNA config
        m_pa_lna_params.pa_cfg.enable = 1;
        m_pa_lna_params.pa_cfg.active_high = 1;
        m_pa_lna_params.pa_cfg.gpio_pin = gpio_lna_pin;
    }

    // Common BLE PA/LNA config
    ble_pa_lna_opts.common_opt.pa_lna.ppi_ch_id_set = m_pa_lna_set_ppi_channel;                   // PPI channel used for radio pin setting
    ble_pa_lna_opts.common_opt.pa_lna.ppi_ch_id_clr = m_pa_lna_clr_ppi_channel;                   // PPI channel used for radio pin clearing
    ble_pa_lna_opts.common_opt.pa_lna.gpiote_ch_id  = (pa_lna_gpiote_ch - NRF_GPIOTE_BASE) >> 2;  // GPIOTE channel used for radio pin toggling
    // Common MESH PA/LNA config, can use same channels as for BLE
    m_pa_lna_params.ppi_ch_id_set = ble_pa_lna_opts.common_opt.pa_lna.ppi_ch_id_set;
    m_pa_lna_params.ppi_ch_id_clr = ble_pa_lna_opts.common_opt.pa_lna.ppi_ch_id_clr;
    m_pa_lna_params.gpiote_ch_id = ble_pa_lna_opts.common_opt.pa_lna.gpiote_ch_id;

    __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "PA/LNA PPI_SET: %u, PPI_CLR: %u, GPIOTE: %u\n", ble_pa_lna_opts.common_opt.pa_lna.ppi_ch_id_set, ble_pa_lna_opts.common_opt.pa_lna.ppi_ch_id_clr, ble_pa_lna_opts.common_opt.pa_lna.gpiote_ch_id);
    
    //ERROR_CHECK(sd_ble_opt_set(BLE_COMMON_OPT_PA_LNA, &ble_pa_lna_opts));
}

This ends up assigning PPI channels 2 & 3 and GPIOTE channel 1. I believe this makes sense since I am using 2 PPI channels for SAADC and 1 GPIOTE channel for app_button.

I added the provisioning_sd_ble_opt_cb() callback and modified prov_start_params in start() for it.

static void provisioning_sd_ble_opt_cb(void) {
    ERROR_CHECK(sd_ble_opt_set(BLE_COMMON_OPT_PA_LNA, &ble_pa_lna_opts));
}

static void start(void) {
    //rtt_input_enable(app_rtt_input_handler, RTT_INPUT_POLL_PERIOD_MS);

    if(!m_device_provisioned) {
        static const uint8_t static_auth_data[NRF_MESH_KEY_SIZE] = STATIC_AUTH_DATA;
        mesh_provisionee_start_params_t prov_start_params =
        {
            .p_static_data = static_auth_data,
          #if ENABLE_PA_LNA
            .prov_sd_ble_opt_set_cb = provisioning_sd_ble_opt_cb,
          #else
            .prov_sd_ble_opt_set_cb = NULL,
          #endif
            .prov_complete_cb = provisioning_complete_cb,
            .prov_device_identification_start_cb = device_identification_start_cb,
            .prov_device_identification_stop_cb = NULL,
            .prov_abort_cb = provisioning_aborted_cb,
            .p_device_uri = EX_URI_LS_SERVER
        };
        ERROR_CHECK(mesh_provisionee_prov_start(&prov_start_params));
    }
    else {
        unicast_address_print();
    }

    ERROR_CHECK(mesh_stack_start());
}

Finally, I modified initialize() to add the calls before and after mesh_init() as per the guide.

static void initialize(void) {
    __LOG_INIT(LOG_SRC_APP | LOG_SRC_FRIEND, LOG_LEVEL_DBG1, LOG_CALLBACK_DEFAULT);

    uint32_t icRevision = NRF_FICR->INFO.VARIANT;
    __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "IC Revision: %08X (%c%c%c%c)\n", icRevision, (icRevision>>24)&0xFF, (icRevision>>16)&0xFF, (icRevision>>8)&0xFF, icRevision&0xFF);

    ERROR_CHECK(app_timer_init());
    hal_leds_init();

#if BUTTON_BOARD
    //ERROR_CHECK(hal_buttons_init(button_event_handler));
    buttons_init(); //using app_button.c
#endif

    //SAADC
    saadc_init();
    saadc_sampling_event_init();
    saadc_sampling_event_enable();

#if ENABLE_PA_LNA
    pa_lna_init(BT840X_CTX, BT840X_CRX);
#endif

    ble_stack_init();

#if MESH_FEATURE_GATT_ENABLED
    gap_params_init();
    conn_params_init();
#endif

#if ENABLE_PA_LNA
    ERROR_CHECK(sd_ble_opt_set(BLE_COMMON_OPT_PA_LNA, &ble_pa_lna_opts));
#endif

    mesh_init();

#if ENABLE_PA_LNA
    mesh_pa_lna_gpiote_enable(&m_pa_lna_params);
#endif
}

After all this, it provisions fine, but times out on the next step where it's requesting the composition data.

Thanks.

  • Hi again,

    You wrote: "Note that we have 2 systems that we need to enable PA/LNA, one is for the softdevice (GATT bearer) when you call sd_ble_opt_set() and one is for the mesh stack (ADV bearer) when you call mesh_pa_lna_gpiote_enable()."

    The sd_ble_opt_set() goes with the proxy connection, correct? I.e. Mesh device to iPad. Also for provisioning.

    And the mesh_pa_lna_gpiote_enable() is for communication between the mesh devices, right?

    Does that mean they can be independently controlled? I.e. I only want the PA/LNA enabled for mesh communication, but not for BLE communication? The TxPower can be different as well?

    Thanks.

  • Hi Ferdi, 
    Yes, correct. The mesh protocol is handled by mesh stack and the Proxy and PB-GATT are handled by the softdevice. 
    The mesh stack runs in the timeslot space when the softdevice gives the mesh stack controls of the radio. They have independent radio configurations. 

  • If I want to only enable the PA/LNA for the mesh stack, but not for the softdevice it needs to toggle the 'bypass' pin on the PA/LNA chip. How would I do this?

    Also, can I do the sd_ble_gap_tx_power_set(BLE_GAP_TX_POWER_ROLE_ADV, m_adv_handle, ...) at the end of the mesh_adv_data_set() function in mesh_adv.c since that is where the handle is set?

    Thanks.

Related