diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 5954c67eb6..9fe3d2127a 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -54,18 +54,6 @@ stages:
/p:BuildPackages=true
displayName: Build
- - script: powershell -ExecutionPolicy ByPass -NoProfile eng\common\msbuild.ps1 -warnaserror:0 -ci
- eng/sendToHelix.proj
- /t:Test
- /p:TestOS=Windows_NT
- /p:Configuration=$(BuildConfiguration)
- /p:HelixBuild=$(Build.BuildNumber)
- /bl:$(Build.SourcesDirectory)/artifacts/log/$(BuildConfiguration)/SendToHelix.binlog
- displayName: Run Helix Tests
- condition: eq(variables['build.reason'], 'PullRequest')
- env:
- SYSTEM_ACCESSTOKEN: $(System.AccessToken)
-
- task: PublishBuildArtifacts@1
displayName: Publish Build logs
condition: always()
diff --git a/eng/sendToHelix.proj b/eng/sendToHelix.proj
index d37984dbc0..b0b76763f0 100644
--- a/eng/sendToHelix.proj
+++ b/eng/sendToHelix.proj
@@ -30,25 +30,6 @@
-
-
-
-
- netcoreapp3.1
- netcoreapp2.0
-
-
-
-
-
-
-
- win-arm
- $(XUnitArguments) -notrait "SkipOnTestRun=Windows_NT"
-
- $(XUnitArguments) -notrait feature=pwm
-
-
diff --git a/src/System.Device.Gpio/System/Device/Gpio/RaspberryBoardInfo.cs b/src/System.Device.Gpio/System/Device/Gpio/RaspberryBoardInfo.cs
index 1f1656d0cf..3afc6c71bb 100644
--- a/src/System.Device.Gpio/System/Device/Gpio/RaspberryBoardInfo.cs
+++ b/src/System.Device.Gpio/System/Device/Gpio/RaspberryBoardInfo.cs
@@ -164,7 +164,7 @@ public Model BoardModel
0x20E0 => Model.RaspberryPi3APlus,
0x20A0 or 0x2100 => Model.RaspberryPiComputeModule3,
0x3111 or 0x3112 or 0x3114 or 0x3115 => Model.RaspberryPi4,
- 0x3140 => Model.RaspberryPiComputeModule4,
+ 0x3140 or 0x3141 => Model.RaspberryPiComputeModule4,
0x3130 => Model.RaspberryPi400,
_ => Model.Unknown,
};
diff --git a/src/devices/Mcp25xxx/FrequencyAndSpeed.cs b/src/devices/Mcp25xxx/FrequencyAndSpeed.cs
new file mode 100644
index 0000000000..26a4af3899
--- /dev/null
+++ b/src/devices/Mcp25xxx/FrequencyAndSpeed.cs
@@ -0,0 +1,176 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Iot.Device.Mcp25xxx
+{
+ ///
+ /// Standard Frequency and Speed for CAN bus
+ ///
+ public enum FrequencyAndSpeed
+ {
+ ///
+ /// 8MHz 1000kBPS
+ ///
+ _8MHz1000KBps,
+
+ ///
+ /// 8MHz 500kBPS
+ ///
+ _8MHz500KBps,
+
+ ///
+ /// 8MHz 250kBPS
+ ///
+ _8MHz250KBps,
+
+ ///
+ /// 8MHz 200kBPS
+ ///
+ _8MHz200KBps,
+
+ ///
+ /// 8MHz 125kBPS
+ ///
+ _8MHz125KBps,
+
+ ///
+ /// 8MHz 100kBPS
+ ///
+ _8MHz100KBps,
+
+ ///
+ /// 8MHz 80kBPS
+ ///
+ _8MHz80KBps,
+
+ ///
+ /// 8MHz 50kBPS
+ ///
+ _8MHz50KBps,
+
+ ///
+ /// 8MHz 40kBPS
+ ///
+ _8MHz40KBps,
+
+ ///
+ /// 8MHz 20kBPS
+ ///
+ _8MHz20KBps,
+
+ ///
+ /// 8MHz 10kBPS
+ ///
+ _8MHz10KBps,
+
+ ///
+ /// 8MHz 5kBPS
+ ///
+ _8MHz5KBps,
+
+ ///
+ /// 16MHz 1000kBPS
+ ///
+ _16MHz1000KBps,
+
+ ///
+ /// 16MHz 500kBPS
+ ///
+ _16MHz500KBps,
+
+ ///
+ /// 16MHz 250kBPS
+ ///
+ _16MHz250KBps,
+
+ ///
+ /// 16MHz 200kBPS
+ ///
+ _16MHz200KBps,
+
+ ///
+ /// 16MHz 125kBPS
+ ///
+ _16MHz125KBps,
+
+ ///
+ /// 16MHz 100kBPS
+ ///
+ _16MHz100KBps,
+
+ ///
+ /// 16MHz 80kBPS
+ ///
+ _16MHz80KBps,
+
+ ///
+ /// 16MHz 50kBPS
+ ///
+ _16MHz50KBps,
+
+ ///
+ /// 16MHz 40kBPS
+ ///
+ _16MHz40KBps,
+
+ ///
+ /// 16MHz 20kBPS
+ ///
+ _16MHz20KBps,
+
+ ///
+ /// 16MHz 10kBPS
+ ///
+ _16MHz10KBps,
+
+ ///
+ /// 16MHz 5kBPS
+ ///
+ _16MHz5KBps,
+
+ ///
+ /// 20MHz 1000kBPS
+ ///
+ _20MHz1000KBps,
+
+ ///
+ /// 20MHz 500kBPS
+ ///
+ _20MHz500KBps,
+
+ ///
+ /// 20MHz 250kBPS
+ ///
+ _20MHz250KBps,
+
+ ///
+ /// 20MHz 200kBPS
+ ///
+ _20MHz200KBps,
+
+ ///
+ /// 20MHz 125kBPS
+ ///
+ _20MHz125KBps,
+
+ ///
+ /// 20MHz 100kBPS
+ ///
+ _20MHz100KBps,
+
+ ///
+ /// 20MHz 80kBPS
+ ///
+ _20MHz80KBps,
+
+ ///
+ /// 20MHz 50kBPS
+ ///
+ _20MHz50KBps,
+
+ ///
+ /// 20MHz 40kBPS
+ ///
+ _20MHz40KBps,
+ }
+}
diff --git a/src/devices/Mcp25xxx/Mcp25xxx.cs b/src/devices/Mcp25xxx/Mcp25xxx.cs
index b3db42b5b2..107ce940c4 100644
--- a/src/devices/Mcp25xxx/Mcp25xxx.cs
+++ b/src/devices/Mcp25xxx/Mcp25xxx.cs
@@ -4,7 +4,14 @@
using System;
using System.Device.Gpio;
using System.Device.Spi;
+using System.IO;
+using System.Threading;
+using Iot.Device.Mcp25xxx.Models;
using Iot.Device.Mcp25xxx.Register;
+using Iot.Device.Mcp25xxx.Register.CanControl;
+using Iot.Device.Mcp25xxx.Register.MessageReceive;
+using Iot.Device.Mcp25xxx.Register.MessageTransmit;
+using Iot.Device.Mcp25xxx.Tests.Register.CanControl;
namespace Iot.Device.Mcp25xxx
{
@@ -13,6 +20,8 @@ namespace Iot.Device.Mcp25xxx
///
public abstract class Mcp25xxx : IDisposable
{
+ /// The MCP2515 implements three transmit buffers. Each of these buffers occupies 14 bytes of SRAM
+ private const int TransmitBufferMaxSize = 14;
private readonly int _reset;
private readonly int _tx0rts;
private readonly int _tx1rts;
@@ -241,6 +250,75 @@ public void Reset()
_spiDevice.WriteByte((byte)InstructionFormat.Reset);
}
+ ///
+ /// If RXB0 contains a valid message and another valid message is received,
+ /// an overflow error will not occur and the new message will be moved into RXB1
+ ///
+ public void EnableRollover()
+ {
+ WriteByte(
+ new RxB0Ctrl(
+ false,
+ true,
+ false,
+ OperatingMode.TurnsMaskFiltersOff));
+ }
+
+ ///
+ /// The configuration registers (CNF1, CNF2, CNF3) control the bit timing for the CAN bus interface.
+ ///
+ /// CAN bus frequency and speed
+ public void SetBitrate(FrequencyAndSpeed frequencyAndSpeed)
+ {
+ var (cnf1Config, cnf2Config, cnf3Config) = McpBitrate.GetBitTimingConfiguration(frequencyAndSpeed);
+ WriteByte(Address.Cnf1, cnf1Config);
+ WriteByte(Address.Cnf2, cnf2Config);
+ WriteByte(Address.Cnf3, cnf3Config);
+ }
+
+ ///
+ /// Set mode of operation
+ ///
+ /// type of operation Mode
+ public void SetMode(OperationMode operationMode)
+ {
+ WriteByte(
+ new CanCtrl(
+ CanCtrl.PinPrescaler.ClockDivideBy8,
+ true,
+ false,
+ false,
+ operationMode));
+ }
+
+ ///
+ /// Read arrived messages
+ ///
+ /// List of messages received
+ public ReceivedCanMessage[] ReadMessages()
+ {
+ var rxStatusResponse = RxStatus();
+
+ switch (rxStatusResponse.ReceivedMessage)
+ {
+ case RxStatusResponse.ReceivedMessageType.MessageInRxB0:
+ byte[] messageRxB0 = ReadRxBuffer(RxBufferAddressPointer.RxB0Sidh, TransmitBufferMaxSize);
+ return new[] { new ReceivedCanMessage(ReceiveBuffer.RxB0, messageRxB0) };
+ case RxStatusResponse.ReceivedMessageType.MessageInRxB1:
+ byte[] messageRxB1 = ReadRxBuffer(RxBufferAddressPointer.RxB1Sidh, TransmitBufferMaxSize);
+ return new[] { new ReceivedCanMessage(ReceiveBuffer.RxB1, messageRxB1) };
+ case RxStatusResponse.ReceivedMessageType.MessagesInBothBuffers:
+ var firstMessage = ReadRxBuffer(RxBufferAddressPointer.RxB0Sidh, TransmitBufferMaxSize);
+ var secondMessage = ReadRxBuffer(RxBufferAddressPointer.RxB1Sidh, TransmitBufferMaxSize);
+ return new[] { new ReceivedCanMessage(ReceiveBuffer.RxB0, firstMessage), new ReceivedCanMessage(ReceiveBuffer.RxB1, secondMessage) };
+ case RxStatusResponse.ReceivedMessageType.NoRxMessage:
+ return Array.Empty();
+ default:
+ throw new Exception(
+ $"Invalid value for {nameof(rxStatusResponse.ReceivedMessage)}: {rxStatusResponse.ReceivedMessage}.");
+ }
+ }
+
///
/// Reads data from the register beginning at the selected address.
///
@@ -315,6 +393,159 @@ public void WriteByte(IRegister register)
});
}
+ ///
+ /// Send message
+ ///
+ /// CAN message
+ public void SendMessage(SendingCanMessage message)
+ {
+ var txBuffer = GetEmptyTxBuffer();
+ SendMessageFromBuffer(txBuffer, message);
+ const int tries = 10;
+ for (var i = 0; i < tries; i++)
+ {
+ if (IsMessageSend(txBuffer))
+ {
+ return;
+ }
+
+ Thread.Sleep(TimeSpan.FromMilliseconds(5));
+ }
+
+ AbortAllPendingTransmissions();
+ throw new IOException($"Cannot Send: {message.Id}#{string.Join(";", message.Data)}");
+ }
+
+ ///
+ /// Get witch buffer empty now
+ ///
+ /// Buffer
+ public TransmitBuffer GetEmptyTxBuffer()
+ {
+ var readStatusResponse = ReadStatus();
+ var tx0Full = readStatusResponse.HasFlag(ReadStatusResponse.Tx0Req);
+ var tx1Full = readStatusResponse.HasFlag(ReadStatusResponse.Tx1Req);
+ var tx2Full = readStatusResponse.HasFlag(ReadStatusResponse.Tx2Req);
+
+ if (!tx0Full)
+ {
+ return TransmitBuffer.Tx0;
+ }
+
+ if (!tx1Full)
+ {
+ return TransmitBuffer.Tx1;
+ }
+
+ if (!tx2Full)
+ {
+ return TransmitBuffer.Tx2;
+ }
+
+ return TransmitBuffer.None;
+ }
+
+ ///
+ /// Send message from specific buffer
+ ///
+ /// Buffer
+ /// CAN message
+ public void SendMessageFromBuffer(TransmitBuffer transmitBuffer, SendingCanMessage message)
+ {
+ var (instructionsAddress, dataAddress) = GetInstructionsAddress(transmitBuffer);
+ var txBufferNumber = TxBxSidh.GetTxBufferNumber(instructionsAddress);
+ ReadOnlySpan buffer = stackalloc byte[5]
+ {
+ new TxBxSidh(txBufferNumber, message.Id[0]).ToByte(),
+ new TxBxSidl(txBufferNumber, message.Id[1]).ToByte(),
+ new TxBxEid8(txBufferNumber, message.Id[2]).ToByte(),
+ new TxBxEid0(txBufferNumber, message.Id[3]).ToByte(),
+ new TxBxDlc(txBufferNumber, message.Data.Length, false).ToByte()
+ };
+
+ Write(instructionsAddress, buffer);
+ Write(dataAddress, (byte[]?)message.Data);
+ SendFromBuffer(transmitBuffer);
+ }
+
+ ///
+ /// Get instructions address from buffer
+ ///
+ /// Type of transmit buffer
+ /// Instructions for specific buffer
+ public Tuple GetInstructionsAddress(TransmitBuffer transmitBuffer)
+ {
+ return transmitBuffer switch
+ {
+ TransmitBuffer.Tx0 => new Tuple(Address.TxB0Sidh, Address.TxB0D0),
+ TransmitBuffer.Tx1 => new Tuple(Address.TxB1Sidh, Address.TxB1D0),
+ TransmitBuffer.Tx2 => new Tuple(Address.TxB2Sidh, Address.TxB2D0),
+ TransmitBuffer.None => throw new ArgumentException("Can not use this Tx buffer", nameof(transmitBuffer), null),
+ _ => throw new ArgumentOutOfRangeException(nameof(transmitBuffer), transmitBuffer, null)
+ };
+ }
+
+ ///
+ /// Command to mcp25xx to send bytes from specific buffer
+ ///
+ /// Type of transmit buffer
+ public void SendFromBuffer(TransmitBuffer transmitBuffer)
+ {
+ switch (transmitBuffer)
+ {
+ case TransmitBuffer.Tx0:
+ RequestToSend(true, false, false);
+ break;
+ case TransmitBuffer.Tx1:
+ RequestToSend(false, true, false);
+ break;
+ case TransmitBuffer.Tx2:
+ RequestToSend(false, false, true);
+ break;
+ case TransmitBuffer.None:
+ throw new ArgumentException("Can not use this Tx buffer", nameof(transmitBuffer), null);
+ default:
+ throw new ArgumentOutOfRangeException(nameof(transmitBuffer), transmitBuffer, null);
+ }
+ }
+
+ ///
+ /// Check is buffer empty
+ ///
+ /// buffer type
+ ///
+ public bool IsMessageSend(TransmitBuffer transmitBuffer)
+ {
+ var readStatusResponse = ReadStatus();
+
+ switch (transmitBuffer)
+ {
+ case TransmitBuffer.Tx0 when readStatusResponse.HasFlag(ReadStatusResponse.Tx0If) &&
+ !readStatusResponse.HasFlag(ReadStatusResponse.Tx0Req):
+ case TransmitBuffer.Tx1 when readStatusResponse.HasFlag(ReadStatusResponse.Tx1If) &&
+ !readStatusResponse.HasFlag(ReadStatusResponse.Tx1Req):
+ case TransmitBuffer.Tx2 when readStatusResponse.HasFlag(ReadStatusResponse.Tx2If) &&
+ !readStatusResponse.HasFlag(ReadStatusResponse.Tx2Req):
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ ///
+ /// Abort send all messages from buffers, buffers will be empty
+ ///
+ public void AbortAllPendingTransmissions()
+ {
+ WriteByte(
+ new CanCtrl(
+ CanCtrl.PinPrescaler.ClockDivideBy8,
+ true,
+ false,
+ true,
+ OperationMode.NormalOperation));
+ }
+
///
/// Writes data to the register beginning at the selected address.
///
diff --git a/src/devices/Mcp25xxx/Mcp25xxx.csproj b/src/devices/Mcp25xxx/Mcp25xxx.csproj
index da9be4129e..a96ed41b05 100644
--- a/src/devices/Mcp25xxx/Mcp25xxx.csproj
+++ b/src/devices/Mcp25xxx/Mcp25xxx.csproj
@@ -4,13 +4,19 @@
false
+
+
+
+
+
+
diff --git a/src/devices/Mcp25xxx/McpBitrate.cs b/src/devices/Mcp25xxx/McpBitrate.cs
new file mode 100644
index 0000000000..142c1bb267
--- /dev/null
+++ b/src/devices/Mcp25xxx/McpBitrate.cs
@@ -0,0 +1,61 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+
+namespace Iot.Device.Mcp25xxx
+{
+ ///
+ /// Bit Timing Configuration Registers
+ ///
+ public static class McpBitrate
+ {
+ private static readonly Dictionary> s_bitTimingConfiguration = new()
+ {
+ { FrequencyAndSpeed._8MHz1000KBps, new Tuple(0x00, 0x80, 0x80) },
+ { FrequencyAndSpeed._8MHz500KBps, new Tuple(0x00, 0x90, 0x82) },
+ { FrequencyAndSpeed._8MHz250KBps, new Tuple(0x00, 0xB1, 0x85) },
+ { FrequencyAndSpeed._8MHz200KBps, new Tuple(0x00, 0xB4, 0x86) },
+ { FrequencyAndSpeed._8MHz125KBps, new Tuple(0x01, 0xB1, 0x85) },
+ { FrequencyAndSpeed._8MHz100KBps, new Tuple(0x01, 0xB4, 0x86) },
+ { FrequencyAndSpeed._8MHz80KBps, new Tuple(0x01, 0xBF, 0x87) },
+ { FrequencyAndSpeed._8MHz50KBps, new Tuple(0x03, 0xB4, 0x86) },
+ { FrequencyAndSpeed._8MHz40KBps, new Tuple(0x03, 0xBF, 0x87) },
+ { FrequencyAndSpeed._8MHz20KBps, new Tuple(0x07, 0xBF, 0x87) },
+ { FrequencyAndSpeed._8MHz10KBps, new Tuple(0x0F, 0xBF, 0x87) },
+ { FrequencyAndSpeed._8MHz5KBps, new Tuple(0x1F, 0xBF, 0x87) },
+ { FrequencyAndSpeed._16MHz1000KBps, new Tuple(0x00, 0xD0, 0x82) },
+ { FrequencyAndSpeed._16MHz500KBps, new Tuple(0x00, 0xF0, 0x86) },
+ { FrequencyAndSpeed._16MHz250KBps, new Tuple(0x41, 0xF1, 0x85) },
+ { FrequencyAndSpeed._16MHz200KBps, new Tuple(0x01, 0xFA, 0x87) },
+ { FrequencyAndSpeed._16MHz125KBps, new Tuple(0x03, 0xF0, 0x86) },
+ { FrequencyAndSpeed._16MHz100KBps, new Tuple(0x03, 0xFA, 0x87) },
+ { FrequencyAndSpeed._16MHz80KBps, new Tuple(0x03, 0xFF, 0x87) },
+ { FrequencyAndSpeed._16MHz50KBps, new Tuple(0x07, 0xFA, 0x87) },
+ { FrequencyAndSpeed._16MHz40KBps, new Tuple(0x07, 0xFF, 0x87) },
+ { FrequencyAndSpeed._16MHz20KBps, new Tuple(0x0F, 0xFF, 0x87) },
+ { FrequencyAndSpeed._16MHz10KBps, new Tuple(0x1F, 0xFF, 0x87) },
+ { FrequencyAndSpeed._16MHz5KBps, new Tuple(0x3F, 0xFF, 0x87) },
+ { FrequencyAndSpeed._20MHz1000KBps, new Tuple(0x00, 0xD9, 0x82) },
+ { FrequencyAndSpeed._20MHz500KBps, new Tuple(0x00, 0xFA, 0x87) },
+ { FrequencyAndSpeed._20MHz250KBps, new Tuple(0x41, 0xFB, 0x86) },
+ { FrequencyAndSpeed._20MHz200KBps, new Tuple(0x01, 0xFF, 0x87) },
+ { FrequencyAndSpeed._20MHz125KBps, new Tuple(0x03, 0xFA, 0x87) },
+ { FrequencyAndSpeed._20MHz100KBps, new Tuple(0x04, 0xFA, 0x87) },
+ { FrequencyAndSpeed._20MHz80KBps, new Tuple(0x04, 0xFF, 0x87) },
+ { FrequencyAndSpeed._20MHz50KBps, new Tuple(0x09, 0xFA, 0x87) },
+ { FrequencyAndSpeed._20MHz40KBps, new Tuple(0x09, 0xFF, 0x87) }
+ };
+
+ ///
+ /// Get bit timing configuration for specific CAN Bus frequency and speed
+ ///
+ /// One of CAN Bus frequency and speed
+ /// The configuration for registers (CNF1, CNF2, CNF3)
+ public static Tuple GetBitTimingConfiguration(FrequencyAndSpeed frequencyAndSpeed)
+ {
+ return s_bitTimingConfiguration[frequencyAndSpeed];
+ }
+ }
+}
diff --git a/src/devices/Mcp25xxx/Models/ReceivedCanMessage.cs b/src/devices/Mcp25xxx/Models/ReceivedCanMessage.cs
new file mode 100644
index 0000000000..8f8562f2f2
--- /dev/null
+++ b/src/devices/Mcp25xxx/Models/ReceivedCanMessage.cs
@@ -0,0 +1,68 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.IO;
+using System.Linq;
+
+namespace Iot.Device.Mcp25xxx.Models
+{
+ ///
+ /// CAN bus message
+ ///
+ public class ReceivedCanMessage
+ {
+ ///
+ /// Buffer received this message
+ ///
+ public ReceiveBuffer Buffer { get; }
+
+ ///
+ /// Received CAN message
+ ///
+ public byte[] RawData { get; }
+
+ ///
+ /// Received CAN message constructor
+ ///
+ /// buffer received this message
+ /// data from buffer
+ public ReceivedCanMessage(ReceiveBuffer buffer, byte[] rawData)
+ {
+ Buffer = buffer;
+ RawData = rawData;
+ }
+
+ ///
+ /// CAN message id
+ ///
+ /// message id
+ /// Raw data can contain not valid id
+ public byte[] GetId()
+ {
+ if (RawData.Length < 4)
+ {
+ throw new InvalidDataException($"Raw data size {RawData.Length} must be greater then 4 bytes.");
+ }
+
+ return new[] { RawData[0], RawData[1], RawData[2], RawData[3] };
+ }
+
+ ///
+ /// CAN message data
+ ///
+ /// message data (max 8 bytes)
+ /// Raw data can contain not valid message
+ public byte[] GetData()
+ {
+ const int messageDataStartIndex = 5;
+ if (RawData.Length < messageDataStartIndex)
+ {
+ throw new InvalidDataException($"Raw data size {RawData.Length} must be greater then {messageDataStartIndex} bytes.");
+ }
+
+ var messageLength = RawData[4] & 0x0F;
+ return RawData.Skip(messageDataStartIndex).Take(messageLength).ToArray();
+ }
+ }
+}
diff --git a/src/devices/Mcp25xxx/Models/SendingCanMessage.cs b/src/devices/Mcp25xxx/Models/SendingCanMessage.cs
new file mode 100644
index 0000000000..a672203d24
--- /dev/null
+++ b/src/devices/Mcp25xxx/Models/SendingCanMessage.cs
@@ -0,0 +1,70 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+
+namespace Iot.Device.Mcp25xxx.Models
+{
+ ///
+ /// CAN bus message
+ ///
+ public class SendingCanMessage
+ {
+ ///
+ /// Four bytes CAN id
+ ///
+ public byte[] Id { get; }
+
+ ///
+ /// CAN data (max 8 bytes)
+ ///
+ public byte[] Data { get; }
+
+ private SendingCanMessage(byte[] id, byte[] data)
+ {
+ Id = id;
+ Data = data;
+ }
+
+ ///
+ /// Create new standard CAN message
+ ///
+ /// Two bytes id
+ /// message data max 8 bytes
+ public static SendingCanMessage CreateStandard(byte[] shortId, byte[] data)
+ {
+ if (shortId.Length != 2)
+ {
+ throw new ArgumentException($"Id size {shortId.Length} must be 2 bytes.", nameof(shortId));
+ }
+
+ if (data.Length > 8)
+ {
+ throw new ArgumentException($"Data size {data.Length} more than 8 bytes.", nameof(data));
+ }
+
+ var id = new byte[] { shortId[0], shortId[1], 0, 0 };
+ return new SendingCanMessage(id, data);
+ }
+
+ ///
+ /// Create new extended CAN message
+ ///
+ /// Four bytes id
+ /// message data max 8 bytes
+ public static SendingCanMessage CreateExtended(byte[] id, byte[] data)
+ {
+ if (id.Length != 4)
+ {
+ throw new ArgumentException($"Id size {id.Length} must be 4 bytes.", nameof(id));
+ }
+
+ if (data.Length > 8)
+ {
+ throw new ArgumentException($"Data size {data.Length} more than 8 bytes.", nameof(data));
+ }
+
+ return new SendingCanMessage(id, data);
+ }
+ }
+}
diff --git a/src/devices/Mcp25xxx/README.md b/src/devices/Mcp25xxx/README.md
index 8f959b9654..0ac35999a4 100644
--- a/src/devices/Mcp25xxx/README.md
+++ b/src/devices/Mcp25xxx/README.md
@@ -80,31 +80,34 @@ Console.WriteLine($"Tx2If: {readStatusResponse.HasFlag(ReadStatusResponse.Tx2If)
You can transmit a message like this:
```csharp
-Console.WriteLine("Transmit Message");
+Console.WriteLine("Send simple message");
+const int id = 1;
+var message = new[] { (byte)1 };
+var twoByteId = new Tuple((id >> 3), (id << 5));
+mcp25xxx.SendMessage(twoByteId, message);
+```
-mcp25xxx.WriteByte(
- new CanCtrl(CanCtrl.PinPrescaler.ClockDivideBy8,
- false,
- false,
- false,
- OperationMode.NormalOperation));
+### Read messages from all buffers
-byte[] data = new byte[] { 0b0000_0001, 0b0010_0011, 0b0100_0101, 0b0110_0111, 0b1000_1001 };
+You can save all message from buffer to memmory like this:
-mcp25xxx.Write(
- Address.TxB0Sidh,
- new byte[]
+```csharp
+ConcurrentQueue readBuffer = new();
+Console.WriteLine("Start read from buffer mcp25xx buffer to memory buffer");
+while (!ct.IsCancellationRequested)
+{
+ var messages = mcp25xxx.ReadMessages(10);
+ foreach (var message in messages)
{
- new TxBxSidh(0, 0b0000_1001).ToByte(), new TxBxSidl(0, 0b001, false, 0b00).ToByte(),
- new TxBxEid8(0, 0b0000_0000).ToByte(), new TxBxEid0(0, 0b0000_0000).ToByte(),
- new TxBxDlc(0, data.Length, false).ToByte()
- });
+ readBuffer.Enqueue(message);
+ }
+}
+```
-mcp25xxx.Write(Address.TxB0D0, data);
+You can read messaged from memmory like this:
-// Send with TxB0 buffer.
-mcp25xxx.RequestToSend(true, false, false);
-cp25xxx.RequestToSend(false, false, true);
+```csharp
+var messageOrNull = readBuffer.TryDequeue(out var bytes) ? bytes : null;
```
**Note**: You will find detailed way of using this binding in the [sample file](samples)
diff --git a/src/devices/Mcp25xxx/ReceiveBuffer.cs b/src/devices/Mcp25xxx/ReceiveBuffer.cs
new file mode 100644
index 0000000000..2ccfa36900
--- /dev/null
+++ b/src/devices/Mcp25xxx/ReceiveBuffer.cs
@@ -0,0 +1,26 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Iot.Device.Mcp25xxx
+{
+ ///
+ /// Receive buffers
+ ///
+ public enum ReceiveBuffer
+ {
+ ///
+ /// Receive buffer 0
+ ///
+ RxB0 = 0,
+
+ ///
+ /// Receive buffer 1
+ ///
+ RxB1 = 1,
+
+ ///
+ /// No one transmit buffer
+ ///
+ None = 3
+ }
+}
diff --git a/src/devices/Mcp25xxx/TransmitBuffer.cs b/src/devices/Mcp25xxx/TransmitBuffer.cs
new file mode 100644
index 0000000000..cef9de1f81
--- /dev/null
+++ b/src/devices/Mcp25xxx/TransmitBuffer.cs
@@ -0,0 +1,31 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Iot.Device.Mcp25xxx
+{
+ ///
+ /// Transmit buffers
+ ///
+ public enum TransmitBuffer
+ {
+ ///
+ /// Transmit buffer 0
+ ///
+ Tx0 = 0,
+
+ ///
+ /// Transmit buffer 1
+ ///
+ Tx1 = 1,
+
+ ///
+ /// Transmit buffer 2
+ ///
+ Tx2 = 2,
+
+ ///
+ /// No one transmit buffer
+ ///
+ None = 3
+ }
+}
diff --git a/src/devices/Mcp25xxx/samples/Program.cs b/src/devices/Mcp25xxx/samples/Program.cs
index 92839b4175..7112df0d24 100644
--- a/src/devices/Mcp25xxx/samples/Program.cs
+++ b/src/devices/Mcp25xxx/samples/Program.cs
@@ -2,8 +2,11 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System;
+using System.Collections.Concurrent;
using System.Device.Spi;
+using System.Threading;
using Iot.Device.Mcp25xxx;
+using Iot.Device.Mcp25xxx.Models;
using Iot.Device.Mcp25xxx.Register;
using Iot.Device.Mcp25xxx.Register.AcceptanceFilter;
using Iot.Device.Mcp25xxx.Register.BitTimeConfiguration;
@@ -14,10 +17,14 @@
using Iot.Device.Mcp25xxx.Register.MessageReceive;
using Iot.Device.Mcp25xxx.Register.MessageTransmit;
-Console.WriteLine("Hello Mcp25xxx Sample!");
+ConcurrentQueue readBuffer = new();
+Console.WriteLine("Hello Mcp25xxx Sample!");
using Mcp25xxx mcp25xxx = GetMcp25xxxDevice();
Reset(mcp25xxx);
+SetBitrate(mcp25xxx);
+EnableRollover(mcp25xxx);
+SetNormalMode(mcp25xxx);
// ReadAllRegisters(mcp25xxx);
ReadAllRegistersWithDetails(mcp25xxx);
@@ -31,6 +38,9 @@
// TransmitMessage(mcp25xxx);
// LoopbackMode(mcp25xxx);
// ReadAllRegisters(mcp25xxx);
+// Task.Run(() => ReadToBufferLoop(mcp25xxx, CancellationToken.None), CancellationToken.None);
+SendMessage(mcp25xxx);
+
// Methods
Mcp25xxx GetMcp25xxxDevice()
{
@@ -45,6 +55,52 @@ void Reset(Mcp25xxx mcp25xxx)
mcp25xxx.Reset();
}
+void SetBitrate(Mcp25xxx mcp25xxx)
+{
+ Console.WriteLine("Set bitrate 16MHz 500kBPS");
+ mcp25xxx.SetBitrate(FrequencyAndSpeed._16MHz500KBps);
+}
+
+void EnableRollover(Mcp25xxx mcp25xxx)
+{
+ Console.WriteLine("Enable rollover to second buffer");
+ mcp25xxx.EnableRollover();
+}
+
+void SetNormalMode(Mcp25xxx mcp25xxx)
+{
+ Console.WriteLine("Set normal work mode");
+ mcp25xxx.SetMode(OperationMode.NormalOperation);
+}
+
+void ReadToBufferLoop(Mcp25xxx mcp25xxx, CancellationToken ct)
+{
+ Console.WriteLine("Start read from buffer mcp25xx buffer to memory buffer");
+ while (!ct.IsCancellationRequested)
+ {
+ var messages = mcp25xxx.ReadMessages();
+ foreach (var message in messages)
+ {
+ readBuffer.Enqueue(message);
+ }
+ }
+}
+
+ReceivedCanMessage? GetBufferMessageOrNull()
+{
+ return readBuffer.TryDequeue(out var message) ? message : null;
+}
+
+void SendMessage(Mcp25xxx mcp25xxx)
+{
+ Console.WriteLine("Send simple message");
+ const int id = 1;
+ var messageData = new[] { (byte)1 };
+ var twoByteId = new byte[] { id >> 3, id << 5 };
+ var message = SendingCanMessage.CreateStandard(twoByteId, messageData);
+ mcp25xxx.SendMessage(message);
+}
+
void ReadAllRegisters(Mcp25xxx mcp25xxx)
{
Console.WriteLine("Read Instruction for All Registers");
diff --git a/src/devices/Mcp25xxx/tests/Mcp25xxxSpiDevice.cs b/src/devices/Mcp25xxx/tests/Mcp25xxxSpiDevice.cs
index 0dbcbfe33e..f6d880c732 100644
--- a/src/devices/Mcp25xxx/tests/Mcp25xxxSpiDevice.cs
+++ b/src/devices/Mcp25xxx/tests/Mcp25xxxSpiDevice.cs
@@ -8,10 +8,12 @@ namespace Iot.Device.Mcp25xxx.Tests
{
public class Mcp25xxxSpiDevice : SpiDevice
{
+ public event Action? TransferCompleted;
+
public override SpiConnectionSettings ConnectionSettings => throw new NotImplementedException();
- public byte[]? LastReadBuffer { get; set; }
+ public byte[]? NextReadBuffer { get; set; }
- public byte LastReadByte { get; set; }
+ public byte NextReadByte { get; set; }
public byte[]? LastWriteBuffer { get; private set; }
@@ -19,15 +21,16 @@ public class Mcp25xxxSpiDevice : SpiDevice
public override void Read(Span buffer)
{
- LastReadBuffer = buffer.ToArray();
+ NextReadBuffer = buffer.ToArray();
}
- public override byte ReadByte() => LastReadByte;
+ public override byte ReadByte() => NextReadByte;
public override void TransferFullDuplex(ReadOnlySpan writeBuffer, Span readBuffer)
{
LastWriteBuffer = writeBuffer.ToArray();
- LastReadBuffer = readBuffer.ToArray();
+ NextReadBuffer.CopyTo(readBuffer);
+ TransferCompleted?.Invoke();
}
public override void Write(ReadOnlySpan buffer)
diff --git a/src/devices/Mcp25xxx/tests/Mcp25xxxTests.cs b/src/devices/Mcp25xxx/tests/Mcp25xxxTests.cs
index 36265933c4..551262590e 100644
--- a/src/devices/Mcp25xxx/tests/Mcp25xxxTests.cs
+++ b/src/devices/Mcp25xxx/tests/Mcp25xxxTests.cs
@@ -1,7 +1,11 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System;
+using System.Device.Spi;
using Iot.Device.Mcp25xxx.Register;
+using Iot.Device.Mcp25xxx.Tests.Register.CanControl;
+using Moq;
using Xunit;
namespace Iot.Device.Mcp25xxx.Tests
@@ -23,11 +27,16 @@ public void Send_Reset_Instruction()
public void Send_Read_Instruction_By_Address(Address address)
{
byte[] expectedWriteBuffer = new byte[] { 0b0000_0011, (byte)address, 0b0000_0000 };
+ byte[] reply = new byte[]
+ {
+ 0, 0, 0xff
+ };
var mcp25xxxSpiDevice = new Mcp25xxxSpiDevice();
+ mcp25xxxSpiDevice.NextReadBuffer = reply;
Mcp25xxx mcp25xxx = new Mcp25625(mcp25xxxSpiDevice);
- mcp25xxx.Read(address);
+ byte b = mcp25xxx.Read(address);
Assert.Equal(expectedWriteBuffer, mcp25xxxSpiDevice?.LastWriteBuffer);
- Assert.Equal(3, mcp25xxxSpiDevice?.LastReadBuffer?.Length);
+ Assert.Equal(0xff, b);
}
[Theory]
@@ -41,9 +50,9 @@ public void Send_ReadRxBuffer_Instruction(byte instructionFormat, RxBufferAddres
expectedWriteBuffer[0] = instructionFormat;
var mcp25xxxSpiDevice = new Mcp25xxxSpiDevice();
Mcp25xxx mcp25xxx = new Mcp25625(mcp25xxxSpiDevice);
+ mcp25xxxSpiDevice.NextReadBuffer = new byte[byteCount];
mcp25xxx.ReadRxBuffer(addressPointer, byteCount);
Assert.Equal(expectedWriteBuffer, mcp25xxxSpiDevice.LastWriteBuffer);
- Assert.Equal(byteCount, mcp25xxxSpiDevice.LastReadBuffer?.Length - 1);
}
[Theory]
@@ -95,20 +104,32 @@ public void Send_ReadStatus_Instruction()
byte[] expectedWriteBuffer = new byte[] { 0b1010_0000, 0b0000_0000 };
var mcp25xxxSpiDevice = new Mcp25xxxSpiDevice();
Mcp25xxx mcp25xxx = new Mcp25625(mcp25xxxSpiDevice);
- mcp25xxx.ReadStatus();
+ mcp25xxxSpiDevice.NextReadBuffer = new byte[]
+ {
+ 0, 3
+ };
+
+ var response = mcp25xxx.ReadStatus();
+ Assert.Equal(ReadStatusResponse.Rx0If | ReadStatusResponse.Rx1If, response);
Assert.Equal(expectedWriteBuffer, mcp25xxxSpiDevice.LastWriteBuffer);
- Assert.Equal(1, mcp25xxxSpiDevice.LastReadBuffer?.Length - 1);
}
[Fact]
public void Send_RxStatus_Instruction()
{
byte[] expectedWriteBuffer = new byte[] { 0b1011_0000, 0b0000_0000 };
+ byte[] reply = new byte[]
+ {
+ 0, 0xC2
+ };
var mcp25xxxSpiDevice = new Mcp25xxxSpiDevice();
+ mcp25xxxSpiDevice.NextReadBuffer = reply;
Mcp25xxx mcp25xxx = new Mcp25625(mcp25xxxSpiDevice);
- mcp25xxx.RxStatus();
+ var status = mcp25xxx.RxStatus();
Assert.Equal(expectedWriteBuffer, mcp25xxxSpiDevice.LastWriteBuffer);
- Assert.Equal(1, mcp25xxxSpiDevice.LastReadBuffer?.Length - 1);
+ Assert.Equal(RxStatusResponse.MessageReceivedType.StandardDataFrame, status.MessageTypeReceived);
+ Assert.Equal(RxStatusResponse.ReceivedMessageType.MessagesInBothBuffers, status.ReceivedMessage);
+ Assert.Equal(RxStatusResponse.FilterMatchType.RxF2, status.FilterMatch);
}
[Theory]
@@ -122,5 +143,62 @@ public void Send_BitModify_Instruction(Address address, byte mask, byte value)
mcp25xxx.BitModify(address, mask, value);
Assert.Equal(expectedWriteBuffer, mcp25xxxSpiDevice.LastWriteBuffer);
}
+
+ [Fact]
+ public void Send_EnableRollover_Instruction()
+ {
+ byte[] expectedWriteBuffer = { 0b0000_0010, (byte)Address.RxB0Ctrl, 0b0110_0110 };
+ var mcp25xxxSpiDevice = new Mcp25xxxSpiDevice();
+ Mcp25xxx mcp25xxx = new Mcp25625(mcp25xxxSpiDevice);
+ mcp25xxx.EnableRollover();
+ Assert.Equal(expectedWriteBuffer, mcp25xxxSpiDevice.LastWriteBuffer);
+ }
+
+ [Fact]
+ public void Send_SetBitrate_Instruction()
+ {
+ byte[] lastExpectedWriteBuffer = { 0b0000_0010, (byte)Address.Cnf3, 0x86 };
+ var mcp25xxxSpiDevice = new Mcp25xxxSpiDevice();
+ Mcp25xxx mcp25xxx = new Mcp25625(mcp25xxxSpiDevice);
+ mcp25xxx.SetBitrate(FrequencyAndSpeed._16MHz500KBps);
+ Assert.Equal(lastExpectedWriteBuffer, mcp25xxxSpiDevice.LastWriteBuffer);
+ }
+
+ [Fact]
+ public void Send_SetMode_Instruction()
+ {
+ byte[] lastExpectedWriteBuffer = { 0b0000_0010, (byte)Address.CanCtrl, 0b0000_0111 };
+ var mcp25xxxSpiDevice = new Mcp25xxxSpiDevice();
+ Mcp25xxx mcp25xxx = new Mcp25625(mcp25xxxSpiDevice);
+ mcp25xxx.SetMode(OperationMode.NormalOperation);
+ Assert.Equal(lastExpectedWriteBuffer, mcp25xxxSpiDevice.LastWriteBuffer);
+ }
+
+ [Fact]
+ public void ReceiveMessagesSuccess()
+ {
+ var mcp25xxxSpiDevice = new Mcp25xxxSpiDevice();
+ mcp25xxxSpiDevice.TransferCompleted += () =>
+ {
+ // The second reply;
+ mcp25xxxSpiDevice.NextReadBuffer = new byte[]
+ {
+ 0 /* dummy */, 0xb, 0xa, 0, 0, 0x4 /* Msg length*/, 1, 2, 3, 4
+ };
+ };
+
+ byte[] reply = new byte[]
+ {
+ 0, 0x42
+ };
+ Mcp25xxx mcp25xxx = new Mcp25625(mcp25xxxSpiDevice);
+ mcp25xxxSpiDevice.NextReadBuffer = reply;
+ var msg = mcp25xxx.ReadMessages();
+ Assert.Single(msg);
+
+ Assert.Equal(0xa0b, BitConverter.ToInt32(msg[0].GetId(), 0));
+ Assert.Equal(4, msg[0].GetData().Length);
+ Assert.Equal(1, msg[0].GetData()[0]);
+ }
}
}
diff --git a/src/devices/Mcp25xxx/tests/Models/ReceivedCanMessageTests.cs b/src/devices/Mcp25xxx/tests/Models/ReceivedCanMessageTests.cs
new file mode 100644
index 0000000000..7299e129f2
--- /dev/null
+++ b/src/devices/Mcp25xxx/tests/Models/ReceivedCanMessageTests.cs
@@ -0,0 +1,62 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.IO;
+using Iot.Device.Mcp25xxx.Models;
+using Xunit;
+
+namespace Iot.Device.Mcp25xxx.Tests.Models
+{
+ public class ReceivedCanMessageTests
+ {
+ [Theory]
+ [InlineData(new byte[] { 0, 1, 2, 3 }, new byte[] { 0, 1, 2, 3 })]
+ [InlineData(new byte[] { 4, 3, 2, 1, 0 }, new byte[] { 4, 3, 2, 1 })]
+ [InlineData(new byte[] { 10, 11, 12, 13, 14, 15 }, new byte[] { 10, 11, 12, 13 })]
+ public void Get_Message_Id(byte[] rawData, byte[] id)
+ {
+ var message = new ReceivedCanMessage(ReceiveBuffer.RxB0, rawData);
+ Assert.Equal(id, message.GetId());
+ }
+
+ [Theory]
+ [InlineData(new byte[0])]
+ [InlineData(new byte[] { 1 })]
+ [InlineData(new byte[] { 1, 2 })]
+ [InlineData(new byte[] { 1, 2, 3 })]
+ public void When_Invalid_RawData_Get_Message_Id_Thrown(byte[] rawData)
+ {
+ var message = new ReceivedCanMessage(ReceiveBuffer.RxB0, rawData);
+ Assert.Throws(() => message.GetId());
+ }
+
+ [Theory]
+ [InlineData(new byte[] { 0, 1, 2, 3, 0 }, new byte[0])]
+ [InlineData(new byte[] { 0, 1, 2, 3, 1, 14 }, new byte[] { 14 })]
+ [InlineData(new byte[] { 0, 1, 2, 3, 2, 14, 15 }, new byte[] { 14, 15 })]
+ [InlineData(new byte[] { 0, 1, 2, 3, 3, 14, 15, 16 }, new byte[] { 14, 15, 16 })]
+ [InlineData(new byte[] { 0, 1, 2, 3, 4, 14, 15, 16, 17 }, new byte[] { 14, 15, 16, 17 })]
+ [InlineData(new byte[] { 0, 1, 2, 3, 5, 14, 15, 16, 17, 18 }, new byte[] { 14, 15, 16, 17, 18 })]
+ [InlineData(new byte[] { 0, 1, 2, 3, 6, 14, 15, 16, 17, 18, 19 }, new byte[] { 14, 15, 16, 17, 18, 19 })]
+ [InlineData(new byte[] { 0, 1, 2, 3, 7, 14, 15, 16, 17, 18, 19, 20 }, new byte[] { 14, 15, 16, 17, 18, 19, 20 })]
+ [InlineData(new byte[] { 0, 1, 2, 3, 8, 14, 15, 16, 17, 18, 19, 20, 21 }, new byte[] { 14, 15, 16, 17, 18, 19, 20, 21 })]
+ public void Get_Message_Data(byte[] rawData, byte[] id)
+ {
+ var message = new ReceivedCanMessage(ReceiveBuffer.RxB0, rawData);
+ Assert.Equal(id, message.GetData());
+ }
+
+ [Theory]
+ [InlineData(new byte[0])]
+ [InlineData(new byte[] { 1 })]
+ [InlineData(new byte[] { 2, 3 })]
+ [InlineData(new byte[] { 1, 2, 3 })]
+ [InlineData(new byte[] { 1, 2, 3, 4 })]
+ public void When_Invalid_RawData_Get_Message_Data_Thrown(byte[] rawData)
+ {
+ var message = new ReceivedCanMessage(ReceiveBuffer.RxB0, rawData);
+ Assert.Throws(() => message.GetData());
+ }
+
+ }
+}
diff --git a/src/devices/Mcp25xxx/tests/Models/SendingCanMessageTests.cs b/src/devices/Mcp25xxx/tests/Models/SendingCanMessageTests.cs
new file mode 100644
index 0000000000..6f91282d12
--- /dev/null
+++ b/src/devices/Mcp25xxx/tests/Models/SendingCanMessageTests.cs
@@ -0,0 +1,93 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using Iot.Device.Mcp25xxx.Models;
+using Xunit;
+
+namespace Iot.Device.Mcp25xxx.Tests.Models
+{
+ public class SendingCanMessageTests
+ {
+ [Theory]
+ [InlineData(new byte[] { 1, 2 }, new byte[] { 1, 2, 0, 0 })]
+ [InlineData(new byte[] { 3, 4 }, new byte[] { 3, 4, 0, 0 })]
+ public void Create_Standard_Message_Id(byte[] id, byte[] resultId)
+ {
+ var message = SendingCanMessage.CreateStandard(id, Array.Empty());
+ Assert.Equal(resultId, message.Id);
+ }
+
+ [Theory]
+ [InlineData(new byte[0])]
+ [InlineData(new byte[] { 1 })]
+ [InlineData(new byte[] { 1, 2, 3 })]
+ [InlineData(new byte[] { 1, 2, 3, 4 })]
+ public void When_Invalid_Id_Create_Standard_Message_Thrown(byte[] id)
+ {
+ Assert.Throws(() => SendingCanMessage.CreateStandard(id, Array.Empty()));
+ }
+
+ [Theory]
+ [InlineData(new byte[0], new byte[0])]
+ [InlineData(new byte[] { 1 }, new byte[] { 1 })]
+ [InlineData(new byte[] { 1, 2 }, new byte[] { 1, 2 })]
+ [InlineData(new byte[] { 1, 2, 3, 4 }, new byte[] { 1, 2, 3, 4 })]
+ [InlineData(new byte[] { 1, 2, 3, 4, 5, 6 }, new byte[] { 1, 2, 3, 4, 5, 6 })]
+ [InlineData(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 }, new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 })]
+ public void Create_Standard_Message_With_Data(byte[] data, byte[] resultData)
+ {
+ var message = SendingCanMessage.CreateStandard(new byte[2], data);
+ Assert.Equal(resultData, message.Data);
+ }
+
+ [Theory]
+ [InlineData(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 })]
+ [InlineData(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 })]
+ public void When_Invalid_Data_Create_Standard_Message_Thrown(byte[] data)
+ {
+ Assert.Throws(() => SendingCanMessage.CreateStandard(new byte[2], data));
+ }
+
+ [Theory]
+ [InlineData(new byte[] { 1, 2, 3, 4 }, new byte[] { 1, 2, 3, 4 })]
+ [InlineData(new byte[] { 3, 4, 5, 6 }, new byte[] { 3, 4, 5, 6 })]
+ public void Create_Extended_Message_Id(byte[] id, byte[] resultId)
+ {
+ var message = SendingCanMessage.CreateExtended(id, Array.Empty());
+ Assert.Equal(resultId, message.Id);
+ }
+
+ [Theory]
+ [InlineData(new byte[0])]
+ [InlineData(new byte[] { 1 })]
+ [InlineData(new byte[] { 1, 2 })]
+ [InlineData(new byte[] { 1, 2, 3 })]
+ [InlineData(new byte[] { 1, 2, 3, 4, 5 })]
+ public void When_Invalid_Id_Create_Extended_Message_Thrown(byte[] id)
+ {
+ Assert.Throws(() => SendingCanMessage.CreateExtended(id, Array.Empty()));
+ }
+
+ [Theory]
+ [InlineData(new byte[0], new byte[0])]
+ [InlineData(new byte[] { 1 }, new byte[] { 1 })]
+ [InlineData(new byte[] { 1, 2 }, new byte[] { 1, 2 })]
+ [InlineData(new byte[] { 1, 2, 3, 4 }, new byte[] { 1, 2, 3, 4 })]
+ [InlineData(new byte[] { 1, 2, 3, 4, 5, 6 }, new byte[] { 1, 2, 3, 4, 5, 6 })]
+ [InlineData(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 }, new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 })]
+ public void Create_Extended_Message_With_Data(byte[] data, byte[] resultData)
+ {
+ var message = SendingCanMessage.CreateStandard(new byte[2], data);
+ Assert.Equal(resultData, message.Data);
+ }
+
+ [Theory]
+ [InlineData(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 })]
+ [InlineData(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 })]
+ public void When_Invalid_Data_Create_Extended_Message_Thrown(byte[] data)
+ {
+ Assert.Throws(() => SendingCanMessage.CreateStandard(new byte[2], data));
+ }
+ }
+}