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 +}