.NET Core 5 SX127X library Part2

Register Reading and Writing

Now that Serial Peripheral(SPI) connectivity for my .Net Core 5 dotnet/iot SX127X library is working, the next step is to build a “generic” class for my two reference Rapsberry Pi HATS.

The Uputronics Raspberry PiZero LoRa(TM) Expansion Board supports both standard Chip Select(CS) lines (switch selectable which is really useful) and the reset pin is not connected.

Uputronics Raspberry PIZero LoRa Expansion board on a Raspberry PI 3 device

The M2M 1 Channel LoRaWan Gateway Shield for Raspberry PI has a “non-standard” CS pin and the reset pin is connected to pin 17.

M2M Single channel shield on Raspberry Pi 3 Device

In my previous post the spiDevice.TransferFullDuplex method worked for a standard CS line (CS0 or CS1), and for a non-standard CS pin, though the CS line configured in SpiConnectionSettings was “unusable” by other applications.

static void Main(string[] args)
{
	Byte regOpMode;
	ushort preamble;
	byte[] frequencyBytes;
	// Uptronics has no reset pin uses CS0 or CS1
	//SX127XDevice sX127XDevice = new SX127XDevice(chipSelectLine: 0); 
	//SX127XDevice sX127XDevice = new SX127XDevice(chipSelectLine: 1); 

	// M2M device has reset pin uses non standard chip select 
	//SX127XDevice sX127XDevice = new SX127XDevice(chipSelectLine: 0, chipSelectLogicalPinNumber: 25, resetPin: 17);
	SX127XDevice sX127XDevice = new SX127XDevice(chipSelectLine: 1, chipSelectLogicalPinNumber:25, resetPin: 17);

	Console.WriteLine("In FSK mode");
	sX127XDevice.RegisterDump();


	Console.WriteLine("Read RegOpMode (read byte)");
	regOpMode = sX127XDevice.ReadByte(0x1);
	Debug.WriteLine($"RegOpMode 0x{regOpMode:x2}");

	Console.WriteLine("Set LoRa mode and sleep mode (write byte)");
	sX127XDevice.WriteByte(0x01, 0b10000000);

	Console.WriteLine("Read RegOpMode (read byte)");
	regOpMode = sX127XDevice.ReadByte(0x1);
	Debug.WriteLine($"RegOpMode 0x{regOpMode:x2}");


	Console.WriteLine("In LoRa mode");
	sX127XDevice.RegisterDump();


	Console.WriteLine("Read the preamble (read word)"); // Should be 0x08
	preamble = sX127XDevice.ReadWordMsbLsb(0x20);
	Debug.WriteLine($"Preamble 0x{preamble:x2} - Bits {Convert.ToString(preamble, 2).PadLeft(16, '0')}");

	Console.WriteLine("Set the preamble to 0x8000 (write word)");
	sX127XDevice.WriteWordMsbLsb(0x20, 0x8000);

	Console.WriteLine("Read the preamble (read word)"); // Should be 0x08
	preamble = sX127XDevice.ReadWordMsbLsb(0x20);
	Debug.WriteLine($"Preamble 0x{preamble:x2} - Bits {Convert.ToString(preamble, 2).PadLeft(16, '0')}");


	Console.WriteLine("Read the centre frequency"); // RegFrfMsb 0x6c RegFrfMid 0x80 RegFrfLsb 0x00 which is 433MHz
	frequencyBytes = sX127XDevice.ReadBytes(0x06, 3);
	Console.WriteLine($"Frequency Msb 0x{frequencyBytes[0]:x2} Mid 0x{frequencyBytes[1]:x2} Lsb 0x{frequencyBytes[2]:x2}");

	Console.WriteLine("Set the centre frequency"); 
	byte[] frequencyWriteBytes = { 0xE4, 0xC0, 0x00 };
	sX127XDevice.WriteBytes(0x06, frequencyWriteBytes);

	Console.WriteLine("Read the centre frequency"); // RegFrfMsb 0xE4 RegFrfMid 0xC0 RegFrfLsb 0x00 which is 915MHz
	frequencyBytes = sX127XDevice.ReadBytes(0x06, 3);
	Console.WriteLine($"Frequency Msb 0x{frequencyBytes[0]:x2} Mid 0x{frequencyBytes[1]:x2} Lsb 0x{frequencyBytes[2]:x2}");


	sX127XDevice.RegisterDump();

	// Sleep forever
	Thread.Sleep(-1);
}

I use RegisterDump multiple times to show the updates working.

