Fanstel BT840XE - Move UART Pins to Use PA/LNA (FEM)

753 views
Skip to first unread message

Rob Crouthamel

unread,
Aug 12, 2019, 11:22:31 AM8/12/19
to openthread-users
FYI, if you are using the Fanstel BT840XE module with Power Amplifier & Low-Noise Amplifier and nRF52840, be aware that the control pins for the PA/LNA conflict with the OpenThread default assignment of the UART (CLI) pins.  You'll want to edit UART_PIN_RX and UART_PIN_TX in /examples/platforms/nrf52840/platform-config.h to use different pins, then recompile the libraries.

- Rob

Stuart Longland

unread,
Oct 13, 2019, 6:36:07 PM10/13/19
to openthre...@googlegroups.com
One silly question with these dongles… I've actually made a fork of
OpenThread to support these:

https://github.com/vrtsystems/openthread/tree/feature/fanstel-bt840x

Principle difference is the Makefile for nRF52840 now supports
specifying BOARD=bt840x which moves the RX and TX UART pins around.

I might add support for other board-specific mods as well as that
becomes necessary.

The bit I'm struggling with though is getting code into the damn things.

- Nordic's nRFConnect programmer tells me the firmware image needs to be
signed.
- I've tried flashing using a Black Magic Probe: it refuses to chip erase.
- I've also tried building the "open" programmer and loading it via a
PCA10066 dev board, it still insists on a signed image and just flatly
refuses to accept the code. `dmesg` shows it still thinks its running
the "secure" boot loader.

Right now I've got a dongle that boots up with a red LED (solid, not
blinking or anything). Not promising.

Fanstel mention a key in the documentation and say the private key is
published (the one given in their PDF is presumably the corresponding
*public* key), they don't say _where_ that private key is published, so
I'm a bit stuck with how to sign the image.

Regards,
--
Stuart Longland (aka Redhatter, VK4MSL)

I haven't lost my mind...
...it's backed up on a tape somewhere.

Rob Crouthamel

unread,
Oct 14, 2019, 12:07:22 PM10/14/19
to openthread-users
Hi Stuart,

I suspect with enough work, you *could* get the Fanstel bootloader to accept your image.  In addition to figuring out the key, you'd need to know what hardware and firmware version their bootloader was expecting, etc.  You may also need to change the flash locations for the NCP image to work properly with the bootloader.  These are important things to master later in the development cycle and you're probably best working though some of the secure bootloader examples and tutorials provided by Nordic before attempting to modify the NCP image and/or Fanstel bootloader to pull this off.

I program these devices using the SWD interface and the appropriate Tag-Connect cable.  I'm too cheap to buy a proper Segger J-Link adapter, so I jumper the Tag-Connect cable onto the pins of a Nordic nRF52 dev kit.  From there you can program with a variety of methods.  Since its so tedious, I ususally create a .BAT file to perform the actual programming of a pre-compiled .hex file.  For example, the .BAT file I am currently using for NCP on the USB840 is shown below:

-------------------------------
@ECHO OFF
ECHO.
ECHO Ready to program Fanstel USB840F USB Dongle with NCP over Native USB.  Connect a board!
ECHO.
PAUSE
nrfjprog --chiperase --family NRF52 --program C:\1-BUILD\Thread_NCP\v3_USB840F\usb\hex\thread_ncp_ftd_usb_USB840F.hex --reset
PAUSE
-------------------------------

Best,
Rob

Stuart Longland

unread,
Oct 15, 2019, 11:16:02 PM10/15/19
to openthre...@googlegroups.com
On 15/10/19 2:07 am, Rob Crouthamel wrote:
>
> I suspect with enough work, you *could* get the Fanstel bootloader to
> accept your image.  In addition to figuring out the key, you'd need to
> know what hardware and firmware version their bootloader was expecting,
> etc.  You may also need to change the flash locations for the NCP image
> to work properly with the bootloader.  These are important things to
> master later in the development cycle and you're probably best working
> though some of the secure bootloader examples and tutorials provided by
> Nordic before attempting to modify the NCP image and/or Fanstel
> bootloader to pull this off.

Well, in the end this is what worked…

I contacted Fanstel, Leo Chen was able to provide me with the private
key (private.pem).

The ot-ncp-ftd binary was dumped to Intel hex format, then bundled up as
a DFU update using the following command:

$ nrfutil pkg generate --hw-version 52 --sd-req 0 \
--application /tmp/ot-cli-ftd.nrf.hex \
--application-version 1 \
--key-file /tmp/private.pem /tmp/ot-ncp-ftd.zip

Getting --sd-req right was the hardest bit.

This could then be flashed:
$ nrfutil dfu usb-serial -pkg /tmp/ot-ncp-ftd.zip -p /dev/ttyACM0

