Connect PineCone BL602 to LoRa Transceiver

📝 7 Mar 2021

Suppose we have a garden in our home. (Or rooftop)

Is there an affordable way to monitor our garden with Environmental Sensors (and Soil Sensors)…

Here’s a solution: PineCone BL602 RISC-V Board with a LoRa Transceiver!

Today we shall transmit some LoRa packets by connecting PineCone BL602 to a LoRa Transceiver: Semtech SX1276 or Hope RF96

UPDATE: We have a new LoRa Driver for SX1262 (Pine64 RFM90 LoRa Module)… Check this out

The LoRa Firmware in this article will run on PineCone, Pinenut and Any BL602 Board.

PineCone BL602 RISC-V Board connected to Hope RF96 LoRa Transceiver

PineCone BL602 RISC-V Board connected to Hope RF96 LoRa Transceiver

§1 Connect BL602 to LoRa Transceiver

Connect BL602 to SX1276 or RF96 as follows…

PineCone BL602 RISC-V Board connected to Hope RF96 LoRa Transceiver

BL602 PinSX1276 / RF96 PinWire Colour
GPIO 1ISO (MISO)Green
GPIO 2Do Not Connect
GPIO 3SCKYellow
GPIO 4OSI (MOSI)Blue
GPIO 14NSSOrange
GPIO 17RSTWhite
3V33.3VRed
GNDGNDBlack

CAUTION: Always connect the Antenna before Powering On… Or the LoRa Transceiver may get damaged! See this

Here’s a closer look at the pins connected on BL602…

PineCone BL602 RISC-V Board connected to Hope RF96 LoRa Transceiver

Why is BL602 Pin 2 unused?

GPIO 2 is the Unused SPI Chip Select on BL602.

We won’t use this pin because we’ll control Chip Select ourselves on GPIO 14. (See this)

Here are the pins connected on our LoRa Transceiver: SX1276 or RF96…

(ISO and OSI appear flipped in this pic… Rotate your phone / computer screen 180 degrees for the proper perspective)

PineCone BL602 RISC-V Board connected to Hope RF96 LoRa Transceiver

Why are so many pins on SX1276 (or RF96) unused?

Unlike WiFi, LoRa networks can be really simple. Today we shall configure our LoRa Transceiver for the simplest “Fire And Forget” Mode…

  1. Blast out a packet of 64 bytes over the airwaves

  2. Don’t verify whether our packet has been received

  3. Don’t receive any packets

This is ideal for simple sensors (like our garden sensors) that are powered by batteries and can tolerate a few lost packets. (Because we’ll send the sensor data periodically anyway)

So this means we won’t use all the pins on SX1276 (or RF96)?

Yep we may leave pins D0 to D5 disconnected. (Otherwise we’ll run out of pins on BL602!)

§1.1 Getting the LoRa Transceiver and Antenna

The LoRa Transceiver should support the right LoRa Frequency for your region: 434, 780, 868, 915 or 923 MHz…

To find a LoRa Transceiver for your region, go to the Tindie Maker Marketplace and search for SX1276 or RF96. Look for a transceiver that matches your frequency.

The length of the antenna depends on the frequency. They are standard parts and should be easy to find.

I bought the Hope RF96 Breakout Board (923 MHz) and Antenna (923 MHz) from M2M Shop on Tindie. (See this)

What if my region allows multiple LoRa Frequencies?

Choose the LoRa Frequency that’s most popular in your region for The Things Network. (That’s the free, public LoRaWAN network)

Because one day we will probably connect our LoRa Transceiver to The Things Network for collecting sensor data.

Here are the base stations worldwide for The Things Network

§2 Initialise LoRa Transceiver

Let’s look at the code inside our LoRa Firmware for BL602: sdk_app_lora

Super Important: We should set the LoRa Frequency in demo.c like so…

/// TODO: We are using LoRa Frequency 923 MHz 
/// for Singapore. Change this for your region.
#define USE_BAND_923

In a while we shall change 923 to the LoRa Frequency for our region: 434, 780, 868, 915 or 923 MHz. (Check this list)

For now we’ll study this function init_driver that initialises the LoRa Driver for SX1276 (and RF96) in demo.c