...
Loaded '/usr/lib/dotnet/shared/Microsoft.NETCore.App/5.0.4/Microsoft.Win32.Primitives.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
In FSK mode
Register dump
Register 0x00 - Value 0X00 - Bits 00000000
Register 0x01 - Value 0X09 - Bits 00001001
Register 0x02 - Value 0X1a - Bits 00011010
Register 0x03 - Value 0X0b - Bits 00001011
Register 0x04 - Value 0X00 - Bits 00000000
Register 0x05 - Value 0X52 - Bits 01010010
Register 0x06 - Value 0X6c - Bits 01101100
Register 0x07 - Value 0X80 - Bits 10000000
...
Register 0x1f - Value 0X40 - Bits 01000000
Register 0x20 - Value 0X00 - Bits 00000000
Register 0x21 - Value 0X00 - Bits 00000000
Register 0x22 - Value 0X00 - Bits 00000000
...
Register 0x41 - Value 0X00 - Bits 00000000
Register 0x42 - Value 0X12 - Bits 00010010

Read RegOpMode (read byte)
RegOpMode 0x09
Set LoRa mode and sleep mode (write byte)
Read RegOpMode (read byte)
RegOpMode 0x80
In LoRa mode
Register dump
Register 0x00 - Value 0Xdf - Bits 11011111
Register 0x01 - Value 0X80 - Bits 10000000
Register 0x02 - Value 0X1a - Bits 00011010
Register 0x03 - Value 0X0b - Bits 00001011
Register 0x04 - Value 0X00 - Bits 00000000
Register 0x05 - Value 0X52 - Bits 01010010
Register 0x06 - Value 0X6c - Bits 01101100
Register 0x07 - Value 0X80 - Bits 10000000
...
Register 0x1f - Value 0X64 - Bits 01100100
Register 0x20 - Value 0X00 - Bits 00000000
Register 0x21 - Value 0X08 - Bits 00001000
Register 0x22 - Value 0X01 - Bits 00000001
...
Register 0x41 - Value 0X00 - Bits 00000000
Register 0x42 - Value 0X12 - Bits 00010010

Read the preamble (read word)
Preamble 0x08 - Bits 0000000000001000
Set the preamble to 0x8000 (write word)
Read the preamble (read word)
Preamble 0x8000 - Bits 1000000000000000
Read the centre frequency
Frequency Msb 0x6c Mid 0x80 Lsb 0x00
Set the centre frequency
Read the centre frequency
Frequency Msb 0xe4 Mid 0xc0 Lsb 0x00
Register dump
Register 0x00 - Value 0Xb9 - Bits 10111001
Register 0x01 - Value 0X80 - Bits 10000000
Register 0x02 - Value 0X1a - Bits 00011010
Register 0x03 - Value 0X0b - Bits 00001011
Register 0x04 - Value 0X00 - Bits 00000000
Register 0x05 - Value 0X52 - Bits 01010010
Register 0x06 - Value 0Xe4 - Bits 11100100
Register 0x07 - Value 0Xc0 - Bits 11000000
...
Register 0x1f - Value 0X64 - Bits 01100100
Register 0x20 - Value 0X80 - Bits 10000000
Register 0x21 - Value 0X00 - Bits 00000000
Register 0x22 - Value 0X01 - Bits 00000001
...
Register 0x3f - Value 0X00 - Bits 00000000
Register 0x40 - Value 0X00 - Bits 00000000

Summary

In this iteration I added support for resetting the SX127X module (where supported by the Raspberry PI HAT) and an spiDevice.TransferFullDuplex based implementation for reading/writing individual bytes/words and reading/writing arrays of bytes.

public byte[] ReadBytes(byte registerAddress, byte length)
{
	Span<byte> writeBuffer = stackalloc byte[length + 1];
	Span<byte> readBuffer = stackalloc byte[writeBuffer.Length];

	if (SX127XTransceiver == null)
	{
		throw new ApplicationException("SX127XDevice is not initialised");
	}

	writeBuffer[0] = registerAddress &= RegisterAddressReadMask;

	if (this.ChipSelectLogicalPinNumber != 0)
	{
		gpioController.Write(ChipSelectLogicalPinNumber, PinValue.Low);
	}

	this.SX127XTransceiver.TransferFullDuplex(writeBuffer, readBuffer);

	if (this.ChipSelectLogicalPinNumber != 0)
	{
		gpioController.Write(ChipSelectLogicalPinNumber, PinValue.High);
	}

	return readBuffer[1..readBuffer.Length].ToArray();
}

I used stackalloc so the memory for the writeBuffer and readBuffer doesn’t have to be tidied up by the .Net Garbage Collector(GC).

