Hi, thanks. There are three options:
- Wait for us to fix
- If you know how to rebuild library, go I2c.cs, line 718, change to
public TimeSpan Timeout { get ; set ; }
- Or just use the the fixed below as external class
using GHIElectronics.TinyCLR.Devices.Gpio;
using GHIElectronics.TinyCLR.Devices.I2c.Provider;
using System;
namespace GHIElectronics.TinyCLR.Devices.I2c
{
public class I2cControllerSoftwareProvider2 : II2cControllerProvider
{
private readonly bool usePullups;
private readonly GpioPin sda;
private readonly GpioPin scl;
private byte writeAddress;
private byte readAddress;
private bool start;
static string NotSupported = "Not supported.";
public struct I2cTransferResult2
{
public I2cTransferStatus Status { get; }
public int BytesWritten { get; }
public int BytesRead { get; }
public int BytesTransferred => this.BytesWritten + this.BytesRead;
internal I2cTransferResult2(I2cTransferStatus status, int bytesWritten, int bytesRead)
{
this.Status = status;
this.BytesWritten = bytesWritten;
this.BytesRead = bytesRead;
}
}
public event ErrorReceivedEventHandler ErrorReceived
{
add
{
throw new NotSupportedException(NotSupported);
}
remove => throw new NotSupportedException(NotSupported);
}
public event FrameReceivedEventHandler FrameReceived
{
add
{
throw new NotSupportedException(NotSupported);
}
remove
{
throw new NotSupportedException(NotSupported);
}
}
public I2cControllerSoftwareProvider2(GpioPin sdaPin, GpioPin sclPin) : this(sdaPin, sclPin, true) { }
public I2cControllerSoftwareProvider2(GpioPin sdaPin, GpioPin sclPin, bool usePullups)
{
this.usePullups = usePullups;
this.sda = sdaPin;
this.scl = sclPin;
}
public void Dispose()
{
this.sda.Dispose();
this.scl.Dispose();
}
public void SetActiveSettings(I2cConnectionSettings connectionSettings)
{
if (connectionSettings.AddressFormat != I2cAddressFormat.SevenBit) throw new NotSupportedException();
if (connectionSettings.Mode == I2cMode.Slave) throw new NotSupportedException();
this.writeAddress = (byte)(connectionSettings.SlaveAddress << 1);
this.readAddress = (byte)((connectionSettings.SlaveAddress << 1) | 1);
this.start = false;
this.ReleaseScl();
this.ReleaseSda();
}
public I2cTransferStatus WriteRead(byte[] writeBuffer, int writeOffset, int writeLength, byte[] readBuffer, int readOffset, int readLength, out int written, out int read)
{
written = 0;
read = 0;
try
{
var res = this.Write(writeBuffer, writeOffset, writeLength, true, readLength == 0);
written = res.BytesWritten;
read = res.BytesRead;
if (res.Status == I2cTransferStatus.FullTransfer && readLength != 0)
{
res = this.Read(readBuffer, readOffset, readLength, true, true);
written += res.BytesWritten;
read += res.BytesRead;
}
this.ReleaseScl();
this.ReleaseSda();
return res.Status;
}
catch (I2cClockStretchTimeoutException)
{
return I2cTransferStatus.ClockStretchTimeout;
}
}
private I2cTransferResult2 Write(byte[] buffer, int offset, int length, bool sendStart, bool sendStop)
{
if (!this.Send(sendStart, length == 0, this.writeAddress))
return new I2cTransferResult2(I2cTransferStatus.SlaveAddressNotAcknowledged, 0, 0);
for (var i = 0; i < length; i++)
if (!this.Send(false, i == length - 1 && sendStop, buffer[i + offset]))
return new I2cTransferResult2(I2cTransferStatus.PartialTransfer, i, 0);
return new I2cTransferResult2(I2cTransferStatus.FullTransfer, length, 0);
}
private I2cTransferResult2 Read(byte[] buffer, int offset, int length, bool sendStart, bool sendStop)
{
if (!this.Send(sendStart, length == 0, this.readAddress))
return new I2cTransferResult2(I2cTransferStatus.SlaveAddressNotAcknowledged, 0, 0);
for (var i = 0; i < length; i++)
if (!this.Receive(i < length - 1, i == length - 1 && sendStop, out buffer[i + offset]))
return new I2cTransferResult2(I2cTransferStatus.PartialTransfer, 0, i);
return new I2cTransferResult2(I2cTransferStatus.FullTransfer, 0, length);
}
private void ClearScl()
{
this.scl.SetDriveMode(GpioPinDriveMode.Output);
this.scl.Write(GpioPinValue.Low);
}
private void ClearSda()
{
this.sda.SetDriveMode(GpioPinDriveMode.Output);
this.sda.Write(GpioPinValue.Low);
}
private void ReleaseScl()
{
this.scl.SetDriveMode(this.usePullups ? GpioPinDriveMode.InputPullUp : GpioPinDriveMode.Input);
this.ReadScl();
}
private void ReleaseSda()
{
this.sda.SetDriveMode(this.usePullups ? GpioPinDriveMode.InputPullUp : GpioPinDriveMode.Input);
this.ReadSda();
}
private bool ReadScl()
{
this.scl.SetDriveMode(this.usePullups ? GpioPinDriveMode.InputPullUp : GpioPinDriveMode.Input);
return this.scl.Read() == GpioPinValue.High;
}
private bool ReadSda()
{
this.sda.SetDriveMode(this.usePullups ? GpioPinDriveMode.InputPullUp : GpioPinDriveMode.Input);
return this.sda.Read() == GpioPinValue.High;
}
private void WaitForScl()
{
const long TimeoutInTicks = 1000 * 1000 * 10; // Timeout: 1 second
const long DelayInTicks = (1000000 / 2000) * 10; // Max frequency: 2KHz
var currentTicks = DateTime.Now.Ticks;
var timeout = true;
while (DateTime.Now.Ticks - currentTicks < DelayInTicks / 2) ;
while (timeout && DateTime.Now.Ticks - currentTicks < TimeoutInTicks)
{
if (this.ReadScl()) timeout = false;
}
if (timeout)
throw new I2cClockStretchTimeoutException();
var periodClockInTicks = DateTime.Now.Ticks - currentTicks;
currentTicks = DateTime.Now.Ticks;
while (DateTime.Now.Ticks - currentTicks < periodClockInTicks) ;
}
private bool WriteBit(bool bit)
{
if (bit)
this.ReleaseSda();
else
this.ClearSda();
this.WaitForScl();
if (bit && !this.ReadSda())
return false;
this.ClearScl();
return true;
}
private bool ReadBit()
{
this.ReleaseSda();
this.WaitForScl();
var bit = this.ReadSda();
this.ClearScl();
return bit;
}
private bool SendStart()
{
if (this.start)
{
this.ReleaseSda();
this.WaitForScl();
}
if (!this.ReadSda())
return false;
this.ClearSda();
this.ClearScl();
this.start = true;
return true;
}
private bool SendStop()
{
this.ClearSda();
this.WaitForScl();
if (!this.ReadSda())
return false;
this.start = false;
return true;
}
private bool Send(bool sendStart, bool sendStop, byte data)
{
if (sendStart)
this.SendStart();
for (var bit = 0; bit < 8; bit++)
{
this.WriteBit((data & 0x80) != 0);
data <<= 1;
}
var nack = this.ReadBit();
if (sendStop)
this.SendStop();
return !nack;
}
private bool Receive(bool sendAck, bool sendStop, out byte data)
{
data = 0;
for (var bit = 0; bit < 8; bit++)
data = (byte)((data << 1) | (this.ReadBit() ? 1 : 0));
var res = this.WriteBit(!sendAck);
return (!sendStop || this.SendStop()) && res;
}
private class I2cClockStretchTimeoutException : Exception
{
}
public int WriteBufferSize { get => throw new NotSupportedException(NotSupported); set => throw new NotSupportedException(NotSupported); }
public int ReadBufferSize { get => throw new NotSupportedException(NotSupported); set => throw new NotSupportedException(NotSupported); }
public int BytesToWrite => throw new NotSupportedException(NotSupported);
public int BytesToRead => throw new NotSupportedException(NotSupported);
public TimeSpan Timeout { get ; set ; }
public void ClearWriteBuffer() => throw new NotSupportedException(NotSupported);
public void ClearReadBuffer() => throw new NotSupportedException(NotSupported);
}
}
usage: same, but here just in case.
var sda = GpioController.GetDefault().OpenPin(sdaPin);
var scl = GpioController.GetDefault().OpenPin(sclPin);
var provider = new I2cControllerSoftwareProvider2(sda, scl, true);
var i2cControllers = I2cController.FromProvider(provider);