提交 05318ee1 编写于 作者: M Máté Kullai 提交者: Krzysztof Wicher

Mcp3428 (#357)

* First working prototype

* And now after saving it, this one really works

* Sample project

* Document cleanup

* Separated statics into Helper class

* Async implementation

* Other two parts of the MCP342x family

* Readme

* Fix build error

* Removed C# 8 dependency for pull request

* Done fritzing, never again

* Input polarity fixed + readme

* File headers

* Formatting

* Fix header + removed unused delegate

* WIP: No separate class for async

* Removed using static

* Removed Console dependency,
Renamed AddressFromPins

* Removed unused interface

* Merged partial class

* Format

* Review findings

* Created base class for devices

* Review findings
上级 26b722c2
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace Iot.Device.Mcp3428
{
/// <summary>
/// Possible gain values of the ADC
/// </summary>
public enum AdcGain : byte { X1 = 0, X2 = 1, X4 = 2, X8 = 3 }
/// <summary>
/// Possible operation modes of the ADC
/// </summary>
[System.Flags]
public enum AdcMode : byte { OneShot = 0, Continuous = 16 }
/// <summary>
/// Possible connection states for the address pins
/// </summary>
public enum PinState : byte { Low = 0, High = 1, Floating = 2 }
/// <summary>
/// Possible resolution values of the ADC
/// </summary>
[System.Flags]
public enum AdcResolution : byte { Bit12 = 0, Bit14 = 4, Bit16 = 8 }
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace Iot.Device.Mcp3428
{
public readonly struct ConversionResult
{
public ConversionResult(byte channel, short rawData, AdcResolution resolution)
{
Channel = channel;
RawValue = rawData;
VoltageDivisor = Helpers.LsbDivisor(resolution);
}
/// <summary>
/// ID of the measuring channel.
/// </summary>
/// <value>The channel.</value>
public byte Channel { get; }
/// <summary>
/// Raw measurement data. Has to be scaled based on the measurement resolution to get voltage.
/// </summary>
/// <value>The raw data.</value>
public short RawValue { get; }
/// <summary>
/// Divisor to scale raw data.
/// </summary>
/// <value>The divisor.</value>
public ushort VoltageDivisor { get; }
/// <summary>
/// Accuracy of the voltage measurement
/// </summary>
/// <value>The LSB value.</value>
public double Accuracy => (double)1 / VoltageDivisor;
/// <summary>
/// Gets the voltage.
/// </summary>
/// <value>The voltage.</value>
/// <autogeneratedoc />
public double Voltage => (double)RawValue / VoltageDivisor;
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
namespace Iot.Device.Mcp3428
{
internal static class Helpers
{
/// <summary>
/// Gets the voltage value corresponding to the least significant bit based on resolution.
/// </summary>
/// <param name="res">The resolution.</param>
/// <returns>System.Double.</returns>
/// <exception cref="ArgumentOutOfRangeException">res - null</exception>
public static double LSBValue(AdcResolution res)
{
switch (res)
{
case AdcResolution.Bit12:
return 1e-3;
case AdcResolution.Bit14:
return 250e-6;
case AdcResolution.Bit16:
return 62.5e-6;
default:
throw new ArgumentOutOfRangeException(nameof(res), res, null);
}
}
/// <summary>
/// Gets the divisor to scale raw data based on resolution. = 1/LSB
/// </summary>
/// <param name="res">The resolution.</param>
/// <returns>System.UInt16.</returns>
/// <exception cref="ArgumentOutOfRangeException">res - null</exception>
public static ushort LsbDivisor(AdcResolution res)
{
switch (res)
{
case AdcResolution.Bit12:
return 1000;
case AdcResolution.Bit14:
return 4000;
case AdcResolution.Bit16:
return 16000;
default:
throw new ArgumentOutOfRangeException(nameof(res), res, null);
}
}
/// <summary>
/// Determine device I2C address based on the configuration pin states. Based on documentation TABLE 5-3-
/// </summary>
/// <param name="adr0">The adr0 pin state</param>
/// <param name="adr1">The adr1 pin state</param>
/// <returns>System.Int32.</returns>
public static byte I2CAddressFromPins(PinState adr0, PinState adr1)
{
byte addr = 0b1101000; // Base value from doc
var idx = (byte)adr0 << 4 + (byte)adr1;
switch (idx)
{
case 0:
case 0x22:
break;
case 0x02:
addr += 1;
break;
case 0x01:
addr += 2;
break;
case 0x10:
addr += 4;
break;
case 0x12:
addr += 5;
break;
case 0x11:
addr += 6;
break;
case 0x20:
addr += 3;
break;
case 0x21:
addr += 7;
break;
default:
throw new ArgumentException("Invalid combination");
}
return addr;
}
public static byte SetChannelBits(byte configByte, int channel)
{
if (channel > 3 || channel < 0)
throw new ArgumentException("Channel numbers are only valid 0 to 3", nameof(channel));
return (byte)((configByte & ~Helpers.Masks.ChannelMask) | ((byte)channel << 5));
}
public static byte SetGainBits(byte configByte, AdcGain gain)
{
return (byte)((configByte & ~Helpers.Masks.GainMask) | (byte)gain);
}
public static byte SetModeBit(byte configByte, AdcMode mode)
{
return (byte)((configByte & ~Helpers.Masks.ModeMask) | (byte)mode);
}
public static byte SetReadyBit(byte configByte, bool ready)
{
return (byte)(ready ? configByte & ~Helpers.Masks.ReadyMask : configByte | Helpers.Masks.ReadyMask);
}
public static byte SetResolutionBits(byte configByte, AdcResolution resolution)
{
return (byte)((configByte & ~Helpers.Masks.ResolutionMask) | (byte)resolution);
}
public static int UpdateFrequency(AdcResolution res)
{
switch (res)
{
case AdcResolution.Bit12:
return 240;
case AdcResolution.Bit14:
return 60;
case AdcResolution.Bit16:
return 15;
default:
throw new ArgumentOutOfRangeException(nameof(res), res, null);
}
}
// From datasheet 5.2
public static class Masks
{
public const byte ChannelMask = 0b01100000;
public const byte GainMask = 0b00000011;
public const byte ModeMask = 0b00010000;
public const byte ReadyMask = 0b10000000;
public const byte ResolutionMask = 0b00001100;
}
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Device.I2c;
namespace Iot.Device.Mcp3428
{
public class Mcp3426 : Mcp342x
{
/// <summary>
/// The number of channels
/// </summary>
/// <autogeneratedoc />
private const int NumChannels = 2;
public const int I2CAddress = 0x68;
/// <inheritdoc />
public Mcp3426(I2cDevice i2CDevice) : base(i2CDevice, NumChannels)
{
}
/// <inheritdoc />
public Mcp3426(I2cDevice i2CDevice, AdcMode mode = AdcMode.Continuous, AdcResolution resolution = AdcResolution.Bit12, AdcGain pgaGain = AdcGain.X1) : this(i2CDevice)
{
SetConfig(0, mode: mode, resolution: resolution, pgaGain: pgaGain);
}
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Device.I2c;
namespace Iot.Device.Mcp3428
{
public class Mcp3427 : Mcp342x
{
/// <summary>
/// The number of channels
/// </summary>
/// <autogeneratedoc />
private const int NumChannels = 2;
/// <inheritdoc />
public Mcp3427(I2cDevice i2CDevice) : base(i2CDevice, NumChannels)
{
}
/// <inheritdoc />
public Mcp3427(I2cDevice i2CDevice, AdcMode mode = AdcMode.Continuous, AdcResolution resolution = AdcResolution.Bit12, AdcGain pgaGain = AdcGain.X1) : this(i2CDevice)
{
SetConfig(0, mode: mode, resolution: resolution, pgaGain: pgaGain);
}
/// <summary>
/// Determine device I2C address based on the configuration pin states.
/// </summary>
/// <param name="adr0">The adr0 pin state</param>
/// <param name="adr1">The adr1 pin state</param>
/// <returns>System.Int32.</returns>
public static int I2CAddressFromPins(PinState adr0, PinState adr1) { return Helpers.I2CAddressFromPins(adr0, adr1); }
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Device.I2c;
namespace Iot.Device.Mcp3428
{
public class Mcp3428 : Mcp342x
{
/// <summary>
/// The number of channels
/// </summary>
/// <autogeneratedoc />
private const int NumChannels = 4;
/// <inheritdoc />
public Mcp3428(I2cDevice i2CDevice) : base(i2CDevice, NumChannels)
{
}
/// <inheritdoc />
public Mcp3428(I2cDevice i2CDevice, AdcMode mode = AdcMode.Continuous, AdcResolution resolution = AdcResolution.Bit12, AdcGain pgaGain = AdcGain.X1) : this(i2CDevice)
{
SetConfig(0, mode: mode, resolution: resolution, pgaGain: pgaGain);
}
/// <summary>
/// Determine device I2C address based on the configuration pin states.
/// </summary>
/// <param name="adr0">The adr0 pin state</param>
/// <param name="adr1">The adr1 pin state</param>
/// <returns>System.Int32.</returns>
public static int I2CAddressFromPins(PinState adr0, PinState adr1) { return Helpers.I2CAddressFromPins(adr0, adr1); }
}
}
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<!--Disabling default items so samples source won't get build by the main library-->
<EnableDefaultItems>false</EnableDefaultItems>
</PropertyGroup>
<ItemGroup>
<Compile Include="*.cs" />
<None Include="README.md" />
<PackageReference Include="System.Device.Gpio" Version="$(SystemDeviceGpioPackageVersion)" />
</ItemGroup>
</Project>
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.Device.I2c;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace Iot.Device.Mcp3428
{
public abstract class Mcp342x : IDisposable
{
/// <summary>
/// Protected constructor to easily generate MCP3426/7 devices whose only difference is channel count and I2C address
/// </summary>
/// <param name="i2CDevice">The i2 c device.</param>
/// <param name="channels">The channels.</param>
/// <autogeneratedoc />
protected Mcp342x(I2cDevice i2CDevice, int channels)
{
_i2cDevice = i2CDevice;
ChannelCount = channels;
ReadValue(); // Don't like this in constructor, makes sure props are valid
}
/// <summary>
/// Gets the last transmitted bytes. Debug function
/// </summary>
/// <value>The last bytes.</value>
/// <autogeneratedoc />
public ReadOnlyMemory<byte> LastBytes => _readBuffer;
/// <summary>
/// Channel most recently read
/// </summary>
/// <value>The last channel.</value>
/// <autogeneratedoc />
public byte LastChannel => LastConversion.Channel;
public AdcMode Mode
{
get => _mode;
set
{
WriteConfig(Helpers.SetModeBit(LastConfigByte, value));
_mode = value;
}
}
/// <summary>
/// Gets or sets the input gain.
/// </summary>
/// <value>The pga gain.</value>
/// <autogeneratedoc />
public AdcGain InputGain
{
get => _pgaGain;
set
{
WriteConfig(Helpers.SetGainBits(LastConfigByte, value));
_pgaGain = value;
}
}
/// <summary>
/// Gets or sets the bit resolution of the result.
/// </summary>
/// <value>The resolution.</value>
/// <autogeneratedoc />
public AdcResolution Resolution
{
get => _resolution;
set
{
WriteConfig(Helpers.SetResolutionBits(LastConfigByte, value));
_resolution = value;
}
}
public void Dispose()
{
_i2cDevice?.Dispose();
}
/// <summary>
/// Reads the channel.
/// </summary>
/// <param name="channel">The channel.</param>
/// <returns>System.Double.</returns>
/// <autogeneratedoc />
public double ReadChannel(int channel)
{
return ReadValue(channel);
}
private readonly I2cDevice _i2cDevice;
private readonly byte[] _readBuffer = new byte[3];
private bool _isReadyBit = false;
private byte _lastChannel = 0xFF;
private ConversionResult _lastConversion;
private AdcMode _mode = AdcMode.Continuous;
//Config params
private AdcGain _pgaGain = AdcGain.X1;
private AdcResolution _resolution = AdcResolution.Bit12;
private byte LastConfigByte => _readBuffer[2];
/// <summary>
/// Initiates One-shot reading and waits for the conversion to finish.
/// </summary>
/// <param name="channel">The channel.</param>
/// <exception cref="System.IO.IOException">
/// Device is not in One-Shot mode
/// or
/// ADC Conversion was not ready after {tries}
/// </exception>
/// <autogeneratedoc />
protected void OneShotRead(int channel = -1)
{
if (Mode != AdcMode.OneShot)
throw new IOException("Device is not in One-Shot mode");
_isReadyBit = false;
var conf = Helpers.SetReadyBit(LastConfigByte, false);
if (channel >= 0 && channel != LastChannel)
{
conf = Helpers.SetChannelBits(conf, channel);
}
WriteConfig(conf);
using (var source = new CancellationTokenSource(TimeSpan.FromMilliseconds(WaitTime * 5)))
{
WaitForConversion(TimeSpan.FromMilliseconds(WaitTime), cancellationToken: source.Token);
if (!_isReadyBit)
{
throw new IOException($"ADC Conversion was not ready after {WaitTime * 5} ms.");
}
}
}
protected void WaitForConversion(TimeSpan? waitSpan = null, Action<int> progressCallback = null, CancellationToken cancellationToken = default)
{
waitSpan = waitSpan ?? TimeSpan.FromMilliseconds(WaitTime);
var allms = 0;
_isReadyBit = false;
while (!_isReadyBit && !cancellationToken.IsCancellationRequested)
{
_i2cDevice.Read(_readBuffer);
ReadConfigByte(LastConfigByte);
if (_isReadyBit)
break;
Thread.Sleep(waitSpan.Value);
cancellationToken.ThrowIfCancellationRequested();
allms += (int)(waitSpan.Value.TotalMilliseconds);
progressCallback?.Invoke(allms);
}
cancellationToken.ThrowIfCancellationRequested();
}
protected void ReadConfigByte(byte config)
{
_isReadyBit = (config & Helpers.Masks.ReadyMask) == 0; // Negated bit
_lastChannel = (byte)((config & Helpers.Masks.ChannelMask) >> 5);
_mode = (AdcMode)(config & Helpers.Masks.ModeMask);
_pgaGain = (AdcGain)(config & Helpers.Masks.GainMask);
_resolution = (AdcResolution)(config & Helpers.Masks.ResolutionMask);
}
protected double ReadValue(int channel = -1)
{
if (Mode == AdcMode.OneShot)
{
OneShotRead(channel);
}
else
{
if (channel > 0 && channel != LastChannel)
{
var conf = Helpers.SetChannelBits(LastConfigByte, channel);
WriteConfig(conf);
}
using (var source = new CancellationTokenSource(TimeSpan.FromMilliseconds(WaitTime * 5)))
{
WaitForConversion(TimeSpan.FromMilliseconds(WaitTime / 5), cancellationToken: source.Token);
}
}
var value = BinaryPrimitives.ReadInt16BigEndian(_readBuffer.AsSpan().Slice(0, 2));
LastConversion = new ConversionResult(_lastChannel, value, Resolution);
return LastConversion.Voltage;
}
/// <summary>
/// Write configuration register and read back value
/// </summary>
/// <param name="channel">The channel.</param>
/// <param name="mode">The mode.</param>
/// <param name="resolution">The resolution.</param>
/// <param name="pgaGain">The pga gain.</param>
/// <param name="errorList">List to write errors on failure</param>
/// <returns><c>true</c> if all values are set correctly, <c>false</c> otherwise.</returns>
/// <exception cref="ArgumentOutOfRangeException">channel</exception>
protected bool SetConfig(int channel = 0, AdcMode mode = AdcMode.Continuous,
AdcResolution resolution = AdcResolution.Bit12, AdcGain pgaGain = AdcGain.X1, IList<string> errorList = null)
{
if (channel < 0 || channel > ChannelCount - 1)
throw new ArgumentOutOfRangeException(nameof(channel));
byte conf = 0;
var ok = true;
conf = Helpers.SetModeBit(conf, mode);
conf = Helpers.SetChannelBits(conf, channel);
conf = Helpers.SetGainBits(conf, pgaGain);
conf = Helpers.SetResolutionBits(conf, resolution);
conf = Helpers.SetReadyBit(conf, false);
_i2cDevice.WriteByte(conf);
_i2cDevice.Read(_readBuffer);
ReadConfigByte(LastConfigByte);
if (_lastChannel != channel)
{
errorList?.Add($"Channel update failed from {_lastChannel} to {channel}");
ok = false;
}
if (Resolution != resolution)
{
errorList?.Add($"Resolution update failed from {Resolution} to {resolution}");
ok = false;
}
if (mode != Mode)
{
errorList?.Add($"Mode update failed from {Mode} to {mode}");
ok = false;
}
if (InputGain != pgaGain)
{
errorList?.Add($"PGAGain update failed from {InputGain} to {pgaGain}");
ok = false;
}
if (!ok)
{ // Only use console on error
errorList?.Add($"Sent config byte {conf:X}, received {LastConfigByte:X}");
}
return ok;
}
protected int WaitTime => (int)(1000.0 / Helpers.UpdateFrequency(Resolution));
public int ChannelCount { get; }
protected ConversionResult LastConversion
{
get
{
return _lastConversion;
}
set
{
_lastConversion = value;
OnConversion?.Invoke(this, _lastConversion);
}
}
public event EventHandler<ConversionResult> OnConversion;
protected void WriteConfig(byte configByte)
{
_i2cDevice.WriteByte(configByte);
}
private static int _asyncThreshold = 20;
/// <summary>
/// Sets the lower time limit in ms. If the current configuration reads data faster the synchronous API is used.
/// This can save some overhead.
/// </summary>
/// <remarks>
/// The default configuration is 20ms. This means that only in 16 bit resolution read waits asynchronously.
/// Setting it to 0 or lower disables the function.
/// </remarks>
/// <param name="thresh">Time limit in ms. Default: 20ms</param>
/// <autogeneratedoc />
public static void SetAsyncThreshold(int thresh) { _asyncThreshold = thresh; }
/// <summary>
/// One-shot read as an asynchronous operation. Initiates read and waits for it to finish.
/// Async API was required as reading in 16bit resolution can take more than 60ms.
/// </summary>
/// <param name="channel">The channel.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>ValueTask.</returns>
/// <exception cref="System.IO.IOException">
/// Device is not in One-Shot mode
/// or
/// ADC Conversion was not ready after {tries}
/// </exception>
/// <autogeneratedoc />
protected ValueTask OneShotReadAsync(int channel = -1, CancellationToken cancellationToken = default)
{
if (Mode != AdcMode.OneShot)
throw new IOException("Device is not in One-Shot mode");
_isReadyBit = false;
var conf = Helpers.SetReadyBit(LastConfigByte, false);
if (channel >= 0 && channel != LastChannel)
{
conf = Helpers.SetChannelBits(conf, channel);
}
WriteConfig(conf);
return WaitForConversionAsync(TimeSpan.FromMilliseconds(WaitTime), cancellationToken: cancellationToken);
}
protected async ValueTask<ConversionResult> ReadValueAsync(int channel = -1, CancellationToken cancellationToken = default)
{
if (Mode == AdcMode.OneShot)
{
await OneShotReadAsync(channel, cancellationToken);
}
else
{
if (channel > 0 && channel != LastChannel)
{
var conf = Helpers.SetChannelBits(LastConfigByte, channel);
WriteConfig(conf);
}
await WaitForConversionAsync(TimeSpan.FromMilliseconds(WaitTime / 5), cancellationToken: cancellationToken); // In continuous mode poll more often
}
cancellationToken.ThrowIfCancellationRequested();
var value = BinaryPrimitives.ReadInt16BigEndian(_readBuffer.AsSpan().Slice(0, 2));
LastConversion = new ConversionResult(_lastChannel, value, Resolution);
return LastConversion;
}
protected async ValueTask WaitForConversionAsync(TimeSpan? waitSpan = null, Action<int> progressCallback = null, CancellationToken cancellationToken = default)
{
waitSpan = waitSpan ?? TimeSpan.FromMilliseconds(WaitTime);
var allms = 0;
_isReadyBit = false;
while (!_isReadyBit && !cancellationToken.IsCancellationRequested)
{
_i2cDevice.Read(_readBuffer);
ReadConfigByte(LastConfigByte);
if (_isReadyBit)
break;
await Task.Delay(waitSpan.Value, cancellationToken);
allms += (int)(waitSpan.Value.TotalMilliseconds);
progressCallback?.Invoke(allms);
}
cancellationToken.ThrowIfCancellationRequested();
}
/// <summary>
/// Reads the channel. Async API is mostly useful for greater resolutions and one-shot mode, when conversion time can be significant.
/// </summary>
/// <param name="channel">The channel.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>ValueTask&lt;System.Double&gt;.</returns>
/// <autogeneratedoc />
public async ValueTask<double> ReadChannelAsync(int channel, CancellationToken cancellationToken = default)
{
if ((Resolution == AdcResolution.Bit12 && Mode == AdcMode.Continuous) || WaitTime < _asyncThreshold)
return ReadValue(channel);
await ReadValueAsync(channel, cancellationToken);
return LastConversion.Voltage;
}
}
}
# Mcp3428
## Summary
The library implements the Microchip MCP3428 16 bit A/D converter with I2C interface. It has hardware configurable I2C address and software configurable resolution and gain. Can work in continuous and on-demand conversion modes.
## Device Family
"The MCP3426, MCP3427 and MCP3428 devices (MCP3426/7/8) are the low noise and high accuracy 16 Bit Delta-Sigma Analog-to-Digital (ΔΣ A/D) Converter family members of the MCP342X series from Microchip Technology Inc. These devices can convert analog inputs to digital codes with up to 16 bits of resolution." - Datasheet
The 3 devices differ only in addressing capability and channel number. The library implements all of them.
**[MCP3428]**: <http://ww1.microchip.com/downloads/en/DeviceDoc/22226a.pdf>
## Binding Notes
On the MCP3428 you can select 8 different I2C addresses that the device answers on. It's done by connecting two pins, Adr0 and Adr1 to supply voltage or ground or leaving then floating. The library has a helper method to choose the address based on pin states.
With this instantiating the device and reading the first channel is done like this:
```csharp
var options = new I2cConnectionSettings(1,
Mcp3428.AddressFromPins(PinState.Low, PinState.Floating));
using (var dev = new UnixI2cDevice(options))
using (var adc = new Mcp3428(dev)) // Default settings
{
var ch1 = adc.ReadChannel(0);
Console.WriteLine($"ADC Channel value: {ch1} V");
}
```
The library provides an async API as reading with 16 bit resolution can take up to 60-80ms. It's in a separate class called `Mcp3428Async`.
## References
Provide any references to other tutorials, blogs and hardware related to the component that could help others get started.
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Device.I2c;
using System.Device.I2c.Drivers;
using System.Diagnostics;
using System.Threading.Tasks;
namespace Iot.Device.Mcp3428.Samples
{
internal class Program
{
private static async Task Main(string[] args)
{
Console.WriteLine("Hello Mcp3428 Sample!");
var options = new I2cConnectionSettings(1,
Mcp3428.I2CAddressFromPins(PinState.Low, PinState.Low));
using (var dev = new UnixI2cDevice(options))
{
using (var adc = new Mcp3428(dev, AdcMode.OneShot, resolution: AdcResolution.Bit16, pgaGain: AdcGain.X1))
{
var watch = new Stopwatch();
watch.Start();
while (true)
{
for (int i = 0; i < 4; i++)
{
var last = watch.ElapsedMilliseconds;
var value = adc.ReadChannel(i);
foreach (var b in adc.LastBytes.ToArray())
{
Console.Write($"{b:X} ");
}
Console.WriteLine();
Console.WriteLine($"ADC Channel[{adc.LastChannel + 1}] read in {watch.ElapsedMilliseconds - last} ms, value: {value} V");
await Task.Delay(500);
}
Console.WriteLine($"mode {adc.Mode}, gain {adc.InputGain}, res {adc.Resolution}");
await Task.Delay(1000);
}
}
}
}
}
}
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="../Mcp3428.csproj" />
</ItemGroup>
</Project>
# MCP3428 example
Simple example for measuring LED forward voltage using the MCP3428 ADC and a Raspberry Pi.
```csharp
// I2C addres based on pin configuration
var addr = Mcp3428.AddressFromPins(PinState.Low, PinState.Low);
var options = new I2cConnectionSettings(1, addr);
using (var dev = new UnixI2cDevice(options))
using (var adc = new Mcp3428(dev, ModeEnum.OneShot, ResolutionEnum.Bit16, GainEnum.X1))
{
var ch1 = adc.ReadChannel(0);
Console.WriteLine($"LED forward voltage value: {ch1} V");
}
```
Device connection ([some fritzing mess](rpi_led_adc_i2c.png)):
![Raspberry Pi Breadboard diagram](rpi_led_adc_i2c.png)
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册