/// Command to initialise the SX1276 / RF96 driver
static void init_driver(char *buf, int len, int argc, char **argv) {
    //  Set the LoRa Callback Functions
    RadioEvents_t radio_events;
    memset(&radio_events, 0, sizeof(radio_events));  //  Must init radio_events to null, because radio_events lives on stack!
    radio_events.TxDone    = on_tx_done;
    radio_events.RxDone    = on_rx_done;
    radio_events.TxTimeout = on_tx_timeout;
    radio_events.RxTimeout = on_rx_timeout;
    radio_events.RxError   = on_rx_error;

init_driver begins by defining the Callback Functions that will be called when we have transmitted or received a LoRa Packet (successfully or unsuccessfully).

But we’re doing LoRa “Fire And Forget” Mode… We’re not checking for errors and we’re not receiving LoRa Packets, remember?

Yep so these Callback Functions are not used today. They will be used in future when we receive LoRa Packets and check for errors.

Next we call Radio.Init to initialise BL602’s SPI Port and the LoRa Transceiver

    //  Init the SPI Port and the LoRa Transceiver
    Radio.Init(&radio_events);

Radio.Init will set some registers on our LoRa Transceiver (over SPI).

Then we call Radio.SetChannel to set the LoRa Frequency

    //  Set the LoRa Frequency, which is specific to our region.
    //  For USE_BAND_923: RF_FREQUENCY is set to 923000000.
    Radio.SetChannel(RF_FREQUENCY);

Radio.SetChannel configures the LoRa Frequency by writing to the Frequency Registers in our LoRa Transceiver.

We get ready to transmit by calling Radio.SetTxConfig

    //  Configure the LoRa Transceiver for transmitting messages
    Radio.SetTxConfig(
        MODEM_LORA,
        LORAPING_TX_OUTPUT_POWER,
        0,        //  Frequency deviation: Unused with LoRa
        LORAPING_BANDWIDTH,
        LORAPING_SPREADING_FACTOR,
        LORAPING_CODINGRATE,
        LORAPING_PREAMBLE_LENGTH,
        LORAPING_FIX_LENGTH_PAYLOAD_ON,
        true,     //  CRC enabled
        0,        //  Frequency hopping disabled
        0,        //  Hop period: N/A
        LORAPING_IQ_INVERSION_ON,
        LORAPING_TX_TIMEOUT_MS
    );

These LoRa Parameters should match the settings in the LoRa Receiver. For details, check the Appendix.

At the end of the function we call Radio.SetRxConfig to get ready for receiving LoRa Packets…

    //  Configure the LoRa Transceiver for receiving messages
    Radio.SetRxConfig(
        MODEM_LORA,
        LORAPING_BANDWIDTH,
        LORAPING_SPREADING_FACTOR,
        LORAPING_CODINGRATE,
        0,        //  AFC bandwidth: Unused with LoRa
        LORAPING_PREAMBLE_LENGTH,
        LORAPING_SYMBOL_TIMEOUT,
        LORAPING_FIX_LENGTH_PAYLOAD_ON,
        0,        //  Fixed payload length: N/A
        true,     //  CRC enabled
        0,        //  Frequency hopping disabled
        0,        //  Hop period: N/A
        LORAPING_IQ_INVERSION_ON,
        true      //  Continuous receive mode
    );    
}

Since we’re not receiving LoRa Packets, this code won’t be used.

(The code in this article is based on the LoRa Ping program from Mynewt OS. More about this in the Appendix.)

§3 Transmit LoRa Packet

Now that we have initialised our LoRa Transceiver, let’s send a LoRa Packet!

We’ll send a PING message like so: demo.c

/// Command to send a LoRa message. Assume that 
/// SX1276 / RF96 driver has been initialised.
static void send_message(char *buf, int len, int argc, char **argv) {
    //  Send the "PING" message
    send_once(1);
}

send_message calls send_once to send the PING message.

send_once is defined here…

/// We send a "PING" message
const uint8_t loraping_ping_msg[] = "PING";

/// We expect a "PONG" response (in future)
const uint8_t loraping_pong_msg[] = "PONG";

/// 64-byte buffer for our LoRa message
static uint8_t loraping_buffer[LORAPING_BUFFER_SIZE];  