> I program these devices using the SWD interface and the appropriate
> Tag-Connect cable.  I'm too cheap to buy a proper Segger J-Link adapter,
> so I jumper the Tag-Connect cable onto the pins of a Nordic nRF52 dev
> kit.  From there you can program with a variety of methods.  Since its
> so tedious, I ususally create a .BAT file to perform the actual
> programming of a pre-compiled .hex file.  For example, the .BAT file I
> am currently using for NCP on the USB840 is shown below:

Yeah, for some reason this thing will just _not_ flash over SWD… tried
using the nRF52 PDK as a JTAG programmer, using a Black Magic Probe.

Maybe the boot-loader in this revision is a bit more strict and doesn't
let you work around it with SWD.

Do you recall if there was anything special needed to handle the PA/LNA
on these units? CC2538+CC2592 required some special set-up which is now
supported in OpenThread with suitable build options set, and Fanstel do
mention some code for driving the SKY661112.

I haven't tried integrating this code yet.

Rob Crouthamel

unread,
Oct 17, 2019, 5:19:29 PM10/17/19
to openthread-users
Hi Stuart,

Sorry for the delay.  Yes, it took me a bit to figure out the FEM setup.  My application is multiprotocol, so I have to setup the FEM for BLE and Thread individually.  Last time I tried this code, BLE was working well.  Thread code was running, but the FEM didn't appear to actually be working.  Nordic's Openthread contributors have done a bunch of work on the radio driver since, so I'm getting ready to give it another shot.

Here's my code.  Pin definitions required.  Again, watch out for conflicts with the CLI UART pins -- that stumped me for a couple of days.

Main:
 #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); //CHL High = High Power Mode
 nrf_gpio_pin_clear
(APP_CPS_PIN); //Low = active, High = bypass
 pa_lna_prep
(APP_PA_PIN,APP_LNA_PIN);
 pa_lna_init_sd
(APP_PA_PIN,APP_LNA_PIN);
 pa_lna_init_thread
(APP_PA_PIN,APP_LNA_PIN);
 
#endif
...pa_lna.c
#include <stdint.h>
#include <string.h>

#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"
#include <openthread/platform/platform-fem.h>


void pa_lna_prep(uint32_t gpio_pa_pin, uint32_t gpio_lna_pin)
{
    ret_code_t err_code;
    err_code = nrf_drv_gpiote_init();
    if(err_code != NRF_ERROR_INVALID_STATE)
        APP_ERROR_CHECK(err_code);

    nrf_drv_gpiote_out_config_t config = GPIOTE_CONFIG_OUT_TASK_TOGGLE(false);

    err_code = nrf_drv_gpiote_out_init(gpio_pa_pin, &config);
        APP_ERROR_CHECK(err_code);

    err_code = nrf_drv_gpiote_out_init(gpio_lna_pin, &config);
        APP_ERROR_CHECK(err_code);
    
    err_code = nrf_drv_ppi_init();
    if(err_code != NRF_ERROR_MODULE_ALREADY_INITIALIZED)
        APP_ERROR_CHECK(err_code);
}

void pa_lna_init_sd(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));
    
    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);
    
    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)
        {
            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)
        {         
            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_DEBUG("PA/LNA Successfully Enabled for BLE");
    }
    return;
}

void pa_lna_init_thread(uint32_t gpio_pa_pin, uint32_t gpio_lna_pin)
{
    PlatformFemConfigParams cfg;
    uint32_t gpiote_ch_pa = NULL;
    uint32_t gpiote_ch_lna = NULL;
    ret_code_t err_code;  

    memset(&cfg, 0, sizeof(cfg));

    // Get some PPI channels to use
    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);

    if(gpio_pa_pin != NULL)
    {
        if(gpiote_ch_pa == NULL)
        {       
            gpiote_ch_pa = nrf_drv_gpiote_out_task_addr_get(gpio_pa_pin);
        }
    
        // PA config
        cfg.mPaCfg.mEnable     = 1;
        cfg.mPaCfg.mActiveHigh = 1;
        cfg.mPaCfg.mGpioPin    = gpio_pa_pin;
    }

    if(gpio_lna_pin != NULL)
    {
        if(gpiote_ch_lna == NULL)
        {     
            gpiote_ch_lna = nrf_drv_gpiote_out_task_addr_get(gpio_lna_pin);
        }
        
        // LNA config
        cfg.mLnaCfg.mEnable     = 1;
        cfg.mLnaCfg.mActiveHigh = 1;
        cfg.mLnaCfg.mGpioPin    = gpio_lna_pin;
    }

    cfg.mPpiChIdClr    = ppi_clr_ch;
    cfg.mPpiChIdSet    = ppi_set_ch;    

    //cfg.mGpiotePaChId  = gpiote_ch_pa;
    cfg.mGpiotePaChId  = (gpiote_ch_pa - NRF_GPIOTE_BASE) >> 2;   // GPIOTE channel used for pa pin toggling

    //cfg.mGpioteLnaChId = gpiote_ch_lna;
    cfg.mGpiotePaChId  = (gpiote_ch_lna - NRF_GPIOTE_BASE) >> 2;   // GPIOTE channel used for pa pin toggling
    
    PlatformFemSetConfigParams(&cfg);
    return;
}

 
I'm sure my code isn't perfect, feedback would be great.  I'm also hoping Nordic puts a FEM example in the next release of their SDK for Thread.

