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.