/// Send a LoRa message. If is_ping is 0, 
/// send "PONG". Otherwise send "PING".
static void send_once(int is_ping) {
    //  Copy the "PING" or "PONG" message to the transmit buffer
    if (is_ping) {
        memcpy(loraping_buffer, loraping_ping_msg, 4);
    } else {
        memcpy(loraping_buffer, loraping_pong_msg, 4);
    }

Here we copy PING into our 64-byte Transmit Buffer.

We fill up the remaining space in the Transmit Buffer with the values 0, 1, 2, …

    //  Fill up the remaining space in the transmit 
    //  buffer (64 bytes) with values 0, 1, 2, ...
    for (int i = 4; i < sizeof loraping_buffer; i++) {
        loraping_buffer[i] = i - 4;
    }

Then we call Radio.Send to transmit the 64-byte buffer as a LoRa Packet…

    //  Send the transmit buffer (64 bytes)
    Radio.Send(
        loraping_buffer,        //  Transmit buffer
        sizeof loraping_buffer  //  Buffer size: 64 bytes
    );
}

Did we forget to specify the receipient for the LoRa message…?

That’s the simplicity of the LoRa “Fire And Forget” Mode… It broadcasts our message over the airwaves!

We shouldn’t broadcast sensitive messages in the clear. But we’ll allow it for our simple garden sensors. (LoRaWAN supports encrypted messages, as we’ll learn in a while)

(The Radio functions belong to the LoRa SX1276 Driver that was ported from Mynewt OS to BL602. More about this in the Appendix)

§4 Build and Run the LoRa Firmware

Let’s run the LoRa Demo Firmware for BL602.

Find out which LoRa Frequency we should use for your region…

Download the Firmware Binary File sdk_app_lora.bin for your LoRa Frequency…

Alternatively, we may build the Firmware Binary File sdk_app_lora.bin from the source code

## Download the lora branch of lupyuen's bl_iot_sdk
git clone --recursive --branch lora https://github.com/lupyuen/bl_iot_sdk
cd bl_iot_sdk/customer_app/sdk_app_lora

## TODO: Set the LoRa Frequency in sdk_app_lora/demo.c. 
## Edit the file and look for the line...
##   #define USE_BAND_923
## Change 923 to the LoRa Frequency for your region: 
##   434, 780, 868, 915 or 923 MHz
## See https://www.thethingsnetwork.org/docs/lorawan/frequencies-by-country.html

## TODO: Change this to the full path of bl_iot_sdk
export BL60X_SDK_PATH=$HOME/bl_iot_sdk
export CONFIG_CHIP_NAME=BL602
make

## For WSL: Copy the firmware to /mnt/c/blflash, which refers to c:\blflash in Windows
mkdir /mnt/c/blflash
cp build_out/sdk_app_lora.bin /mnt/c/blflash

More details on building bl_iot_sdk

(Remember to use the lora branch, not the default master branch)

§4.1 Flash the firmware

Follow these steps to install blflash

  1. “Install rustup”

  2. “Download and build blflash”

We assume that our Firmware Binary File sdk_app_lora.bin has been copied to the blflash folder.

Set BL602 to Flashing Mode and restart the board…

For PineCone:

  1. Set the PineCone Jumper (IO 8) to the H Position (Like this)

  2. Press the Reset Button

For BL10:

  1. Connect BL10 to the USB port

  2. Press and hold the D8 Button (GPIO 8)

  3. Press and release the EN Button (Reset)

  4. Release the D8 Button

For Ai-Thinker Ai-WB2, Pinenut and MagicHome BL602:

  1. Disconnect the board from the USB Port

  2. Connect GPIO 8 to 3.3V

  3. Reconnect the board to the USB port

Enter these commands to flash sdk_app_lora.bin to BL602 over UART…

## For Linux:
blflash flash build_out/sdk_app_lora.bin \
    --port /dev/ttyUSB0

## For macOS:
blflash flash build_out/sdk_app_lora.bin \
    --port /dev/tty.usbserial-1420 \
    --initial-baud-rate 230400 \
    --baud-rate 230400

## For Windows: Change COM5 to the BL602 Serial Port
blflash flash c:\blflash\sdk_app_lora.bin --port COM5

(For WSL: Do this under plain old Windows CMD, not WSL, because blflash needs to access the COM port)

More details on flashing firmware

§4.2 Run the firmware

Set BL602 to Normal Mode (Non-Flashing) and restart the board…

For PineCone:

  1. Set the PineCone Jumper (IO 8) to the L Position (Like this)

  2. Press the Reset Button