Best,
Rob

Stuart Longland

unread,
Nov 28, 2019, 2:36:52 AM11/28/19
to openthre...@googlegroups.com
Hi Rob,

On 18/10/19 7:19 am, Rob Crouthamel wrote:
> Sorry for the delay. Yes, it took me a bit to figure out the FEM setup.
> My application is multiprotocol, so I have to setup the FEM for BLE and
> Thread individually. Last time I tried this code, BLE was working well.
> Thread code was running, but the FEM didn't appear to actually be working.
> Nordic's Openthread contributors have done a bunch of work on the radio
> driver since, so I'm getting ready to give it another shot.

I gave that code a try, but found a few things:
- the Nordic SDK has changed a lot, and so the API calls needed
migration to make it all work (e.g. nrf_drv → nrfx for GPIOTE and PPI)
- after managing to get it to compile, I found `scan` would crash the
nRF52840.

Some digging revealed that the 802.15.4 core code in the Nordic SDK
seems to have hooks there for handling a FEM, as you say though,
precious little in the way of documentation and examples. I think I
managed to muddle my way through.

I haven't tried multi-protocol stuff, but this seemed to make the FEM
get up and boogey.

I tried the command line example application, telling it to ping ff03::1
repeatedly whilst I watched the radio frequencies around 2405MHz with a
HackRF One and GQRX. I seem to be getting similar signal strengths to
our WideSky Hub which uses a CC2538+CC2592.

The FEM interface looks very generic, so I've tried to keep the
integration into OpenThread that way, following a similar pattern to
what I did for CC2592.

There's an extra function there for controlling the CHL pin, everything
else is managed by the FEM. There's scope to handle other
configurations of the SKY66112.

$ git clone -b feature/fanstel-bt840x \
https://github.com/vrtsystems/openthread.git
$ make -f examples/Makefile-nrf52840 BOOTLOADER=USB USB=1 BOARD=bt840x

I'll have to do some receive tests, but things look promising at least
on the transmit side.

Regards,

Paweł Czaplewski

unread,
Jun 30, 2020, 7:33:37 AM6/30/20
to openthread-users
Hi there,
Were you finally able to notice any signal strength increase with your changes? I have tested openthread version with your commits and didn't see any improvement.
Maximum range in a building is very poor - around 15m (with NRF52840 SoC programmable txpower set to max of +8dbm) without enclosure. I'm afraid that if power amplifier and LNA are not enabled they may attenuate the signal - may that be the case?

I am testing on these dongles:

USB840X-V6 with BT840X-V5 module and USB840F-V6 with BT840X-V4 module


If it is so that not enabled PA is reducing the signal strength USB840F-BT840F should give a little better result (version without front end module).

Regards, Pawel

r...@farmjenny.com

unread,
Nov 30, 2020, 2:38:31 PM11/30/20
to openthread-users

Stuart,

Thanks for all of your previous help.  I recently doubled back to your posts on using the Fanstel BT840X variants with FEM.  I am also following your issue https://github.com/openthread/openthread/issues/4995  and your related fork https://github.com/vrtsystems/openthread/tree/feature/fanstel-bt840x.

I see that you were working on this as recently as Aug 2020, but the Google group posts on the subject are from late 2019.  In this post from Dec 5, 2019, you were seeing a 12dB discrepancy in power compared to a TI design.  More recently, in issue 4995, you report success in the field with your forked code, where the comments show that you’re seeing about 22dBm ( 14dBm PA gain plus 8dBm from the radio) – this seems to correspond roughly to the +21dBm max output of the SKY66112-11 (I suppose it saturates?).

I assume you’re controlling the power of the radio itself thru the NCP’s API (wpantund, ot-agent).  With your fork and its changes in radio.c, it appears that we should be passing the value desired *after* the PA (e.g., +22dBm) and should let radio.c and the 802.15.4 correct this and set the nrf52 accordingly.  Can you confirm my understanding?

FYI, I am using the BT840X on a Pi HAT I designed, using RCP over SPI, not on the USB840X -- so I build with a couple of different switches.

Thanks,

Rob

Stuart Longland

