Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/devices/Device-Index.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@
* [STUSB4500 - Autonomous USB-C PD controller for Power Sinks / UFP](StUsb4500/README.md)
* [System.Device.Model - attributes for device bindings](System.Device.Model/README.md)
* [TCS3472x Sensors](Tcs3472x/README.md)
* [TCA954X Multiplexer](Tca954x/README.md)
* [TLC1543 - 10-bit ADC with 11 input channels](Tlc1543/README.md)
* [TM1637 - Segment Display](Tm1637/README.md)
* [TSL256x - Illuminance sensor](Tsl256x/README.md)
Expand Down
2 changes: 1 addition & 1 deletion src/devices/Pca95x4/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Pca95x4 - I2C GPIO Expander

The PCA95x4 provides 8 bits of General Purpose parallel Input/Output (GPIO) expansion for I2C-bus applications.
The PCA95x4 provides 8 bits of General Purpose parallel Input/Output (GPIO) expansion for I2C-bus applications. Not to be confused with the PCA954x, which is a group of I2C bus multiplexers.

## Documentation

Expand Down
1 change: 1 addition & 0 deletions src/devices/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ Our vision: the majority of .NET bindings are written completely in .NET languag
* [Mcp23xxx - I/O Expander device family](Mcp23xxx/README.md)
* [NXP/TI PCx857x](Pcx857x/README.md)
* [Pca95x4 - I2C GPIO Expander](Pca95x4/README.md)
* [TCA9548A - TCA9548A Low-Voltage 8-Channel I2C Switch with Reset](Tca954x/README.md)

### CAN BUS libraries/modules

Expand Down
60 changes: 60 additions & 0 deletions src/devices/Tca954x/MultiplexerChannel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;

namespace Iot.Device.Tca954x
{
/// <summary>
/// Available channels
/// </summary>
[Flags]
public enum MultiplexerChannel : byte
{
/// <summary>
/// No channel is selected
/// </summary>
None = 0,

/// <summary>
/// Channel 0 Byte (2^0 = 1)
/// </summary>
Channel0 = 0x01,

/// <summary>
/// Channel 1 Byte (2^1 = 2)
/// </summary>
Channel1 = 0x02,

/// <summary>
/// Channel 2 Byte (2^2 = 4)
/// </summary>
Channel2 = 0x04,

/// <summary>
/// Channel 3 Byte (2^3 = 8)
/// </summary>
Channel3 = 0x08,

/// <summary>
/// Channel 4 Byte (2^4 = 16)
/// </summary>
Channel4 = 0x10,

/// <summary>
/// Channel 5 Byte (2^5 = 32)
/// </summary>
Channel5 = 0x20,

/// <summary>
/// Channel 6 Byte (2^6 = 64)
/// </summary>
Channel6 = 0x40,

/// <summary>
/// Channel 7 Byte (2^7 = 128)
/// </summary>
Channel7 = 0x80
}

}
Binary file added src/devices/Tca954x/PossibleMuxAddress.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
115 changes: 115 additions & 0 deletions src/devices/Tca954x/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# TCA954X - TCA954X Low-Voltage Multi-Channel I2C Switch with Reset

The TCA954X/PCA954X group of devices is a set of I2C switches. Several types are available, with 2, 4 or 8 channels, and different voltage specifications. The first letter is sometimes also "P". Not to be confused with PCA95x4, which is a set of GPIO expanders.

The TCA9548A device has eight bidirectional translating switches that can be controlled through the I2C bus. The SCL/SDA upstream pair fans out to eight downstream pairs, or channels.

All operations for basic usage has been developed. If needed, RESET operation can also be implemented.

## Documentation

![TCA9548A](./TCA9548A.jpg)

