diff --git a/Source/Meadow.Foundation.Peripherals/ICs.ADC.Ads1x15/Driver/Ads1x15Base.Enums.cs b/Source/Meadow.Foundation.Peripherals/ICs.ADC.Ads1x15/Driver/Ads1x15Base.Enums.cs
index 9daf174bc..9b3ab69d0 100644
--- a/Source/Meadow.Foundation.Peripherals/ICs.ADC.Ads1x15/Driver/Ads1x15Base.Enums.cs
+++ b/Source/Meadow.Foundation.Peripherals/ICs.ADC.Ads1x15/Driver/Ads1x15Base.Enums.cs
@@ -35,27 +35,27 @@ public enum Addresses : byte
public enum FsrGain
{
///
- /// 2/3rds
+ /// Gain = 2/3rds. Range: ±6.144 V
///
TwoThirds = 0x00,
///
- /// 1
+ /// Gain = 1. Range: ±4.096 V
///
One = 0x01,
///
- /// 2
+ /// Gain = 2. Range: ±2.048 V
///
Two = 0x02,
///
- /// 4
+ /// Gain = 4. Range: ±1.024 V
///
Four = 0x03,
///
- /// 8
+ /// Gain = 8. Range: ±0.512 V
///
Eight = 0x04,
///
- /// 16
+ /// Gain = 16. Range: ±0.256 V
///
Sixteen = 0x05
}
diff --git a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Sc16is7x2/Driver/FifoBuffer.cs b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Sc16is7x2/Driver/FifoBuffer.cs
deleted file mode 100644
index 74aeee7fe..000000000
--- a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Sc16is7x2/Driver/FifoBuffer.cs
+++ /dev/null
@@ -1,137 +0,0 @@
-using System;
-using System.Text;
-
-namespace Meadow.Foundation.ICs.IOExpanders;
-
-///
-/// This FIFO buffer is implemented as a circular buffer. (Standard computer science stuff)
-/// It operates on bytes. The only special method is WriteString,
-/// which writes a string to the buffer after converting it with Encoding.ASCII.GetBytes().
-///
-public class FifoBuffer
-{
- private readonly byte[] _buffer;
- private int _nextRead;
- private int _nextWrite;
- private int _count;
-
- ///
- /// The number of bytes that can be stored in the buffer.
- ///
- public int Capacity => _buffer.Length;
-
- ///
- /// The number of bytes currently stored in the buffer. Increases with Write() and decreases with Read().
- ///
- public int Count => _count;
-
- ///
- /// Create a FIFO byte buffer with a specified capacity.
- /// The buffer is circular, so it will reuse the allocated space.
- /// Write operations will consume space in the buffer, and read operations will free up space.
- /// If you try to write more data than its capacity, it will throw an error.
- ///
- /// The size of the circular buffer, containing bytes.
- ///
- public FifoBuffer(int capacity)
- {
- if (capacity < 1)
- throw new ArgumentException("Capacity must be greater than 0.", nameof(capacity));
-
- _buffer = new byte[capacity];
- _nextRead = 0; // The next read position.
- _nextWrite = 0; // The next write position.
- _count = 0; // The number of items in the buffer.
- }
-
- ///
- /// Clear/empty the FIFO buffer.
- ///
- public void Clear()
- {
- _nextRead = 0; // The read position.
- _nextWrite = 0; // The write position.
- _count = 0; // The number of items in the buffer.
- }
-
- ///
- /// Add new item to the _nestWrite position in the buffer.
- /// Then move the _nextWrite position. If it goes beyond the capacity, it will wrap around to 0.
- /// If you try to write more data than the capacity, it will throw an error.
- ///
- ///
- ///
- public void Write(byte item)
- {
- if (_count == Capacity)
- throw new InvalidOperationException("The buffer is full.");
-
- _buffer[_nextWrite] = item;
- _nextWrite = (_nextWrite + 1) % Capacity; // Move _nextWrite forward and possibly wrap.
- _count++;
- }
-
- ///
- /// Method to remove and return the oldest item. The one at the _nextRead position.
- /// The _nextRead position will be moved. If it goes beyond the capacity, it will wrap around to 0.
- /// If you try to read from an empty buffer, it will throw an error.
- ///
- ///
- ///
- public byte Read()
- {
- if (_count == 0)
- throw new InvalidOperationException("The buffer is empty.");
-
- var item = _buffer[_nextRead];
- _nextRead = (_nextRead + 1) % Capacity; // Move _nextRead forward and possibly wrap.
- _count--;
- return item;
- }
-
- ///
- /// Peek at element at index, relative to the _nextRead position.
- /// The byte at relative index 0 is the oldest one, so looking at [0] is the same as Peek().
- ///
- ///
- ///
- ///
- public byte this[int index]
- {
- get
- {
- if (index < 0 || index >= _count)
- throw new ArgumentOutOfRangeException(nameof(index));
- return _buffer[(_nextRead + index) % Capacity];
- }
- }
-
- ///
- /// Method to just return the oldest item. The one at the _nextRead position. (Withoit removing it)
- /// If the buffer is empty, it will throw an error.
- ///
- ///
- ///
- public byte Peek()
- {
- if (_count == 0)
- throw new InvalidOperationException("The buffer is empty.");
-
- return _buffer[_nextRead];
- }
-
- ///
- /// Bonus method to add a string to the buffer.
- /// Each character in the string will be converted to a byte and written to the buffer.
- /// This method can be usefull if you need to tag some data written to the buffer with a string.
- ///
- ///
- public void WriteString(string text)
- {
- byte[] bytes = Encoding.ASCII.GetBytes(text);
- foreach (var c in text)
- {
- Write((byte)c);
- }
- }
-}
diff --git a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Sc16is7x2/Driver/Sc16is7x2.Enums.cs b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Sc16is7x2/Driver/Sc16is7x2.Enums.cs
index 5bfe9545c..b315da6d9 100644
--- a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Sc16is7x2/Driver/Sc16is7x2.Enums.cs
+++ b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Sc16is7x2/Driver/Sc16is7x2.Enums.cs
@@ -1,7 +1,82 @@
-namespace Meadow.Foundation.ICs.IOExpanders
+using System;
+
+namespace Meadow.Foundation.ICs.IOExpanders
{
public partial class Sc16is7x2
{
+ ///
+ /// Address pin connection used for calculating peripherla addresses
+ ///
+ public enum AddressConnection
+ {
+ ///
+ /// Address pin tied to VDD
+ ///
+ VDD,
+ ///
+ /// Address pin tied to GND
+ ///
+ GND,
+ ///
+ /// Address pin tied to SCL
+ ///
+ SCL,
+ ///
+ /// Address pin tied to SDA
+ ///
+ SDA
+ }
+
+ ///
+ /// Calculates a bus address based on the connection of the address pins
+ ///
+ /// A0's pin connection
+ /// A1's pin connection
+ public static Addresses CalculateAddress(AddressConnection a0, AddressConnection a1)
+ {
+ switch (a1)
+ {
+ case AddressConnection.VDD:
+ switch (a0)
+ {
+ case AddressConnection.VDD: return Addresses.Address_0x48;
+ case AddressConnection.GND: return Addresses.Address_0x49;
+ case AddressConnection.SCL: return Addresses.Address_0x4A;
+ case AddressConnection.SDA: return Addresses.Address_0x4B;
+ }
+ break;
+ case AddressConnection.GND:
+ switch (a0)
+ {
+ case AddressConnection.VDD: return Addresses.Address_0x4C;
+ case AddressConnection.GND: return Addresses.Address_0x4D;
+ case AddressConnection.SCL: return Addresses.Address_0x4E;
+ case AddressConnection.SDA: return Addresses.Address_0x4F;
+ }
+ break;
+ case AddressConnection.SCL:
+ switch (a0)
+ {
+ case AddressConnection.VDD: return Addresses.Address_0x50;
+ case AddressConnection.GND: return Addresses.Address_0x51;
+ case AddressConnection.SCL: return Addresses.Address_0x52;
+ case AddressConnection.SDA: return Addresses.Address_0x53;
+ }
+ break;
+ case AddressConnection.SDA:
+ switch (a0)
+ {
+ case AddressConnection.VDD: return Addresses.Address_0x54;
+ case AddressConnection.GND: return Addresses.Address_0x55;
+ case AddressConnection.SCL: return Addresses.Address_0x56;
+ case AddressConnection.SDA: return Addresses.Address_0x57;
+ }
+ break;
+ }
+
+ throw new ArgumentException();
+ }
+
///
/// The list of possible I2C addresses for the peripheral
///
diff --git a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Sc16is7x2/Driver/Sc16is7x2.I2c.cs b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Sc16is7x2/Driver/Sc16is7x2.I2c.cs
index 0137598dc..01251f964 100644
--- a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Sc16is7x2/Driver/Sc16is7x2.I2c.cs
+++ b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Sc16is7x2/Driver/Sc16is7x2.I2c.cs
@@ -29,6 +29,7 @@ public Sc16is7x2(II2cBus i2cBus, Frequency oscillatorFrequency, Addresses addres
: this(oscillatorFrequency, irq, latchGpioInterrupt)
{
_i2cComms = new I2cCommunications(i2cBus, (byte)address);
+ Reset();
}
}
-}
\ No newline at end of file
+}
diff --git a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Sc16is7x2/Driver/Sc16is7x2.Sc16is7x2Channel.cs b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Sc16is7x2/Driver/Sc16is7x2.Sc16is7x2Channel.cs
index 0323ee3d7..68cff5bb2 100644
--- a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Sc16is7x2/Driver/Sc16is7x2.Sc16is7x2Channel.cs
+++ b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Sc16is7x2/Driver/Sc16is7x2.Sc16is7x2Channel.cs
@@ -23,9 +23,9 @@ public class Sc16is7x2Channel : ISerialPort
private StopBits _stopBits;
///
- public int BytesToRead => (_irqReadBuffer == null)
- ? _controller.GetReadFifoCount(_channel)
- : _controller.GetReadFifoCount(_channel) + _irqReadBuffer.Count;
+ public int BytesToRead => (_irqReadBuffer == null)
+ ? GetReadHwFifoCount()
+ : GetReadHwFifoCount() + _irqReadBuffer.Count;
///
public bool IsOpen { get; private set; }
@@ -49,7 +49,16 @@ public class Sc16is7x2Channel : ISerialPort
private readonly Channels _channel;
private readonly IDigitalInterruptPort? _irq;
- private readonly FifoBuffer? _irqReadBuffer;
+ private readonly CircularBuffer _irqReadBuffer;
+
+ public bool IsIrqDriven => _irq != null;
+
+ // Optimizations for fast read. Precalculated values for this channel.
+ private byte _rhrAddress;
+ private byte _lsrAddress;
+ private byte _rxlvlAddress;
+ private byte _txlvlAddress;
+ private IByteCommunications _comms;
///
/// This method is never called directly from user code.
@@ -71,19 +80,27 @@ internal Sc16is7x2Channel(Sc16is7x2 controller, string portName, Channels channe
_controller = controller;
_channel = channel;
- Initialize(baudRate, dataBits, parity, stopBits);
+ _rhrAddress = CalculateChannelAddress((byte)Registers.RHR);
+ _lsrAddress = CalculateChannelAddress((byte)Registers.LSR);
+ _rxlvlAddress = CalculateChannelAddress((byte)Registers.RXLVL);
+ _txlvlAddress = CalculateChannelAddress((byte)Registers.TXLVL);
+ _comms = controller.Comms;
+
+ InitChannel(baudRate, dataBits, parity, stopBits);
+
if (isRS485)
{
- InitializeRS485(invertDE);
+ EnableRS485(invertDE);
}
if (irq != null)
{
// Setting up IRQ read with a large software FIFO buffer to offload the hardware FIFO of only 64 bytes.
_irq = irq;
- _irqReadBuffer = new FifoBuffer(readBufferSize);
- _controller.EnableReceiveInterrupts(_channel);
- _irq.Changed += OnInterruptLineChanged;
+ _irqReadBuffer = new CircularBuffer(readBufferSize);
+ // 21.04.2024, KW: We'll try to move this to Open(). And we'll remove this handler in Close().
+ //EnableReceiveInterrupts();
+ //_irq.Changed += UartChannelInterruptHandler;
}
// https://github.com/WildernessLabs/Meadow_Issues/issues/74
@@ -98,23 +115,25 @@ internal Sc16is7x2Channel(Sc16is7x2 controller, string portName, Channels channe
// a.RaiseInterrupt(this, new DigitalPortResult());
}
- internal void OnInterruptLineChanged(object sender, DigitalPortResult e)
+ internal void UartChannelInterruptHandler(object sender, DigitalPortResult e)
{
// If the first message after reboot is longer than the FIFO buffer, we already have a buffer overrun at this point.
// For any consecutive messages, it works fine.
// Ref: https://github.com/WildernessLabs/Meadow_Issues/issues/74
- //int count = _controller.GetReadFifoCount(_channel);
- //Resolver.Log.Info($"Channel {_channel} port FIFO: {count} bytes");
+ int count = GetReadHwFifoCount();
+ //Resolver.Log.Info($"->UART interrupt. Channel={_channel} port HW FIFO: {count} bytes");
+
+ if (count == 0) return; // The IRQ was not for us.
if (_irqReadBuffer == null)
{
- if (_controller.ReceiveInterruptPending(_channel))
+ if (ReceiveInterruptPending())
this.DataReceived?.Invoke(this, new SerialDataReceivedEventArgs(SerialDataType.Chars));
}
else
- {
+ {
// If this is IRQ reading, shortcut the user callback. Empty FIFO ASAP.
- ReadAllIrqFifo();
+ ReadAllIrqFifo();
if (_irqReadBuffer.Count > 0)
this.DataReceived?.Invoke(this, new SerialDataReceivedEventArgs(SerialDataType.Chars));
}
@@ -124,7 +143,7 @@ internal void OnInterruptLineChanged(object sender, DigitalPortResult e)
public int BaudRate
{
get => _baudRate;
- set => _baudRate = _controller.SetBaudRate(_channel, value);
+ set => _baudRate = SetBaudRate(value);
}
///
@@ -133,7 +152,7 @@ public int DataBits
get => _dataBits;
set
{
- _controller.SetLineSettings(_channel, value, Parity, StopBits);
+ SetLineSettings(value, Parity, StopBits);
_dataBits = value;
}
}
@@ -144,7 +163,7 @@ public Parity Parity
get => _parity;
set
{
- _controller.SetLineSettings(_channel, DataBits, value, StopBits);
+ SetLineSettings(DataBits, value, StopBits);
_parity = value;
}
}
@@ -155,24 +174,17 @@ public StopBits StopBits
get => _stopBits;
set
{
- _controller.SetLineSettings(_channel, DataBits, Parity, value);
+ SetLineSettings(DataBits, Parity, value);
_stopBits = value;
}
}
- private void Initialize(int baudRate, int dataBits, Parity parity, StopBits stopBits)
- {
- _dataBits = dataBits;
- _stopBits = stopBits;
- _controller.Reset();
- _controller.EnableFifo(_channel);
- _baudRate = _controller.SetBaudRate(_channel, baudRate);
- _controller.SetLineSettings(_channel, dataBits, parity, stopBits);
- }
-
- private void InitializeRS485(bool invertDE)
+ private void InitChannel(int baudRate, int dataBits, Parity parity, StopBits stopBits)
{
- _controller.EnableRS485(_channel, invertDE);
+ //_controller.Reset();
+ EnableFifo();
+ _baudRate = SetBaudRate(baudRate);
+ SetLineSettings(dataBits, parity, stopBits);
}
///
@@ -181,25 +193,42 @@ private void InitializeRS485(bool invertDE)
internal void ReadAllIrqFifo()
{
if (_irqReadBuffer == null) return;
+
int totalRead = 0;
- int count = _controller.GetReadFifoCount(_channel); // How may bytes to read.
+ int count = GetReadHwFifoCount(); // How may bytes to read.
+ //int count = _comms.ReadRegister(_rxlvlAddress); // How may bytes to read.
while (count > 0)
{
for (int i = 0; i < count; i++)
- _irqReadBuffer.Write((byte)_controller.ReadByte(_channel));
+ {
+ byte b = _comms.ReadRegister(_rhrAddress);
+ try
+ {
+ _irqReadBuffer.Append(b);
+ }
+ catch (Exception ex)
+ {
+ Resolver.Log.Error($"!!!!!---> ReadAllIrqFifo: Channel={_channel} Exception: {ex.Message}");
+ BufferOverrun?.Invoke(this, new ThreadExceptionEventArgs(ex));
+ }
+ }
totalRead += count;
- count = _controller.GetReadFifoCount(_channel); // Check that we're all done. To make sure IRQ is reset.
+ //Resolver.Log.Info($"---> ReadAllIrqFifo: Channel={_channel} Read {count}/{totalRead}");
- byte lsr = _controller.ReadChannelRegister(Registers.LSR, _channel);
+ //byte lsr = _controller.ReadChannelRegister(Registers.LSR, _channel);
+ byte lsr = _comms.ReadRegister(_lsrAddress);
if ((lsr & RegisterBits.LSR_OVERRUN_ERROR) > 0)
{
- _irqReadBuffer.WriteString("[BUFFER OVERRUN]"); // Not sure to keep this, but nice when debugging.
+ Resolver.Log.Error("[OVERRUN]"); // Not sure to keep this, but nice when debugging.
BufferOverrun?.Invoke(this, EventArgs.Empty);
}
+
+ count = GetReadHwFifoCount(); // Check that we're all done. To make sure IRQ is reset.
+ //count = _comms.ReadRegister(_rxlvlAddress); // Check that we're all done. To make sure IRQ is reset.
}
if (totalRead > 0)
{
- Resolver.Log.Info($"---> ReadAllIrqFifo: Channel {_channel} port read {totalRead} bytes");
+ //Resolver.Log.Info($"---> ReadAllIrqFifo: Channel={_channel} Done {count}/{totalRead}");
}
}
@@ -210,22 +239,22 @@ public int ReadByte()
{
// The normal way...
// check if data is available
- if (!_controller.IsFifoDataAvailable(_channel))
+ if (!IsHwFifoDataAvailable())
{
return -1;
}
// read the data
- return _controller.ReadByte(_channel);
+ return ReadHwFifoByte();
}
else
{
- // IRQ fast read....
+ // IRQ fast read from software FIFO....
ReadAllIrqFifo();
if (_irqReadBuffer.Count == 0)
return -1;
else
- return _irqReadBuffer.Read();
+ return _irqReadBuffer.Remove();
}
}
@@ -241,13 +270,13 @@ public int Read(byte[] buffer, int offset, int count)
if (_irqReadBuffer == null)
{
// The normal way...
- var available = _controller.GetReadFifoCount(_channel);
+ var available = GetReadHwFifoCount();
// read either the available or count, whichever is less, unless available is 0, in which case we wait until timeout
while (available == 0)
{
Thread.Sleep(10);
- available = _controller.GetReadFifoCount(_channel);
+ available = GetReadHwFifoCount();
if (timeout > 0)
{
@@ -262,7 +291,7 @@ public int Read(byte[] buffer, int offset, int count)
for (var i = 0; i < toRead; i++)
{
- buffer[i + offset] = _controller.ReadByte(_channel);
+ buffer[i + offset] = ReadHwFifoByte();
}
return toRead;
@@ -294,7 +323,7 @@ public int Read(byte[] buffer, int offset, int count)
var toRead = _irqReadBuffer.Count <= count ? _irqReadBuffer.Count : count;
for (var i = 0; i < toRead; i++)
{
- buffer[i + offset] = _irqReadBuffer.Read();
+ buffer[i + offset] = _irqReadBuffer.Remove();
}
return toRead;
}
@@ -306,7 +335,7 @@ public byte[] ReadAll()
if (_irqReadBuffer == null)
{
// The normal way...
- var available = _controller.GetReadFifoCount(_channel);
+ var available = GetReadHwFifoCount();
var buffer = new byte[available];
Read(buffer, 0, available);
return buffer;
@@ -317,10 +346,11 @@ public byte[] ReadAll()
ReadAllIrqFifo(); // Always read whatever is in the FIFO.
var available = _irqReadBuffer.Count;
var buffer = new byte[available];
- for (int i = 0; i < available; i++)
- {
- buffer[i] = _irqReadBuffer.Read();
- }
+ //for (int i = 0; i < available; i++)
+ //{
+ // buffer[i] = _irqReadBuffer.Read();
+ //}
+ _irqReadBuffer.MoveItemsTo(buffer, 0, available);
return buffer;
}
}
@@ -328,29 +358,41 @@ public byte[] ReadAll()
///
public void ClearReceiveBuffer()
{
- _controller.ResetReadFifo(_channel);
- if (_irqReadBuffer != null)
+ ResetReadHwFifo();
+ if (IsIrqDriven)
_irqReadBuffer.Clear();
}
///
- public void Close()
+ public void Dispose()
{
- IsOpen = false;
- if (_irqReadBuffer != null)
- _irqReadBuffer.Clear();
+ // nop
}
///
- public void Dispose()
+ public void Open()
{
- // nop
+ if (!IsOpen)
+ {
+ IsOpen = true;
+ ResetWriteHwFifo();
+ ClearReceiveBuffer();
+ if (IsIrqDriven)
+ _irq.Changed += UartChannelInterruptHandler;
+ EnableReceiveInterrupts();
+ }
}
///
- public void Open()
+ public void Close()
{
- IsOpen = true;
+ if (IsOpen)
+ {
+ IsOpen = false;
+ DisableReceiveInterrupts();
+ if (IsIrqDriven)
+ _irq.Changed -= UartChannelInterruptHandler;
+ }
}
///
@@ -378,13 +420,13 @@ public int Write(byte[] buffer, int offset, int count)
timeout = Environment.TickCount + (int)ReadTimeout.TotalMilliseconds;
}
// wait for THR to be empty
- while (!_controller.IsTransmitHoldingRegisterEmpty(_channel))
+ while (!IsTransmitHoldingRegisterEmpty())
{
Thread.Sleep(10);
}
// write until we're either written all or the THR is full
- var available = _controller.GetWriteFifoSpace(_channel);
+ var available = GetWriteHwFifoSpace();
while (remaining > 0)
{
@@ -392,7 +434,7 @@ public int Write(byte[] buffer, int offset, int count)
while (available <= 0)
{
Thread.Sleep(10);
- available = _controller.GetWriteFifoSpace(_channel);
+ available = GetWriteHwFifoSpace();
if (timeout > 0)
{
@@ -403,18 +445,266 @@ public int Write(byte[] buffer, int offset, int count)
}
}
- _controller.WriteByte(_channel, buffer[index]);
+ WriteHwFifoByte(buffer[index]);
index++;
available--;
remaining--;
if (available == 0)
{
- available = _controller.GetWriteFifoSpace(_channel);
+ available = GetWriteHwFifoSpace();
}
}
return count;
}
+
+
+ // ******************* Hardware communication methods *******************
+ // Moved here from Sc16is7x2.cs so we can ommit the channel parameter
+ // and unnecessary calls to parent class. (We keep calls local)
+
+ internal void EnableFifo()
+ {
+ var fcr = ReadChannelRegister(Registers.FCR);
+ fcr |= RegisterBits.FCR_FIFO_ENABLE;
+ WriteChannelRegister(Registers.FCR, fcr);
+ }
+
+ internal void EnableReceiveInterrupts()
+ {
+ //var ier = ReadChannelRegister(Registers.IER);
+ //ier |= RegisterBits.IER_RHR_ENABLE;
+ //WriteChannelRegister(Registers.IER, ier);
+ SetChannelRegisterBits(Registers.IER, RegisterBits.IER_RHR_ENABLE);
+ }
+
+ internal void DisableReceiveInterrupts()
+ {
+ //var ier = ReadChannelRegister(Registers.IER);
+ //ier &= (byte)~(RegisterBits.IER_RHR_ENABLE);
+ //WriteChannelRegister(Registers.IER, ier);
+ ClearChannelRegisterBits(Registers.IER, RegisterBits.IER_RHR_ENABLE);
+ }
+
+ internal bool ReceiveInterruptPending()
+ {
+ // IIR[0] is 0 for any pending interrupt
+ // RHR will be IIR[2] *exclusively*
+ var iir = ReadChannelRegister(Registers.IIR);
+ return (iir & RegisterBits.IIR_RHR_INTERRUPT) == RegisterBits.IIR_RHR_INTERRUPT;
+ }
+
+ internal void SetLineSettings(int dataBits, Parity parity, StopBits stopBits)
+ {
+ var lcr = ReadChannelRegister(Registers.LCR);
+ lcr &= unchecked((byte)~0x3f); // clear all of the line setting bits for simplicity
+
+ switch (dataBits)
+ {
+ case 5:
+ lcr |= RegisterBits.LCR_5_DATA_BITS;
+ break;
+ case 6:
+ lcr |= RegisterBits.LCR_6_DATA_BITS;
+ break;
+ case 7:
+ lcr |= RegisterBits.LCR_7_DATA_BITS;
+ break;
+ case 8:
+ lcr |= RegisterBits.LCR_8_DATA_BITS;
+ break;
+ default:
+ throw new ArgumentOutOfRangeException(nameof(dataBits));
+
+ }
+
+ if (stopBits == StopBits.Two)
+ {
+ lcr |= RegisterBits.LCR_2_STOP_BITS;
+ }
+
+ switch (parity)
+ {
+ case Parity.None:
+ lcr |= RegisterBits.LCR_PARITY_NONE;
+ break;
+ case Parity.Odd:
+ lcr |= RegisterBits.LCR_PARITY_ODD;
+ break;
+ case Parity.Even:
+ lcr |= RegisterBits.LCR_PARITY_EVEN;
+ break;
+ // device supports mark and space, but Meadow doesn't have values for it
+ }
+
+ WriteChannelRegister(Registers.LCR, lcr);
+ }
+
+ internal int SetBaudRate(int baudRate)
+ {
+ // the part baud rate is a division of the oscillator frequency, not necessarily the value requested
+ var mcr = ReadChannelRegister(Registers.MCR);
+ var prescaler = ((mcr & RegisterBits.MCR_CLOCK_DIVISOR) == 0) ? 1 : 4;
+ var divisor1 = _controller.OscillatorFrequency.Hertz / prescaler;
+ var divisor2 = baudRate * 16;
+
+ if (divisor2 > divisor1) throw new ArgumentOutOfRangeException(nameof(baudRate), "Oscillator does not allow requested baud rate");
+
+ var divisor = (ushort)Math.Ceiling(divisor1 / divisor2);
+
+ // enable the divisor latch
+ var lcr = ReadChannelRegister(Registers.LCR);
+ lcr |= RegisterBits.LCR_DIVISOR_LATCH_ENABLE;
+ WriteChannelRegister(Registers.LCR, lcr);
+
+ // set the baud rate
+ WriteChannelRegister(Registers.DLL, (byte)(divisor & 0xff));
+ WriteChannelRegister(Registers.DLH, (byte)(divisor >> 8));
+
+ // disable the divisor latch
+ lcr &= unchecked((byte)~RegisterBits.LCR_DIVISOR_LATCH_ENABLE);
+ WriteChannelRegister(Registers.LCR, lcr);
+
+ // return the actual baud rate achieved
+ return (int)(divisor1 / divisor / 16);
+ }
+
+ internal void EnableRS485(bool invertDE)
+ {
+ var efcr = ReadChannelRegister(Registers.EFCR);
+ efcr |= RegisterBits.EFCR_9BITMODE | RegisterBits.EFCR_RTSCON;
+
+ if (invertDE)
+ {
+ efcr |= RegisterBits.EFCR_RTSINVER;
+ }
+ else
+ {
+ efcr &= unchecked((byte)~RegisterBits.EFCR_RTSINVER);
+ }
+
+ WriteChannelRegister(Registers.EFCR, efcr);
+ }
+
+
+ // ******************* UART read methods *******************
+
+ ///
+ /// Returns the number of bytes in the receive FIFO.
+ ///
+ ///
+ internal int GetReadHwFifoCount()
+ {
+ return _comms.ReadRegister(_rxlvlAddress);
+ }
+
+ ///
+ /// Returns true if there is data available in the receive FIFO.
+ ///
+ ///
+ internal bool IsHwFifoDataAvailable()
+ {
+ return GetReadHwFifoCount() > 0;
+ }
+
+ internal void ResetReadHwFifo()
+ {
+ var fcr = ReadChannelRegister(Registers.FCR);
+ fcr |= RegisterBits.FCR_RX_FIFO_RESET;
+ WriteChannelRegister(Registers.FCR, fcr);
+ }
+
+ internal byte ReadHwFifoByte()
+ {
+ return ReadChannelRegister(Registers.RHR);
+ }
+
+
+ // ******************* UART write methods *******************
+
+ ///
+ /// Returns the empty space in the transmit FIFO.
+ ///
+ ///
+ internal int GetWriteHwFifoSpace()
+ {
+ return _comms.ReadRegister(_txlvlAddress);
+ }
+
+ ///
+ /// Reading status from the THR bit in the LSR register.
+ ///
+ ///
+ internal bool IsTransmitHoldingRegisterEmpty()
+ {
+ var lsr = ReadChannelRegister(Registers.LSR);
+ return (lsr & RegisterBits.LSR_THR_EMPTY) == RegisterBits.LSR_THR_EMPTY;
+ }
+
+ internal void ResetWriteHwFifo()
+ {
+ var fcr = ReadChannelRegister(Registers.FCR);
+ fcr |= RegisterBits.FCR_TX_FIFO_RESET;
+ WriteChannelRegister(Registers.FCR, fcr);
+ }
+
+ internal void WriteHwFifoByte(byte data)
+ {
+ WriteChannelRegister(Registers.THR, data);
+ }
+
+
+ // ******************* Channel communication methods *******************
+
+ private byte CalculateChannelAddress(byte register)
+ {
+ // see page 40 of the data sheet for explanation of this
+ var subaddress = (byte)((register << 3) | ((byte)_channel << 1));
+ return subaddress;
+ }
+
+ private byte ReadChannelRegister(Registers register)
+ {
+ // see page 40 of the data sheet for explanation of this
+ var subaddress = (byte)(((byte)register << 3) | ((byte)_channel << 1));
+ byte v = _comms.ReadRegister(subaddress);
+ return v;
+ }
+
+ private void WriteChannelRegister(Registers register, byte value)
+ {
+ // see page 40 of the data sheet for explanation of this
+ var subaddress = (byte)(((byte)register << 3) | ((byte)_channel << 1));
+ _comms.WriteRegister(subaddress, value);
+
+ //int b = GetWriteHwFifoSpace();
+ //int c = GetReadHwFifoCount();
+ //Resolver.Log.Info($"->UART write. Channel={_channel} port HW FIFO: {b} bytes Read FIFO: {c}");
+ }
+
+ ///
+ /// Sets bits in the register if the corresponding bit in the value parameter is set.
+ ///
+ ///
+ ///
+ private void SetChannelRegisterBits(Registers register, byte value)
+ {
+ byte currentValue = ReadChannelRegister(register);
+ currentValue |= value; // Set the bits we're going to change
+ WriteChannelRegister(register, currentValue);
+ }
+
+ ///
+ /// Clears bits in the register if the corresponding bit in the mask parameter is set.
+ ///
+ ///
+ ///
+ private void ClearChannelRegisterBits(Registers register, byte mask)
+ {
+ byte currentValue = ReadChannelRegister(register);
+ currentValue &= (byte)~mask; // Flip all bits in mask, then AND with currentValue
+ WriteChannelRegister(register, currentValue);
+ }
}
}
diff --git a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Sc16is7x2/Driver/Sc16is7x2.Spi.cs b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Sc16is7x2/Driver/Sc16is7x2.Spi.cs
index 01daa1b50..a85ea067d 100644
--- a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Sc16is7x2/Driver/Sc16is7x2.Spi.cs
+++ b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Sc16is7x2/Driver/Sc16is7x2.Spi.cs
@@ -39,6 +39,7 @@ public Sc16is7x2(ISpiBus spiBus, Frequency oscillatorFrequency, IDigitalOutputPo
: this(oscillatorFrequency, irq, latchGpioInterrupt)
{
_spiComms = new SpiCommunications(spiBus, chipSelect, DefaultSpiBusSpeed);
+ Reset();
}
}
-}
\ No newline at end of file
+}
diff --git a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Sc16is7x2/Driver/Sc16is7x2.cs b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Sc16is7x2/Driver/Sc16is7x2.cs
index 593ab0e18..47abf097d 100644
--- a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Sc16is7x2/Driver/Sc16is7x2.cs
+++ b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Sc16is7x2/Driver/Sc16is7x2.cs
@@ -22,7 +22,7 @@ public abstract partial class Sc16is7x2 : ISerialController, IDigitalInputOutput
private Sc16is7x2Channel? _channelA;
private Sc16is7x2Channel? _channelB;
- private Frequency _oscillatorFrequency;
+ public Frequency OscillatorFrequency { get; private set; }
private IDigitalInterruptPort? _irq;
private bool _latchGpioInterrupt;
@@ -33,16 +33,17 @@ public abstract partial class Sc16is7x2 : ISerialController, IDigitalInputOutput
internal Sc16is7x2(Frequency oscillatorFrequency, IDigitalInterruptPort? irq, bool latchGpioInterrupt = false)
{
- _oscillatorFrequency = oscillatorFrequency;
+ OscillatorFrequency = oscillatorFrequency;
_irq = irq;
_latchGpioInterrupt = latchGpioInterrupt;
Pins = new PinDefinitions(this);
- if (irq != null)
- {
- irq.Changed += GpioInterruptHandler;
- }
+ // This has to move to GPIO init. We don't know yet if comms init will succeed.
+ //if (irq != null)
+ //{
+ // irq.Changed += GpioInterruptHandler;
+ //}
}
private IByteCommunications Comms
@@ -66,10 +67,10 @@ private IByteCommunications Comms
/// The software FIFO read buffer size. (Not the 64 bytes on chip FIFO)
public ISerialPort CreateSerialPort(SerialPortName portName, int baudRate = 9600, int dataBits = 8, Parity parity = Parity.None, StopBits stopBits = StopBits.One, int readBufferSize = 1024)
{
- //if (_irq != null && _irq.InterruptMode != InterruptMode.EdgeRising)
- //{
- // throw new ArgumentException("If an interrupt port is provided, it must be a rising edge interrupt");
- //}
+ if (_irq != null && _irq.InterruptMode != InterruptMode.EdgeFalling)
+ {
+ Resolver.Log.Warn($"Warning: You have specified InterruptMode={_irq.InterruptMode}. SC16IS7x2 have a falling edge IRQ signal.");
+ }
switch (portName.SystemName)
{
@@ -129,305 +130,101 @@ public ISerialPort CreateRs485SerialPort(Sc16SerialPortName portName, int baudRa
throw new Exception("Unknown port");
}
- internal bool ReceiveInterruptPending(Channels channel)
- {
- // IIR[0] is 0 for any pending interrupt
- // RHR will be IIR[2] *exclusively*
- var iir = ReadChannelRegister(Registers.IIR, channel);
- return (iir & RegisterBits.IIR_RHR_INTERRUPT) == RegisterBits.IIR_RHR_INTERRUPT;
- }
-
- internal void EnableReceiveInterrupts(Channels channel)
- {
- var ier = ReadChannelRegister(Registers.IER, channel);
- ier |= RegisterBits.IER_RHR_ENABLE;
- WriteChannelRegister(Registers.IER, channel, ier);
- }
-
- internal void EnableRS485(Channels channel, bool invertDE)
- {
- var efcr = ReadChannelRegister(Registers.EFCR, channel);
- efcr |= RegisterBits.EFCR_9BITMODE | RegisterBits.EFCR_RTSCON;
-
- if (invertDE)
- {
- efcr |= RegisterBits.EFCR_RTSINVER;
- }
- else
- {
- efcr &= unchecked((byte)~RegisterBits.EFCR_RTSINVER);
- }
-
- WriteChannelRegister(Registers.EFCR, channel, efcr);
- }
-
- internal void WriteByte(Channels channel, byte data)
- {
- WriteChannelRegister(Registers.THR, channel, data);
- }
-
///
- /// Reads the empty space in the transmit fifo
+ /// Reset the device
///
- ///
- ///
- internal int GetWriteFifoSpace(Channels channel)
- {
- return ReadChannelRegister(Registers.TXLVL, channel);
- }
-
- internal int GetReadFifoCount(Channels channel)
- {
- return ReadChannelRegister(Registers.RXLVL, channel);
- }
-
- internal void ResetReadFifo(Channels channel)
- {
- var fcr = ReadChannelRegister(Registers.FCR, channel);
- fcr |= RegisterBits.FCR_RX_FIFO_RESET;
- WriteChannelRegister(Registers.FCR, channel, fcr);
- }
-
- internal bool IsTransmitHoldingRegisterEmpty(Channels channel)
- {
- var thr = ReadChannelRegister(Registers.LSR, channel);
- return (thr & RegisterBits.LSR_THR_EMPTY) == RegisterBits.LSR_THR_EMPTY;
- }
-
- internal bool IsFifoDataAvailable(Channels channel)
- {
- return GetReadFifoCount(channel) > 0;
- }
-
- internal byte ReadByte(Channels channel)
- {
- return ReadChannelRegister(Registers.RHR, channel);
- }
-
- internal void SetLineSettings(Channels channel, int dataBits, Parity parity, StopBits stopBits)
- {
- var lcr = ReadChannelRegister(Registers.LCR, channel);
- lcr &= unchecked((byte)~0x3f); // clear all of the line setting bits for simplicity
-
- switch (dataBits)
- {
- case 5:
- lcr |= RegisterBits.LCR_5_DATA_BITS;
- break;
- case 6:
- lcr |= RegisterBits.LCR_6_DATA_BITS;
- break;
- case 7:
- lcr |= RegisterBits.LCR_7_DATA_BITS;
- break;
- case 8:
- lcr |= RegisterBits.LCR_8_DATA_BITS;
- break;
- default:
- throw new ArgumentOutOfRangeException(nameof(dataBits));
-
- }
-
- if (stopBits == StopBits.Two)
- {
- lcr |= RegisterBits.LCR_2_STOP_BITS;
- }
-
- switch (parity)
- {
- case Parity.None:
- lcr |= RegisterBits.LCR_PARITY_NONE;
- break;
- case Parity.Odd:
- lcr |= RegisterBits.LCR_PARITY_ODD;
- break;
- case Parity.Even:
- lcr |= RegisterBits.LCR_PARITY_EVEN;
- break;
- // device supports mark and space, but Meadow doesn't have values for it
- }
-
- WriteChannelRegister(Registers.LCR, channel, lcr);
- }
-
- internal int SetBaudRate(Channels channel, int baudRate)
- {
- // the part baud rate is a division of the oscillator frequency, not necessarily the value requested
- var mcr = ReadChannelRegister(Registers.MCR, channel);
- var prescaler = ((mcr & RegisterBits.MCR_CLOCK_DIVISOR) == 0) ? 1 : 4;
- var divisor1 = _oscillatorFrequency.Hertz / prescaler;
- var divisor2 = baudRate * 16;
-
- if (divisor2 > divisor1) throw new ArgumentOutOfRangeException(nameof(baudRate), "Oscillator does not allow requested baud rate");
-
- var divisor = (ushort)Math.Ceiling(divisor1 / divisor2);
-
- // enable the divisor latch
- var lcr = ReadChannelRegister(Registers.LCR, channel);
- lcr |= RegisterBits.LCR_DIVISOR_LATCH_ENABLE;
- WriteChannelRegister(Registers.LCR, channel, lcr);
-
- // set the baud rate
- WriteChannelRegister(Registers.DLL, channel, (byte)(divisor & 0xff));
- WriteChannelRegister(Registers.DLH, channel, (byte)(divisor >> 8));
-
- // disable the divisor latch
- lcr &= unchecked((byte)~RegisterBits.LCR_DIVISOR_LATCH_ENABLE);
- WriteChannelRegister(Registers.LCR, channel, lcr);
-
- // return the actual baud rate achieved
- return (int)(divisor1 / divisor / 16);
- }
-
internal void Reset()
{
- var value = ReadChannelRegister(Registers.IOControl, Channels.Both);
+ var value = ReadRegister(Registers.IOControl);
value |= RegisterBits.IOCTL_RESET;
try
{
- WriteChannelRegister(Registers.IOControl, Channels.Both, value);
+ WriteRegister(Registers.IOControl, value);
}
- catch
+ catch (Exception ex)
{
// we expect to get a NACK on this. Very confusing
+ // 06.10.2024, KW: Ignore this error. We get it even when the device works fine. ("Error code 70")
+ Resolver.Log.Trace($"Ignoring SC16IS7x2 error on reset: {ex.Message}");
}
}
- internal void EnableFifo(Channels channel)
- {
- var fcr = ReadChannelRegister(Registers.FCR, channel);
- fcr |= RegisterBits.FCR_FIFO_ENABLE;
- WriteChannelRegister(Registers.FCR, channel, fcr);
- }
-
- private byte ReadChannelRegister(Registers register, Channels channel)
- {
- // see page 40 of the data sheet for explanation of this
- var subaddress = (byte)(((byte)register << 3) | ((byte)channel << 1));
- byte v = Comms.ReadRegister(subaddress);
- if (register == Registers.IOState)
- {
- //Resolver.Log.Info($"ReadChannelRegister: {register} {channel} {v:X2}");
- if (v == 0)
- {
- byte v2 = Comms.ReadRegister(subaddress);
- if (v2 != v)
- {
- v = v2; // Fix buggy value!
- Resolver.Log.Info($"ReadChannelRegister: {register} {channel} {v2:X2} (Corrected value!)");
- }
- }
- }
- return v;
- }
-
- private void WriteChannelRegister(Registers register, Channels channel, byte value)
- {
- // see page 40 of the data sheet for explanation of this
- var subaddress = (byte)(((byte)register << 3) | ((byte)channel << 1));
- //if (register == Registers.IOState)
- //{
- // Resolver.Log.Info($"WriteChannelRegister: {register} {channel} {value:X2}");
- //}
- Comms.WriteRegister(subaddress, value);
- }
-
- private void SetChannelRegisterBits(Registers register, Channels channel, byte value)
- {
- byte currentValue = ReadChannelRegister(register, channel);
- currentValue |= value; // Set the bits we're going to change
- WriteChannelRegister(register, channel, currentValue);
- }
-
- private void ClearChannelRegisterBits(Registers register, Channels channel, byte mask)
- {
- byte currentValue = ReadChannelRegister(register, channel);
- currentValue &= (byte)~mask; // Flip all bits in value, then AND with currentValue
- WriteChannelRegister(register, channel, currentValue);
- }
+ /********************* GPIO **********************/
+ private IDigitalPort[] gpioPorts = new IDigitalPort[8];
- /********************* GPIO **********************/
+ ///
+ /// Is the pin valid for this device instance
+ ///
+ /// The IPin to validate
+ /// True if pin is valid
+ protected bool IsValidPin(IPin pin) => Pins.AllPins.Contains(pin);
- private bool initDone = false;
+ private bool initGpioDone = false;
private void InitGpio()
{
- if (initDone) return;
+ if (initGpioDone) return;
- var a = ReadChannelRegister(Registers.IER, Channels.A);
- a &= unchecked((byte)~RegisterBits.IER_SLEEP_MODE_ENABLE);
- WriteChannelRegister(Registers.IER, Channels.A, a);
- var b = ReadChannelRegister(Registers.IER, Channels.B);
- b &= unchecked((byte)~RegisterBits.IER_SLEEP_MODE_ENABLE);
- WriteChannelRegister(Registers.IER, Channels.B, b);
-
- byte ioControlBefore = ReadGpioRegister(Registers.IOControl);
if (_latchGpioInterrupt)
- SetGpioRegisterBit(Registers.IOControl, RegisterBits.IOCTL_IO_LATCH);
+ SetRegisterBit(Registers.IOControl, RegisterBits.IOCTL_IO_LATCH);
else
- ClearGpioRegisterBits(Registers.IOControl, RegisterBits.IOCTL_IO_LATCH);
- ClearGpioRegisterBits(Registers.IOControl, RegisterBits.IOCTL_GPIO_7to4);
- ClearGpioRegisterBits(Registers.IOControl, RegisterBits.IOCTL_GPIO_3to0);
- byte ioControlAfter = ReadGpioRegister(Registers.IOControl);
- //Resolver.Log.Info($"ioControl: {ioControlBefore} -> {ioControlAfter}");
+ ClearRegisterBit(Registers.IOControl, RegisterBits.IOCTL_IO_LATCH);
+ ClearRegisterBit(Registers.IOControl, RegisterBits.IOCTL_GPIO_7to4);
+ ClearRegisterBit(Registers.IOControl, RegisterBits.IOCTL_GPIO_3to0);
// Set direction of all GPIO's to input
- WriteGpioRegister(Registers.IODir, ioDir);
+ WriteRegister(Registers.IODir, ioDir);
- initDone = true;
+ // 22.04.2024, KW: This has been moved here from the constructor. Add the handler _after_ comms init is OK.
+ if (_irq != null)
+ {
+ _irq.Changed += GpioInterruptHandler;
+ }
+
+ initGpioDone = true;
+ }
+
+ ///
+ /// Get the Interrupt Identification Register (IIR) value.
+ ///
+ ///
+ public byte GetInterruptSource()
+ {
+ byte iir = ReadRegister(Registers.IIR);
+ iir &= RegisterBits.IIR_Id0 + RegisterBits.IIR_Id1 + RegisterBits.IIR_Id2 +
+ RegisterBits.IIR_Id3 + RegisterBits.IIR_Id4 + RegisterBits.IIR_Id5;
+ return iir;
}
- int debugId = 0;
byte lastInputState = 0;
private void GpioInterruptHandler(object sender, DigitalPortResult e)
{
try
{
- //lock(this)
+ //byte iir = GetInterruptSource();
+
+ //Resolver.Log.Info($"HandleGpioInterrupt. Interrupt pin state: {e.Old?.State} {e.New.State} {e.New.Time.ToString("hh:mm:ss.fffffff")} {_irq?.State}");
+ byte state = ReadIoState();
+ byte dirMask = (byte)~ioDir; // Only look at the input pins
+ byte masked = (byte)(state & dirMask);
+ //Resolver.Log.Info($"State: {state:X2} {dirMask:X2} {masked:X2}");
+ //Resolver.Log.Info($"LastState: {lastState} NewState: {state}");
+ if (masked == lastInputState) return;
+
+ byte diff = (byte)(masked ^ lastInputState);
+ //Resolver.Log.Info($"GPIO state: {lastInputState} -> {state}");
+ for (byte i = 0; i < 8; i++)
{
- //int id = debugId++;
- //Resolver.Log.Info($"GpioInterruptHandler... {id}");
-
- //// Prioritize reading the IRQ FIFO's
- //if (_channelA?.IsOpen ?? false)
- // _channelA.OnInterruptLineChanged(sender, e);
- //if (_channelB?.IsOpen ?? false)
- // _channelB.OnInterruptLineChanged(sender, e);
-
- byte iir = GetInterruptSource();
-
- //Resolver.Log.Info($"HandleGpioInterrupt. Interrupt pin state: {e.Old?.State} {e.New.State} {e.New.Time.ToString("hh:mm:ss.fffffff")} {_irq?.State}");
- byte state = ReadGpioRegister(Registers.IOState);
- byte dirMask = (byte)~ioDir; // Only look at the input pins
- byte masked = (byte)(state & dirMask);
- //Resolver.Log.Info($"State: {state:X2} {dirMask:X2} {masked:X2}");
- //Resolver.Log.Info($"LastState: {lastState} NewState: {state}");
- if (masked == lastInputState) return;
-
- byte diff = (byte)(masked ^ lastInputState);
- //Resolver.Log.Info($"GPIO state: {lastState} -> {state}");
- for (byte i = 0; i < 8; i++)
+ if ((diff & (1 << i)) == 0) continue; // No change on this pin
+ if (gpioPorts[i] == null) continue; // No port defined for this pin
+ if (gpioPorts[i] is DigitalInputPort port)
{
- if ((diff & (1 << i)) == 0) continue; // No change on this pin
- if (gpioPorts[i] == null) continue; // No input port defined for this pin
- if (gpioPorts[i] is DigitalInputPort port)
- {
- var newState = BitHelpers.GetBitValue(state, i);
- port.Update(newState);
- }
+ var newState = BitHelpers.GetBitValue(state, i);
+ //Resolver.Log.Info($"Pin {i} new state: {newState}");
+ port.Update(newState);
}
- lastInputState = masked;
-
- //PrintAddressContent();
- //byte irqTest = ReadGpioRegister(Registers.IOIntEna);
- //if (irqTest != irqEna)
- //{
- // Resolver.Log.Info($"irqTest: {irqTest} irqEna: {irqEna}");
- // WriteGpioRegister(Registers.IOIntEna, irqEna);
- //}
-
- //Resolver.Log.Info($"DONE. {id}");
}
+ lastInputState = masked;
}
catch (Exception ex)
{
@@ -435,9 +232,8 @@ private void GpioInterruptHandler(object sender, DigitalPortResult e)
}
}
- /****************** INPUT PORTS ******************/
- private IDigitalPort[] gpioPorts = new IDigitalPort[8];
+ /****************** INPUT PORTS ******************/
///
/// Create a digital input port on a SC16IS7x2 IO expander.
@@ -464,7 +260,7 @@ public IDigitalInputPort CreateDigitalInputPort(
throw new Exception("Only ResistorMode.Disabled is supported. The GPIO ports of SC16IS7x2 does not need/support external pull-up or pull-down resistors.");
}
- var state = ReadGpioRegister(Registers.IOState);
+ var state = ReadIoState();
var initState = BitHelpers.GetBitValue(state, pinIndex);
var port = new DigitalInputPort(pin, initState);
gpioPorts[pinIndex] = port;
@@ -478,8 +274,6 @@ public IDigitalInputPort CreateDigitalInputPort(
throw new Exception("Pin is out of range");
}
- byte irqEna = 0;
-
///
/// Configure the hardware port settings on the SC16IS7x2
///
@@ -496,12 +290,11 @@ private void ConfigureInputPort(IPin pin)
if (_irq != null)
{
- irqEna = BitHelpers.SetBit(irqEna, bitIndex, true);
- SetGpioRegisterBit(Registers.IOIntEna, bitIndex);
+ SetRegisterBit(Registers.IOIntEna, bitIndex);
}
else
{
- ClearGpioRegisterBit(Registers.IOIntEna, bitIndex);
+ ClearRegisterBit(Registers.IOIntEna, bitIndex);
}
}
else
@@ -524,7 +317,7 @@ private void PreValidatedSetPortDirection(IPin pin, PortDirectionType direction)
if (newIoDir == ioDir) return;
//Resolver.Log.Info($"newIoDir: {newIoDir}");
- WriteGpioRegister(Registers.IODir, newIoDir);
+ WriteRegister(Registers.IODir, newIoDir);
ioDir = newIoDir;
}
@@ -560,7 +353,7 @@ public bool ReadPort(IPin pin)
if (portDir == true)
throw new Exception($"Cant read from port {pin.Name}. It is not configured as input");
- var gpio = ReadGpioRegister(Registers.IOState);
+ var gpio = ReadIoState();
// Return the value on that port
return BitHelpers.GetBitValue(gpio, (byte)pin.Key);
@@ -627,111 +420,105 @@ private void WriteToGpioPort(IPin pin, bool value)
{
byte bitIndex = (byte)pin.Key;
if (value)
- SetGpioRegisterBit(Registers.IOState, bitIndex);
+ SetIoStateBit(bitIndex);
else
- ClearGpioRegisterBit(Registers.IOState, bitIndex);
+ ClearIoStateBit(bitIndex);
}
+
/*********** INPUT/OUTPUT PORT HELPERS ***********/
- ///
- /// Get the Interrupt Identification Register (IIR) value.
- ///
- ///
- public byte GetInterruptSource()
+ private void SetRegisterBit(Registers register, int bitIndex)
{
- byte iir = ReadGpioRegister(Registers.IIR);
- iir &= RegisterBits.IIR_Id0 +
- RegisterBits.IIR_Id1 +
- RegisterBits.IIR_Id2 +
- RegisterBits.IIR_Id3 +
- RegisterBits.IIR_Id4 +
- RegisterBits.IIR_Id5;
- return iir;
+ if (bitIndex > 7 || bitIndex < 0) { throw new ArgumentOutOfRangeException(); }
+
+ var oldValue = ReadRegister(register);
+ byte newValue = (byte)(oldValue | ((byte)(1 << bitIndex)));
+ WriteRegister(register, newValue);
}
- ///
- /// Get the interrupt source type from the IIR register value. (Interrupt Identification Register)
- /// Ref. page 14 and 24 in datasheet. Mostly for debugging.
- /// Not sure if multiple sources can be active at the same time. If so, this will need to be modified.
- ///
- /// The IIR register value. (Interrupt Identification Register)
- ///
- public InterruptSourceType GetInterruptSourceType(byte iir)
+ private void ClearRegisterBit(Registers register, int bitIndex)
{
- iir &= (RegisterBits.IIR_Id0 +
- RegisterBits.IIR_Id1 +
- RegisterBits.IIR_Id2 +
- RegisterBits.IIR_Id3 +
- RegisterBits.IIR_Id4 +
- RegisterBits.IIR_Id5);
- if (iir == RegisterBits.IIR_IdNone) return InterruptSourceType.None;
- if (iir == RegisterBits.IIR_IdReceiverLineStatus) return InterruptSourceType.ReceiverLineStatus;
- if (iir == RegisterBits.IIR_IdRxTimeout) return InterruptSourceType.RxTimeout;
- if (iir == RegisterBits.IIR_IdRHR) return InterruptSourceType.RHR;
- if (iir == RegisterBits.IIR_IdTHR) return InterruptSourceType.THR;
- if (iir == RegisterBits.IIR_IdModemStatus) return InterruptSourceType.ModemStatus;
- if (iir == RegisterBits.IIR_IdGpioPins) return InterruptSourceType.GpioPins;
- if (iir == RegisterBits.IIR_IdXoff) return InterruptSourceType.Xoff;
- if (iir == RegisterBits.IIR_IdCtsRts) return InterruptSourceType.CtsRts;
- Resolver.Log.Info($"UNKNOWN INTERRUPT SOURCE (or combination): {iir}");
- return InterruptSourceType.Unknown;
+ if (bitIndex > 7 || bitIndex < 0) { throw new ArgumentOutOfRangeException(); }
+ var oldValue = ReadRegister(register);
+ var newValue = (byte)(oldValue & ((byte)~(1 << bitIndex)));
+ WriteRegister(register, newValue);
}
- ///
- /// Is the pin valid for this device instance
- ///
- /// The IPin to validate
- /// True if pin is valid
- protected bool IsValidPin(IPin pin) => Pins.AllPins.Contains(pin);
-
- private byte ReadGpioRegister(Registers register)
+ // Read a register that is not channel specific.
+ private byte ReadRegister(Registers register)
{
- return ReadChannelRegister(register, Channels.Both);
+ byte v = Comms.ReadRegister((byte)((byte)register << 3));
+ return v;
}
- private void WriteGpioRegister(Registers register, byte value)
+ // Write to a register that is not channel specific.
+ private void WriteRegister(Registers register, byte value)
{
- WriteChannelRegister(register, Channels.Both, value);
+ Comms.WriteRegister((byte)((byte)register << 3), value);
}
- private void SetGpioRegisterBits(Registers register, byte value)
+
+ /********************* OPTIMIZATIONS **********************/
+
+ ///
+ /// Specific method to read the IOState register. This is an optimization.
+ /// We don't want to slow down all other register reads with the validation
+ /// for this specific register.
+ ///
+ ///
+ private byte ReadIoState()
{
- SetChannelRegisterBits(register, Channels.Both, value);
+ // See page 40 of the data sheet for explanation of this
+ byte v = Comms.ReadRegister((byte)Registers.IOState << 3);
+ if (v == 0)
+ {
+ byte v2 = Comms.ReadRegister((byte)Registers.IOState << 3);
+ if (v2 != v)
+ {
+ v = v2; // Fix buggy value!
+ Resolver.Log.Info($"ReadIoState: {v2:X2} (Corrected value!)");
+ }
+ }
+ return v;
}
- private void ClearGpioRegisterBits(Registers register, byte mask)
+ private void WriteIoState(byte value)
{
- ClearChannelRegisterBits(register, Channels.Both, mask);
+ // see page 40 of the data sheet for explanation of this
+ Comms.WriteRegister((byte)Registers.IOState << 3, value);
}
- private void SetGpioRegisterBit(Registers register, int bitIndex)
+ // This is an optimization for the GPIO ports.
+ private void ClearIoStateBit(int bitIndex)
{
- SetChannelRegisterBit(register, Channels.Both, bitIndex);
+ if (bitIndex > 7 || bitIndex < 0) { throw new ArgumentOutOfRangeException(); }
+
+ var oldValue = ReadIoState();
+ byte newValue = (byte)(oldValue & ((byte)~(1 << bitIndex)));
+ if (oldValue != newValue)
+ WriteIoState(newValue);
}
- private void SetChannelRegisterBit(Registers register, Channels channel, int bitIndex)
+ // This is an optimization for the IOState register.
+ private void SetIoStateBit(int bitIndex)
{
if (bitIndex > 7 || bitIndex < 0) { throw new ArgumentOutOfRangeException(); }
- var oldValue = ReadChannelRegister(register, channel);
+ var oldValue = ReadIoState();
byte newValue = (byte)(oldValue | ((byte)(1 << bitIndex)));
- WriteChannelRegister(register, channel, newValue);
+ if (oldValue != newValue)
+ WriteIoState(newValue);
}
- private void ClearGpioRegisterBit(Registers register, int bitIndex)
- {
- ClearChannelRegisterBit(register, Channels.Both, bitIndex);
- }
+ /*********************** NICE TO HAVE METHODS ***********************/
- private void ClearChannelRegisterBit(Registers register, Channels channel, int bitIndex)
+ private byte DebugReadChannelRegister(Registers register, Channels channel)
{
- if (bitIndex > 7 || bitIndex < 0) { throw new ArgumentOutOfRangeException(); }
- var oldValue = ReadChannelRegister(register, channel);
- var newValue = (byte)(oldValue & ((byte)~(1 << bitIndex)));
- WriteChannelRegister(register, channel, newValue);
- //var check = ReadChannelRegister(register, channel);
- //Resolver.Log.Info($"ClearChannelRegisterBit: Old={oldValue} New={newValue} Check={check}");
+ // see page 40 of the data sheet for explanation of this
+ var subaddress = (byte)(((byte)register << 3) | ((byte)channel << 1));
+ byte v = Comms.ReadRegister(subaddress);
+ return v;
}
///
@@ -742,19 +529,52 @@ public void PrintAddressContent()
Resolver.Log.Info($"Register: 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F");
string a = $"Channel A: ";
for (int r = 0; r < 16; r++)
- a += $"{ReadChannelRegister((Registers)r, Channels.A):X2} ";
+ a += $"{DebugReadChannelRegister((Registers)r, Channels.A):X2} ";
Resolver.Log.Info(a);
string b = $"Channel B: ";
for (int r = 0; r < 16; r++)
- b += $"{ReadChannelRegister((Registers)r, Channels.B):X2} ";
+ b += $"{DebugReadChannelRegister((Registers)r, Channels.B):X2} ";
Resolver.Log.Info(b);
}
+ ///
+ /// Nice-to-have conversion from byte to binary string.
+ ///
+ ///
+ ///
public string ByteToBinaryString(byte b)
{
// Format the byte as a binary string and pad it with zeroes
string binaryString = $"0b{Convert.ToString(b, 2).PadLeft(8, '0')}";
return binaryString;
}
+
+ ///
+ /// Get the interrupt source type from the IIR register value. (Interrupt Identification Register)
+ /// Ref. page 14 and 24 in datasheet. Mostly for debugging.
+ /// Not sure if multiple sources can be active at the same time. If so, this will need to be modified.
+ ///
+ /// The IIR register value. (Interrupt Identification Register)
+ ///
+ public InterruptSourceType GetInterruptSourceType(byte iir)
+ {
+ iir &= (RegisterBits.IIR_Id0 +
+ RegisterBits.IIR_Id1 +
+ RegisterBits.IIR_Id2 +
+ RegisterBits.IIR_Id3 +
+ RegisterBits.IIR_Id4 +
+ RegisterBits.IIR_Id5);
+ if (iir == RegisterBits.IIR_IdNone) return InterruptSourceType.None;
+ if (iir == RegisterBits.IIR_IdReceiverLineStatus) return InterruptSourceType.ReceiverLineStatus;
+ if (iir == RegisterBits.IIR_IdRxTimeout) return InterruptSourceType.RxTimeout;
+ if (iir == RegisterBits.IIR_IdRHR) return InterruptSourceType.RHR;
+ if (iir == RegisterBits.IIR_IdTHR) return InterruptSourceType.THR;
+ if (iir == RegisterBits.IIR_IdModemStatus) return InterruptSourceType.ModemStatus;
+ if (iir == RegisterBits.IIR_IdGpioPins) return InterruptSourceType.GpioPins;
+ if (iir == RegisterBits.IIR_IdXoff) return InterruptSourceType.Xoff;
+ if (iir == RegisterBits.IIR_IdCtsRts) return InterruptSourceType.CtsRts;
+ Resolver.Log.Info($"UNKNOWN INTERRUPT SOURCE (or combination): {iir}");
+ return InterruptSourceType.Unknown;
+ }
}
-}
\ No newline at end of file
+}