Enumerations, Constants and Masks
This was a huge (and tedious) task. I went thru the SX127X datasheet and created enumerations, masks, flags, defaults, and constants for most of the LoRa mode configuration options. The list of LoRa registers with a brief overview of each one starts on page 108 and finishes on page 115 of the datasheet.
The registers enumeration is approximately fifty lines long, some of which I have ignored because I don’t need them or I can’t figure out what they do.
// Registers from SemTech SX127X Datasheet
enum Registers : byte
{
MinValue = RegOpMode,
RegFifo = 0x0,
RegOpMode = 0x01,
//Reserved 0x02-0x06
RegFrMsb = 0x06,
RegFrMid = 0x7,
RegFrLsb = 0x08,
RegPAConfig = 0x09,
//RegPARamp = 0x0A, // not included as FSK/OOK functionality
RegOcp = 0x0B,
RegLna = 0x0C,
RegFifoAddrPtr = 0x0D,
RegFifoTxBaseAddr = 0x0E,
RegFifoRxCurrent = 0x10,
RegIrqFlagsMask = 0x11,
RegIrqFlags = 0x12,
RegRxNbBytes = 0x13,
// RegRxHeaderCntValueMsb=0x14
// RegRxHeaderCntValueLsb=0x15
// RegRxPacketCntValueMsb=0x16
// RegRxPacketCntValueMsb=0x17
// RegModemStat=0x18
RegPktSnrValue=0x19,
RegPktRssiValue=0x1A,
RegRssiValue=0x1B,
RegHopChannel=0x1C,
RegModemConfig1 = 0x1D,
RegModemConfig2 = 0x1E,
RegSymbTimeout = 0x1F,
RegPreambleMsb = 0x20,
RegPreambleLsb = 0x21,
RegPayloadLength = 0x22,
RegMaxPayloadLength = 0x23,
RegHopPeriod = 0x24,
// RegFifiRxByteAddr = 0x25
RegModemConfig3 = 0x26,
RegPpmCorrection = 0x27,
// RegFeiMsb = 0x28
// RegFeiMid = 0x29
// RegFeiLsb = 0x2A
// Reserved 0x2B
// RegRssiWideband = 0x2C
// Reserved 0x2D-0x30
RegDetectOptimize = 0x31,
// Reserved 0x32
RegInvertIQ = 0x33,
// Reserved 0x34-0x36
RegDetectionThreshold = 0x37,
// Reserved 0x38
RegSyncWord = 0x39,
RegInvertIQ2 = 0x3B,
RegDioMapping1 = 0x40,
RegVersion = 0x42,
MaxValue = RegVersion,
}
For each register I worked out from the documentation what it was used for, did I need to implement it and if so how. For example RegOpMode controls the operating mode of the module and has a state machine as well
// RegOpMode mode flags
private const byte RegOpModeLongRangeModeLoRa = 0b10000000;
private const byte RegOpModeLongRangeModeFskOok = 0b00000000;
private const byte RegOpModeLongRangeModeDefault = RegOpModeLongRangeModeFskOok;
private const byte RegOpModeAcessSharedRegLoRa = 0b00000000;
private const byte RegOpModeAcessSharedRegFsk = 0b01000000;
private const byte RegOpModeAcessSharedRegDefault = RegOpModeAcessSharedRegLoRa;
private const byte RegOpModeLowFrequencyModeOnHighFrequency = 0b00000000;
private const byte RegOpModeLowFrequencyModeOnLowFrequency = 0b00001000;
private const byte RegOpModeLowFrequencyModeOnDefault = RegOpModeLowFrequencyModeOnLowFrequency;
[Flags]
public enum RegOpModeMode : byte
{
Sleep = 0b00000000,
StandBy = 0b00000001,
FrequencySynthesisTX = 0b00000010,
Transmit = 0b00000011,
FrequencySynthesisRX = 0b00000100,
ReceiveContinuous = 0b00000101,
ReceiveSingle = 0b00000110,
ChannelActivityDetection = 0b00000111,
};
// Frequency configuration magic numbers from Semtech SX127X specs
private const double SX127X_FXOSC = 32000000.0;
private const double SX127X_FSTEP = SX127X_FXOSC / 524288.0;
private const double SX127XMidBandThreshold = 525000000.0; // Search for RF_MID_BAND_THRESH GitHub LoRaNet LoRaMac-node/src/boards/sx1276-board.h
private const int RssiAdjustmentHF = -157;
private const int RssiAdjustmentLF = -164;
// RegFrMsb, RegFrMid, RegFrLsb
private const double FrequencyDefault = 434000000.0;
Some of the documentation is incredibly detailed but has little impact on my use case. (If someone needs this sort of functionality I will add it)
Some operations have state machines which add even more implementation complexity
In my library I reset the SX127X then configure any “non-default” settings as the application starts. My applications ten to change only a limited number of registers once they are running. For any register(s) that can be changed while the application is running I have a “shadow” variable for each of them so I don’t have to read the register before writing it.
public void Initialise(RegOpModeMode modeAfterInitialise, // RegOpMode
double frequency = FrequencyDefault, // RegFrMsb, RegFrMid, RegFrLsb
bool rxDoneignoreIfCrcMissing = true, bool rxDoneignoreIfCrcInvalid = true,
bool paBoost = false, byte maxPower = RegPAConfigMaxPowerDefault, byte outputPower = RegPAConfigOutputPowerDefault, // RegPaConfig
bool ocpOn = RegOcpDefault, byte ocpTrim = RegOcpOcpTrimDefault, // RegOcp
RegLnaLnaGain lnaGain = LnaGainDefault, bool lnaBoost = LnaBoostDefault, // RegLna
RegModemConfigBandwidth bandwidth = RegModemConfigBandwidthDefault, RegModemConfigCodingRate codingRate = RegModemConfigCodingRateDefault, RegModemConfigImplicitHeaderModeOn implicitHeaderModeOn = RegModemConfigImplicitHeaderModeOnDefault, //RegModemConfig1
RegModemConfig2SpreadingFactor spreadingFactor = RegModemConfig2SpreadingFactorDefault, bool txContinuousMode = false, bool rxPayloadCrcOn = false,
ushort symbolTimeout = SymbolTimeoutDefault,
ushort preambleLength = PreambleLengthDefault,
byte payloadLength = PayloadLengthDefault,
byte payloadMaxLength = PayloadMaxLengthDefault,
byte freqHoppingPeriod = FreqHoppingPeriodDefault,
bool lowDataRateOptimize = LowDataRateOptimizeDefault, bool agcAutoOn = AgcAutoOnDefault,
byte ppmCorrection = ppmCorrectionDefault,
RegDetectOptimizeDectionOptimize detectionOptimize = RegDetectOptimizeDectionOptimizeDefault,
bool invertIQRX = InvertIqRXDefault, bool invertIQTX = InvertIqTXDefault,
RegisterDetectionThreshold detectionThreshold = RegisterDetectionThresholdDefault,
byte syncWord = RegSyncWordDefault)
{
RegOpModeModeCurrent = modeAfterInitialise; // TODO
Frequency = frequency; // Store this away for RSSI adjustments
RxDoneIgnoreIfCrcMissing = rxDoneignoreIfCrcMissing;
RxDoneIgnoreIfCrcInvalid = rxDoneignoreIfCrcInvalid;
InvertIQRX = invertIQRX;
InvertIQTX = invertIQTX;
// Strobe Reset pin briefly to factory reset SX127X chip
if (ResetLogicalPinNumber != 0)
{
gpioController.Write(ResetLogicalPinNumber, PinValue.Low);
Thread.Sleep(20);
gpioController.Write(ResetLogicalPinNumber, PinValue.High);
Thread.Sleep(20);
}
// Put the device into sleep mode so registers can be changed
SetMode(RegOpModeMode.Sleep);
// Configure RF Carrier frequency
if (frequency != FrequencyDefault)
{
byte[] bytes = BitConverter.GetBytes((long)(frequency / SX127X_FSTEP));
this.WriteByte((byte)Registers.RegFrMsb, bytes[2]);
this.WriteByte((byte)Registers.RegFrMid, bytes[1]);
this.WriteByte((byte)Registers.RegFrLsb, bytes[0]);
}
....
}
Summary
There are a huge number of configuration options for an SX127X device so my library exposes the ones required for common use cases. If a scenario is not supported the ReadByte, ReadBytes, ReadWordMsbLsb, WriteByte, WriteBytes, WriteWordMsbLsb and RegisterDump methods are available. But, beware the SX127X is complex to configure and operate.