TCA9548A [datasheet](https://www.ti.com/lit/ds/symlink/tca9548a.pdf)

You will find this device as ["Adafruit TCA9548A 1-to-8 I2C Multiplexer Breakout"](https://learn.adafruit.com/adafruit-tca9548a-1-to-8-i2c-multiplexer-breakout)

A list of [different variants](https://www.ti.com/interface/i2c/switches-and-multiplexers/products.html?keyMatch=TCA9548) is available on the Texas Instruments Website.

## Usage

Create a ```Tca9548A``` class and pass the I2C device. The default I2C address is provided in the class.

```csharp
Console.WriteLine("Hello TCA9548A!");
Comment thread
pgrawehr marked this conversation as resolved.
var bus = I2cBus.Create(1);
Tca9548A tca9548a = new Tca9548A(bus.CreateDevice(Tca9548A.DefaultI2cAddress), bus);

// Get all connected I2C interfaces
foreach (var channelBuses in tca9548a)
{
var deviceAddress = ((Tca9548AChannelBus)channelBuses).PerformBusScan();
foreach (var device in deviceAddress)
{
if (device == Bno055Sensor.DefaultI2cAddress || device == Bno055Sensor.SecondI2cAddress)
{
Bno055Sensor bno055Sensor = new Bno055Sensor(channelBuses.CreateDevice(device));
Console.WriteLine($"Id: {bno055Sensor.Info.ChipId}, AccId: {bno055Sensor.Info.AcceleratorId}, GyroId: {bno055Sensor.Info.GyroscopeId}, MagId: {bno055Sensor.Info.MagnetometerId}");
Console.WriteLine($"Firmware version: {bno055Sensor.Info.FirmwareVersion}, Bootloader: {bno055Sensor.Info.BootloaderVersion}");
Console.WriteLine($"Temperature source: {bno055Sensor.TemperatureSource}, Operation mode: {bno055Sensor.OperationMode}, Units: {bno055Sensor.Units}");
Console.WriteLine($"Powermode: {bno055Sensor.PowerMode}");
var calibrationStatus = bno055Sensor.GetCalibrationStatus();
Console.WriteLine($"Calibration Status : {calibrationStatus}");
var magneto = bno055Sensor.Magnetometer;
Console.WriteLine($"Magnetometer X: {magneto.X} Y: {magneto.Y} Z: {magneto.Z}");
var gyro = bno055Sensor.Gyroscope;
Console.WriteLine($"Gyroscope X: {gyro.X} Y: {gyro.Y} Z: {gyro.Z}");
var accele = bno055Sensor.Accelerometer;
Console.WriteLine($"Acceleration X: {accele.X} Y: {accele.Y} Z: {accele.Z}");
var orien = bno055Sensor.Orientation;
Console.WriteLine($"Orientation Heading: {orien.X} Roll: {orien.Y} Pitch: {orien.Z}");
var line = bno055Sensor.LinearAcceleration;
Console.WriteLine($"Linear acceleration X: {line.X} Y: {line.Y} Z: {line.Z}");
var gravity = bno055Sensor.Gravity;
Console.WriteLine($"Gravity X: {gravity.X} Y: {gravity.Y} Z: {gravity.Z}");
var qua = bno055Sensor.Quaternion;
Console.WriteLine($"Quaternion X: {qua.X} Y: {qua.Y} Z: {qua.Z} W: {qua.W}");
var temp = bno055Sensor.Temperature.DegreesCelsius;
Console.WriteLine($"Temperature: {temp} °C");
}
else if (device == Bmp180.DefaultI2cAddress)
{
using Bmp180 i2cBmp280 = new(channelBuses.CreateDevice(device));
// set samplings
i2cBmp280.SetSampling(Sampling.Standard);
// read values
Temperature tempValue = i2cBmp280.ReadTemperature();
Console.WriteLine($"Temperature: {tempValue.DegreesCelsius:0.#}\u00B0C");
Pressure preValue = i2cBmp280.ReadPressure();
Console.WriteLine($"Pressure: {preValue.Hectopascals:0.##}hPa");

// Note that if you already have the pressure value and the temperature, you could also calculate altitude by
// calling WeatherHelper.CalculateAltitude(preValue, Pressure.MeanSeaLevel, tempValue) which would be more performant.
Length altValue = i2cBmp280.ReadAltitude(WeatherHelper.MeanSeaLevel);

Console.WriteLine($"Altitude: {altValue:0.##}m");
Thread.Sleep(1000);

// set higher sampling
i2cBmp280.SetSampling(Sampling.UltraLowPower);

// read values
tempValue = i2cBmp280.ReadTemperature();
Console.WriteLine($"Temperature: {tempValue.DegreesCelsius:0.#}\u00B0C");
preValue = i2cBmp280.ReadPressure();
Console.WriteLine($"Pressure: {preValue.Hectopascals:0.##}hPa");

// Note that if you already have the pressure value and the temperature, you could also calculate altitude by
// calling WeatherHelper.CalculateAltitude(preValue, Pressure.MeanSeaLevel, tempValue) which would be more performant.
altValue = i2cBmp280.ReadAltitude(WeatherHelper.MeanSeaLevel);
Console.WriteLine($"Altitude: {altValue:0.##}m");
}

Thread.Sleep(1000);
}

}
```

### Sample wiring

**Important** to understand:
If you are getting lower data rate from sensor connected via Multiplexer on Raspberry Pi, you need to adjust the bus speed.

This example uses the I2C bus 1 with GPIO 2 and 3 on raspberry pi and gathers data from to BNO055 sensors and 4 BMP180 sensors over TCA9548A Mux

![Wiring sample](TCA9548A_Connections.png)

## Possible TCA9548A Addresses

![Possible Addresses](PossibleMuxAddress.png)

## TCA9546A - Low Voltage 4-Channel I2C

TCA9546A [datasheet](https://www.ti.com/lit/ds/symlink/tca9546a.pdf)
You can use the same class to control the 4-Channel Multiplexer also.
Binary file added src/devices/Tca954x/TCA9548A.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/devices/Tca954x/TCA9548A_Connections.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/devices/Tca954x/TCA9548A_usage.fzz
Binary file not shown.
197 changes: 197 additions & 0 deletions src/devices/Tca954x/Tca9548A.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Device.I2c;
using System.Device.Model;
using System.IO;

namespace Iot.Device.Tca954x
{
/// <summary>
/// Tca9548A - 8-Channel I2C Switch/Multiplexer
/// </summary>
[Interface("Tca9548A - 8-Channel I2C Switch/Multiplexer")]
public class Tca9548A : IList<I2cBus>, IDisposable
{
private readonly bool _shouldDispose;
private readonly List<Tca9548AChannelBus> _channelBuses = new List<Tca9548AChannelBus>();
private MultiplexerChannel? _activeChannels;

/// <summary>
/// The default I2C Address, page 15 of the main documentation
/// https://www.ti.com/lit/ds/symlink/tca9548a.pdf
/// </summary>
public const byte DefaultI2cAddress = 0x70;

/// <summary>
/// Array of all possible Multiplexer Channels
/// </summary>
private static readonly MultiplexerChannel[] DeviceChannels = new MultiplexerChannel[]
{
MultiplexerChannel.Channel0, MultiplexerChannel.Channel1, MultiplexerChannel.Channel2, MultiplexerChannel.Channel3, MultiplexerChannel.Channel4,
MultiplexerChannel.Channel5, MultiplexerChannel.Channel6, MultiplexerChannel.Channel7
};

private I2cDevice _i2CDevice;

/// <summary>
/// Creates a Multiplexer Instance
/// </summary>
/// <param name="i2cDevice">The I2C Device of the Mux itself</param>
/// <param name="mainBus">The bus the mux is connected to</param>
/// <param name="shouldDispose">true to dispose the I2C device at dispose</param>
/// <exception cref="ArgumentNullException">Exception thrown if I2C device is null</exception>
public Tca9548A(I2cDevice i2cDevice, I2cBus mainBus, bool shouldDispose = true)
{
_i2CDevice = i2cDevice ?? throw new ArgumentNullException(nameof(i2cDevice));
_shouldDispose = shouldDispose;
_activeChannels = null; // We don't know the state of the multiplexer
foreach (var channel in DeviceChannels)
{
_channelBuses.Add(new Tca9548AChannelBus(this, mainBus, channel));
}
}

/// <summary>
/// Gets Channel busses of the multiplexer
/// </summary>
/// <param name="index">channel number</param>
/// <returns></returns>
public I2cBus this[int index] => _channelBuses[index];

/// <summary>
/// Select a group of multiplexer channels.
/// </summary>
/// <param name="multiplexChannels">The channels to select</param>
/// <remarks>
/// In most cases, a single channel will be selected at a time, but it is possible to write to several channels at once. Reading
/// from multiple channels at once will result in undefined behavior.
/// </remarks>
public void SelectChannel(MultiplexerChannel multiplexChannels)
{
if (TryGetSelectedChannel(out var channel) && channel != multiplexChannels)
{
_i2CDevice.WriteByte(Convert.ToByte(multiplexChannels));
_activeChannels = multiplexChannels;
}
}

/// <summary>
/// Try getting the selected channel on Multiplexer
/// </summary>
/// <param name="selectedChannel"> selected Multiplexer Channel</param>
/// <returns> true if able to retrieve selected channel</returns>
public bool TryGetSelectedChannel(out MultiplexerChannel selectedChannel)
{
try
{
if (_activeChannels.HasValue)
{
selectedChannel = _activeChannels.Value;
return true;
}

var channel = _i2CDevice.ReadByte();
if (Enum.IsDefined(typeof(MultiplexerChannel), channel))
{
selectedChannel = (MultiplexerChannel)channel;
return true;
}
else
{
selectedChannel = MultiplexerChannel.Channel0;
return false;
}
}
catch (Exception)
{
selectedChannel = MultiplexerChannel.Channel0;
return false;
}
}

/// <inheritdoc/>
public void Dispose()
{
if (_shouldDispose)
{
_i2CDevice?.Dispose();
}

_i2CDevice = null!;
}

/// <inheritdoc/>
public int Count => _channelBuses.Count;

/// <inheritdoc/>
public bool IsReadOnly => true;

I2cBus IList<I2cBus>.this[int index] { get => _channelBuses[index]; set => _channelBuses[index] = (Tca9548AChannelBus)value; }

/// <inheritdoc/>
public int IndexOf(I2cBus item)
{
return _channelBuses.IndexOf((Tca9548AChannelBus)item);
}

/// <inheritdoc/>
public void Insert(int index, I2cBus item)
{
_channelBuses.Insert(index, (Tca9548AChannelBus)item);
}

/// <inheritdoc/>
public void RemoveAt(int index)
{
_channelBuses.RemoveAt(index);
}

/// <inheritdoc/>
public void Add(I2cBus item)
{
_channelBuses.Add((Tca9548AChannelBus)item);
}

/// <inheritdoc/>
public void Clear()
{
_channelBuses.Clear();
}

/// <inheritdoc/>
public bool Contains(I2cBus item)
{
return _channelBuses.Contains((Tca9548AChannelBus)item);
}

/// <inheritdoc/>
public void CopyTo(I2cBus[] array, int arrayIndex)
{
_channelBuses.CopyTo((Tca9548AChannelBus[])array, arrayIndex);

}

/// <inheritdoc/>
public bool Remove(I2cBus item)
{
return _channelBuses.Remove((Tca9548AChannelBus)item);
}

/// <inheritdoc/>
public IEnumerator<I2cBus> GetEnumerator()
{
return _channelBuses.GetEnumerator();
}

/// <inheritdoc/>
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}

}
Loading