For BL10:

  1. Press and release the EN Button (Reset)

For Ai-Thinker Ai-WB2, Pinenut and MagicHome BL602:

  1. Disconnect the board from the USB Port

  2. Connect GPIO 8 to GND

  3. Reconnect the board to the USB port

After restarting, connect to BL602’s UART Port at 2 Mbps like so…

For Linux:

screen /dev/ttyUSB0 2000000

For macOS: Use CoolTerm (See this)

For Windows: Use putty (See this)

Alternatively: Use the Web Serial Terminal (See this)

More details on connecting to BL602

§4.3 Enter LoRa commands

Let’s enter some commands to transmit a LoRa Packet!

  1. Press Enter to reveal the command prompt.

  2. Enter help to see the available commands…

    help
    ====User Commands====
    read_registers           : Read registers
    send_message             : Send LoRa message
    spi_result               : Show SPI counters
  3. First we initialise our LoRa Transceiver.

    Enter this command…

    init_driver

    This command calls the function init_driver, which we have seen earlier.

  4. We should see this…

    init_driver
    port0 eventloop init = 42010760
    [HAL] [SPI] Init :
    port=0, mode=0, polar_phase = 1, freq=200000, tx_dma_ch=2, rx_dma_ch=3, pin_clk=3, pin_cs=2, pin_mosi=1, pin_miso=4
    set rwspeed = 200000
    hal_gpio_init: cs:2, clk:3, mosi:1, miso: 4
    hal_gpio_init: SPI controller mode
    hal_spi_init.

    The above messages say that our SPI Port has been configured by the BL602 SPI HAL.

    hal_spi_transfer = 1
    transfer xfer[0].len = 1
    Tx DMA src=0x4200cc58, dest=0x4000a288, size=1, si=1, di=0, i=1
    Rx DMA src=0x4000a28c, dest=0x4200cc54, size=1, si=0, di=1, i=1
    recv all event group.
    ...

    init_driver has just configured our SPI Transceiver by setting the registers over SPI.

  5. Next we transmit a LoRa Packet

    send_message

    This command calls the function send_message, which we have seen earlier.

  6. We should see this…

    send_message
    hal_spi_transfer = 1
    transfer xfer[0].len = 1
    Tx DMA src=0x4200cc58, dest=0x4000a288, size=1, si=1, di=0, i=1
    Rx DMA src=0x4000a28c, dest=0x4200cc54, size=1, si=0, di=1, i=1
    recv all event group.
    ...

    That’s send_message blasting the 64-byte LoRa Packet to the airwave, in the simple “Fire And Forget” Mode.

    (The LoRa Driver copies the 64-byte Transmit Buffer to our LoRa Transceiver over SPI, byte by byte. Hence the numerous SPI requests.)

    Watch the video on YouTube

    Check out the complete log

  7. If we wish to transmit LoRa Packets automatically on startup (without entering any commands), check out the LoRa Ping Firmware…

    LoRa Ping Firmware for BL602

§5 Troubleshoot LoRa

How will we know whether BL602 is connected correctly to the LoRa Transceiver?

Enter this command to read the first 16 registers from our LoRa Transceiver over SPI…

read_registers

We should see…

...
Register 0x02 = 0x1a
Register 0x03 = 0x0b
Register 0x04 = 0x00
Register 0x05 = 0x52
...

Take the values of Registers 2, 3, 4 and 5.

Compare them with the Register Table in the SX1276 (or RF96) Datasheet.

The values should be identical: 0x1a, 0x0b, 0x00, 0x52

Reading registers from our LoRa transceiver

Can we find out the frequency that’s used by our LoRa Transceiver?

Enter these commands…

init_driver

read_registers

Look for the values of Registers 6, 7 and 8

Register 0x06 = 0x6c
Register 0x07 = 0x80
Register 0x08 = 0x00

Put the values together and multiply by the Frequency Step

0x6c8000 * 61.03515625

This produces 434,000,000… Which means that our LoRa Transceiver is transmitting at 434 MHz.

Computing the LoRa frequency

What about the ACTUAL frequency that our LoRa Transceiver is transmitting on?

Use a Spectrum Analyser like RF Explorer.

In the next section we’ll use a more advanced tool for spectrum analysis: Software Defined Radio.

RF Explorer (at right) featured in WandaVision season 1 episode 4

