.NET Core SX127X library Part6

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.

SX127X data sheet page 108 Register 0x0 to 0x07

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

State transition diagram for transmitting messages in LoRa mode

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.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.