public void WriteBytes(byte address, byte[] bytes)
{
	Span<byte> writeBuffer = stackalloc byte[bytes.Length + 1];
	Span<byte> readBuffer = stackalloc byte[writeBuffer.Length];

	if (SX127XTransceiver == null)
	{
		throw new ApplicationException("SX127XDevice is not initialised");
	}

	writeBuffer[0] = address |= RegisterAddressWriteMask;
	for (byte index = 0; index < bytes.Length; index++)
	{
		writeBuffer[index + 1] = bytes[index];
	}

	if (this.ChipSelectLogicalPinNumber != 0)
	{
		gpioController.Write(ChipSelectLogicalPinNumber, PinValue.Low);
	}

	this.SX127XTransceiver.TransferFullDuplex(writeBuffer, readBuffer);

	if (this.ChipSelectLogicalPinNumber != 0)
	{
		gpioController.Write(ChipSelectLogicalPinNumber, PinValue.High);
	}
}

In the WriteBytes method copying the bytes from the bytes[] parameter to the span with a for loop is a bit ugly but I couldn’t find a better way. One odd thing I noticed was that if I wrote a lot of debug output the text would be truncated in the output window

Frequency Msb 0xe4 Mid 0xc0 Lsb 0x00
Register dump
Register 0x00 - Value 0Xb9 - Bits 10111001
Register 0x01 - Value 0X80 - Bits 10000000
Register 0x02 - Value 0X1a - Bits 00011010
Register 0x03 - Value 0X0b - Bits 00001011
Register 0x04 - Value 0X00 - Bits 00000000
Register 0x05 - Value 0X52 - Bits 01010010
Register 0x06 - Value 0Xe4 - Bits 11100100
Register 0x07 - Value 0Xc0 - Bits 11000000
Register 0x08 - Value 0X00 - Bits 00000000
Register 0x09 - Value 0X4f - Bits 01001111
Register 0x0a - Value 0X09 - Bits 00001001
Register 0x0b - Value 0X2b - Bits 00101011
Register 0x0c - Value 0X20 - Bits 00100000
Register 0x0d - Value 0X02 - Bits 00000010
Register 0x0e - Value 0X80 - Bits 10000000
Register 0x0f - Value 0X00 - Bits 00000000
Register 0x10 - Value 0X00 - Bits 00000000
Register 0x11 - Value 0X00 - Bits 00000000
Register 0x12 - Value 0X00 - Bits 00000000
Register 0x13 - Value 0X00 - Bits 00000000
Register 0x14 - Value 0X00 - Bits 00000000
Register 0x15 - Value 0X00 - Bits 00000000
Register 0x16 - Value 0X00 - Bits 00000000
Register 0x17 - Value 0X00 - Bits 00000000
Register 0x18 - Value 0X10 - Bits 00010000
Register 0x19 - Value 0X00 - Bits 00000000
Register 0x1a - Value 0X00 - Bits 00000000
Register 0x1b - Value 0X00 - Bits 00000000
Register 0x1c - Value 0X00 - Bits 00000000
Register 0x1d - Value 0X72 - Bits 01110010
Register 0x1e - Value 0X70 - Bits 01110000
Register 0x1f - Value 0X64 - Bits 01100100
Register 0x20 - Value 0X80 - Bits 10000000
Register 0x21 - Value 0X00 - Bits 00000000
Register 0x22 - Value 0X01 - Bits 00000001
Register 0x23 - Value 0Xff - Bits 11111111
Register 0x24 - Value 0X00 - Bits 00000000
Register 0x25 - Value 0X00 - Bits 00000000
Register 0x26 - Value 0X04 - Bits 00000100
Register 0x27 - Value 0X00 - Bits 00000000
Register 0x28 - Value 0X00 - Bits 00000000
Register 0x29 - Value 0X00 - Bits 00000000
Register 0x2a - Value 0X00 - Bits 00000000
Register 0x2b - Value 0X00 - Bits 00000000
Register 0x2c - Value 0X00 - Bits 00000000
Register 0x2d - Value 0X50 - Bits 01010000
Register 0x2e - Value 0X14 - Bits 00010100
Register 0x2f - Value 0X45 - Bits 01000101
Register 0x30 - Value 0X55 - Bits 01010101
Register 0x31 - Value 0Xc3 - Bits 11000011
Register 0x32 - Value 0X05 - Bits 00000101
Register 0x33 - Value 0X27 - Bits 00100111
Register 0x34 - Value 0X1c - Bits 00011100
Register 0x35 - Value 0X0a - Bits 00001010
Register 0x36 - Value 0X03 - Bits 00000011
Register 0x37 - Value 0X0a - Bits 00001010
Register 0x38 - Value 0X42 - Bits 01000010
Register 0x39 - Value 0X12 - Bits 00010010
Register 0x3a - Value 0X49 - Bits 01001001
Register 0x3b - Value 0X1d - Bits 00011101
Register 0x3c - Value 0X00 - Bits 00000000
Register 0x3d - Value 0Xaf - Bits 10101111
Register 0x3e - Value 0X00 - Bits 00000000
Register 0x3f - Value 0X00 - Bits 00000000
Register 0x40 - Value 0X00 - Bits 00000000

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.