RF Explorer (at right) featured in WandaVision season 1 episode 4

See the source code for read_registers

See the output from read_registers

§6 Visualise LoRa with Software Defined Radio

Our LoRa packet

What’s this? A glowing helix? Magic seahorse?

That’s how our 64-byte LoRa Packet appears when captured with a Software Defined Radio!

Watch the video on YouTube

LoRa Packets look like a column of diagonal strokes. Here’s a clearer example…

Source

This is called a LoRa Chirp… A clever way to transmit packets over great distances (with little power) by shifting the pitch (frequency) up or down during transmission…

Source

(Yep it’s inspired by chirping birds)

Why is this important for LoRa?

LoRa operates on the Radio Frequency band known as the ISM Band (for Industrial, Scientific and Medical purpose).

The ISM Band is used by many types of wireless gadgets. (It’s like 2.4 GHz WiFi, but at a lower frequency: 434, 868, 915 or 923 MHz) And it’s prone to noise and interference caused by other gadgets.

By transmitting packets in this unique chirping pattern, LoRa ensures that packets will be delivered over long distances in spite of the noise and interference.

(LoRa doesn’t guarantee 100% reliable delivery, of course)

Airspy R2 SDR

Airspy R2 SDR

§6.1 Capture LoRa packets with Airspy SDR

Let’s capture and visualise our LoRa Packet with Airspy R2 SDR (Software Defined Radio)…

  1. Place the Airspy R2 SDR close to our LoRa Antenna (See pic above)

  2. Download and install CubicSDR

  3. Launch CubicSDR. Set the Airspy SDR Sample Rate to 10 MHz

  4. Set the Center Frequency to our LoRa Frequency

  5. Click and drag the Speed Bar (at right) to the maximum speed

  6. Run the LoRa Firmware for BL602 and enter the commands…

    init_driver
    
    send_message
  7. Our LoRa Packet should scroll down like so…

CubicSDR software with Airspy R2 SDR

CubicSDR software with Airspy R2 SDR

Watch the video on YouTube

If there is a lot of background noise, cover the LoRa Transceiver and Airspy SDR with a Metal Pot (as an improvised Faraday Cage)…

Improvised Faraday Cage

Improvised Faraday Cage

(I bought my Airspy R2 here)

§7 LoRa vs LoRaWAN

What’s the difference between LoRa, LoRaWAN and The Things Network?

  1. LoRa = The wireless network protocol

    WiFi is also a wireless network protocol.

    (More about LoRa)

  2. LoRaWAN = A managed, secure LoRa network

    It’s like going to Starbucks and connecting to their WiFi.

    (More about LoRaWAN)

  3. The Things Network = The free LoRaWAN network that’s operated by volunteers around the world.

    People actually set up base stations and allow free access.

    Our garden sensors could connect to The Things Network… So that we may browse the sensor data conveniently.

    (More about The Things Network)

    (There are commercial LoRaWAN networks, like Helium and potentially Amazon Sidewalk)

3 Levels of LoRa! Where are we right now?

Our BL602 implementation of LoRa is at Level 1. Well actually, half of Level 1. (Since we only transmit packets)

To complete Level 1 of our Wireless IoT Endeavour, we need to receive LoRa Packets.

In the next two articles, we shall use RAKwireless WisBlock as a LoRa Node for receiving the LoRa Packets from BL602 (and the other way around).

Read the followup articles here…

(Many thanks to RAKwireless for providing the WisBlock Connected Box!)

RAKwireless WisBlock Connected Box

RAKwireless WisBlock Connected Box

§8 What’s Next

We have come a loooong way since I first experimented with LoRa in 2016

Now is the right time to build LoRa gadgets. Stay tuned for more LoRa Adventures!

Meanwhile there’s plenty more code in the BL602 IoT SDK to be deciphered and documented: ADC, DAC, WiFi, Bluetooth LE,

Come Join Us… Make BL602 Better!

🙏 👍 😀

Got a question, comment or suggestion? Create an Issue or submit a Pull Request here…

lupyuen.github.io/src/lora.md

