Hardware Abstraction Layer
The LMIC library is separated into a large portion of portable code and a small platform-specific part. By implementing the functions of this hardware abstraction layer with the specified semantics, the library can be easily ported to new hardware platforms.
HAL Interface
The following groups of hardware components must be supported:
-
Up to four digital I/O lines are needed in output mode to drive the radio’s antenna switch (
RX
andTX
), the SPI chip select (NSS
), and the reset line (RST
). -
Three digital I/O lines are needed in input mode to sense the radio’s transmitter and receiver states (
DIO0
,DIO1
andDIO2
). -
A SPI unit is needed to read and write the radio’s registers.
-
A timer unit is needed to precisely record events and to schedule new protocol actions.
-
An interrupt controller cab be used to forward interrupts generated by the digital input lines.
This section describes the function interface required to access these hardware components:
void hal_init ()
Initialize the hardware abstraction layer. Configure all components (IO, SPI, TIMER, IRQ) for further use with the hal_xxx()
functions. This function is deprecated and obsolete. The LMIC library calls hal_init_ex()
instead. The client cannot call hal_init()
or hal_init_ex()
directly,as they are called from os_init()
/os_init_ex()
, and they must only be called once.
void hal_init_ex (const void *pHalData)
Initialize the hardware abstraction layer. Configure all components (IO, SPI, TIMER, IRQ) for further use with the hal_xxx()
functions. pHalData is a pointer to HAL-specific data. When running with the Arduino HAL, this must be a pointer to a lmic_pinmap
structure. The LMIC library calls hal_init_ex()
. The client cannot call hal_init()
or hal_init_ex()
directly, as they are called from os_init()
/os_init_ex()
, and they must only be called once.
void hal_failed ()
Perform “fatal failure” action. This function will be called by code assertions on fatal conditions. Possible actions could be HALT or reboot.
void hal_pin_rxtx (u1_t val)
Drive the digital output pins RX and TX (0=receive, 1=transmit).
void hal_pin_rst (u1_t val)
Control the radio RST pin (0=low, 1=high, 2=floating)
void radio_irq_handler (u1_t dio)
When the HAL detects a rising edge on any of the three input lines DIO0
, DIO1
and DIO2
, it must notify the LMIC. It may do this by calling the function radio_irq_handler()
. It must set dio to indicate the line which generated the interrupt (0
, 1
, 2
). This routine is a wrapper for radio_irq_handler_v2()
, and just calls os_getTime()
to get the current time. If your hardware can capture the interrupt time more accurately, your HAL should use radio_irq_handler_v2()
.
void radio_irq_handler_v2 (u1_t dio, os_time_t tIrq)
When the HAL detects a rising edge on any of the three input lines DIO0
, DIO1
and DIO2
, it must notify the LMIC. If the HAL has a high-accuracy time-stamp for when the line changed state, it should call the function radio_irq_handler_v2()
. Set dio
to indicate the line which changed (0
, 1
, 2
). Set tIrq
to the time-stamp of when the line changed state.
void hal_spi_read(u1_t cmd, u1_t* buf, size_t len)
Perform a SPI read. Write the command byte cmd
, then read len
bytes into the buffer starting at buf.
void hal_spi_write(u1_t cmd, const u1_t* buf, size_t len)
Perform a SPI write. Write the command byte cmd
, followed len
bytes from buffer starting at buf
.
u4_t hal_ticks ()
Return 32-bit system time in ticks (same units as ostime_t
) – but note that this is unsigned, whereas ostime_t
is signed.
void hal_waitUntil (u4_t time)
Busy-wait until specified timestamp (in ticks) is reached.
u1_t hal_checkTimer (u4_t targettime)
Check and rewind timer for given targettime
. Return 1 if targettime
is close (not worthwhile programming the timer). Otherwise rewind timer for exact targettime
or for full timer period and return 0. The only action required when targettime
is reached is that the CPU wakes up from possible sleep states.
void hal_disableIRQs ()
Disable all CPU interrupts. Might be invoked nested. But will always be followed by matching call to hal_enableIRQs()
.
void hal_enableIRQs ()
Enable CPU interrupts. When invoked nested, only the outmost invocation actually must enable the interrupts.
void hal_sleep ()
Sleep until interrupt occurs. Preferably system components can be put in low-power mode before sleep, and be re-initialized after sleep. When using the Arduino reference implementation, this is a no-op; the LMIC returns to the caller, who is responsible for arranging to sleep.
s1_t hal_getRssiCal ()
Get the RSSI calibration for the radio, in dB. The radio driver adds this to the indicated RSSI to convert to absolute dB, for doing listen-before-talk computations. Not used unless listen-before-talk is configured for this region.
ostime_t hal_setModulePower (bool val)
Request that the module be powered up or down. If true, TCXO power should be activated, and any normally high-Z control lines should be activated. This function returns the number of ticks of delay that must be inserted before using the radio. If module-level power control is not implemented, or if the radio is already in the desired state, this routine can just return zero. Normally a delay of a few milliseconds is needed when turning power on, but no delay is needed if power is already on or if turning power off.
HAL Reference Implementation for Arduino
The Arduino LMIC library includes a reference implementation of the HAL for the Arduino. Please refer to README.md for information about the implementation.