mirror of
https://github.com/manuelbl/ttn-esp32.git
synced 2025-08-17 20:20:34 +02:00
Reorganize directories
This commit is contained in:
68
src/lmic/config.h
Normal file
68
src/lmic/config.h
Normal file
@ -0,0 +1,68 @@
|
||||
/*******************************************************************************
|
||||
*
|
||||
* ttn-esp32 - The Things Network device library for ESP-IDF / SX127x
|
||||
*
|
||||
* Copyright (c) 2018 Manuel Bleichenbacher
|
||||
*
|
||||
* Licensed under MIT License
|
||||
* https://opensource.org/licenses/MIT
|
||||
*
|
||||
* This the hardware abstraction layer to run LMIC in on ESP32 using ESP-iDF.
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef _lmic_config_h_
|
||||
#define _lmic_config_h_
|
||||
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_TTN_LORA_FREQ_EU_868)
|
||||
#define CFG_eu868 1
|
||||
#elif defined(CONFIG_TTN_LORA_FREQ_US_915)
|
||||
#define CFG_us915 1
|
||||
#else
|
||||
#define TTN_IS_DISABLED 1
|
||||
#define CFG_eu868 1
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_TTN_RADIO_SX1272_73)
|
||||
#define CFG_sx1272_radio 1
|
||||
#elif defined(CONFIG_TTN_RADIO_SX1276_77_78_79)
|
||||
#define CFG_sx1276_radio 1
|
||||
#else
|
||||
#error TTN LoRa radio chip must be configured using 'make menuconfig'
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_TTN_TIMER_0_GROUP_0)
|
||||
#define TTN_TIMER TIMER_0
|
||||
#define TTN_TIMER_GROUP TIMER_GROUP_0
|
||||
#define TTN_CLEAR_TIMER_ALARM TIMERG0.int_clr_timers.t0 = 1
|
||||
#elif defined(CONFIG_TTN_TIMER_1_GROUP_0)
|
||||
#define TTN_TIMER TIMER_1
|
||||
#define TTN_TIMER_GROUP TIMER_GROUP_0
|
||||
#define TTN_CLEAR_TIMER_ALARM TIMERG0.int_clr_timers.t1 = 1
|
||||
#elif defined(CONFIG_TTN_TIMER_0_GROUP_1)
|
||||
#define TTN_TIMER TIMER_0
|
||||
#define TTN_TIMER_GROUP TIMER_GROUP_1
|
||||
#define TTN_CLEAR_TIMER_ALARM TIMERG1.int_clr_timers.t0 = 1
|
||||
#elif defined(CONFIG_TTN_TIMER_1_GROUP_1)
|
||||
#define TTN_TIMER TIMER_1
|
||||
#define TTN_TIMER_GROUP TIMER_GROUP_1
|
||||
#define TTN_CLEAR_TIMER_ALARM TIMERG1.int_clr_timers.t1 = 1
|
||||
#else
|
||||
#error TTN timer must be configured using 'make menuconfig'
|
||||
#endif
|
||||
|
||||
// 16 μs per tick
|
||||
// LMIC requires ticks to be 15.5μs - 100 μs long
|
||||
#define US_PER_OSTICK 16
|
||||
#define OSTICKS_PER_SEC (1000000 / US_PER_OSTICK)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // _lmic_config_h_
|
119
src/lmic/hal.h
Executable file
119
src/lmic/hal.h
Executable file
@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright (c) 2014-2016 IBM Corporation.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the <organization> nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _hal_hpp_
|
||||
#define _hal_hpp_
|
||||
|
||||
#include "oslmic.h"
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* initialize hardware (IO, SPI, TIMER, IRQ).
|
||||
*/
|
||||
void hal_init (void);
|
||||
|
||||
/*
|
||||
* drive radio NSS pin (0=low, 1=high).
|
||||
*/
|
||||
void hal_pin_nss (u1_t val);
|
||||
|
||||
/*
|
||||
* drive radio RX/TX pins (0=rx, 1=tx).
|
||||
*/
|
||||
void hal_pin_rxtx (u1_t val);
|
||||
|
||||
/*
|
||||
* control radio RST pin (0=low, 1=high, 2=floating)
|
||||
*/
|
||||
void hal_pin_rst (u1_t val);
|
||||
|
||||
/*
|
||||
* perform SPI write transaction with radio
|
||||
* - write the command byte 'cmd'
|
||||
* - write 'len' bytes in 'buf'
|
||||
*/
|
||||
void hal_spi_write(u1_t cmd, const u1_t* buf, int len);
|
||||
|
||||
/*
|
||||
* perform SPI read transaction with radio
|
||||
* - write the command byte 'cmd'
|
||||
* - read 'len' bytes into 'buf'
|
||||
*/
|
||||
void hal_spi_read(u1_t cmd, u1_t* buf, int len);
|
||||
|
||||
/*
|
||||
* disable all CPU interrupts.
|
||||
* - might be invoked nested
|
||||
* - will be followed by matching call to hal_enableIRQs()
|
||||
*/
|
||||
void hal_disableIRQs (void);
|
||||
|
||||
/*
|
||||
* enable CPU interrupts.
|
||||
*/
|
||||
void hal_enableIRQs (void);
|
||||
|
||||
/*
|
||||
* put system and CPU in low-power mode, sleep until interrupt.
|
||||
*/
|
||||
void hal_sleep (void);
|
||||
|
||||
/*
|
||||
* return 32-bit system time in ticks.
|
||||
*/
|
||||
u4_t hal_ticks (void);
|
||||
|
||||
/*
|
||||
* busy-wait until specified timestamp (in ticks) is reached.
|
||||
*/
|
||||
void hal_waitUntil (u4_t time);
|
||||
|
||||
/*
|
||||
* check and rewind timer for target time.
|
||||
* - return 1 if target time is close
|
||||
* - otherwise rewind timer for target time or full period and return 0
|
||||
*/
|
||||
u1_t hal_checkTimer (u4_t targettime);
|
||||
|
||||
/*
|
||||
* perform fatal failure action.
|
||||
* - called by assertions
|
||||
* - action could be HALT or reboot
|
||||
*/
|
||||
void hal_failed (const char *file, u2_t line);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif // _hal_hpp_
|
2400
src/lmic/lmic.c
Executable file
2400
src/lmic/lmic.c
Executable file
File diff suppressed because it is too large
Load Diff
340
src/lmic/lmic.h
Executable file
340
src/lmic/lmic.h
Executable file
@ -0,0 +1,340 @@
|
||||
/*
|
||||
* Copyright (c) 2014-2016 IBM Corporation.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the <organization> nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
//! @file
|
||||
//! @brief LMIC API
|
||||
|
||||
#ifndef _lmic_h_
|
||||
#define _lmic_h_
|
||||
|
||||
#include "config.h"
|
||||
#include "oslmic.h"
|
||||
#include "lorabase.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// LMIC version
|
||||
#define LMIC_VERSION_MAJOR 1
|
||||
#define LMIC_VERSION_MINOR 6
|
||||
#define LMIC_VERSION_BUILD 1468577746
|
||||
|
||||
enum { MAX_FRAME_LEN = 64 }; //!< Library cap on max frame length
|
||||
enum { TXCONF_ATTEMPTS = 8 }; //!< Transmit attempts for confirmed frames
|
||||
enum { MAX_MISSED_BCNS = 20 }; // threshold for triggering rejoin requests
|
||||
enum { MAX_RXSYMS = 100 }; // stop tracking beacon beyond this
|
||||
|
||||
enum { LINK_CHECK_CONT = 12 , // continue with this after reported dead link
|
||||
LINK_CHECK_DEAD = 24 , // after this UP frames and no response from NWK assume link is dead
|
||||
LINK_CHECK_INIT = -12 , // UP frame count until we inc datarate
|
||||
LINK_CHECK_OFF =-128 }; // link check disabled
|
||||
|
||||
enum { TIME_RESYNC = 6*128 }; // secs
|
||||
enum { TXRX_GUARD_ms = 6000 }; // msecs - don't start TX-RX transaction before beacon
|
||||
enum { JOIN_GUARD_ms = 9000 }; // msecs - don't start Join Req/Acc transaction before beacon
|
||||
enum { TXRX_BCNEXT_secs = 2 }; // secs - earliest start after beacon time
|
||||
enum { RETRY_PERIOD_secs = 3 }; // secs - random period for retrying a confirmed send
|
||||
|
||||
#if defined(CFG_eu868) // EU868 spectrum ====================================================
|
||||
|
||||
enum { MAX_CHANNELS = 16 }; //!< Max supported channels
|
||||
enum { MAX_BANDS = 4 };
|
||||
|
||||
enum { LIMIT_CHANNELS = (1<<4) }; // EU868 will never have more channels
|
||||
//! \internal
|
||||
struct band_t {
|
||||
u2_t txcap; // duty cycle limitation: 1/txcap
|
||||
s1_t txpow; // maximum TX power
|
||||
u1_t lastchnl; // last used channel
|
||||
ostime_t avail; // channel is blocked until this time
|
||||
};
|
||||
TYPEDEF_xref2band_t; //!< \internal
|
||||
|
||||
#elif defined(CFG_us915) // US915 spectrum =================================================
|
||||
|
||||
enum { MAX_XCHANNELS = 2 }; // extra channels in RAM, channels 0-71 are immutable
|
||||
enum { MAX_TXPOW_125kHz = 30 };
|
||||
|
||||
#endif // ==========================================================================
|
||||
|
||||
// Keep in sync with evdefs.hpp::drChange
|
||||
enum { DRCHG_SET, DRCHG_NOJACC, DRCHG_NOACK, DRCHG_NOADRACK, DRCHG_NWKCMD };
|
||||
enum { KEEP_TXPOW = -128 };
|
||||
|
||||
|
||||
#if !defined(DISABLE_PING)
|
||||
//! \internal
|
||||
struct rxsched_t {
|
||||
u1_t dr;
|
||||
u1_t intvExp; // 0..7
|
||||
u1_t slot; // runs from 0 to 128
|
||||
u1_t rxsyms;
|
||||
ostime_t rxbase;
|
||||
ostime_t rxtime; // start of next spot
|
||||
u4_t freq;
|
||||
};
|
||||
TYPEDEF_xref2rxsched_t; //!< \internal
|
||||
#endif // !DISABLE_PING
|
||||
|
||||
|
||||
#if !defined(DISABLE_BEACONS)
|
||||
//! Parsing and tracking states of beacons.
|
||||
enum { BCN_NONE = 0x00, //!< No beacon received
|
||||
BCN_PARTIAL = 0x01, //!< Only first (common) part could be decoded (info,lat,lon invalid/previous)
|
||||
BCN_FULL = 0x02, //!< Full beacon decoded
|
||||
BCN_NODRIFT = 0x04, //!< No drift value measured yet
|
||||
BCN_NODDIFF = 0x08 }; //!< No differential drift measured yet
|
||||
//! Information about the last and previous beacons.
|
||||
struct bcninfo_t {
|
||||
ostime_t txtime; //!< Time when the beacon was sent
|
||||
s1_t rssi; //!< Adjusted RSSI value of last received beacon
|
||||
s1_t snr; //!< Scaled SNR value of last received beacon
|
||||
u1_t flags; //!< Last beacon reception and tracking states. See BCN_* values.
|
||||
u4_t time; //!< GPS time in seconds of last beacon (received or surrogate)
|
||||
//
|
||||
u1_t info; //!< Info field of last beacon (valid only if BCN_FULL set)
|
||||
s4_t lat; //!< Lat field of last beacon (valid only if BCN_FULL set)
|
||||
s4_t lon; //!< Lon field of last beacon (valid only if BCN_FULL set)
|
||||
};
|
||||
#endif // !DISABLE_BEACONS
|
||||
|
||||
// purpose of receive window - lmic_t.rxState
|
||||
enum { RADIO_RST=0, RADIO_TX=1, RADIO_RX=2, RADIO_RXON=3 };
|
||||
// Netid values / lmic_t.netid
|
||||
enum { NETID_NONE=(int)~0U, NETID_MASK=(int)0xFFFFFF };
|
||||
// MAC operation modes (lmic_t.opmode).
|
||||
enum { OP_NONE = 0x0000,
|
||||
OP_SCAN = 0x0001, // radio scan to find a beacon
|
||||
OP_TRACK = 0x0002, // track my networks beacon (netid)
|
||||
OP_JOINING = 0x0004, // device joining in progress (blocks other activities)
|
||||
OP_TXDATA = 0x0008, // TX user data (buffered in pendTxData)
|
||||
OP_POLL = 0x0010, // send empty UP frame to ACK confirmed DN/fetch more DN data
|
||||
OP_REJOIN = 0x0020, // occasionally send JOIN REQUEST
|
||||
OP_SHUTDOWN = 0x0040, // prevent MAC from doing anything
|
||||
OP_TXRXPEND = 0x0080, // TX/RX transaction pending
|
||||
OP_RNDTX = 0x0100, // prevent TX lining up after a beacon
|
||||
OP_PINGINI = 0x0200, // pingable is initialized and scheduling active
|
||||
OP_PINGABLE = 0x0400, // we're pingable
|
||||
OP_NEXTCHNL = 0x0800, // find a new channel
|
||||
OP_LINKDEAD = 0x1000, // link was reported as dead
|
||||
OP_TESTMODE = 0x2000, // developer test mode
|
||||
};
|
||||
// TX-RX transaction flags - report back to user
|
||||
enum { TXRX_ACK = 0x80, // confirmed UP frame was acked
|
||||
TXRX_NACK = 0x40, // confirmed UP frame was not acked
|
||||
TXRX_NOPORT = 0x20, // set if a frame with a port was RXed, clr if no frame/no port
|
||||
TXRX_PORT = 0x10, // set if a frame with a port was RXed, LMIC.frame[LMIC.dataBeg-1] => port
|
||||
TXRX_DNW1 = 0x01, // received in 1st DN slot
|
||||
TXRX_DNW2 = 0x02, // received in 2dn DN slot
|
||||
TXRX_PING = 0x04 }; // received in a scheduled RX slot
|
||||
// Event types for event callback
|
||||
enum _ev_t { EV_SCAN_TIMEOUT=1, EV_BEACON_FOUND,
|
||||
EV_BEACON_MISSED, EV_BEACON_TRACKED, EV_JOINING,
|
||||
EV_JOINED, EV_RFU1, EV_JOIN_FAILED, EV_REJOIN_FAILED,
|
||||
EV_TXCOMPLETE, EV_LOST_TSYNC, EV_RESET,
|
||||
EV_RXCOMPLETE, EV_LINK_DEAD, EV_LINK_ALIVE };
|
||||
typedef enum _ev_t ev_t;
|
||||
|
||||
enum {
|
||||
// This value represents 100% error in LMIC.clockError
|
||||
MAX_CLOCK_ERROR = 65536,
|
||||
};
|
||||
|
||||
struct lmic_t {
|
||||
// Radio settings TX/RX (also accessed by HAL)
|
||||
ostime_t txend;
|
||||
ostime_t rxtime;
|
||||
u4_t freq;
|
||||
s1_t rssi;
|
||||
s1_t snr;
|
||||
rps_t rps;
|
||||
u1_t rxsyms;
|
||||
u1_t dndr;
|
||||
s1_t txpow; // dBm
|
||||
|
||||
osjob_t osjob;
|
||||
|
||||
// Channel scheduling
|
||||
#if defined(CFG_eu868)
|
||||
band_t bands[MAX_BANDS];
|
||||
u4_t channelFreq[MAX_CHANNELS];
|
||||
u2_t channelDrMap[MAX_CHANNELS];
|
||||
u2_t channelMap;
|
||||
#elif defined(CFG_us915)
|
||||
u4_t xchFreq[MAX_XCHANNELS]; // extra channel frequencies (if device is behind a repeater)
|
||||
u2_t xchDrMap[MAX_XCHANNELS]; // extra channel datarate ranges ---XXX: ditto
|
||||
u2_t channelMap[(72+MAX_XCHANNELS+15)/16]; // enabled bits
|
||||
u2_t chRnd; // channel randomizer
|
||||
#endif
|
||||
u1_t txChnl; // channel for next TX
|
||||
u1_t globalDutyRate; // max rate: 1/2^k
|
||||
ostime_t globalDutyAvail; // time device can send again
|
||||
|
||||
u4_t netid; // current network id (~0 - none)
|
||||
u2_t opmode;
|
||||
u1_t upRepeat; // configured up repeat
|
||||
s1_t adrTxPow; // ADR adjusted TX power
|
||||
u1_t datarate; // current data rate
|
||||
u1_t errcr; // error coding rate (used for TX only)
|
||||
u1_t rejoinCnt; // adjustment for rejoin datarate
|
||||
#if !defined(DISABLE_BEACONS)
|
||||
s2_t drift; // last measured drift
|
||||
s2_t lastDriftDiff;
|
||||
s2_t maxDriftDiff;
|
||||
#endif
|
||||
|
||||
u2_t clockError; // Inaccuracy in the clock. CLOCK_ERROR_MAX
|
||||
// represents +/-100% error
|
||||
|
||||
u1_t pendTxPort;
|
||||
u1_t pendTxConf; // confirmed data
|
||||
u1_t pendTxLen; // +0x80 = confirmed
|
||||
u1_t pendTxData[MAX_LEN_PAYLOAD];
|
||||
|
||||
u2_t devNonce; // last generated nonce
|
||||
u1_t nwkKey[16]; // network session key
|
||||
u1_t artKey[16]; // application router session key
|
||||
devaddr_t devaddr;
|
||||
u4_t seqnoDn; // device level down stream seqno
|
||||
u4_t seqnoUp;
|
||||
|
||||
u1_t dnConf; // dn frame confirm pending: LORA::FCT_ACK or 0
|
||||
s1_t adrAckReq; // counter until we reset data rate (0=off)
|
||||
u1_t adrChanged;
|
||||
|
||||
u1_t rxDelay; // Rx delay after TX
|
||||
|
||||
u1_t margin;
|
||||
bit_t ladrAns; // link adr adapt answer pending
|
||||
bit_t devsAns; // device status answer pending
|
||||
u1_t adrEnabled;
|
||||
u1_t moreData; // NWK has more data pending
|
||||
#if !defined(DISABLE_MCMD_DCAP_REQ)
|
||||
bit_t dutyCapAns; // have to ACK duty cycle settings
|
||||
#endif
|
||||
#if !defined(DISABLE_MCMD_SNCH_REQ)
|
||||
u1_t snchAns; // answer set new channel
|
||||
#endif
|
||||
// 2nd RX window (after up stream)
|
||||
u1_t dn2Dr;
|
||||
u4_t dn2Freq;
|
||||
#if !defined(DISABLE_MCMD_DN2P_SET)
|
||||
u1_t dn2Ans; // 0=no answer pend, 0x80+ACKs
|
||||
#endif
|
||||
|
||||
// Class B state
|
||||
#if !defined(DISABLE_BEACONS)
|
||||
u1_t missedBcns; // unable to track last N beacons
|
||||
u1_t bcninfoTries; // how often to try (scan mode only)
|
||||
#endif
|
||||
#if !defined(DISABLE_MCMD_PING_SET) && !defined(DISABLE_PING)
|
||||
u1_t pingSetAns; // answer set cmd and ACK bits
|
||||
#endif
|
||||
#if !defined(DISABLE_PING)
|
||||
rxsched_t ping; // pingable setup
|
||||
#endif
|
||||
|
||||
// Public part of MAC state
|
||||
u1_t txCnt;
|
||||
u1_t txrxFlags; // transaction flags (TX-RX combo)
|
||||
u1_t dataBeg; // 0 or start of data (dataBeg-1 is port)
|
||||
u1_t dataLen; // 0 no data or zero length data, >0 byte count of data
|
||||
u1_t frame[MAX_LEN_FRAME];
|
||||
|
||||
#if !defined(DISABLE_BEACONS)
|
||||
u1_t bcnChnl;
|
||||
u1_t bcnRxsyms; //
|
||||
ostime_t bcnRxtime;
|
||||
bcninfo_t bcninfo; // Last received beacon info
|
||||
#endif
|
||||
|
||||
u1_t noRXIQinversion;
|
||||
};
|
||||
//! \var struct lmic_t LMIC
|
||||
//! The state of LMIC MAC layer is encapsulated in this variable.
|
||||
DECLARE_LMIC; //!< \internal
|
||||
|
||||
//! Construct a bit map of allowed datarates from drlo to drhi (both included).
|
||||
#define DR_RANGE_MAP(drlo,drhi) (((u2_t)0xFFFF<<(drlo)) & ((u2_t)0xFFFF>>(15-(drhi))))
|
||||
#if defined(CFG_eu868)
|
||||
enum { BAND_MILLI=0, BAND_CENTI=1, BAND_DECI=2, BAND_AUX=3 };
|
||||
bit_t LMIC_setupBand (u1_t bandidx, s1_t txpow, u2_t txcap);
|
||||
#endif
|
||||
bit_t LMIC_setupChannel (u1_t channel, u4_t freq, u2_t drmap, s1_t band);
|
||||
void LMIC_disableChannel (u1_t channel);
|
||||
#if defined(CFG_us915)
|
||||
void LMIC_enableChannel (u1_t channel);
|
||||
void LMIC_enableSubBand (u1_t band);
|
||||
void LMIC_disableSubBand (u1_t band);
|
||||
void LMIC_selectSubBand (u1_t band);
|
||||
#endif
|
||||
|
||||
void LMIC_setDrTxpow (dr_t dr, s1_t txpow); // set default/start DR/txpow
|
||||
void LMIC_setAdrMode (bit_t enabled); // set ADR mode (if mobile turn off)
|
||||
#if !defined(DISABLE_JOIN)
|
||||
bit_t LMIC_startJoining (void);
|
||||
#endif
|
||||
|
||||
void LMIC_shutdown (void);
|
||||
void LMIC_init (void);
|
||||
void LMIC_start (void);
|
||||
void LMIC_reset (void);
|
||||
void LMIC_clrTxData (void);
|
||||
void LMIC_setTxData (void);
|
||||
int LMIC_setTxData2 (u1_t port, xref2u1_t data, u1_t dlen, u1_t confirmed);
|
||||
void LMIC_sendAlive (void);
|
||||
|
||||
#if !defined(DISABLE_BEACONS)
|
||||
bit_t LMIC_enableTracking (u1_t tryBcnInfo);
|
||||
void LMIC_disableTracking (void);
|
||||
#endif
|
||||
|
||||
#if !defined(DISABLE_PING)
|
||||
void LMIC_stopPingable (void);
|
||||
void LMIC_setPingable (u1_t intvExp);
|
||||
#endif
|
||||
#if !defined(DISABLE_JOIN)
|
||||
void LMIC_tryRejoin (void);
|
||||
#endif
|
||||
|
||||
void LMIC_setSession (u4_t netid, devaddr_t devaddr, xref2u1_t nwkKey, xref2u1_t artKey);
|
||||
void LMIC_setLinkCheckMode (bit_t enabled);
|
||||
void LMIC_setClockError(u2_t error);
|
||||
|
||||
// Declare onEvent() function, to make sure any definition will have the
|
||||
// C conventions, even when in a C++ file.
|
||||
DECL_ON_LMIC_EVENT;
|
||||
|
||||
// Special APIs - for development or testing
|
||||
// !!!See implementation for caveats!!!
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // _lmic_h_
|
407
src/lmic/lorabase.h
Executable file
407
src/lmic/lorabase.h
Executable file
@ -0,0 +1,407 @@
|
||||
/*
|
||||
* Copyright (c) 2014-2016 IBM Corporation.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the <organization> nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _lorabase_h_
|
||||
#define _lorabase_h_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// ================================================================================
|
||||
// BEG: Keep in sync with lorabase.hpp
|
||||
//
|
||||
|
||||
enum _cr_t { CR_4_5=0, CR_4_6, CR_4_7, CR_4_8 };
|
||||
enum _sf_t { FSK=0, SF7, SF8, SF9, SF10, SF11, SF12, SFrfu };
|
||||
enum _bw_t { BW125=0, BW250, BW500, BWrfu };
|
||||
typedef u1_t cr_t;
|
||||
typedef u1_t sf_t;
|
||||
typedef u1_t bw_t;
|
||||
typedef u1_t dr_t;
|
||||
// Radio parameter set (encodes SF/BW/CR/IH/NOCRC)
|
||||
typedef u2_t rps_t;
|
||||
TYPEDEF_xref2rps_t;
|
||||
|
||||
enum { ILLEGAL_RPS = 0xFF };
|
||||
enum { DR_PAGE_EU868 = 0x00 };
|
||||
enum { DR_PAGE_US915 = 0x10 };
|
||||
|
||||
// Global maximum frame length
|
||||
enum { STD_PREAMBLE_LEN = 8 };
|
||||
enum { MAX_LEN_FRAME = 64 };
|
||||
enum { LEN_DEVNONCE = 2 };
|
||||
enum { LEN_ARTNONCE = 3 };
|
||||
enum { LEN_NETID = 3 };
|
||||
enum { DELAY_JACC1 = 5 }; // in secs
|
||||
enum { DELAY_DNW1 = 1 }; // in secs down window #1
|
||||
enum { DELAY_EXTDNW2 = 1 }; // in secs
|
||||
enum { DELAY_JACC2 = DELAY_JACC1+(int)DELAY_EXTDNW2 }; // in secs
|
||||
enum { DELAY_DNW2 = DELAY_DNW1 +(int)DELAY_EXTDNW2 }; // in secs down window #1
|
||||
enum { BCN_INTV_exp = 7 };
|
||||
enum { BCN_INTV_sec = 1<<BCN_INTV_exp };
|
||||
enum { BCN_INTV_ms = BCN_INTV_sec*1000L };
|
||||
enum { BCN_INTV_us = BCN_INTV_ms*1000L };
|
||||
enum { BCN_RESERVE_ms = 2120 }; // space reserved for beacon and NWK management
|
||||
enum { BCN_GUARD_ms = 3000 }; // end of beacon period to prevent interference with beacon
|
||||
enum { BCN_SLOT_SPAN_ms = 30 }; // 2^12 reception slots a this span
|
||||
enum { BCN_WINDOW_ms = BCN_INTV_ms-(int)BCN_GUARD_ms-(int)BCN_RESERVE_ms };
|
||||
enum { BCN_RESERVE_us = 2120000 };
|
||||
enum { BCN_GUARD_us = 3000000 };
|
||||
enum { BCN_SLOT_SPAN_us = 30000 };
|
||||
|
||||
#if defined(CFG_eu868) // ==============================================
|
||||
|
||||
enum _dr_eu868_t { DR_SF12=0, DR_SF11, DR_SF10, DR_SF9, DR_SF8, DR_SF7, DR_SF7B, DR_FSK, DR_NONE };
|
||||
enum { DR_DFLTMIN = DR_SF7 };
|
||||
enum { DR_PAGE = DR_PAGE_EU868 };
|
||||
|
||||
// Default frequency plan for EU 868MHz ISM band
|
||||
// Bands:
|
||||
// g1 : 1% 14dBm
|
||||
// g2 : 0.1% 14dBm
|
||||
// g3 : 10% 27dBm
|
||||
// freq band datarates
|
||||
enum { EU868_F1 = 868100000, // g1 SF7-12
|
||||
EU868_F2 = 868300000, // g1 SF7-12 FSK SF7/250
|
||||
EU868_F3 = 868500000, // g1 SF7-12
|
||||
EU868_F4 = 868850000, // g2 SF7-12
|
||||
EU868_F5 = 869050000, // g2 SF7-12
|
||||
EU868_F6 = 869525000, // g3 SF7-12
|
||||
EU868_J4 = 864100000, // g2 SF7-12 used during join
|
||||
EU868_J5 = 864300000, // g2 SF7-12 ditto
|
||||
EU868_J6 = 864500000, // g2 SF7-12 ditto
|
||||
};
|
||||
enum { EU868_FREQ_MIN = 863000000,
|
||||
EU868_FREQ_MAX = 870000000 };
|
||||
|
||||
enum { CHNL_PING = 5 };
|
||||
enum { FREQ_PING = EU868_F6 }; // default ping freq
|
||||
enum { DR_PING = DR_SF9 }; // default ping DR
|
||||
enum { CHNL_DNW2 = 5 };
|
||||
enum { FREQ_DNW2 = EU868_F6 };
|
||||
enum { DR_DNW2 = DR_SF12 };
|
||||
enum { CHNL_BCN = 5 };
|
||||
enum { FREQ_BCN = EU868_F6 };
|
||||
enum { DR_BCN = DR_SF9 };
|
||||
enum { AIRTIME_BCN = 144384 }; // micros
|
||||
|
||||
enum {
|
||||
// Beacon frame format EU SF9
|
||||
OFF_BCN_NETID = 0,
|
||||
OFF_BCN_TIME = 3,
|
||||
OFF_BCN_CRC1 = 7,
|
||||
OFF_BCN_INFO = 8,
|
||||
OFF_BCN_LAT = 9,
|
||||
OFF_BCN_LON = 12,
|
||||
OFF_BCN_CRC2 = 15,
|
||||
LEN_BCN = 17
|
||||
};
|
||||
|
||||
#elif defined(CFG_us915) // =========================================
|
||||
|
||||
enum _dr_us915_t { DR_SF10=0, DR_SF9, DR_SF8, DR_SF7, DR_SF8C, DR_NONE,
|
||||
// Devices behind a router:
|
||||
DR_SF12CR=8, DR_SF11CR, DR_SF10CR, DR_SF9CR, DR_SF8CR, DR_SF7CR };
|
||||
enum { DR_DFLTMIN = DR_SF8C };
|
||||
enum { DR_PAGE = DR_PAGE_US915 };
|
||||
|
||||
// Default frequency plan for US 915MHz
|
||||
enum { US915_125kHz_UPFBASE = 902300000,
|
||||
US915_125kHz_UPFSTEP = 200000,
|
||||
US915_500kHz_UPFBASE = 903000000,
|
||||
US915_500kHz_UPFSTEP = 1600000,
|
||||
US915_500kHz_DNFBASE = 923300000,
|
||||
US915_500kHz_DNFSTEP = 600000
|
||||
};
|
||||
enum { US915_FREQ_MIN = 902000000,
|
||||
US915_FREQ_MAX = 928000000 };
|
||||
|
||||
enum { CHNL_PING = 0 }; // used only for default init of state (follows beacon - rotating)
|
||||
enum { FREQ_PING = US915_500kHz_DNFBASE + CHNL_PING*US915_500kHz_DNFSTEP }; // default ping freq
|
||||
enum { DR_PING = DR_SF10CR }; // default ping DR
|
||||
enum { CHNL_DNW2 = 0 };
|
||||
enum { FREQ_DNW2 = US915_500kHz_DNFBASE + CHNL_DNW2*US915_500kHz_DNFSTEP };
|
||||
enum { DR_DNW2 = DR_SF12CR };
|
||||
enum { CHNL_BCN = 0 }; // used only for default init of state (rotating beacon scheme)
|
||||
enum { DR_BCN = DR_SF10CR };
|
||||
enum { AIRTIME_BCN = 72192 }; // micros
|
||||
|
||||
enum {
|
||||
// Beacon frame format US SF10
|
||||
OFF_BCN_NETID = 0,
|
||||
OFF_BCN_TIME = 3,
|
||||
OFF_BCN_CRC1 = 7,
|
||||
OFF_BCN_INFO = 9,
|
||||
OFF_BCN_LAT = 10,
|
||||
OFF_BCN_LON = 13,
|
||||
OFF_BCN_RFU1 = 16,
|
||||
OFF_BCN_CRC2 = 17,
|
||||
LEN_BCN = 19
|
||||
};
|
||||
|
||||
#endif // ===================================================
|
||||
|
||||
enum {
|
||||
// Join Request frame format
|
||||
OFF_JR_HDR = 0,
|
||||
OFF_JR_ARTEUI = 1,
|
||||
OFF_JR_DEVEUI = 9,
|
||||
OFF_JR_DEVNONCE = 17,
|
||||
OFF_JR_MIC = 19,
|
||||
LEN_JR = 23
|
||||
};
|
||||
enum {
|
||||
// Join Accept frame format
|
||||
OFF_JA_HDR = 0,
|
||||
OFF_JA_ARTNONCE = 1,
|
||||
OFF_JA_NETID = 4,
|
||||
OFF_JA_DEVADDR = 7,
|
||||
OFF_JA_RFU = 11,
|
||||
OFF_JA_DLSET = 11,
|
||||
OFF_JA_RXDLY = 12,
|
||||
OFF_CFLIST = 13,
|
||||
LEN_JA = 17,
|
||||
LEN_JAEXT = 17+16
|
||||
};
|
||||
enum {
|
||||
// Data frame format
|
||||
OFF_DAT_HDR = 0,
|
||||
OFF_DAT_ADDR = 1,
|
||||
OFF_DAT_FCT = 5,
|
||||
OFF_DAT_SEQNO = 6,
|
||||
OFF_DAT_OPTS = 8,
|
||||
};
|
||||
enum { MAX_LEN_PAYLOAD = MAX_LEN_FRAME-(int)OFF_DAT_OPTS-4 };
|
||||
enum {
|
||||
// Bitfields in frame format octet
|
||||
HDR_FTYPE = 0xE0,
|
||||
HDR_RFU = 0x1C,
|
||||
HDR_MAJOR = 0x03
|
||||
};
|
||||
enum { HDR_FTYPE_DNFLAG = 0x20 }; // flags DN frame except for HDR_FTYPE_PROP
|
||||
enum {
|
||||
// Values of frame type bit field
|
||||
HDR_FTYPE_JREQ = 0x00,
|
||||
HDR_FTYPE_JACC = 0x20,
|
||||
HDR_FTYPE_DAUP = 0x40, // data (unconfirmed) up
|
||||
HDR_FTYPE_DADN = 0x60, // data (unconfirmed) dn
|
||||
HDR_FTYPE_DCUP = 0x80, // data confirmed up
|
||||
HDR_FTYPE_DCDN = 0xA0, // data confirmed dn
|
||||
HDR_FTYPE_REJOIN = 0xC0, // rejoin for roaming
|
||||
HDR_FTYPE_PROP = 0xE0
|
||||
};
|
||||
enum {
|
||||
HDR_MAJOR_V1 = 0x00,
|
||||
};
|
||||
enum {
|
||||
// Bitfields in frame control octet
|
||||
FCT_ADREN = 0x80,
|
||||
FCT_ADRARQ = 0x40,
|
||||
FCT_ACK = 0x20,
|
||||
FCT_MORE = 0x10, // also in DN direction: Class B indicator
|
||||
FCT_OPTLEN = 0x0F,
|
||||
};
|
||||
enum {
|
||||
// In UP direction: signals class B enabled
|
||||
FCT_CLASSB = FCT_MORE
|
||||
};
|
||||
enum {
|
||||
NWKID_MASK = (int)0xFE000000,
|
||||
NWKID_BITS = 7
|
||||
};
|
||||
|
||||
// MAC uplink commands downwlink too
|
||||
enum {
|
||||
// Class A
|
||||
MCMD_LCHK_REQ = 0x02, // - link check request : -
|
||||
MCMD_LADR_ANS = 0x03, // - link ADR answer : u1:7-3:RFU, 3/2/1: pow/DR/Ch ACK
|
||||
MCMD_DCAP_ANS = 0x04, // - duty cycle answer : -
|
||||
MCMD_DN2P_ANS = 0x05, // - 2nd DN slot status : u1:7-2:RFU 1/0:datarate/channel ack
|
||||
MCMD_DEVS_ANS = 0x06, // - device status ans : u1:battery 0,1-254,255=?, u1:7-6:RFU,5-0:margin(-32..31)
|
||||
MCMD_SNCH_ANS = 0x07, // - set new channel : u1: 7-2=RFU, 1/0:DR/freq ACK
|
||||
// Class B
|
||||
MCMD_PING_IND = 0x10, // - pingability indic : u1: 7=RFU, 6-4:interval, 3-0:datarate
|
||||
MCMD_PING_ANS = 0x11, // - ack ping freq : u1: 7-1:RFU, 0:freq ok
|
||||
MCMD_BCNI_REQ = 0x12, // - next beacon start : -
|
||||
};
|
||||
|
||||
// MAC downlink commands
|
||||
enum {
|
||||
// Class A
|
||||
MCMD_LCHK_ANS = 0x02, // link check answer : u1:margin 0-254,255=unknown margin / u1:gwcnt
|
||||
MCMD_LADR_REQ = 0x03, // link ADR request : u1:DR/TXPow, u2:chmask, u1:chpage/repeat
|
||||
MCMD_DCAP_REQ = 0x04, // duty cycle cap : u1:255 dead [7-4]:RFU, [3-0]:cap 2^-k
|
||||
MCMD_DN2P_SET = 0x05, // 2nd DN window param: u1:7-4:RFU/3-0:datarate, u3:freq
|
||||
MCMD_DEVS_REQ = 0x06, // device status req : -
|
||||
MCMD_SNCH_REQ = 0x07, // set new channel : u1:chidx, u3:freq, u1:DRrange
|
||||
// Class B
|
||||
MCMD_PING_SET = 0x11, // set ping freq : u3: freq
|
||||
MCMD_BCNI_ANS = 0x12, // next beacon start : u2: delay(in TUNIT millis), u1:channel
|
||||
};
|
||||
|
||||
enum {
|
||||
MCMD_BCNI_TUNIT = 30 // time unit of delay value in millis
|
||||
};
|
||||
enum {
|
||||
MCMD_LADR_ANS_RFU = 0xF8, // RFU bits
|
||||
MCMD_LADR_ANS_POWACK = 0x04, // 0=not supported power level
|
||||
MCMD_LADR_ANS_DRACK = 0x02, // 0=unknown data rate
|
||||
MCMD_LADR_ANS_CHACK = 0x01, // 0=unknown channel enabled
|
||||
};
|
||||
enum {
|
||||
MCMD_DN2P_ANS_RFU = 0xFC, // RFU bits
|
||||
MCMD_DN2P_ANS_DRACK = 0x02, // 0=unknown data rate
|
||||
MCMD_DN2P_ANS_CHACK = 0x01, // 0=unknown channel enabled
|
||||
};
|
||||
enum {
|
||||
MCMD_SNCH_ANS_RFU = 0xFC, // RFU bits
|
||||
MCMD_SNCH_ANS_DRACK = 0x02, // 0=unknown data rate
|
||||
MCMD_SNCH_ANS_FQACK = 0x01, // 0=rejected channel frequency
|
||||
};
|
||||
enum {
|
||||
MCMD_PING_ANS_RFU = 0xFE,
|
||||
MCMD_PING_ANS_FQACK = 0x01
|
||||
};
|
||||
|
||||
enum {
|
||||
MCMD_DEVS_EXT_POWER = 0x00, // external power supply
|
||||
MCMD_DEVS_BATT_MIN = 0x01, // min battery value
|
||||
MCMD_DEVS_BATT_MAX = 0xFE, // max battery value
|
||||
MCMD_DEVS_BATT_NOINFO = 0xFF, // unknown battery level
|
||||
};
|
||||
|
||||
// Bit fields byte#3 of MCMD_LADR_REQ payload
|
||||
enum {
|
||||
MCMD_LADR_CHP_125ON = 0x60, // special channel page enable, bits applied to 64..71
|
||||
MCMD_LADR_CHP_125OFF = 0x70, // ditto
|
||||
MCMD_LADR_N3RFU_MASK = 0x80,
|
||||
MCMD_LADR_CHPAGE_MASK = 0xF0,
|
||||
MCMD_LADR_REPEAT_MASK = 0x0F,
|
||||
MCMD_LADR_REPEAT_1 = 0x01,
|
||||
MCMD_LADR_CHPAGE_1 = 0x10
|
||||
};
|
||||
// Bit fields byte#0 of MCMD_LADR_REQ payload
|
||||
enum {
|
||||
MCMD_LADR_DR_MASK = 0xF0,
|
||||
MCMD_LADR_POW_MASK = 0x0F,
|
||||
MCMD_LADR_DR_SHIFT = 4,
|
||||
MCMD_LADR_POW_SHIFT = 0,
|
||||
#if defined(CFG_eu868)
|
||||
MCMD_LADR_SF12 = DR_SF12<<4,
|
||||
MCMD_LADR_SF11 = DR_SF11<<4,
|
||||
MCMD_LADR_SF10 = DR_SF10<<4,
|
||||
MCMD_LADR_SF9 = DR_SF9 <<4,
|
||||
MCMD_LADR_SF8 = DR_SF8 <<4,
|
||||
MCMD_LADR_SF7 = DR_SF7 <<4,
|
||||
MCMD_LADR_SF7B = DR_SF7B<<4,
|
||||
MCMD_LADR_FSK = DR_FSK <<4,
|
||||
|
||||
MCMD_LADR_20dBm = 0,
|
||||
MCMD_LADR_14dBm = 1,
|
||||
MCMD_LADR_11dBm = 2,
|
||||
MCMD_LADR_8dBm = 3,
|
||||
MCMD_LADR_5dBm = 4,
|
||||
MCMD_LADR_2dBm = 5,
|
||||
#elif defined(CFG_us915)
|
||||
MCMD_LADR_SF10 = DR_SF10<<4,
|
||||
MCMD_LADR_SF9 = DR_SF9 <<4,
|
||||
MCMD_LADR_SF8 = DR_SF8 <<4,
|
||||
MCMD_LADR_SF7 = DR_SF7 <<4,
|
||||
MCMD_LADR_SF8C = DR_SF8C<<4,
|
||||
MCMD_LADR_SF12CR = DR_SF12CR<<4,
|
||||
MCMD_LADR_SF11CR = DR_SF11CR<<4,
|
||||
MCMD_LADR_SF10CR = DR_SF10CR<<4,
|
||||
MCMD_LADR_SF9CR = DR_SF9CR<<4,
|
||||
MCMD_LADR_SF8CR = DR_SF8CR<<4,
|
||||
MCMD_LADR_SF7CR = DR_SF7CR<<4,
|
||||
|
||||
MCMD_LADR_30dBm = 0,
|
||||
MCMD_LADR_28dBm = 1,
|
||||
MCMD_LADR_26dBm = 2,
|
||||
MCMD_LADR_24dBm = 3,
|
||||
MCMD_LADR_22dBm = 4,
|
||||
MCMD_LADR_20dBm = 5,
|
||||
MCMD_LADR_18dBm = 6,
|
||||
MCMD_LADR_16dBm = 7,
|
||||
MCMD_LADR_14dBm = 8,
|
||||
MCMD_LADR_12dBm = 9,
|
||||
MCMD_LADR_10dBm = 10
|
||||
#endif
|
||||
};
|
||||
|
||||
// Device address
|
||||
typedef u4_t devaddr_t;
|
||||
|
||||
// RX quality (device)
|
||||
enum { RSSI_OFF=64, SNR_SCALEUP=4 };
|
||||
|
||||
inline sf_t getSf (rps_t params) { return (sf_t)(params & 0x7); }
|
||||
inline rps_t setSf (rps_t params, sf_t sf) { return (rps_t)((params & ~0x7) | sf); }
|
||||
inline bw_t getBw (rps_t params) { return (bw_t)((params >> 3) & 0x3); }
|
||||
inline rps_t setBw (rps_t params, bw_t cr) { return (rps_t)((params & ~0x18) | (cr<<3)); }
|
||||
inline cr_t getCr (rps_t params) { return (cr_t)((params >> 5) & 0x3); }
|
||||
inline rps_t setCr (rps_t params, cr_t cr) { return (rps_t)((params & ~0x60) | (cr<<5)); }
|
||||
inline int getNocrc(rps_t params) { return ((params >> 7) & 0x1); }
|
||||
inline rps_t setNocrc(rps_t params, int nocrc) { return (rps_t)((params & ~0x80) | (nocrc<<7)); }
|
||||
inline int getIh (rps_t params) { return ((params >> 8) & 0xFF); }
|
||||
inline rps_t setIh (rps_t params, int ih) { return (rps_t)((params & ~0xFF00) | (ih<<8)); }
|
||||
inline rps_t makeRps (sf_t sf, bw_t bw, cr_t cr, int ih, int nocrc) {
|
||||
return sf | (bw<<3) | (cr<<5) | (nocrc?(1<<7):0) | ((ih&0xFF)<<8);
|
||||
}
|
||||
#define MAKERPS(sf,bw,cr,ih,nocrc) ((rps_t)((sf) | ((bw)<<3) | ((cr)<<5) | ((nocrc)?(1<<7):0) | ((ih&0xFF)<<8)))
|
||||
// Two frames with params r1/r2 would interfere on air: same SFx + BWx
|
||||
inline int sameSfBw(rps_t r1, rps_t r2) { return ((r1^r2)&0x1F) == 0; }
|
||||
|
||||
extern const u1_t _DR2RPS_CRC[];
|
||||
inline rps_t updr2rps (dr_t dr) { return (rps_t)_DR2RPS_CRC[dr+1]; }
|
||||
inline rps_t dndr2rps (dr_t dr) { return setNocrc(updr2rps(dr),1); }
|
||||
inline int isFasterDR (dr_t dr1, dr_t dr2) { return dr1 > dr2; }
|
||||
inline int isSlowerDR (dr_t dr1, dr_t dr2) { return dr1 < dr2; }
|
||||
inline dr_t incDR (dr_t dr) { return _DR2RPS_CRC[dr+2]==ILLEGAL_RPS ? dr : (dr_t)(dr+1); } // increase data rate
|
||||
inline dr_t decDR (dr_t dr) { return _DR2RPS_CRC[dr ]==ILLEGAL_RPS ? dr : (dr_t)(dr-1); } // decrease data rate
|
||||
inline dr_t assertDR (dr_t dr) { return _DR2RPS_CRC[dr+1]==ILLEGAL_RPS ? (dr_t)DR_DFLTMIN : dr; } // force into a valid DR
|
||||
inline bit_t validDR (dr_t dr) { return _DR2RPS_CRC[dr+1]!=ILLEGAL_RPS; } // in range
|
||||
inline dr_t lowerDR (dr_t dr, u1_t n) { while(n--){dr=decDR(dr);} return dr; } // decrease data rate by n steps
|
||||
|
||||
//
|
||||
// BEG: Keep in sync with lorabase.hpp
|
||||
// ================================================================================
|
||||
|
||||
|
||||
// Convert between dBm values and power codes (MCMD_LADR_XdBm)
|
||||
s1_t pow2dBm (u1_t mcmd_ladr_p1);
|
||||
// Calculate airtime
|
||||
ostime_t calcAirTime (rps_t rps, u1_t plen);
|
||||
// Sensitivity at given SF/BW
|
||||
int getSensitivity (rps_t rps);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // _lorabase_h_
|
150
src/lmic/oslmic.c
Executable file
150
src/lmic/oslmic.c
Executable file
@ -0,0 +1,150 @@
|
||||
/*
|
||||
* Copyright (c) 2014-2016 IBM Corporation.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the <organization> nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "lmic.h"
|
||||
#include "../hal/hal_esp32.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
// RUNTIME STATE
|
||||
static struct {
|
||||
osjob_t* scheduledjobs;
|
||||
osjob_t* runnablejobs;
|
||||
} OS;
|
||||
|
||||
void os_init () {
|
||||
memset(&OS, 0x00, sizeof(OS));
|
||||
hal_init();
|
||||
radio_init();
|
||||
LMIC_init();
|
||||
}
|
||||
|
||||
ostime_t os_getTime () {
|
||||
return hal_ticks();
|
||||
}
|
||||
|
||||
static u1_t unlinkjob (osjob_t** pnext, osjob_t* job) {
|
||||
for( ; *pnext; pnext = &((*pnext)->next)) {
|
||||
if(*pnext == job) { // unlink
|
||||
*pnext = job->next;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// clear scheduled job
|
||||
void os_clearCallback (osjob_t* job) {
|
||||
hal_disableIRQs();
|
||||
u1_t res = unlinkjob(&OS.scheduledjobs, job);
|
||||
if (res)
|
||||
unlinkjob(&OS.runnablejobs, job);
|
||||
hal_enableIRQs();
|
||||
#if LMIC_DEBUG_LEVEL > 1
|
||||
if (res)
|
||||
lmic_printf("%lu: Cleared job %p\n", os_getTime(), job);
|
||||
#endif
|
||||
}
|
||||
|
||||
// schedule immediately runnable job
|
||||
void os_setCallback (osjob_t* job, osjobcb_t cb) {
|
||||
osjob_t** pnext;
|
||||
hal_disableIRQs();
|
||||
// remove if job was already queued
|
||||
os_clearCallback(job);
|
||||
// fill-in job
|
||||
job->func = cb;
|
||||
job->next = NULL;
|
||||
// add to end of run queue
|
||||
for(pnext=&OS.runnablejobs; *pnext; pnext=&((*pnext)->next));
|
||||
*pnext = job;
|
||||
hal_enableIRQs();
|
||||
#if LMIC_DEBUG_LEVEL > 1
|
||||
lmic_printf("%lu: Scheduled job %p, cb %p ASAP\n", os_getTime(), job, cb);
|
||||
#endif
|
||||
}
|
||||
|
||||
// schedule timed job
|
||||
void os_setTimedCallback (osjob_t* job, ostime_t time, osjobcb_t cb) {
|
||||
osjob_t** pnext;
|
||||
hal_disableIRQs();
|
||||
// remove if job was already queued
|
||||
os_clearCallback(job);
|
||||
// fill-in job
|
||||
job->deadline = time;
|
||||
job->func = cb;
|
||||
job->next = NULL;
|
||||
// insert into schedule
|
||||
for(pnext=&OS.scheduledjobs; *pnext; pnext=&((*pnext)->next)) {
|
||||
if((*pnext)->deadline - time > 0) { // (cmp diff, not abs!)
|
||||
// enqueue before next element and stop
|
||||
job->next = *pnext;
|
||||
break;
|
||||
}
|
||||
}
|
||||
*pnext = job;
|
||||
hal_enableIRQs();
|
||||
#if LMIC_DEBUG_LEVEL > 1
|
||||
lmic_printf("%lu: Scheduled job %p, cb %p at %lu\n", os_getTime(), job, cb, time);
|
||||
#endif
|
||||
}
|
||||
|
||||
// execute jobs from timer and from run queue
|
||||
void os_runloop () {
|
||||
while(1) {
|
||||
os_runloop_once();
|
||||
}
|
||||
}
|
||||
|
||||
void os_runloop_once() {
|
||||
#if LMIC_DEBUG_LEVEL > 1
|
||||
bool has_deadline = false;
|
||||
#endif
|
||||
|
||||
osjob_t* j = NULL;
|
||||
hal_enterCriticalSection();
|
||||
// check for runnable jobs
|
||||
if(OS.runnablejobs) {
|
||||
j = OS.runnablejobs;
|
||||
OS.runnablejobs = j->next;
|
||||
} else if(OS.scheduledjobs && hal_checkTimer(OS.scheduledjobs->deadline)) { // check for expired timed jobs
|
||||
j = OS.scheduledjobs;
|
||||
OS.scheduledjobs = j->next;
|
||||
#if LMIC_DEBUG_LEVEL > 1
|
||||
has_deadline = true;
|
||||
#endif
|
||||
}
|
||||
if(j) { // run job callback
|
||||
#if LMIC_DEBUG_LEVEL > 1
|
||||
lmic_printf("%lu: Running job %p, cb %p, deadline %lu\n", os_getTime(), j, j->func, has_deadline ? j->deadline : 0);
|
||||
#endif
|
||||
j->func(j);
|
||||
hal_leaveCriticalSection();
|
||||
} else { // nothing pending
|
||||
hal_leaveCriticalSection();
|
||||
hal_sleep(); // wake by irq (timer already restarted)
|
||||
}
|
||||
}
|
242
src/lmic/oslmic.h
Executable file
242
src/lmic/oslmic.h
Executable file
@ -0,0 +1,242 @@
|
||||
/*
|
||||
* Copyright (c) 2014-2016 IBM Corporation.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the <organization> nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
//! \file
|
||||
#ifndef _oslmic_h_
|
||||
#define _oslmic_h_
|
||||
|
||||
// Dependencies required for the LoRa MAC in C to run.
|
||||
// These settings can be adapted to the underlying system.
|
||||
// You should not, however, change the lmic.[hc]
|
||||
|
||||
#include "config.h"
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//================================================================================
|
||||
//================================================================================
|
||||
// Target platform as C library
|
||||
typedef uint8_t bit_t;
|
||||
typedef uint8_t u1_t;
|
||||
typedef int8_t s1_t;
|
||||
typedef uint16_t u2_t;
|
||||
typedef int16_t s2_t;
|
||||
typedef unsigned long u4_t;
|
||||
typedef long s4_t;
|
||||
typedef unsigned int uint;
|
||||
typedef const char* str_t;
|
||||
|
||||
#include <string.h>
|
||||
#include "hal.h"
|
||||
#define EV(a,b,c) /**/
|
||||
#define DO_DEVDB(field1,field2) /**/
|
||||
#if !defined(CFG_noassert)
|
||||
#define ASSERT(cond) if(!(cond)) hal_failed(__FILE__, __LINE__)
|
||||
#else
|
||||
#define ASSERT(cond) /**/
|
||||
#endif
|
||||
|
||||
#define os_clearMem(a,b) memset(a,0,b)
|
||||
#define os_copyMem(a,b,c) memcpy(a,b,c)
|
||||
|
||||
typedef struct osjob_t osjob_t;
|
||||
typedef struct band_t band_t;
|
||||
typedef struct chnldef_t chnldef_t;
|
||||
typedef struct rxsched_t rxsched_t;
|
||||
typedef struct bcninfo_t bcninfo_t;
|
||||
typedef const u1_t* xref2cu1_t;
|
||||
typedef u1_t* xref2u1_t;
|
||||
#define TYPEDEF_xref2rps_t typedef rps_t* xref2rps_t
|
||||
#define TYPEDEF_xref2rxsched_t typedef rxsched_t* xref2rxsched_t
|
||||
#define TYPEDEF_xref2chnldef_t typedef chnldef_t* xref2chnldef_t
|
||||
#define TYPEDEF_xref2band_t typedef band_t* xref2band_t
|
||||
#define TYPEDEF_xref2osjob_t typedef osjob_t* xref2osjob_t
|
||||
|
||||
#define SIZEOFEXPR(x) sizeof(x)
|
||||
|
||||
#define ON_LMIC_EVENT(ev) onEvent(ev)
|
||||
#define DECL_ON_LMIC_EVENT void onEvent(ev_t e)
|
||||
|
||||
typedef s4_t ostime_t;
|
||||
|
||||
|
||||
extern u4_t AESAUX[];
|
||||
extern u4_t AESKEY[];
|
||||
#define AESkey ((u1_t*)AESKEY)
|
||||
#define AESaux ((u1_t*)AESAUX)
|
||||
#define FUNC_ADDR(func) (&(func))
|
||||
|
||||
u1_t radio_rand1 (void);
|
||||
#define os_getRndU1() radio_rand1()
|
||||
|
||||
#define DEFINE_LMIC struct lmic_t LMIC
|
||||
#define DECLARE_LMIC extern struct lmic_t LMIC
|
||||
|
||||
void radio_init (void);
|
||||
void radio_irq_handler (u1_t dio, ostime_t t);
|
||||
void os_init (void);
|
||||
void os_runloop (void);
|
||||
void os_runloop_once();
|
||||
|
||||
//================================================================================
|
||||
|
||||
|
||||
#ifndef RX_RAMPUP
|
||||
#define RX_RAMPUP (us2osticks(2000))
|
||||
#endif
|
||||
#ifndef TX_RAMPUP
|
||||
#define TX_RAMPUP (us2osticks(2000))
|
||||
#endif
|
||||
|
||||
#ifndef OSTICKS_PER_SEC
|
||||
#define OSTICKS_PER_SEC 32768
|
||||
#elif OSTICKS_PER_SEC < 10000 || OSTICKS_PER_SEC > 64516
|
||||
#error Illegal OSTICKS_PER_SEC - must be in range [10000:64516]. One tick must be 15.5us .. 100us long.
|
||||
#endif
|
||||
|
||||
#if !HAS_ostick_conv
|
||||
#define us2osticks(us) ((ostime_t)( ((int64_t)(us) * OSTICKS_PER_SEC) / 1000000))
|
||||
#define ms2osticks(ms) ((ostime_t)( ((int64_t)(ms) * OSTICKS_PER_SEC) / 1000))
|
||||
#define sec2osticks(sec) ((ostime_t)( (int64_t)(sec) * OSTICKS_PER_SEC))
|
||||
#define osticks2ms(os) ((s4_t)(((os)*(int64_t)1000 ) / OSTICKS_PER_SEC))
|
||||
#define osticks2us(os) ((s4_t)(((os)*(int64_t)1000000 ) / OSTICKS_PER_SEC))
|
||||
// Special versions
|
||||
#define us2osticksCeil(us) ((ostime_t)( ((int64_t)(us) * OSTICKS_PER_SEC + 999999) / 1000000))
|
||||
#define us2osticksRound(us) ((ostime_t)( ((int64_t)(us) * OSTICKS_PER_SEC + 500000) / 1000000))
|
||||
#define ms2osticksCeil(ms) ((ostime_t)( ((int64_t)(ms) * OSTICKS_PER_SEC + 999) / 1000))
|
||||
#define ms2osticksRound(ms) ((ostime_t)( ((int64_t)(ms) * OSTICKS_PER_SEC + 500) / 1000))
|
||||
#endif
|
||||
|
||||
|
||||
struct osjob_t; // fwd decl.
|
||||
typedef void (*osjobcb_t) (struct osjob_t*);
|
||||
struct osjob_t {
|
||||
struct osjob_t* next;
|
||||
ostime_t deadline;
|
||||
osjobcb_t func;
|
||||
};
|
||||
TYPEDEF_xref2osjob_t;
|
||||
|
||||
|
||||
#ifndef HAS_os_calls
|
||||
|
||||
#ifndef os_getDevKey
|
||||
void os_getDevKey (xref2u1_t buf);
|
||||
#endif
|
||||
#ifndef os_getArtEui
|
||||
void os_getArtEui (xref2u1_t buf);
|
||||
#endif
|
||||
#ifndef os_getDevEui
|
||||
void os_getDevEui (xref2u1_t buf);
|
||||
#endif
|
||||
#ifndef os_setCallback
|
||||
void os_setCallback (xref2osjob_t job, osjobcb_t cb);
|
||||
#endif
|
||||
#ifndef os_setTimedCallback
|
||||
void os_setTimedCallback (xref2osjob_t job, ostime_t time, osjobcb_t cb);
|
||||
#endif
|
||||
#ifndef os_clearCallback
|
||||
void os_clearCallback (xref2osjob_t job);
|
||||
#endif
|
||||
#ifndef os_getTime
|
||||
ostime_t os_getTime (void);
|
||||
#endif
|
||||
#ifndef os_getTimeSecs
|
||||
uint os_getTimeSecs (void);
|
||||
#endif
|
||||
#ifndef os_radio
|
||||
void os_radio (u1_t mode);
|
||||
#endif
|
||||
#ifndef os_getBattLevel
|
||||
u1_t os_getBattLevel (void);
|
||||
#endif
|
||||
|
||||
#ifndef os_rlsbf4
|
||||
//! Read 32-bit quantity from given pointer in little endian byte order.
|
||||
u4_t os_rlsbf4 (xref2cu1_t buf);
|
||||
#endif
|
||||
#ifndef os_wlsbf4
|
||||
//! Write 32-bit quntity into buffer in little endian byte order.
|
||||
void os_wlsbf4 (xref2u1_t buf, u4_t value);
|
||||
#endif
|
||||
#ifndef os_rmsbf4
|
||||
//! Read 32-bit quantity from given pointer in big endian byte order.
|
||||
u4_t os_rmsbf4 (xref2cu1_t buf);
|
||||
#endif
|
||||
#ifndef os_wmsbf4
|
||||
//! Write 32-bit quntity into buffer in big endian byte order.
|
||||
void os_wmsbf4 (xref2u1_t buf, u4_t value);
|
||||
#endif
|
||||
#ifndef os_rlsbf2
|
||||
//! Read 16-bit quantity from given pointer in little endian byte order.
|
||||
u2_t os_rlsbf2 (xref2cu1_t buf);
|
||||
#endif
|
||||
#ifndef os_wlsbf2
|
||||
//! Write 16-bit quntity into buffer in little endian byte order.
|
||||
void os_wlsbf2 (xref2u1_t buf, u2_t value);
|
||||
#endif
|
||||
|
||||
//! Get random number (default impl for u2_t).
|
||||
#ifndef os_getRndU2
|
||||
#define os_getRndU2() ((u2_t)((os_getRndU1()<<8)|os_getRndU1()))
|
||||
#endif
|
||||
#ifndef os_crc16
|
||||
u2_t os_crc16 (xref2u1_t d, uint len);
|
||||
#endif
|
||||
|
||||
#endif // !HAS_os_calls
|
||||
|
||||
#define lmic_printf printf
|
||||
|
||||
// ======================================================================
|
||||
// AES support
|
||||
// !!Keep in sync with lorabase.hpp!!
|
||||
|
||||
#ifndef AES_ENC // if AES_ENC is defined as macro all other values must be too
|
||||
#define AES_ENC 0x00
|
||||
#define AES_DEC 0x01
|
||||
#define AES_MIC 0x02
|
||||
#define AES_CTR 0x04
|
||||
#define AES_MICNOAUX 0x08
|
||||
#endif
|
||||
#ifndef AESkey // if AESkey is defined as macro all other values must be too
|
||||
extern xref2u1_t AESkey;
|
||||
extern xref2u1_t AESaux;
|
||||
#endif
|
||||
#ifndef os_aes
|
||||
u4_t os_aes (u1_t mode, xref2u1_t buf, u2_t len);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // _oslmic_h_
|
896
src/lmic/radio.c
Executable file
896
src/lmic/radio.c
Executable file
@ -0,0 +1,896 @@
|
||||
/*
|
||||
* Copyright (c) 2014-2016 IBM Corporation.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the <organization> nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "rom/ets_sys.h"
|
||||
#include "lmic.h"
|
||||
|
||||
// ----------------------------------------
|
||||
// Registers Mapping
|
||||
#define RegFifo 0x00 // common
|
||||
#define RegOpMode 0x01 // common
|
||||
#define FSKRegBitrateMsb 0x02
|
||||
#define FSKRegBitrateLsb 0x03
|
||||
#define FSKRegFdevMsb 0x04
|
||||
#define FSKRegFdevLsb 0x05
|
||||
#define RegFrfMsb 0x06 // common
|
||||
#define RegFrfMid 0x07 // common
|
||||
#define RegFrfLsb 0x08 // common
|
||||
#define RegPaConfig 0x09 // common
|
||||
#define RegPaRamp 0x0A // common
|
||||
#define RegOcp 0x0B // common
|
||||
#define RegLna 0x0C // common
|
||||
#define FSKRegRxConfig 0x0D
|
||||
#define LORARegFifoAddrPtr 0x0D
|
||||
#define FSKRegRssiConfig 0x0E
|
||||
#define LORARegFifoTxBaseAddr 0x0E
|
||||
#define FSKRegRssiCollision 0x0F
|
||||
#define LORARegFifoRxBaseAddr 0x0F
|
||||
#define FSKRegRssiThresh 0x10
|
||||
#define LORARegFifoRxCurrentAddr 0x10
|
||||
#define FSKRegRssiValue 0x11
|
||||
#define LORARegIrqFlagsMask 0x11
|
||||
#define FSKRegRxBw 0x12
|
||||
#define LORARegIrqFlags 0x12
|
||||
#define FSKRegAfcBw 0x13
|
||||
#define LORARegRxNbBytes 0x13
|
||||
#define FSKRegOokPeak 0x14
|
||||
#define LORARegRxHeaderCntValueMsb 0x14
|
||||
#define FSKRegOokFix 0x15
|
||||
#define LORARegRxHeaderCntValueLsb 0x15
|
||||
#define FSKRegOokAvg 0x16
|
||||
#define LORARegRxPacketCntValueMsb 0x16
|
||||
#define LORARegRxpacketCntValueLsb 0x17
|
||||
#define LORARegModemStat 0x18
|
||||
#define LORARegPktSnrValue 0x19
|
||||
#define FSKRegAfcFei 0x1A
|
||||
#define LORARegPktRssiValue 0x1A
|
||||
#define FSKRegAfcMsb 0x1B
|
||||
#define LORARegRssiValue 0x1B
|
||||
#define FSKRegAfcLsb 0x1C
|
||||
#define LORARegHopChannel 0x1C
|
||||
#define FSKRegFeiMsb 0x1D
|
||||
#define LORARegModemConfig1 0x1D
|
||||
#define FSKRegFeiLsb 0x1E
|
||||
#define LORARegModemConfig2 0x1E
|
||||
#define FSKRegPreambleDetect 0x1F
|
||||
#define LORARegSymbTimeoutLsb 0x1F
|
||||
#define FSKRegRxTimeout1 0x20
|
||||
#define LORARegPreambleMsb 0x20
|
||||
#define FSKRegRxTimeout2 0x21
|
||||
#define LORARegPreambleLsb 0x21
|
||||
#define FSKRegRxTimeout3 0x22
|
||||
#define LORARegPayloadLength 0x22
|
||||
#define FSKRegRxDelay 0x23
|
||||
#define LORARegPayloadMaxLength 0x23
|
||||
#define FSKRegOsc 0x24
|
||||
#define LORARegHopPeriod 0x24
|
||||
#define FSKRegPreambleMsb 0x25
|
||||
#define LORARegFifoRxByteAddr 0x25
|
||||
#define LORARegModemConfig3 0x26
|
||||
#define FSKRegPreambleLsb 0x26
|
||||
#define FSKRegSyncConfig 0x27
|
||||
#define LORARegFeiMsb 0x28
|
||||
#define FSKRegSyncValue1 0x28
|
||||
#define LORAFeiMib 0x29
|
||||
#define FSKRegSyncValue2 0x29
|
||||
#define LORARegFeiLsb 0x2A
|
||||
#define FSKRegSyncValue3 0x2A
|
||||
#define FSKRegSyncValue4 0x2B
|
||||
#define LORARegRssiWideband 0x2C
|
||||
#define FSKRegSyncValue5 0x2C
|
||||
#define FSKRegSyncValue6 0x2D
|
||||
#define FSKRegSyncValue7 0x2E
|
||||
#define FSKRegSyncValue8 0x2F
|
||||
#define FSKRegPacketConfig1 0x30
|
||||
#define FSKRegPacketConfig2 0x31
|
||||
#define LORARegDetectOptimize 0x31
|
||||
#define FSKRegPayloadLength 0x32
|
||||
#define FSKRegNodeAdrs 0x33
|
||||
#define LORARegInvertIQ 0x33
|
||||
#define FSKRegBroadcastAdrs 0x34
|
||||
#define FSKRegFifoThresh 0x35
|
||||
#define FSKRegSeqConfig1 0x36
|
||||
#define FSKRegSeqConfig2 0x37
|
||||
#define LORARegDetectionThreshold 0x37
|
||||
#define FSKRegTimerResol 0x38
|
||||
#define FSKRegTimer1Coef 0x39
|
||||
#define LORARegSyncWord 0x39
|
||||
#define FSKRegTimer2Coef 0x3A
|
||||
#define FSKRegImageCal 0x3B
|
||||
#define FSKRegTemp 0x3C
|
||||
#define FSKRegLowBat 0x3D
|
||||
#define FSKRegIrqFlags1 0x3E
|
||||
#define FSKRegIrqFlags2 0x3F
|
||||
#define RegDioMapping1 0x40 // common
|
||||
#define RegDioMapping2 0x41 // common
|
||||
#define RegVersion 0x42 // common
|
||||
// #define RegAgcRef 0x43 // common
|
||||
// #define RegAgcThresh1 0x44 // common
|
||||
// #define RegAgcThresh2 0x45 // common
|
||||
// #define RegAgcThresh3 0x46 // common
|
||||
// #define RegPllHop 0x4B // common
|
||||
// #define RegTcxo 0x58 // common
|
||||
#define RegPaDac 0x5A // common
|
||||
// #define RegPll 0x5C // common
|
||||
// #define RegPllLowPn 0x5E // common
|
||||
// #define RegFormerTemp 0x6C // common
|
||||
// #define RegBitRateFrac 0x70 // common
|
||||
|
||||
// ----------------------------------------
|
||||
// spread factors and mode for RegModemConfig2
|
||||
#define SX1272_MC2_FSK 0x00
|
||||
#define SX1272_MC2_SF7 0x70
|
||||
#define SX1272_MC2_SF8 0x80
|
||||
#define SX1272_MC2_SF9 0x90
|
||||
#define SX1272_MC2_SF10 0xA0
|
||||
#define SX1272_MC2_SF11 0xB0
|
||||
#define SX1272_MC2_SF12 0xC0
|
||||
// bandwidth for RegModemConfig1
|
||||
#define SX1272_MC1_BW_125 0x00
|
||||
#define SX1272_MC1_BW_250 0x40
|
||||
#define SX1272_MC1_BW_500 0x80
|
||||
// coding rate for RegModemConfig1
|
||||
#define SX1272_MC1_CR_4_5 0x08
|
||||
#define SX1272_MC1_CR_4_6 0x10
|
||||
#define SX1272_MC1_CR_4_7 0x18
|
||||
#define SX1272_MC1_CR_4_8 0x20
|
||||
#define SX1272_MC1_IMPLICIT_HEADER_MODE_ON 0x04 // required for receive
|
||||
#define SX1272_MC1_RX_PAYLOAD_CRCON 0x02
|
||||
#define SX1272_MC1_LOW_DATA_RATE_OPTIMIZE 0x01 // mandated for SF11 and SF12
|
||||
// transmit power configuration for RegPaConfig
|
||||
#define SX1272_PAC_PA_SELECT_PA_BOOST 0x80
|
||||
#define SX1272_PAC_PA_SELECT_RFIO_PIN 0x00
|
||||
|
||||
|
||||
// sx1276 RegModemConfig1
|
||||
#define SX1276_MC1_BW_125 0x70
|
||||
#define SX1276_MC1_BW_250 0x80
|
||||
#define SX1276_MC1_BW_500 0x90
|
||||
#define SX1276_MC1_CR_4_5 0x02
|
||||
#define SX1276_MC1_CR_4_6 0x04
|
||||
#define SX1276_MC1_CR_4_7 0x06
|
||||
#define SX1276_MC1_CR_4_8 0x08
|
||||
|
||||
#define SX1276_MC1_IMPLICIT_HEADER_MODE_ON 0x01
|
||||
|
||||
// sx1276 RegModemConfig2
|
||||
#define SX1276_MC2_RX_PAYLOAD_CRCON 0x04
|
||||
|
||||
// sx1276 RegModemConfig3
|
||||
#define SX1276_MC3_LOW_DATA_RATE_OPTIMIZE 0x08
|
||||
#define SX1276_MC3_AGCAUTO 0x04
|
||||
|
||||
// preamble for lora networks (nibbles swapped)
|
||||
#define LORA_MAC_PREAMBLE 0x34
|
||||
|
||||
#define RXLORA_RXMODE_RSSI_REG_MODEM_CONFIG1 0x0A
|
||||
#ifdef CFG_sx1276_radio
|
||||
#define RXLORA_RXMODE_RSSI_REG_MODEM_CONFIG2 0x70
|
||||
#elif CFG_sx1272_radio
|
||||
#define RXLORA_RXMODE_RSSI_REG_MODEM_CONFIG2 0x74
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
// ----------------------------------------
|
||||
// Constants for radio registers
|
||||
#define OPMODE_LORA 0x80
|
||||
#define OPMODE_MASK 0x07
|
||||
#define OPMODE_SLEEP 0x00
|
||||
#define OPMODE_STANDBY 0x01
|
||||
#define OPMODE_FSTX 0x02
|
||||
#define OPMODE_TX 0x03
|
||||
#define OPMODE_FSRX 0x04
|
||||
#define OPMODE_RX 0x05
|
||||
#define OPMODE_RX_SINGLE 0x06
|
||||
#define OPMODE_CAD 0x07
|
||||
|
||||
// ----------------------------------------
|
||||
// Bits masking the corresponding IRQs from the radio
|
||||
#define IRQ_LORA_RXTOUT_MASK 0x80
|
||||
#define IRQ_LORA_RXDONE_MASK 0x40
|
||||
#define IRQ_LORA_CRCERR_MASK 0x20
|
||||
#define IRQ_LORA_HEADER_MASK 0x10
|
||||
#define IRQ_LORA_TXDONE_MASK 0x08
|
||||
#define IRQ_LORA_CDDONE_MASK 0x04
|
||||
#define IRQ_LORA_FHSSCH_MASK 0x02
|
||||
#define IRQ_LORA_CDDETD_MASK 0x01
|
||||
|
||||
#define IRQ_FSK1_MODEREADY_MASK 0x80
|
||||
#define IRQ_FSK1_RXREADY_MASK 0x40
|
||||
#define IRQ_FSK1_TXREADY_MASK 0x20
|
||||
#define IRQ_FSK1_PLLLOCK_MASK 0x10
|
||||
#define IRQ_FSK1_RSSI_MASK 0x08
|
||||
#define IRQ_FSK1_TIMEOUT_MASK 0x04
|
||||
#define IRQ_FSK1_PREAMBLEDETECT_MASK 0x02
|
||||
#define IRQ_FSK1_SYNCADDRESSMATCH_MASK 0x01
|
||||
#define IRQ_FSK2_FIFOFULL_MASK 0x80
|
||||
#define IRQ_FSK2_FIFOEMPTY_MASK 0x40
|
||||
#define IRQ_FSK2_FIFOLEVEL_MASK 0x20
|
||||
#define IRQ_FSK2_FIFOOVERRUN_MASK 0x10
|
||||
#define IRQ_FSK2_PACKETSENT_MASK 0x08
|
||||
#define IRQ_FSK2_PAYLOADREADY_MASK 0x04
|
||||
#define IRQ_FSK2_CRCOK_MASK 0x02
|
||||
#define IRQ_FSK2_LOWBAT_MASK 0x01
|
||||
|
||||
// ----------------------------------------
|
||||
// DIO function mappings D0D1D2D3
|
||||
#define MAP_DIO0_LORA_RXDONE 0x00 // 00------
|
||||
#define MAP_DIO0_LORA_TXDONE 0x40 // 01------
|
||||
#define MAP_DIO1_LORA_RXTOUT 0x00 // --00----
|
||||
#define MAP_DIO1_LORA_NOP 0x30 // --11----
|
||||
#define MAP_DIO2_LORA_NOP 0xC0 // ----11--
|
||||
|
||||
#define MAP_DIO0_FSK_READY 0x00 // 00------ (packet sent / payload ready)
|
||||
#define MAP_DIO1_FSK_NOP 0x30 // --11----
|
||||
#define MAP_DIO2_FSK_TXNOP 0x04 // ----01--
|
||||
#define MAP_DIO2_FSK_TIMEOUT 0x08 // ----10--
|
||||
|
||||
|
||||
// FSK IMAGECAL defines
|
||||
#define RF_IMAGECAL_AUTOIMAGECAL_MASK 0x7F
|
||||
#define RF_IMAGECAL_AUTOIMAGECAL_ON 0x80
|
||||
#define RF_IMAGECAL_AUTOIMAGECAL_OFF 0x00 // Default
|
||||
|
||||
#define RF_IMAGECAL_IMAGECAL_MASK 0xBF
|
||||
#define RF_IMAGECAL_IMAGECAL_START 0x40
|
||||
|
||||
#define RF_IMAGECAL_IMAGECAL_RUNNING 0x20
|
||||
#define RF_IMAGECAL_IMAGECAL_DONE 0x00 // Default
|
||||
|
||||
|
||||
// RADIO STATE
|
||||
// (initialized by radio_init(), used by radio_rand1())
|
||||
static u1_t randbuf[16];
|
||||
|
||||
|
||||
#ifdef CFG_sx1276_radio
|
||||
#define LNA_RX_GAIN (0x20|0x1)
|
||||
#elif CFG_sx1272_radio
|
||||
#define LNA_RX_GAIN (0x20|0x03)
|
||||
#else
|
||||
#error Missing CFG_sx1272_radio/CFG_sx1276_radio
|
||||
#endif
|
||||
|
||||
|
||||
static void writeReg (u1_t addr, u1_t data ) {
|
||||
hal_spi_write(addr | 0x80, &data, 1);
|
||||
/*
|
||||
hal_pin_nss(0);
|
||||
hal_spi(addr | 0x80);
|
||||
hal_spi(data);
|
||||
hal_pin_nss(1);
|
||||
*/
|
||||
}
|
||||
|
||||
static u1_t readReg (u1_t addr) {
|
||||
u1_t buf[1];
|
||||
hal_spi_read(addr & 0x7f, buf, 1);
|
||||
return buf[0];
|
||||
/*
|
||||
hal_pin_nss(0);
|
||||
hal_spi(addr & 0x7F);
|
||||
u1_t val = hal_spi(0x00);
|
||||
hal_pin_nss(1);
|
||||
return val;
|
||||
*/
|
||||
}
|
||||
|
||||
static void writeBuf (u1_t addr, xref2u1_t buf, u1_t len) {
|
||||
hal_spi_write(addr | 0x80, buf, len);
|
||||
/*
|
||||
hal_pin_nss(0);
|
||||
hal_spi(addr | 0x80);
|
||||
for (u1_t i=0; i<len; i++) {
|
||||
hal_spi(buf[i]);
|
||||
}
|
||||
hal_pin_nss(1);
|
||||
*/
|
||||
}
|
||||
|
||||
static void readBuf (u1_t addr, xref2u1_t buf, u1_t len) {
|
||||
hal_spi_read(addr & 0x7f, buf, len);
|
||||
/*
|
||||
hal_pin_nss(0);
|
||||
hal_spi(addr & 0x7F);
|
||||
for (u1_t i=0; i<len; i++) {
|
||||
buf[i] = hal_spi(0x00);
|
||||
}
|
||||
hal_pin_nss(1);
|
||||
*/
|
||||
}
|
||||
|
||||
static void opmode (u1_t mode) {
|
||||
writeReg(RegOpMode, (readReg(RegOpMode) & ~OPMODE_MASK) | mode);
|
||||
}
|
||||
|
||||
static void opmodeLora() {
|
||||
u1_t u = OPMODE_LORA;
|
||||
#ifdef CFG_sx1276_radio
|
||||
u |= 0x8; // TBD: sx1276 high freq
|
||||
#endif
|
||||
writeReg(RegOpMode, u);
|
||||
}
|
||||
|
||||
static void opmodeFSK() {
|
||||
u1_t u = 0;
|
||||
#ifdef CFG_sx1276_radio
|
||||
u |= 0x8; // TBD: sx1276 high freq
|
||||
#endif
|
||||
writeReg(RegOpMode, u);
|
||||
}
|
||||
|
||||
// configure LoRa modem (cfg1, cfg2)
|
||||
static void configLoraModem () {
|
||||
sf_t sf = getSf(LMIC.rps);
|
||||
|
||||
#ifdef CFG_sx1276_radio
|
||||
u1_t mc1 = 0, mc2 = 0, mc3 = 0;
|
||||
|
||||
switch (getBw(LMIC.rps)) {
|
||||
case BW125: mc1 |= SX1276_MC1_BW_125; break;
|
||||
case BW250: mc1 |= SX1276_MC1_BW_250; break;
|
||||
case BW500: mc1 |= SX1276_MC1_BW_500; break;
|
||||
default:
|
||||
ASSERT(0);
|
||||
}
|
||||
switch( getCr(LMIC.rps) ) {
|
||||
case CR_4_5: mc1 |= SX1276_MC1_CR_4_5; break;
|
||||
case CR_4_6: mc1 |= SX1276_MC1_CR_4_6; break;
|
||||
case CR_4_7: mc1 |= SX1276_MC1_CR_4_7; break;
|
||||
case CR_4_8: mc1 |= SX1276_MC1_CR_4_8; break;
|
||||
default:
|
||||
ASSERT(0);
|
||||
}
|
||||
|
||||
if (getIh(LMIC.rps)) {
|
||||
mc1 |= SX1276_MC1_IMPLICIT_HEADER_MODE_ON;
|
||||
writeReg(LORARegPayloadLength, getIh(LMIC.rps)); // required length
|
||||
}
|
||||
// set ModemConfig1
|
||||
writeReg(LORARegModemConfig1, mc1);
|
||||
|
||||
mc2 = (SX1272_MC2_SF7 + ((sf-1)<<4));
|
||||
if (getNocrc(LMIC.rps) == 0) {
|
||||
mc2 |= SX1276_MC2_RX_PAYLOAD_CRCON;
|
||||
}
|
||||
writeReg(LORARegModemConfig2, mc2);
|
||||
|
||||
mc3 = SX1276_MC3_AGCAUTO;
|
||||
if ((sf == SF11 || sf == SF12) && getBw(LMIC.rps) == BW125) {
|
||||
mc3 |= SX1276_MC3_LOW_DATA_RATE_OPTIMIZE;
|
||||
}
|
||||
writeReg(LORARegModemConfig3, mc3);
|
||||
#elif CFG_sx1272_radio
|
||||
u1_t mc1 = (getBw(LMIC.rps)<<6);
|
||||
|
||||
switch( getCr(LMIC.rps) ) {
|
||||
case CR_4_5: mc1 |= SX1272_MC1_CR_4_5; break;
|
||||
case CR_4_6: mc1 |= SX1272_MC1_CR_4_6; break;
|
||||
case CR_4_7: mc1 |= SX1272_MC1_CR_4_7; break;
|
||||
case CR_4_8: mc1 |= SX1272_MC1_CR_4_8; break;
|
||||
}
|
||||
|
||||
if ((sf == SF11 || sf == SF12) && getBw(LMIC.rps) == BW125) {
|
||||
mc1 |= SX1272_MC1_LOW_DATA_RATE_OPTIMIZE;
|
||||
}
|
||||
|
||||
if (getNocrc(LMIC.rps) == 0) {
|
||||
mc1 |= SX1272_MC1_RX_PAYLOAD_CRCON;
|
||||
}
|
||||
|
||||
if (getIh(LMIC.rps)) {
|
||||
mc1 |= SX1272_MC1_IMPLICIT_HEADER_MODE_ON;
|
||||
writeReg(LORARegPayloadLength, getIh(LMIC.rps)); // required length
|
||||
}
|
||||
// set ModemConfig1
|
||||
writeReg(LORARegModemConfig1, mc1);
|
||||
|
||||
// set ModemConfig2 (sf, AgcAutoOn=1 SymbTimeoutHi=00)
|
||||
writeReg(LORARegModemConfig2, (SX1272_MC2_SF7 + ((sf-1)<<4)) | 0x04);
|
||||
#else
|
||||
#error Missing CFG_sx1272_radio/CFG_sx1276_radio
|
||||
#endif /* CFG_sx1272_radio */
|
||||
}
|
||||
|
||||
static void configChannel () {
|
||||
// set frequency: FQ = (FRF * 32 Mhz) / (2 ^ 19)
|
||||
uint64_t frf = ((uint64_t)LMIC.freq << 19) / 32000000;
|
||||
writeReg(RegFrfMsb, (u1_t)(frf>>16));
|
||||
writeReg(RegFrfMid, (u1_t)(frf>> 8));
|
||||
writeReg(RegFrfLsb, (u1_t)(frf>> 0));
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void configPower () {
|
||||
#ifdef CFG_sx1276_radio
|
||||
// no boost used for now
|
||||
s1_t pw = (s1_t)LMIC.txpow;
|
||||
if(pw >= 17) {
|
||||
pw = 15;
|
||||
} else if(pw < 2) {
|
||||
pw = 2;
|
||||
}
|
||||
// check board type for BOOST pin
|
||||
writeReg(RegPaConfig, (u1_t)(0x80|(pw&0xf)));
|
||||
writeReg(RegPaDac, readReg(RegPaDac)|0x4);
|
||||
|
||||
#elif CFG_sx1272_radio
|
||||
// set PA config (2-17 dBm using PA_BOOST)
|
||||
s1_t pw = (s1_t)LMIC.txpow;
|
||||
if(pw > 17) {
|
||||
pw = 17;
|
||||
} else if(pw < 2) {
|
||||
pw = 2;
|
||||
}
|
||||
writeReg(RegPaConfig, (u1_t)(0x80|(pw-2)));
|
||||
#else
|
||||
#error Missing CFG_sx1272_radio/CFG_sx1276_radio
|
||||
#endif /* CFG_sx1272_radio */
|
||||
}
|
||||
|
||||
static void txfsk () {
|
||||
// select FSK modem (from sleep mode)
|
||||
writeReg(RegOpMode, 0x10); // FSK, BT=0.5
|
||||
ASSERT(readReg(RegOpMode) == 0x10);
|
||||
// enter standby mode (required for FIFO loading))
|
||||
opmode(OPMODE_STANDBY);
|
||||
// set bitrate
|
||||
writeReg(FSKRegBitrateMsb, 0x02); // 50kbps
|
||||
writeReg(FSKRegBitrateLsb, 0x80);
|
||||
// set frequency deviation
|
||||
writeReg(FSKRegFdevMsb, 0x01); // +/- 25kHz
|
||||
writeReg(FSKRegFdevLsb, 0x99);
|
||||
// frame and packet handler settings
|
||||
writeReg(FSKRegPreambleMsb, 0x00);
|
||||
writeReg(FSKRegPreambleLsb, 0x05);
|
||||
writeReg(FSKRegSyncConfig, 0x12);
|
||||
writeReg(FSKRegPacketConfig1, 0xD0);
|
||||
writeReg(FSKRegPacketConfig2, 0x40);
|
||||
writeReg(FSKRegSyncValue1, 0xC1);
|
||||
writeReg(FSKRegSyncValue2, 0x94);
|
||||
writeReg(FSKRegSyncValue3, 0xC1);
|
||||
// configure frequency
|
||||
configChannel();
|
||||
// configure output power
|
||||
configPower();
|
||||
|
||||
// set the IRQ mapping DIO0=PacketSent DIO1=NOP DIO2=NOP
|
||||
writeReg(RegDioMapping1, MAP_DIO0_FSK_READY|MAP_DIO1_FSK_NOP|MAP_DIO2_FSK_TXNOP);
|
||||
|
||||
// initialize the payload size and address pointers
|
||||
writeReg(FSKRegPayloadLength, LMIC.dataLen+1); // (insert length byte into payload))
|
||||
|
||||
// download length byte and buffer to the radio FIFO
|
||||
writeReg(RegFifo, LMIC.dataLen);
|
||||
writeBuf(RegFifo, LMIC.frame, LMIC.dataLen);
|
||||
|
||||
// enable antenna switch for TX
|
||||
hal_pin_rxtx(1);
|
||||
|
||||
// now we actually start the transmission
|
||||
opmode(OPMODE_TX);
|
||||
}
|
||||
|
||||
static void txlora () {
|
||||
// select LoRa modem (from sleep mode)
|
||||
//writeReg(RegOpMode, OPMODE_LORA);
|
||||
opmodeLora();
|
||||
// can take a moment to change; so try ten times
|
||||
u1_t reg;
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
reg = readReg(RegOpMode);
|
||||
if ((reg & OPMODE_LORA) != 0)
|
||||
break;
|
||||
ets_delay_us(100);
|
||||
}
|
||||
ASSERT((reg & OPMODE_LORA) != 0);
|
||||
|
||||
// enter standby mode (required for FIFO loading))
|
||||
opmode(OPMODE_STANDBY);
|
||||
// configure LoRa modem (cfg1, cfg2)
|
||||
configLoraModem();
|
||||
// configure frequency
|
||||
configChannel();
|
||||
// configure output power
|
||||
writeReg(RegPaRamp, (readReg(RegPaRamp) & 0xF0) | 0x08); // set PA ramp-up time 50 uSec
|
||||
configPower();
|
||||
// set sync word
|
||||
writeReg(LORARegSyncWord, LORA_MAC_PREAMBLE);
|
||||
|
||||
// set the IRQ mapping DIO0=TxDone DIO1=NOP DIO2=NOP
|
||||
writeReg(RegDioMapping1, MAP_DIO0_LORA_TXDONE|MAP_DIO1_LORA_NOP|MAP_DIO2_LORA_NOP);
|
||||
// clear all radio IRQ flags
|
||||
writeReg(LORARegIrqFlags, 0xFF);
|
||||
// mask all IRQs but TxDone
|
||||
writeReg(LORARegIrqFlagsMask, ~IRQ_LORA_TXDONE_MASK);
|
||||
|
||||
// initialize the payload size and address pointers
|
||||
writeReg(LORARegFifoTxBaseAddr, 0x00);
|
||||
writeReg(LORARegFifoAddrPtr, 0x00);
|
||||
writeReg(LORARegPayloadLength, LMIC.dataLen);
|
||||
|
||||
// download buffer to the radio FIFO
|
||||
writeBuf(RegFifo, LMIC.frame, LMIC.dataLen);
|
||||
|
||||
// enable antenna switch for TX
|
||||
hal_pin_rxtx(1);
|
||||
|
||||
// now we actually start the transmission
|
||||
opmode(OPMODE_TX);
|
||||
|
||||
#if LMIC_DEBUG_LEVEL > 0
|
||||
u1_t sf = getSf(LMIC.rps) + 6; // 1 == SF7
|
||||
u1_t bw = getBw(LMIC.rps);
|
||||
u1_t cr = getCr(LMIC.rps);
|
||||
lmic_printf("%lu: TXMODE, freq=%lu, len=%d, SF=%d, BW=%d, CR=4/%d, IH=%d\n",
|
||||
os_getTime(), LMIC.freq, LMIC.dataLen, sf,
|
||||
bw == BW125 ? 125 : (bw == BW250 ? 250 : 500),
|
||||
cr == CR_4_5 ? 5 : (cr == CR_4_6 ? 6 : (cr == CR_4_7 ? 7 : 8)),
|
||||
getIh(LMIC.rps)
|
||||
);
|
||||
#endif
|
||||
}
|
||||
|
||||
// start transmitter (buf=LMIC.frame, len=LMIC.dataLen)
|
||||
static void starttx () {
|
||||
ASSERT( (readReg(RegOpMode) & OPMODE_MASK) == OPMODE_SLEEP );
|
||||
if(getSf(LMIC.rps) == FSK) { // FSK modem
|
||||
txfsk();
|
||||
} else { // LoRa modem
|
||||
txlora();
|
||||
}
|
||||
// the radio will go back to STANDBY mode as soon as the TX is finished
|
||||
// the corresponding IRQ will inform us about completion.
|
||||
}
|
||||
|
||||
enum { RXMODE_SINGLE, RXMODE_SCAN, RXMODE_RSSI };
|
||||
|
||||
static const u1_t rxlorairqmask[] = {
|
||||
[RXMODE_SINGLE] = IRQ_LORA_RXDONE_MASK|IRQ_LORA_RXTOUT_MASK,
|
||||
[RXMODE_SCAN] = IRQ_LORA_RXDONE_MASK,
|
||||
[RXMODE_RSSI] = 0x00,
|
||||
};
|
||||
|
||||
// start LoRa receiver (time=LMIC.rxtime, timeout=LMIC.rxsyms, result=LMIC.frame[LMIC.dataLen])
|
||||
static void rxlora (u1_t rxmode) {
|
||||
// select LoRa modem (from sleep mode)
|
||||
opmodeLora();
|
||||
ASSERT((readReg(RegOpMode) & OPMODE_LORA) != 0);
|
||||
// enter standby mode (warm up))
|
||||
opmode(OPMODE_STANDBY);
|
||||
// don't use MAC settings at startup
|
||||
if(rxmode == RXMODE_RSSI) { // use fixed settings for rssi scan
|
||||
writeReg(LORARegModemConfig1, RXLORA_RXMODE_RSSI_REG_MODEM_CONFIG1);
|
||||
writeReg(LORARegModemConfig2, RXLORA_RXMODE_RSSI_REG_MODEM_CONFIG2);
|
||||
} else { // single or continuous rx mode
|
||||
// configure LoRa modem (cfg1, cfg2)
|
||||
configLoraModem();
|
||||
// configure frequency
|
||||
configChannel();
|
||||
}
|
||||
// set LNA gain
|
||||
writeReg(RegLna, LNA_RX_GAIN);
|
||||
// set max payload size
|
||||
writeReg(LORARegPayloadMaxLength, 64);
|
||||
#if !defined(DISABLE_INVERT_IQ_ON_RX)
|
||||
// use inverted I/Q signal (prevent mote-to-mote communication)
|
||||
|
||||
// XXX: use flag to switch on/off inversion
|
||||
if (LMIC.noRXIQinversion) {
|
||||
writeReg(LORARegInvertIQ, readReg(LORARegInvertIQ) & ~(1<<6));
|
||||
} else {
|
||||
writeReg(LORARegInvertIQ, readReg(LORARegInvertIQ)|(1<<6));
|
||||
}
|
||||
#endif
|
||||
// set symbol timeout (for single rx)
|
||||
writeReg(LORARegSymbTimeoutLsb, LMIC.rxsyms);
|
||||
// set sync word
|
||||
writeReg(LORARegSyncWord, LORA_MAC_PREAMBLE);
|
||||
|
||||
// configure DIO mapping DIO0=RxDone DIO1=RxTout DIO2=NOP
|
||||
writeReg(RegDioMapping1, MAP_DIO0_LORA_RXDONE|MAP_DIO1_LORA_RXTOUT|MAP_DIO2_LORA_NOP);
|
||||
// clear all radio IRQ flags
|
||||
writeReg(LORARegIrqFlags, 0xFF);
|
||||
// enable required radio IRQs
|
||||
writeReg(LORARegIrqFlagsMask, ~rxlorairqmask[rxmode]);
|
||||
|
||||
// enable antenna switch for RX
|
||||
hal_pin_rxtx(0);
|
||||
|
||||
// now instruct the radio to receive
|
||||
if (rxmode == RXMODE_SINGLE) { // single rx
|
||||
hal_waitUntil(LMIC.rxtime); // busy wait until exact rx time
|
||||
opmode(OPMODE_RX_SINGLE);
|
||||
} else { // continous rx (scan or rssi)
|
||||
opmode(OPMODE_RX);
|
||||
}
|
||||
|
||||
#if LMIC_DEBUG_LEVEL > 0
|
||||
if (rxmode == RXMODE_RSSI) {
|
||||
lmic_printf("RXMODE_RSSI\n");
|
||||
} else {
|
||||
u1_t sf = getSf(LMIC.rps) + 6; // 1 == SF7
|
||||
u1_t bw = getBw(LMIC.rps);
|
||||
u1_t cr = getCr(LMIC.rps);
|
||||
lmic_printf("%lu: %s, freq=%lu, SF=%d, BW=%d, CR=4/%d, IH=%d\n",
|
||||
os_getTime(),
|
||||
rxmode == RXMODE_SINGLE ? "RXMODE_SINGLE" : (rxmode == RXMODE_SCAN ? "RXMODE_SCAN" : "UNKNOWN_RX"),
|
||||
LMIC.freq, sf,
|
||||
bw == BW125 ? 125 : (bw == BW250 ? 250 : 500),
|
||||
cr == CR_4_5 ? 5 : (cr == CR_4_6 ? 6 : (cr == CR_4_7 ? 7 : 8)),
|
||||
getIh(LMIC.rps)
|
||||
);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void rxfsk (u1_t rxmode) {
|
||||
// only single rx (no continuous scanning, no noise sampling)
|
||||
ASSERT( rxmode == RXMODE_SINGLE );
|
||||
// select FSK modem (from sleep mode)
|
||||
//writeReg(RegOpMode, 0x00); // (not LoRa)
|
||||
opmodeFSK();
|
||||
ASSERT((readReg(RegOpMode) & OPMODE_LORA) == 0);
|
||||
// enter standby mode (warm up))
|
||||
opmode(OPMODE_STANDBY);
|
||||
// configure frequency
|
||||
configChannel();
|
||||
// set LNA gain
|
||||
//writeReg(RegLna, 0x20|0x03); // max gain, boost enable
|
||||
writeReg(RegLna, LNA_RX_GAIN);
|
||||
// configure receiver
|
||||
writeReg(FSKRegRxConfig, 0x1E); // AFC auto, AGC, trigger on preamble?!?
|
||||
// set receiver bandwidth
|
||||
writeReg(FSKRegRxBw, 0x0B); // 50kHz SSb
|
||||
// set AFC bandwidth
|
||||
writeReg(FSKRegAfcBw, 0x12); // 83.3kHz SSB
|
||||
// set preamble detection
|
||||
writeReg(FSKRegPreambleDetect, 0xAA); // enable, 2 bytes, 10 chip errors
|
||||
// set sync config
|
||||
writeReg(FSKRegSyncConfig, 0x12); // no auto restart, preamble 0xAA, enable, fill FIFO, 3 bytes sync
|
||||
// set packet config
|
||||
writeReg(FSKRegPacketConfig1, 0xD8); // var-length, whitening, crc, no auto-clear, no adr filter
|
||||
writeReg(FSKRegPacketConfig2, 0x40); // packet mode
|
||||
// set sync value
|
||||
writeReg(FSKRegSyncValue1, 0xC1);
|
||||
writeReg(FSKRegSyncValue2, 0x94);
|
||||
writeReg(FSKRegSyncValue3, 0xC1);
|
||||
// set preamble timeout
|
||||
writeReg(FSKRegRxTimeout2, 0xFF);//(LMIC.rxsyms+1)/2);
|
||||
// set bitrate
|
||||
writeReg(FSKRegBitrateMsb, 0x02); // 50kbps
|
||||
writeReg(FSKRegBitrateLsb, 0x80);
|
||||
// set frequency deviation
|
||||
writeReg(FSKRegFdevMsb, 0x01); // +/- 25kHz
|
||||
writeReg(FSKRegFdevLsb, 0x99);
|
||||
|
||||
// configure DIO mapping DIO0=PayloadReady DIO1=NOP DIO2=TimeOut
|
||||
writeReg(RegDioMapping1, MAP_DIO0_FSK_READY|MAP_DIO1_FSK_NOP|MAP_DIO2_FSK_TIMEOUT);
|
||||
|
||||
// enable antenna switch for RX
|
||||
hal_pin_rxtx(0);
|
||||
|
||||
// now instruct the radio to receive
|
||||
hal_waitUntil(LMIC.rxtime); // busy wait until exact rx time
|
||||
opmode(OPMODE_RX); // no single rx mode available in FSK
|
||||
}
|
||||
|
||||
static void startrx (u1_t rxmode) {
|
||||
ASSERT( (readReg(RegOpMode) & OPMODE_MASK) == OPMODE_SLEEP );
|
||||
if(getSf(LMIC.rps) == FSK) { // FSK modem
|
||||
rxfsk(rxmode);
|
||||
} else { // LoRa modem
|
||||
rxlora(rxmode);
|
||||
}
|
||||
// the radio will go back to STANDBY mode as soon as the RX is finished
|
||||
// or timed out, and the corresponding IRQ will inform us about completion.
|
||||
}
|
||||
|
||||
// get random seed from wideband noise rssi
|
||||
void radio_init () {
|
||||
hal_disableIRQs();
|
||||
|
||||
// manually reset radio
|
||||
#ifdef CFG_sx1276_radio
|
||||
hal_pin_rst(0); // drive RST pin low
|
||||
#else
|
||||
hal_pin_rst(1); // drive RST pin high
|
||||
#endif
|
||||
hal_waitUntil(os_getTime()+ms2osticks(1)); // wait >100us
|
||||
hal_pin_rst(2); // configure RST pin floating!
|
||||
hal_waitUntil(os_getTime()+ms2osticks(5)); // wait 5ms
|
||||
|
||||
opmode(OPMODE_SLEEP);
|
||||
|
||||
// some sanity checks, e.g., read version number
|
||||
u1_t v = readReg(RegVersion);
|
||||
#ifdef CFG_sx1276_radio
|
||||
ASSERT(v == 0x12 );
|
||||
#elif CFG_sx1272_radio
|
||||
ASSERT(v == 0x22);
|
||||
#else
|
||||
#error Missing CFG_sx1272_radio/CFG_sx1276_radio
|
||||
#endif
|
||||
// seed 15-byte randomness via noise rssi
|
||||
rxlora(RXMODE_RSSI);
|
||||
while( (readReg(RegOpMode) & OPMODE_MASK) != OPMODE_RX ); // continuous rx
|
||||
for(int i=1; i<16; i++) {
|
||||
for(int j=0; j<8; j++) {
|
||||
u1_t b; // wait for two non-identical subsequent least-significant bits
|
||||
while( (b = readReg(LORARegRssiWideband) & 0x01) == (readReg(LORARegRssiWideband) & 0x01) );
|
||||
randbuf[i] = (randbuf[i] << 1) | b;
|
||||
}
|
||||
}
|
||||
randbuf[0] = 16; // set initial index
|
||||
|
||||
#ifdef CFG_sx1276mb1_board
|
||||
// chain calibration
|
||||
writeReg(RegPaConfig, 0);
|
||||
|
||||
// Launch Rx chain calibration for LF band
|
||||
writeReg(FSKRegImageCal, (readReg(FSKRegImageCal) & RF_IMAGECAL_IMAGECAL_MASK)|RF_IMAGECAL_IMAGECAL_START);
|
||||
while((readReg(FSKRegImageCal)&RF_IMAGECAL_IMAGECAL_RUNNING) == RF_IMAGECAL_IMAGECAL_RUNNING){ ; }
|
||||
|
||||
// Sets a Frequency in HF band
|
||||
u4_t frf = 868000000;
|
||||
writeReg(RegFrfMsb, (u1_t)(frf>>16));
|
||||
writeReg(RegFrfMid, (u1_t)(frf>> 8));
|
||||
writeReg(RegFrfLsb, (u1_t)(frf>> 0));
|
||||
|
||||
// Launch Rx chain calibration for HF band
|
||||
writeReg(FSKRegImageCal, (readReg(FSKRegImageCal) & RF_IMAGECAL_IMAGECAL_MASK)|RF_IMAGECAL_IMAGECAL_START);
|
||||
while((readReg(FSKRegImageCal) & RF_IMAGECAL_IMAGECAL_RUNNING) == RF_IMAGECAL_IMAGECAL_RUNNING) { ; }
|
||||
#endif /* CFG_sx1276mb1_board */
|
||||
|
||||
opmode(OPMODE_SLEEP);
|
||||
|
||||
hal_enableIRQs();
|
||||
}
|
||||
|
||||
// return next random byte derived from seed buffer
|
||||
// (buf[0] holds index of next byte to be returned)
|
||||
u1_t radio_rand1 () {
|
||||
u1_t i = randbuf[0];
|
||||
ASSERT( i != 0 );
|
||||
if( i==16 ) {
|
||||
os_aes(AES_ENC, randbuf, 16); // encrypt seed with any key
|
||||
i = 0;
|
||||
}
|
||||
u1_t v = randbuf[i++];
|
||||
randbuf[0] = i;
|
||||
return v;
|
||||
}
|
||||
|
||||
u1_t radio_rssi () {
|
||||
hal_disableIRQs();
|
||||
u1_t r = readReg(LORARegRssiValue);
|
||||
hal_enableIRQs();
|
||||
return r;
|
||||
}
|
||||
|
||||
static const u2_t LORA_RXDONE_FIXUP[] = {
|
||||
[FSK] = us2osticks(0), // ( 0 ticks)
|
||||
[SF7] = us2osticks(0), // ( 0 ticks)
|
||||
[SF8] = us2osticks(1648), // ( 54 ticks)
|
||||
[SF9] = us2osticks(3265), // ( 107 ticks)
|
||||
[SF10] = us2osticks(7049), // ( 231 ticks)
|
||||
[SF11] = us2osticks(13641), // ( 447 ticks)
|
||||
[SF12] = us2osticks(31189), // (1022 ticks)
|
||||
};
|
||||
|
||||
// called by hal ext IRQ handler
|
||||
// (radio goes to stanby mode after tx/rx operations)
|
||||
void radio_irq_handler (u1_t dio, ostime_t t) {
|
||||
if( (readReg(RegOpMode) & OPMODE_LORA) != 0) { // LORA modem
|
||||
u1_t flags = readReg(LORARegIrqFlags);
|
||||
#if LMIC_DEBUG_LEVEL > 1
|
||||
lmic_printf("%lu: irq: dio: 0x%x flags: 0x%x\n", t, dio, flags);
|
||||
#endif
|
||||
if( flags & IRQ_LORA_TXDONE_MASK ) {
|
||||
// save exact tx time
|
||||
LMIC.txend = t - us2osticks(43); // TXDONE FIXUP
|
||||
} else if( flags & IRQ_LORA_RXDONE_MASK ) {
|
||||
// save exact rx time
|
||||
if(getBw(LMIC.rps) == BW125) {
|
||||
t -= LORA_RXDONE_FIXUP[getSf(LMIC.rps)];
|
||||
}
|
||||
LMIC.rxtime = t;
|
||||
// read the PDU and inform the MAC that we received something
|
||||
LMIC.dataLen = (readReg(LORARegModemConfig1) & SX1272_MC1_IMPLICIT_HEADER_MODE_ON) ?
|
||||
readReg(LORARegPayloadLength) : readReg(LORARegRxNbBytes);
|
||||
// set FIFO read address pointer
|
||||
writeReg(LORARegFifoAddrPtr, readReg(LORARegFifoRxCurrentAddr));
|
||||
// now read the FIFO
|
||||
readBuf(RegFifo, LMIC.frame, LMIC.dataLen);
|
||||
// read rx quality parameters
|
||||
LMIC.snr = readReg(LORARegPktSnrValue); // SNR [dB] * 4
|
||||
LMIC.rssi = readReg(LORARegPktRssiValue) - 125 + 64; // RSSI [dBm] (-196...+63)
|
||||
} else if( flags & IRQ_LORA_RXTOUT_MASK ) {
|
||||
// indicate timeout
|
||||
LMIC.dataLen = 0;
|
||||
}
|
||||
// mask all radio IRQs
|
||||
writeReg(LORARegIrqFlagsMask, 0xFF);
|
||||
// clear radio IRQ flags
|
||||
writeReg(LORARegIrqFlags, 0xFF);
|
||||
} else { // FSK modem
|
||||
u1_t flags1 = readReg(FSKRegIrqFlags1);
|
||||
u1_t flags2 = readReg(FSKRegIrqFlags2);
|
||||
if( flags2 & IRQ_FSK2_PACKETSENT_MASK ) {
|
||||
// save exact tx time
|
||||
LMIC.txend = t;
|
||||
} else if( flags2 & IRQ_FSK2_PAYLOADREADY_MASK ) {
|
||||
// save exact rx time
|
||||
LMIC.rxtime = t;
|
||||
// read the PDU and inform the MAC that we received something
|
||||
LMIC.dataLen = readReg(FSKRegPayloadLength);
|
||||
// now read the FIFO
|
||||
readBuf(RegFifo, LMIC.frame, LMIC.dataLen);
|
||||
// read rx quality parameters
|
||||
LMIC.snr = 0; // determine snr
|
||||
LMIC.rssi = 0; // determine rssi
|
||||
} else if( flags1 & IRQ_FSK1_TIMEOUT_MASK ) {
|
||||
// indicate timeout
|
||||
LMIC.dataLen = 0;
|
||||
} else {
|
||||
ASSERT(0);
|
||||
}
|
||||
}
|
||||
// go from stanby to sleep
|
||||
opmode(OPMODE_SLEEP);
|
||||
// run os job (use preset func ptr)
|
||||
os_setCallback(&LMIC.osjob, LMIC.osjob.func);
|
||||
}
|
||||
|
||||
void os_radio (u1_t mode) {
|
||||
hal_disableIRQs();
|
||||
switch (mode) {
|
||||
case RADIO_RST:
|
||||
// put radio to sleep
|
||||
opmode(OPMODE_SLEEP);
|
||||
break;
|
||||
|
||||
case RADIO_TX:
|
||||
// transmit frame now
|
||||
starttx(); // buf=LMIC.frame, len=LMIC.dataLen
|
||||
break;
|
||||
|
||||
case RADIO_RX:
|
||||
// receive frame now (exactly at rxtime)
|
||||
startrx(RXMODE_SINGLE); // buf=LMIC.frame, time=LMIC.rxtime, timeout=LMIC.rxsyms
|
||||
break;
|
||||
|
||||
case RADIO_RXON:
|
||||
// start scanning for beacon now
|
||||
startrx(RXMODE_SCAN); // buf=LMIC.frame
|
||||
break;
|
||||
}
|
||||
hal_enableIRQs();
|
||||
}
|
Reference in New Issue
Block a user