§9 Notes

  1. This article is the expanded version of this Twitter Thread

  2. How much power would our BL602 + LoRa sensor actually consume? How long would our battery-powered sensor last?

    We would need to do a thorough Power Profiling. (See this)

    Excellent project for schools and universities!

  3. LoRa is a proprietary protocol, but it has been reverse engineered.

    There is an open-source implementation of LoRa with SDR. (See this)

  4. What’s the Maximum LoRa Packet Size?

    A LoRa Packet can have up to 222 bytes of data. (See this)

    So LoRa is good for sending sensor data (like our garden sensors) and short messages… But not graphical images.

  5. Thanks to Pine64 for featuring this article on their podcast!

    Watch the Pine64 Podcast on YouTube

§10 Appendix: LoRa Configuration

We configure the LoRa Frequency here: demo.c

/// TODO: We are using LoRa Frequency 923 MHz for Singapore. Change this for your region.
#define USE_BAND_923

#if defined(USE_BAND_433)
    #define RF_FREQUENCY               434000000 /* Hz */
#elif defined(USE_BAND_780)
    #define RF_FREQUENCY               780000000 /* Hz */
#elif defined(USE_BAND_868)
    #define RF_FREQUENCY               868000000 /* Hz */
#elif defined(USE_BAND_915)
    #define RF_FREQUENCY               915000000 /* Hz */
#elif defined(USE_BAND_923)
    #define RF_FREQUENCY               923000000 /* Hz */
#else
    #error "Please define a frequency band in the compiler options."
#endif

Here are the LoRa Parameters: demo.c

/// LoRa Parameters
#define LORAPING_TX_OUTPUT_POWER            14        /* dBm */

#define LORAPING_BANDWIDTH                  0         /* [0: 125 kHz, */
                                                      /*  1: 250 kHz, */
                                                      /*  2: 500 kHz, */
                                                      /*  3: Reserved] */
#define LORAPING_SPREADING_FACTOR           7         /* [SF7..SF12] */
#define LORAPING_CODINGRATE                 1         /* [1: 4/5, */
                                                      /*  2: 4/6, */
                                                      /*  3: 4/7, */
                                                      /*  4: 4/8] */
#define LORAPING_PREAMBLE_LENGTH            8         /* Same for Tx and Rx */
#define LORAPING_SYMBOL_TIMEOUT             5         /* Symbols */
#define LORAPING_FIX_LENGTH_PAYLOAD_ON      false
#define LORAPING_IQ_INVERSION_ON            false

#define LORAPING_TX_TIMEOUT_MS              3000    /* ms */
#define LORAPING_RX_TIMEOUT_MS              1000    /* ms */
#define LORAPING_BUFFER_SIZE                64      /* LoRa message size */

Below are the LoRa Transceiver Settings for SX1276 and RF96: sx1276.h

#define SX1276_SPI_IDX      0  //  SPI Port 0
#define SX1276_SPI_SDI_PIN  1  //  SPI Serial Data In Pin  (formerly MISO)
#define SX1276_SPI_SDO_PIN  4  //  SPI Serial Data Out Pin (formerly MOSI)
#define SX1276_SPI_CLK_PIN  3  //  SPI Clock Pin
#define SX1276_SPI_CS_PIN  14  //  SPI Chip Select Pin
#define SX1276_SPI_CS_OLD   2  //  Unused SPI Chip Select Pin
#define SX1276_NRESET      17  //  Reset Pin
#define SX1276_DIO0        12  //  DIO0 Pin
#define SX1276_DIO1        11  //  DIO1 Pin
#define SX1276_DIO2         5  //  DIO2 Pin
#define SX1276_DIO3         8  //  DIO3 Pin
#define SX1276_DIO4         0  //  TODO: DIO4 Pin
#define SX1276_DIO5         0  //  TODO: DIO5 Pin
#define SX1276_SPI_BAUDRATE  (200 * 1000)  //  SPI Frequency (200 kHz)
#define SX1276_LF_USE_PA_BOOST  1  //  Enable Power Amplifier Boost for LoRa Frequency below 525 MHz
#define SX1276_HF_USE_PA_BOOST  1  //  Enable Power Amplifier Boost for LoRa Frequency 525 MHz and above

§11 Appendix: Porting LoRa Driver from Mynewt to BL602

In this article, we called some Radio Functions that belong to the LoRa SX1276 Driver.

Here’s where the Radio Functions are defined…

The LoRa SX1276 Driver was ported from Mynewt OS to BL602 IoT SDK…

Here’s how we ported the SX1276 driver code from Mynewt to BL602.

§11.1 GPIO