unread,
Nov 30, 2020, 4:35:53 PM11/30/20
to openthre...@googlegroups.com
Hi Rob,
On 1/12/20 5:38 am, r...@farmjenny.com wrote:
> I see that you were working on this as recently as Aug 2020, but the Google
> group posts on the subject are from late 2019. In this
> <https://groups.google.com/g/openthread-users/c/-Wq70iQNv0o/m/GlVPwdH9BAAJ>
> post from Dec 5, 2019, you were seeing a 12dB discrepancy in power compared
> to a TI design. More recently, in issue 4995, you report success in the
> field with your forked code, where the comments show that you’re seeing
> about 22dBm ( 14dBm PA gain plus 8dBm from the radio) – this seems to
> correspond roughly to the +21dBm max output of the SKY66112-11 (I suppose
> it saturates?).

Yeah, I'm still slowly trying to get to understand how the FEM stuff
works in the nRF52840. If I recall correctly, I needed to alter the
OpenThread code to expose a few additional signals for control of the
front-end.

On the TO-DO list is to track down the nRF21540 DK, which I understand
should be coming on the market in the next few months. Then, I can try
to tweak the implementation so that we can support both Nordic's FEM and
the Skyworks FEM.

I might've missed that bit in the datasheet about the max power being
+21dBm. I'll have to double check. That said, 1dBm isn't much: in this
case it's 125mW vs 158mW. I think the best thing to do would be to try
driving it at +21dBm vs +22dBm and see if there's any "splatter" in the
signal.

Measurements were very much "finger in the air", measured from a HackRF
One (my only tax-deductable piece of ham radio gear so far) with a
2.4GHz antenna situated a few metres away. Comparisons were done
between the USB540X and another reference device (might've been the
CC2592 evaluation board or the WideSky Hub, can't remember).

No fancy measurement equipment here, so I wouldn't put a lot of faith in
the precision of these measurements.

> I assume you’re controlling the power of the radio itself thru the NCP’s
> API (wpantund, ot-agent). With your fork and its changes in radio.c, it
> appears that we should be passing the value desired **after** the PA (e.g.,
> +22dBm) and should let radio.c and the 802.15.4 correct this and set the
> nrf52 accordingly. Can you confirm my understanding?

That's correct, so basically I'm considering the FEM as part of the
NCP/RCP/whatever. This is in-line with what would be expected for radio
frequency licenses, where the license stipulates a maximum radiated power.

It's easier to figure that out when you know absolutely what transmit
power is going "up the coax" rather than having to compute that based
off the power of the PA module and the power level setting of the radio
itself.

> FYI, I am using the BT840X on a Pi HAT
> <https://www.tindie.com/products/farmjenny/lte-thread-border-router-hat/> I
> designed, using RCP over SPI, not on the USB840X -- so I build with a
> couple of different switches.
Nice, we've switched to RevPis as the base computer at the moment, which
fits nicely in the switchboard along with other DIN-rail mounted
equipment. These are basically a Raspberry Pi compute module stuffed
into a DIN-rail case with 9-30V power supply.

https://revolution.kunbus.com/revpi-core/

Not sure if there's an SPI interface exposed internally that would allow
such a module to be made, for now plugging the dongle in the front
works. Maybe later we can try sweet-talking Kunbus into making a
nRF52840 + nRF21540 or nRF52840 + SKY66112 module for Thread use.

r...@farmjenny.com

unread,
Dec 2, 2020, 10:30:16 AM12/2/20
to openthread-users
Hi Stuart,

I took a closer look at the Fanstel and Skyworks documents.  The Skyworks PA doesn't want to see any more than 5dBm at its input, so asking the nRF5 to output +8dBm may* be pushing it too hard (* there is a filter between the nRF52 and the PA, which surely has some loss, but probably not enough to prevent overload).  Fanstel recommends setting the nRF52 to +2dBm (FCC/IC approvals), and tells us we can expect to see about +21/22dBm (depending on model) out of the PA (its rated max).
Figure 4 of the Skyworks datasheet is probably helpful here.  Notice that when you hit the upper limit at each bias point (CHL pin), the PA's TX current consumption spikes without getting much more TX power output.  Like you, I lack a proper RF power meter, so I'll probably use a scope and sense resistor (or one of those great new Nordic Power Profilers (shameless plug, hoping for one in my xmas stocking) to chart nRF52 power setting vs. input current to confirm we're in the 19-20dBm sweet spot below and not too far up on the power curve.  There are, however, a finite number of supported TX power levels in the nRF52 (+8,+7,+6,+5,+4,+3,+2,0,-4,-8,-12,-16,-20,-40dBm).  When you ask for an unsupported value in Openthread, I believe the radio driver selects the 'nearest?' supported value.  I'll probably only get about 4-5 points on this curve in the area of interest, hopefully enough to see the shape.


Best,
Rob

Reply all
Reply to author
Forward
0 new messages