Transmit and Receive with Interrupts
For the final iteration of the “nasty” test harness I got the interrupts working for the transmitting and receiving of messages. It’s not quite simultaneous, the code sends a message every 10 seconds then goes back to receive continuous mode after each message has been sent.
//---------------------------------------------------------------------------------
// Copyright (c) August 2018, devMobile Software
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//---------------------------------------------------------------------------------
namespace devMobile.IoT.Rfm9x.ReceiveTransmitInterrupt
{
using System;
using System.Diagnostics;
using System.Text;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Threading.Tasks;
using Windows.ApplicationModel.Background;
using Windows.Devices.Spi;
using Windows.Devices.Gpio;
public sealed class Rfm9XDevice
{
private SpiDevice Rfm9XLoraModem = null;
private GpioPin ChipSelectGpioPin = null;
private GpioPin InterruptGpioPin = null;
private const byte RegisterAddressReadMask = 0X7f;
private const byte RegisterAddressWriteMask = 0x80;
public Rfm9XDevice(byte chipSelectPin, byte resetPin, byte interruptPin)
{
SpiController spiController = SpiController.GetDefaultAsync().AsTask().GetAwaiter().GetResult();
var settings = new SpiConnectionSettings(0)
{
ClockFrequency = 500000,
Mode = SpiMode.Mode0,
};
// Chip select pin configuration
GpioController gpioController = GpioController.GetDefault();
ChipSelectGpioPin = gpioController.OpenPin(chipSelectPin);
ChipSelectGpioPin.SetDriveMode(GpioPinDriveMode.Output);
ChipSelectGpioPin.Write(GpioPinValue.High);
// Reset pin configuration to do factory reset
GpioPin resetGpioPin = gpioController.OpenPin(resetPin);
resetGpioPin.SetDriveMode(GpioPinDriveMode.Output);
resetGpioPin.Write(GpioPinValue.Low);
Task.Delay(10);
resetGpioPin.Write(GpioPinValue.High);
Task.Delay(10);
// Interrupt pin for RX message & TX done notification
InterruptGpioPin = gpioController.OpenPin(interruptPin);
resetGpioPin.SetDriveMode(GpioPinDriveMode.Input);
InterruptGpioPin.ValueChanged += InterruptGpioPin_ValueChanged;
Rfm9XLoraModem = spiController.GetDevice(settings);
}
private void InterruptGpioPin_ValueChanged(GpioPin sender, GpioPinValueChangedEventArgs args)
{
if (args.Edge != GpioPinEdge.RisingEdge)
{
return;
}
byte IrqFlags = this.RegisterReadByte(0x12); // RegIrqFlags
Debug.WriteLine(string.Format("RegIrqFlags {0}", Convert.ToString(IrqFlags, 2).PadLeft(8, '0')));
if ((IrqFlags & 0b01000000) == 0b01000000) // RxDone
{
Debug.WriteLine("Receive-Message");
byte currentFifoAddress = this.RegisterReadByte(0x10); // RegFifiRxCurrent
this.RegisterWriteByte(0x0d, currentFifoAddress); // RegFifoAddrPtr
byte numberOfBytes = this.RegisterReadByte(0x13); // RegRxNbBytes
// Allocate buffer for message
byte[] messageBytes = new byte[numberOfBytes];
for (int i = 0; i < numberOfBytes; i++)
{
messageBytes[i] = this.RegisterReadByte(0x00); // RegFifo
}
string messageText = UTF8Encoding.UTF8.GetString(messageBytes);
Debug.WriteLine("Received {0} byte message {1}", messageBytes.Length, messageText);
}
if ((IrqFlags & 0b00001000) == 0b00001000) // TxDone
{
this.RegisterWriteByte(0x01, 0b10000101); // RegOpMode set LoRa & RxContinuous
Debug.WriteLine("Transmit-Done");
}
this.RegisterWriteByte(0x40, 0b00000000); // RegDioMapping1 0b00000000 DI0 RxReady & TxReady
this.RegisterWriteByte(0x12, 0xff);// RegIrqFlags
}
public Byte RegisterReadByte(byte address)
{
byte[] writeBuffer = new byte[] { address &= RegisterAddressReadMask };
byte[] readBuffer = new byte[1];
Debug.Assert(Rfm9XLoraModem != null);
ChipSelectGpioPin.Write(GpioPinValue.Low);
Rfm9XLoraModem.Write(writeBuffer);
Rfm9XLoraModem.Read(readBuffer);
ChipSelectGpioPin.Write(GpioPinValue.High);
return readBuffer[0];
}
public ushort RegisterReadWord(byte address)
{
byte[] writeBuffer = new byte[] { address &= RegisterAddressReadMask };
byte[] readBuffer = new byte[2];
Debug.Assert(Rfm9XLoraModem != null);
ChipSelectGpioPin.Write(GpioPinValue.Low);
Rfm9XLoraModem.Write(writeBuffer);
Rfm9XLoraModem.Read(readBuffer);
ChipSelectGpioPin.Write(GpioPinValue.High);
return (ushort)(readBuffer[1] + (readBuffer[0] << 8));
}
public byte[] RegisterRead(byte address, int length)
{
byte[] writeBuffer = new byte[] { address &= RegisterAddressReadMask };
byte[] readBuffer = new byte[length];
Debug.Assert(Rfm9XLoraModem != null);
ChipSelectGpioPin.Write(GpioPinValue.Low);
Rfm9XLoraModem.Write(writeBuffer);
Rfm9XLoraModem.Read(readBuffer);
ChipSelectGpioPin.Write(GpioPinValue.High);
return readBuffer;
}
public void RegisterWriteByte(byte address, byte value)
{
byte[] writeBuffer = new byte[] { address |= RegisterAddressWriteMask, value };
Debug.Assert(Rfm9XLoraModem != null);
ChipSelectGpioPin.Write(GpioPinValue.Low);
Rfm9XLoraModem.Write(writeBuffer);
ChipSelectGpioPin.Write(GpioPinValue.High);
}
public void RegisterWriteWord(byte address, ushort value)
{
byte[] valueBytes = BitConverter.GetBytes(value);
byte[] writeBuffer = new byte[] { address |= RegisterAddressWriteMask, valueBytes[0], valueBytes[1] };
Debug.Assert(Rfm9XLoraModem != null);
ChipSelectGpioPin.Write(GpioPinValue.Low);
Rfm9XLoraModem.Write(writeBuffer);
ChipSelectGpioPin.Write(GpioPinValue.High);
}
public void RegisterWrite(byte address, [ReadOnlyArray()] byte[] bytes)
{
byte[] writeBuffer = new byte[1 + bytes.Length];
Debug.Assert(Rfm9XLoraModem != null);
Array.Copy(bytes, 0, writeBuffer, 1, bytes.Length);
writeBuffer[0] = address |= RegisterAddressWriteMask;
ChipSelectGpioPin.Write(GpioPinValue.Low);
Rfm9XLoraModem.Write(writeBuffer);
ChipSelectGpioPin.Write(GpioPinValue.High);
}
public void RegisterDump()
{
Debug.WriteLine("Register dump");
for (byte registerIndex = 0; registerIndex <= 0x42; registerIndex++)
{
byte registerValue = this.RegisterReadByte(registerIndex);
Debug.WriteLine("Register 0x{0:x2} - Value 0X{1:x2} - Bits {2}", registerIndex, registerValue, Convert.ToString(registerValue, 2).PadLeft(8, '0'));
}
}
}
public sealed class StartupTask : IBackgroundTask
{
private const byte ChipSelectLine = 25;
private const byte ResetLine = 17;
private const byte InterruptLine = 4;
private Rfm9XDevice rfm9XDevice = new Rfm9XDevice(ChipSelectLine, ResetLine, InterruptLine);
private byte NessageCount = Byte.MaxValue;
public void Run(IBackgroundTaskInstance taskInstance)
{
// Put device into LoRa + Sleep mode
rfm9XDevice.RegisterWriteByte(0x01, 0b10000000); // RegOpMode
// Set the frequency to 915MHz
byte[] frequencyWriteBytes = { 0xE4, 0xC0, 0x00 }; // RegFrMsb, RegFrMid, RegFrLsb
rfm9XDevice.RegisterWrite(0x06, frequencyWriteBytes);
rfm9XDevice.RegisterWriteByte(0x0F, 0x0); // RegFifoRxBaseAddress
// More power PA Boost
rfm9XDevice.RegisterWriteByte(0x09, 0b10000000); // RegPaConfig
rfm9XDevice.RegisterWriteByte(0x01, 0b10000101); // RegOpMode set LoRa & RxContinuous
while (true)
{
rfm9XDevice.RegisterWriteByte(0x0E, 0x0); // RegFifoTxBaseAddress
// Set the Register Fifo address pointer
rfm9XDevice.RegisterWriteByte(0x0D, 0x0); // RegFifoAddrPtr
string messageText = "W10 IoT Core LoRa! " + NessageCount.ToString();
NessageCount -= 1;
// load the message into the fifo
byte[] messageBytes = UTF8Encoding.UTF8.GetBytes(messageText);
foreach (byte b in messageBytes)
{
rfm9XDevice.RegisterWriteByte(0x0, b); // RegFifo
}
// Set the length of the message in the fifo
rfm9XDevice.RegisterWriteByte(0x22, (byte)messageBytes.Length); // RegPayloadLength
rfm9XDevice.RegisterWriteByte(0x40, 0b01000000); // RegDioMapping1 0b00000000 DI0 RxReady & TxReady
rfm9XDevice.RegisterWriteByte(0x01, 0b10000011); // RegOpMode
Debug.WriteLine("Sending {0} bytes message {1}", messageBytes.Length, messageText);
Task.Delay(10000).Wait();
}
}
}
}
The diagnostic output shows inbound and outbound messages
RegIrqFlags 01010000
Receive-Message
Received 16 byte message HeLoRa World! 80
Sending 22 bytes message W10 IoT Core LoRa! 255
RegIrqFlags 01011000
Receive-Message
Received 16 byte message HeLoRa World! 80
Transmit-Done
RegIrqFlags 01010000
Receive-Message
Received 16 byte message HeLoRa World! 82
RegIrqFlags 01010000
Receive-Message
Received 16 byte message HeLoRa World! 84
The thread 0xf20 has exited with code 0 (0x0).
The thread 0xbe4 has exited with code 0 (0x0).
RegIrqFlags 01010000
Receive-Message
Received 16 byte message HeLoRa World! 86
RegIrqFlags 01010000
Receive-Message
Received 16 byte message HeLoRa World! 88
Sending 22 bytes message W10 IoT Core LoRa! 254
RegIrqFlags 00001000
Transmit-Done
RegIrqFlags 01110000
Receive-Message
Received 46 byte message ��Lh��ORegIrqFlags 01010000
Receive-Message
Received 16 byte message HeLoRa World! 92
The RegIrqFlags 01011000 indicates the RxDone, ValidHeader, and TxDone flags were set which was what I was expecting. Note the interference, the 46 byte packet
Next step is some refactoring to extract the register access code and figuring out a reasonable approach for RMF9X library.