In Mynewt we call hal_gpio_init_out to configure a GPIO Output Pin and set the output to High: sx1276-board.c

//  Configure Chip Select pin as a GPIO Output Pin and set to High
int rc = hal_gpio_init_out(
    RADIO_NSS,  //  Pin number
    1           //  Set to High
);
assert(rc == 0);

Here’s the equivalent code in BL602: sx1276-board.c

//  Configure Chip Select pin as a GPIO Pin
GLB_GPIO_Type pins[1];
pins[0] = RADIO_NSS;  //  Pin number
BL_Err_Type rc2 = GLB_GPIO_Func_Init(
    GPIO_FUN_SWGPIO,  //  Configure as GPIO 
    pins,             //  Pins to be configured
    sizeof(pins) / sizeof(pins[0])  //  Number of pins (1)
);
assert(rc2 == SUCCESS);    

//  Configure Chip Select pin as a GPIO Output Pin (instead of GPIO Input)
int rc = bl_gpio_enable_output(RADIO_NSS, 0, 0);
assert(rc == 0);

//  Set Chip Select pin to High, to deactivate SX1276
rc = bl_gpio_output_set(RADIO_NSS, 1);
assert(rc == 0);

§11.2 SPI

In Mynewt we configure the SPI Port like so: sx1276-board.c

//  Disable the SPI port
hal_spi_disable(RADIO_SPI_IDX);

//  Configure the SPI port
spi_settings.data_order = HAL_SPI_MSB_FIRST;
spi_settings.data_mode  = HAL_SPI_MODE0;
spi_settings.baudrate   = MYNEWT_VAL(SX1276_SPI_BAUDRATE);
spi_settings.word_size  = HAL_SPI_WORD_SIZE_8BIT;
int rc = hal_spi_config(RADIO_SPI_IDX, &spi_settings);
assert(rc == 0);

//  Enable the SPI port
rc = hal_spi_enable(RADIO_SPI_IDX);
assert(rc == 0);

Here’s how we do the same on BL602: sx1276-board.c

//  Configure the SPI Port
int rc = spi_init(
    &spi_device,    //  SPI Device
    RADIO_SPI_IDX,  //  SPI Port
    0,              //  SPI Mode: 0 for Controller
    //  TODO: Due to a quirk in BL602 SPI, we must set
    //  SPI Polarity-Phase to 1 (CPOL=0, CPHA=1).
    //  But actually Polarity-Phase for SX1276 should be 0 (CPOL=0, CPHA=0). 
    1,                    //  SPI Polarity-Phase
    SX1276_SPI_BAUDRATE,  //  SPI Frequency
    2,                    //  Transmit DMA Channel
    3,                    //  Receive DMA Channel
    SX1276_SPI_CLK_PIN,   //  SPI Clock Pin 
    SX1276_SPI_CS_OLD,    //  Unused SPI Chip Select Pin
    SX1276_SPI_SDI_PIN,   //  SPI Serial Data In Pin  (formerly MISO)
    SX1276_SPI_SDO_PIN    //  SPI Serial Data Out Pin (formerly MOSI)
);
assert(rc == 0);

Note: SPI Mode 0 in Mynewt (CPOL=0, CPHA=0) becomes SPI Polarity-Phase 1 in BL602 (CPOL=0, CPHA=1).

More about this

In Mynewt, we call hal_spi_tx_val to read and write a byte over SPI.

On BL602, we implement hal_spi_tx_val like so: sx1276.c

§11.3 Interrupts

In Mynewt we configure pins D0 to D5 to trigger an interrupt when the input changes: sx1276-board.c

//  Configure GPIO Input Pin for Interrupt
int rc = hal_gpio_irq_init(
    SX1276_DIO0,            //  Pin number
    irqHandlers[0],         //  Interrupt handler
    NULL,                   //  Argument
    HAL_GPIO_TRIG_RISING,   //  Trigger interrupt when input goes from Low to High
    HAL_GPIO_PULL_NONE      //  No pullup and no pulldown
);
assert(rc == 0);
//  Enable the GPIO Input interrupt
hal_gpio_irq_enable(SX1276_DIO0);

For receiving LoRa Packets, we handle GPIO Interrupts with the BL602 Interrupt HAL and the NimBLE Porting Library…

§11.4 Timers

For receiving LoRa Packets with Timeout, we use Timers from the NimBLE Porting Library…