LoRa Basics™ Modem User Manual v1.0 documentation
Porting¶
This chapter provides some basic information about porting LoRa Basics™ Modem to a new MCU.
Overview¶
LoRa Basics Modem runs on top of a hardware abstraction layer (HAL), meant to facilitate porting. In what follows, LBM_DIR refers to the directory containing LoRa Basics Modem, which is named “lora_basics_modem” in the software developer kit (SDK).
MCU Requirements¶
Currently, LoRa Basics Modem has only been tested on 32-bit microcontrollers. The following MCU features are required:
Software MCU reset.
A timer with 1ms resolution (timer accuracy compensation is possible by widening the reception windows).
SPI controller.
A random number generator.
Non-volatile storage for modem state storage.
A dedicated (non-shared) GPIO MCU interrupt for the transceiver is recommended.
Worst-case stack usage is currently unknown. A simple example performing a join and a few uplinks uses approximately 2kB of stack RAM.
RAM and Flash requirements for STM32L476 are listed below. These values were
determined by modifying the apps/lorawan/makefile
, and building, as follows:
APP_TRACE ?= no
MODEM_TRACE ?= no
DEBUG ?= 0
OPT ?= -O2
$ make REGION=EU_868,US_915,CN_470_RP_1_0,CN_470
text data bss dec hex filename
522 0 0 522 20a lr1110_bootloader.o
1242 0 0 1242 4da lr1110_crypto_engine.o
15 0 0 15 f lr1110_driver_version.o
2559 0 0 2559 9ff lr1110_radio.o
486 0 0 486 1e6 lr1110_regmem.o
1540 0 0 1540 604 lr1110_system.o
2140 0 0 2140 85c lr1110_wifi.o
1670 0 0 1670 686 lr1110_gnss.o
2414 0 0 2414 96e ral_lr1110.o
236 0 0 236 ec ralf_lr1110.o
2755 0 0 2755 ac3 radio_planner.o
32 0 0 32 20 radio_planner_hal.o
564 0 0 564 234 smtc_modem_api_lr1110_crypto_engine.o
134 0 0 134 86 smtc_modem_api_lr1110_system.o
1618 0 2780 4398 112e lorawan_api.o
760 0 0 760 2f8 dm_downlink.o
4727 17 394 5138 1412 modem_context.o
4590 0 1826 6416 1910 smtc_modem.o
2392 0 640 3032 bd8 smtc_modem_test.o
756 0 0 756 2f4 fifo_ctrl.o
52 0 0 52 34 modem_utilities.o
106 0 0 106 6a smtc_modem_services_hal.o
950 0 0 950 3b6 smtc_clock_sync.o
912 0 0 912 390 lorawan_certification.o
3244 3 635 3882 f2a modem_supervisor.o
58 0 0 58 3a almanac_update.o
230 0 0 230 e6 stream.o
1882 0 0 1882 75a rose.o
810 0 0 810 32a alc_sync.o
1334 0 0 1334 536 file_upload.o
1161 0 24 1185 4a1 lr1110_se.o
936 16 0 952 3b8 smtc_modem_crypto.o
2971 0 0 2971 b9b region_cn_470.o
1601 0 0 1601 641 region_cn_470_rp_1_0.o
1555 0 0 1555 613 region_eu_868.o
2565 0 0 2565 a05 region_us_915.o
7798 0 0 7798 1e76 lr1_stack_mac_layer.o
2764 0 0 2764 acc lr1mac_core.o
606 0 0 606 25e lr1mac_utilities.o
5908 0 0 5908 1714 smtc_real.o
1313 0 0 1313 521 smtc_duty_cycle.o
836 0 0 836 344 smtc_lbt.o
1980 8 0 1988 7c4 lr1mac_class_c.o
72724 44 6299 79067 134db (TOTALS)
System Design Considerations¶
When designing hardware, consider that LoRa Basics Modem is designed to use the transceiver’s DIO1 pad as an interrupt source.
There are two principal interrupt sources that interact with LoRa Basics Modem: a timer interrupt, and a radio interrupt. The system interrupt priorities must be configured in a way that the timer and radio interrupts do not interrupt one another, or nest.
The current implementation of LoRa Basics Modem was designed to perform certain radio operations in interrupt context. For this reason, HAL API commands are provided to disable and enable these two interrupt sources. Therefore, when designing hardware to run LoRa Basics Modem, it is recommended that the MCU GPIO lines you select for the transceiver’s DIO1 interrupt request line do not share an MCU interrupt flag with other GPIO lines that are used for any timing-critical hardware. If this is done, it will still be possible to react quickly to these interrupt sources if necessary.
Because the timer and radio interrupt service routines may perform radio operations over the transceiver SPI bus, it is likely that interrupt handlers will reconfigure the MCU hardware SPI controller. If the MCU hardware SPI controller is used to communicate with other devices, keep in mind that timer or radio interrupts could occur, interfering with communication. In certain circumstances, it may be possible to coordinate the SPI communication between the various devices, but that is beyond the scope of this document.
The recommendation is that the radio should have exclusive use of its MCU hardware SPI controller device.
Note
Some of the limitations described above may be relaxed in a future LoRa Basics Modem release.
HAL Implementation¶
Porting LoRa Basics Modem to a new architecture requires implementing the API commands described by the prototypes in the following header file:
LBM_DIR/smtc_modem_hal/smtc_modem_hal.h
smtc_modem_hal_reset_mcu()¶
void smtc_modem_hal_reset_mcu(void);
- Brief
LoRa Basics Modem may need to reset the MCU. For instance, on initial startup, or if the state stored in non-volatile memory is corrupt, a fresh configuration will be written to non-volatile memory, and the MCU will be reset.
smtc_modem_hal_reload_wdog()¶
void smtc_modem_hal_reload_wdog(void);
- Brief
If your HAL implementation configures a watchdog timer, then you may also wish to implement this to reload the watchdog timer. Currently, the only code in LoRa Basics Modem that actually calls this HAL API command is the test code in
smtc_modem_test.c
.
smtc_modem_hal_get_time_in_s()¶
uint32_t smtc_modem_hal_get_time_in_s(void);
- Brief
LoRa Basics Modem uses this command to help perform various LoRaWAN® activities that do not have significant time accuracy requirements, such as nb_trans retransmissions.
- Returns
The current system uptime in seconds.
smtc_modem_hal_get_time_compensation_in_s()¶
int32_t smtc_modem_hal_get_time_compensation_in_s(void);
- Brief
Suppose that, due to MCU clock inaccuracy, the principal time source used for
smtc_modem_hal_get_time_in_s()
significantly lags or leads the real time. If the MCU HAL developer is able to quantify this deviation and calculate an integer number of seconds that will additively correct the time source, it should be returned by this HAL API command. Otherwise, this command should return the value 0.- Returns
Additive correction of the time source. Returns zero, if unknown.
smtc_modem_hal_get_compensated_time_in_s()¶
uint32_t smtc_modem_hal_get_compensated_time_in_s(void);
- Brief
This command should be implemented as follows:
uint32_t smtc_modem_hal_get_compensated_time_in_s() { return smtc_modem_hal_get_time_compensation_in_s() + smtc_modem_hal_get_time_in_s(); }
Note
In the future, this HAL API command may be removed.
Note
The ALCSync service can be used to obtain accurate time from the network GPS clock. Currently, the ALCSync implementation is the only LoRa Basics Modem code that uses the compensated time, as determined above. This may seem unnecessary, since the purpose of ALCSync is to provide an accurate clock. However, if the time is accurately compensated by smtc_modem_hal_get_time_compensation_in_s() and smtc_modem_hal_get_compensated_time_in_s(), ALCSync will require less network activity to keep the clock perfectly synchronized.
- Returns
Additive correction of the time source. Returns zero, if unknown.
smtc_modem_hal_get_time_in_ms()¶
uint32_t smtc_modem_hal_get_time_in_ms(void);
- Brief
Currently, this command is used to timestamp radio interrupts and to busy wait until it is time to open an RX window.
- Returns
The system uptime, in milliseconds.
smtc_modem_hal_start_timer()¶
void smtc_modem_hal_start_timer(
uint32_t milliseconds,
void (*callback)(void* context),
void* context
);
- Brief
This HAL API command should start a timer that will expire at the requested time. Upon expiration, it should call the provided callback, and provide the provided callback with context as its sole argument.
The current design of LoRa Basics Modem has only been tested in the case where the provided callback is executed in interrupt context, with interrupts disabled.
Note
This callback may communicate with the radio using the MCU SPI device.
- Parameters
[in]
milliseconds
Number of milliseconds before callback execution
[in]
callback
Callback to execute
[in]
context
Argument that will be passed to callback
smtc_modem_hal_stop_timer()¶
void smtc_modem_hal_stop_timer(void);
- Brief
This HAL API command should stop the timer that may have been started with
smtc_modem_hal_start_timer()
. It is possible that this command will be called when the timer is not running.
smtc_modem_hal_disable_modem_irq()¶
void smtc_modem_hal_disable_modem_irq(void);
- Brief
This HAL API command must disable the two interrupt sources that execute LoRa Basics Modem code: the timer, and the transceiver DIO1 interrupt source. Please also refer to System Design Considerations.
smtc_modem_hal_enable_modem_irq()¶
void smtc_modem_hal_enable_modem_irq(void);
- Brief
This HAL API command must enable the two interrupt sources that execute LoRa Basics Modem code: the timer, and the transceiver DIO1 interrupt source. Please also refer to System Design Considerations.
smtc_modem_hal_context_restore()¶
void smtc_modem_hal_context_restore(
const modem_context_type_t ctx_type,
uint8_t* buffer,
uint32_t size
);
- Brief
This restores to RAM a data structure of type ctx_type that has previously been stored to non-volatile memory by calling smtc_modem_hal_context_store().
- Parameters
[in]
ctx_type
Type of modem context that needs to be restored
[out]
buffer
Buffer pointer to write to
[in]
size
Buffer size to read in bytes
smtc_modem_hal_context_store()¶
void smtc_modem_hal_context_store(
const modem_context_type_t ctx_type,
const uint8_t* buffer,
uint32_t size
);
- Brief
This stores a data structure of type ctx_type from RAM to non-volatile memory.
- Parameters
[in]
ctx_type
Type of modem context that needs to be saved
[in]
buffer
Buffer pointer to write from
[in]
size
Buffer size to written in bytes
smtc_modem_hal_store_crashlog()¶
void smtc_modem_hal_store_crashlog(uint8_t crashlog[CRASH_LOG_SIZE]);
- Brief
This stores the modem crash log to non-volatile memory. On most MCUs, RAM is preserved upon reset, so it may be possible to use RAM for this purpose.
- Parameters
[in]
crashlog
Buffer pointer to write from
smtc_modem_hal_restore_crashlog()¶
void smtc_modem_hal_restore_crashlog(uint8_t crashlog[CRASH_LOG_SIZE]);
- Brief
This retrieves the modem crash log from non-volatile memory. On most MCUs, RAM is preserved upon reset, so it may be possible to use RAM for this purpose.
- Parameters
[out]
crashlog
Buffer pointer to write to
smtc_modem_hal_set_crashlog_status()¶
void smtc_modem_hal_set_crashlog_status(bool available);
- Brief
This command is used to store the modem crash log status to non-volatile memory. On most MCUs, RAM is preserved upon reset, so it may be possible to use RAM for this purpose.
- Parameters
[in]
available
True if a crash log is available, false otherwise
smtc_modem_hal_get_crashlog_status()¶
bool smtc_modem_hal_get_crashlog_status(void);
- Brief
This command is used to get the modem crash log status from non-volatile memory. On most MCUs, RAM is preserved upon reset, so it may be possible to use RAM for this purpose.
- Returns
The crash log status, as written using smtc_modem_hal_set_crashlog_status().
smtc_modem_hal_get_random_nb()¶
uint32_t smtc_modem_hal_get_random_nb(void);
- Brief
Returns a uniformly distributed 32-bit unsigned random integer.
- Returns
The random integer.
smtc_modem_hal_get_random_nb_in_range()¶
uint32_t smtc_modem_hal_get_random_nb_in_range(
const uint32_t val_1,
const uint32_t val_2
);
- Brief
Returns a uniformly distributed unsigned random integer from the closed interval [val_1, …, val_2] or [val_2, …, val_1].
This command may be implemented as follows:
uint32_t smtc_modem_hal_get_random_nb_in_range( const uint32_t val_1, const uint32_t val_2 ) { if( val_1 <= val_2 ) { return ( uint32_t )( ( smtc_modem_hal_get_random_nb( ) % ( val_2 - val_1 + 1 ) ) + val_1 ); } else { return ( uint32_t )( ( smtc_modem_hal_get_random_nb( ) % ( val_1 - val_2 + 1 ) ) + val_2 ); } }
Note
In the future, this HAL API command may be removed.
- Returns
The random integer.
smtc_modem_hal_get_signed_random_nb_in_range()¶
int32_t smtc_modem_hal_get_signed_random_nb_in_range(
const int32_t val_1,
const int32_t val_2
);
- Brief
Returns a uniformly distributed signed random integer from the closed interval [val_1, …, val_2] or [val_2, …, val_1].
This command may be implemented as follows:
int32_t smtc_modem_hal_get_signed_random_nb_in_range( const int32_t val_1, const int32_t val_2 ) { uint32_t tmp_range = 0; // ( val_1 <= val_2 ) ? ( val_2 - val_1 ) : ( val_1 - val_2 ); if( val_1 <= val_2 ) { tmp_range = ( val_2 - val_1 ); return ( int32_t )( ( val_1 + smtc_modem_hal_get_random_nb_in_range( 0, tmp_range ) ) ); } else { tmp_range = ( val_1 - val_2 ); return ( int32_t )( ( val_2 + smtc_modem_hal_get_random_nb_in_range( 0, tmp_range ) ) ); } }
Note
In the future, this HAL API command may be removed.
- Returns
The random integer.
smtc_modem_hal_irq_config_radio_irq()¶
void smtc_modem_hal_irq_config_radio_irq(
void ( *callback )( void* context ),
void* context
);
- Brief
This command is called by the modem to define the callback that should be executed when a radio event occurs.
- Parameters
[in]
callback
The callback that will be executed upon radio interrupt service request
[in]
context
The argument that will be provided to the above callback
smtc_modem_hal_irq_is_radio_irq_pending()¶
bool smtc_modem_hal_irq_is_radio_irq_pending(void);
- Brief
This command must indicate whether an interrupt service request is pending inside the MCU hardware interrupt controller.
- Returns
True, if an interrupt is pending; false, otherwise.
smtc_modem_hal_start_radio_tcxo()¶
bool smtc_modem_hal_start_radio_tcxo(void);
- Brief
This command is called by the modem when the TCXO should be started. If no TCXO is used, implement an empty command.
smtc_modem_hal_stop_radio_tcxo()¶
bool smtc_modem_hal_stop_radio_tcxo(void);
- Brief
This command is called by the modem when the TCXO should be stopped. If no TCXO is used, implement an empty command.
smtc_modem_hal_get_radio_tcxo_startup_delay_ms()¶
uint32_t smtc_modem_hal_get_radio_tcxo_startup_delay_ms(void);
- Brief
Indicates to LoRa Basics Modem how much time the TCXO needs to get started.
- Returns
The needed TCXO startup time, in milliseconds. Returns 0 if no TCXO is used.
smtc_modem_hal_get_battery_level()¶
uint8_t smtc_modem_hal_get_battery_level(void);
- Brief
Indicates the current battery state.
- Returns
A value between 0 (for 0%) and 255 (for 100%).
smtc_modem_hal_get_temperature()¶
int8_t smtc_modem_hal_get_temperature(void);
- Brief
Indicates the current system temperature.
- Returns
The temperature, in degrees Celsius.
smtc_modem_hal_get_voltage()¶
int8_t smtc_modem_hal_get_voltage(void);
- Brief
Indicates the current battery voltage.
- Returns
The battery voltage, in units of 20mV.
smtc_modem_hal_get_board_delay_ms()¶
int8_t smtc_modem_hal_get_board_delay_ms(void);
- Brief
Used to return how much time passes between the moment the MCU calls
ral_set_tx()
orral_set_rx()
, and the moment when the radio receiver enters RX or TX state. This varies depending on the MCU clock speed and SPI bus speed.- Returns
The board delay, in milliseconds.
smtc_modem_hal_print_trace()¶
void smtc_modem_hal_print_trace(
const char* fmt,
...
);
- Brief
This outputs a printf-style variable-length argument list to the logging subsystem.
- Parameters
[in]
fmt
printf-style string
[in]
…
Arguments that accompany fmt
Alternatively, if you have some other command for logging that takes a variable
number of arguments, you may simply wish to comment out the declaration of
smtc_modem_hal_print_trace()
in LBM_DIR/smtc_modem_hal/smtc_modem_hal.h
, then edit
LBM/smtc_modem_core/modem_config/smtc_modem_hal_dbg_trace.h
as follows:
Add a proper
#include
directive pointing to your command declaration, such as‘``#include <stdio.h>
Replace the two instances of
smtc_modem_hal_print_trace
by your logging command, such asprintf
.
Source Code Files¶
The following source files must be compiled for use cases:
LBM/smtc_modem_core/radio_drivers/lr1110_driver/src/lr1110_bootloader.c
LBM/smtc_modem_core/radio_drivers/lr1110_driver/src/lr1110_crypto_engine.c
LBM/smtc_modem_core/radio_drivers/lr1110_driver/src/lr1110_driver_version.c
LBM/smtc_modem_core/radio_drivers/lr1110_driver/src/lr1110_radio.c
LBM/smtc_modem_core/radio_drivers/lr1110_driver/src/lr1110_regmem.c
LBM/smtc_modem_core/radio_drivers/lr1110_driver/src/lr1110_system.c
LBM/smtc_modem_core/radio_drivers/lr1110_driver/src/lr1110_wifi.c
LBM/smtc_modem_core/radio_drivers/lr1110_driver/src/lr1110_gnss.c
LBM/smtc_modem_core/smtc_ral/src/ral_lr1110.c
LBM/smtc_modem_core/smtc_ralf/src/ralf_lr1110.c
LBM/smtc_modem_core/radio_planner/src/radio_planner.c
LBM/smtc_modem_core/radio_planner/src/radio_planner_hal.c
LBM/smtc_modem_core/modem_core/smtc_modem_api_lr1110_crypto_engine.c
LBM/smtc_modem_core/modem_core/smtc_modem_api_lr1110_system.c
LBM/smtc_modem_core/lorawan_api/lorawan_api.c
LBM/smtc_modem_core/device_management/dm_downlink.c
LBM/smtc_modem_core/device_management/modem_context.c
LBM/smtc_modem_core/modem_core/smtc_modem.c
LBM/smtc_modem_core/modem_core/smtc_modem_test.c
LBM/smtc_modem_core/modem_services/fifo_ctrl.c
LBM/smtc_modem_core/modem_services/modem_utilities.c
LBM/smtc_modem_core/modem_services/smtc_modem_services_hal.c
LBM/smtc_modem_core/modem_services/smtc_clock_sync.c
LBM/smtc_modem_core/modem_services/lorawan_certification.c
LBM/smtc_modem_core/modem_supervisor/modem_supervisor.c
LBM/smtc_modem_core/smtc_modem_services/src/almanac_update/almanac_update.c
LBM/smtc_modem_core/smtc_modem_services/src/stream/stream.c
LBM/smtc_modem_core/smtc_modem_services/src/stream/rose.c
LBM/smtc_modem_core/smtc_modem_services/src/alc_sync/alc_sync.c
LBM/smtc_modem_core/smtc_modem_services/src/file_upload/file_upload.c
LBM/smtc_modem_core/smtc_modem_crypto/smtc_modem_crypto.c
LBM/smtc_modem_core/lr1mac/src/smtc_real/src/region_cn_470.c
LBM/smtc_modem_core/lr1mac/src/smtc_real/src/region_cn_470_rp_1_0.c
LBM/smtc_modem_core/lr1mac/src/smtc_real/src/region_eu_868.c
LBM/smtc_modem_core/lr1mac/src/smtc_real/src/region_us_915.c
LBM/smtc_modem_core/lr1mac/src/lr1_stack_mac_layer.c
LBM/smtc_modem_core/lr1mac/src/lr1mac_core.c
LBM/smtc_modem_core/lr1mac/src/lr1mac_utilities.c
LBM/smtc_modem_core/lr1mac/src/smtc_real/src/smtc_real.c
LBM/smtc_modem_core/lr1mac/src/services/smtc_duty_cycle.c
LBM/smtc_modem_core/lr1mac/src/services/smtc_lbt.c
LBM/smtc_modem_core/lr1mac/src/lr1mac_class_c/lr1mac_class_c.c
The following files must be compiled to enable LR1110 hardware cryptography:
LBM/smtc_modem_core/smtc_modem_crypto/lr1110_secure_element/lr1110_se.c
Otherwise, the following files must be compiled to enable software cryptography:
LBM/smtc_modem_core/smtc_modem_crypto/soft_secure_element/aes.c
LBM/smtc_modem_core/smtc_modem_crypto/soft_secure_element/cmac.c
LBM/smtc_modem_core/smtc_modem_crypto/soft_secure_element/soft_se.c
Include Directories¶
The following directories must be included for all use cases:
LBM/smtc_modem_core/radio_drivers/lr1110_driver/src
LBM/smtc_modem_core/smtc_ral/src
LBM/smtc_modem_core/smtc_ralf/src
LBM/smtc_modem_api
LBM/smtc_modem_core/modem_supervisor
LBM/smtc_modem_core/smtc_modem_crypto
LBM/smtc_modem_core/modem_config
LBM/smtc_modem_core/radio_planner/src
LBM/smtc_modem_core/lr1mac/src
LBM/smtc_modem_core/smtc_modem_services/headers
LBM/smtc_modem_core/modem_services
LBM/smtc_modem_core/smtc_modem_services/src/file_upload
LBM/smtc_modem_core/smtc_modem_services/src/stream
LBM/smtc_modem_core/lr1mac/src/smtc_real/src
LBM/smtc_modem_core/lr1mac/src/services
LBM/smtc_modem_core/device_management
LBM/smtc_modem_core/lorawan_api
LBM/smtc_modem_core/lr1mac/src/lr1mac_class_c
LBM/smtc_modem_core/modem_core
LBM/smtc_modem_core/modem_core/smtc_modem_services/headers
LBM/smtc_modem_core/smtc_modem_services/src
LBM/smtc_modem_core/lr1mac
LBM/smtc_modem_core/smtc_modem_services
LBM/smtc_modem_core/lr1mac/src/lr1mac_class_b
LBM
LBM/smtc_modem_hal
LBM/smtc_modem_core/smtc_modem_crypto/smtc_secure_element
The following directories must be included to enable LR1110 hardware cryptography:
LBM/smtc_modem_core/smtc_modem_crypto/lr1110_secure_element
Otherwise, the following directories must be included to enable software cryptography:
LBM/smtc_modem_core/smtc_modem_crypto/soft_secure_element
C Preprocessor Definitions¶
When compiling LoRa Basics Modem, it is necessary to define a number of C preprocessor definitions, described below.
Transceiver Selection¶
To select the transceiver, add the following definitions:
LR1110
LR1110_TRANSCEIVER
Region Selection¶
To select the region, add any combination of the following definitions:
REGION_EU_868
REGION_US_915
REGION_CN_470
REGION_CN_470_RP_1_0
Cryptography Operation¶
To enable hardware cryptography on the LR1110, it is necessary to add the following definition:
USE_LR1110_SE
If hardware cryptography is enabled on the LR1110, you can also select the pre-provisioned DevEUI, JoinEUI, and PIN, by adding:
USE_PRE_PROVISIONED_FEATURES
Logging¶
To disable logging, define MODEM_HAL_DBG_TRACE
to be equal to 0.
To enable additional logging of radio-related operations, define
MODEM_HAL_DBG_TRACE_RP
to be equal to 1.
You may wish to use a high-speed UART to implement the trace, because logging can potentially interfere with modem communication.
Rx Window Debugging Tips¶
LoRaWAN requires accurate receive window timing. This section provides some tips indicating how to verify that the window timing is good.
Clock Error Compensation¶
If your crystal error is more than 500 ppm, add a call (before joining the
network) to the modem smtc_modem_set_crystal_error()
API command to specify
the crystal error, in parts per thousand. Large crystal error values result in
wider Rx windows.
For more information, see Application Note AN1200.24.
Getting Started With Rx Window Fine Tuning¶
Initially, define the HAL API command smtc_modem_hal_get_board_delay_ms() to return 0.
You may also wish to configure the MCU to provide the most accurate possible clock to smtc_modem_hal_get_time_in_ms() and smtc_modem_hal_start_timer(). Having an accurate MCU clock should facilitate debugging.
Enable proper logging, as described in Logging. Using 921600 baud is recommended, if your hardware permits. Please keep in mind that logging code may interfere with timing, depending on your MCU speed and UART baud rate.
Consider the case where LoRa Basics Modem is running without a functioning
packet forwarder, or without an appropiate configuration on the network server.
In this case, uplinks will not be responded to, and will result in an RxTimeout
interrupt. Since LoRa Basics Modem knows what RxTimeout value was used,
the time elapsed between the TxDone interrupt and the RxTimeout interrupt can be
used to position the start of the Rx window. This is the purpose of the fine-tuning algorithm, which can be found in
LBM/smtc_modem_core/lr1mac/src/lr1_stack_mac_layer.c
.
On every reception failure, the fine-tuning algorithm generates log messages like this:
DR3 Fine tune correction (ms) = 1, error fine tune (ms) = 0, lr1_mac->rx_offset_ms = -18
If this algorithm is working properly, on every reception failure for a given data rate, the fine tune correction value for that data rate will be incremented or decremented, until it converges on a value that results in reliable reception. From this point on, error fine tune should stay close to zero. This is a hands-off approach that works in many cases.
Rx Window Debugging Configuration¶
Fine tuning convergence may be slow, or not occur. Debugging this type of problem, and determing what value to use for smtc_modem_hal_get_board_delay_ms(), is the purpose of the following sections of this chapter.
In order to know whether something is interfering with Rx window placement, it is important to know the desired length of the window, as requested by the MCU. This desired window length can then be compared to the actual window length, as measured by a logic analyzer.
With this in mind, temporarily deactivate the window fine-tuning feature by globally defining the preprocessor definition BSP_LR1MAC_DISABLE_FINE_TUNE.
Add IRQ Timing Log Information¶
The following change to command rp_radio_irq()
in
LBM/smtc_modem_core/radio_planner/src/radio_planner.c
will make it possible to
observe in the log the MCU time at which every radio IRQ arrives. Change the
following line of code
SMTC_MODEM_HAL_RP_TRACE_PRINTF( " RP: INFO - Radio IRQ received for hook #%u\n", rp->radio_task_id );
to read
SMTC_MODEM_HAL_RP_TRACE_PRINTF( " RP: INFO - Radio IRQ received for hook #%u at time %u\n", rp->radio_task_id, now );
Add Ready and Trigger Timing Log Information¶
The variable start_time_ms contains the MCU time at which the SetRx
command is
meant to be sent to the MCU. Shortly after, this provokes the opening of the Rx
window.
When functioning properly, the command
lr1_stack_mac_rx_lora_launch_callback_for_rp()
is expected to be executed a
short time before start_time_ms. After preparing the radio for reception, a
while loop inside this command will wait until the current time is equal to
start_time_ms. At this point in time, called the trigger time, the command
ral_set_rx()
will be called. The point at which this while loop was entered is
called the ready time.
The following change to command lr1_stack_mac_rx_lora_launch_callback_for_rp()
in LBM/smtc_modem_core/lr1mac/src/lr1_stack_mac_layer.c
will make it possible to
observe in the log the MCU ready time and the MCU trigger time.
Change the following block of code
// Wait the exact time
while( ( int32_t )( rp->tasks[id].start_time_ms - rp_hal_timestamp_get( ) ) > 0 )
{
}
if( ral_set_rx( &( rp->radio->ral ), rp->radio_params[id].rx.timeout_in_ms ) != RAL_STATUS_OK )
{
smtc_modem_hal_mcu_panic( );
}
rp_stats_set_rx_timestamp( &rp->stats, rp_hal_timestamp_get( ) );
to read
// Wait the exact time
uint32_t tcurrent_ms = rp_hal_timestamp_get( );
while( ( int32_t )( rp->tasks[id].start_time_ms - rp_hal_timestamp_get( ) ) > 0 )
{
}
if( ral_set_rx( &( rp->radio->ral ), rp->radio_params[id].rx.timeout_in_ms ) != RAL_STATUS_OK )
{
smtc_modem_hal_mcu_panic( );
}
rp_stats_set_rx_timestamp( &rp->stats, rp_hal_timestamp_get( ) );
SMTC_MODEM_HAL_TRACE_PRINTF( "RX ready at %d, waited until %d\n", tcurrent_ms, rp->tasks[id].start_time_ms );
Perform a Debugging Session¶
Once you have the logging configured as above, connect a logic analyzer, and run the loRaWAN example.
First, confirm that the MCU ready time is less than the MCU trigger time. If
not, this indicates that there is no margin for error because either
lr1_stack_mac_rx_lora_launch_callback_for_rp()
is being entered too late, or
the radio preparations are taking too long. If you debug this with additional
trace calls, be careful to do so in a way that does not interfere with the
timing. Other possibilities for this include using LED diagnostics.
Referring back to the Add IRQ Timing Log Information section, observe the log to determine at what MCU time the TxDone interrupt was timestamped by the MCU. Define this value tm1.
Referring back to section Add Ready and Trigger Timing Log Information, observe the log to determine at what MCU time the SetTx call was initiated. Define this value tm2.
Define delta1 = tm2 - tm1.
Using a logic analyzer that is able to decode the radio SPI bus communication, search for the last transceiver command preceding the first transceiver post-reset radio interrupt service request. The command should be SetTx, which can be verified by looking at the transceiver user manual. Define ta1 to be the time, according to the logic analyzer, at which the DIO1 line rises. Shortly after this moment at which the DIO1 line rises, the MCU software should read and clear the IRQ status, causing the DIO1 line to fall.
Now, search for the last transceiver command preceding the second post-reset transceiver interrupt service request. This command should be SetRx, which can be verified by looking at the transceiver user manual. Define ta2 to be the time, according to the logic analyzer, at which the NSS line fell right before sending SetRx. This corresponds approximately to the trigger time.
Define delta2 = ta2 – ta1.
delta1 is the MCU time between the TxDone interrupt and the initiation of the SetRx command.
delta2 is the logic analyzer time between the TxDone interrupt and the initiation of the SetRx command.
delta1 and delta2 should be within 1ms of one another, after correcting for the MCU clock accuracy.
Recall that the board delay is the amount of time between the ready time and the moment the transceiver initiates reception. Consider the SetRx command on the logic analyzer, and observe the amount of time between the moment that NSS falls and the moment that NSS rises. This value, in milli-seconds, is reasonably close to the board delay. Edit the smtc_modem_hal_get_board_delay_ms() HAL command so that it returns this value.
You may now wish to re-activate the window fine tuning feature by undefining the preprocessor definition BSP_LR1MAC_DISABLE_FINE_TUNE.