提交 51ae361c 编写于 作者: S Sergey Razmyslov 提交者: Krzysztof Wicher

Add Mpr121 binding. (#286)

* Fix readme

* Fixes.

* Merge

* Fix merge issues

* Small fix

* Fixed comments.

* Fixed screenshot.

* Fixed screenshot.

* Fixed statuses.

* Updated timer

* Fixed comments

* Small fix
上级 4b9853a6
// 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.Collections;
using System.Collections.Generic;
using System.Collections.Immutable;
namespace Iot.Device.Mpr121
{
/// <summary>
/// Represents the arguments of event rising when the channel statuses have been changed.
/// </summary>
public class ChannelStatusesChangedEventArgs : EventArgs
{
/// <summary>
/// The channel statuses.
/// </summary>
public IReadOnlyDictionary<Channels, bool> ChannelStatuses { get; private set; }
/// <summary>
/// Initialize event arguments.
/// </summary>
/// <param name="channelStatuses">The channel statuses.</param>
public ChannelStatusesChangedEventArgs(IReadOnlyDictionary<Channels, bool> channelStatuses) : base()
{
ChannelStatuses = channelStatuses;
}
}
}
\ No newline at end of file
// 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.Mpr121
{
public enum Channels
{
Channel00 = 0,
Channel01,
Channel02,
Channel03,
Channel04,
Channel05,
Channel06,
Channel07,
Channel08,
Channel09,
Channel10,
Channel11
}
}
\ No newline at end of file
// 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;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Device.I2c;
using System.Threading;
namespace Iot.Device.Mpr121
{
/// <summary>
/// Supports MPR121 Proximity Capacitive Touch Sensor Controller.
/// </summary>
public class Mpr121 : IDisposable
{
/// <summary>
/// MPR121 Default I2C Address.
/// </summary>
public static readonly byte DefaultI2cAddress = 0x5A;
private static readonly int CHANNELS_NUMBER = Enum.GetValues(typeof(Channels)).Length;
private I2cDevice _device;
private Timer _timer;
private Dictionary<Channels, bool> _statuses;
private int _periodRefresh;
/// <summary>
/// Notifies about a the channel statuses have been changed.
/// Refresh period can be changed by setting PeriodRefresh property.
/// </summary>
public event EventHandler<ChannelStatusesChangedEventArgs> ChannelStatusesChanged;
/// <summary>
/// Gets or sets the period in milliseconds to refresh the channels statuses.
/// </summary>
/// <remark>
/// Set value 0 to stop the automatically refreshing. Setting the value greater than 0 will start/update auto-refresh.
/// </remark>
public int PeriodRefresh
{
get { return _periodRefresh; }
set
{
_periodRefresh = value;
if (_periodRefresh > 0)
{
_timer.Change(TimeSpan.FromMilliseconds(_periodRefresh), TimeSpan.FromMilliseconds(_periodRefresh));
}
else
{
// Disable the auto-refresh.
_timer.Change(Timeout.Infinite, Timeout.Infinite);
}
}
}
/// <summary>
/// Initialize a MPR121 controller.
/// </summary>
/// <param name="device">The i2c device.</param>
/// <param name="periodRefresh">The period in milliseconds of refresing the channel statuses.</param>
/// <param name="configuration">The controller configuration.</param>
public Mpr121(I2cDevice device, int periodRefresh = -1, Mpr121Configuration configuration = null)
{
configuration = configuration ?? GetDefaultConfiguration();
_device = device;
_timer = new Timer(RefreshChannelStatuses, this, Timeout.Infinite, Timeout.Infinite);
_statuses = new Dictionary<Channels, bool>();
foreach (Channels channel in Enum.GetValues(typeof(Channels)))
{
_statuses.Add(channel, false);
}
InitializeController(configuration);
PeriodRefresh = periodRefresh;
}
public void Dispose()
{
if (_device != null)
{
_device.Dispose();
_device = null;
}
if (_timer != null)
{
_timer.Dispose();
_timer = null;
}
}
/// <summary>
/// Reads the channel statuses of MPR121 controller.
/// </summary>
public IReadOnlyDictionary<Channels, bool> ReadChannelStatuses()
{
RefreshChannelStatuses();
return _statuses.ToImmutableDictionary();
}
/// <summary>
/// Reads the channel status of MPR121 controller.
/// </summary>
/// <param name="channel">The channel to read status.</param>
/// <remark>
/// Please use ReadChannelStatuses() if you need to read statuses of multiple channels.
/// Using this method several times to read status for several channels can affect the performance.
/// </remark>
public bool ReadChannelStatus(Channels channel)
{
RefreshChannelStatuses();
return _statuses[channel];
}
private static Mpr121Configuration GetDefaultConfiguration()
{
return new Mpr121Configuration()
{
MaxHalfDeltaRising = 0x01,
NoiseHalfDeltaRising = 0x01,
NoiseCountLimitRising = 0x00,
FilterDelayCountLimitRising = 0x00,
MaxHalfDeltaFalling = 0x01,
NoiseHalfDeltaFalling = 0x01,
NoiseCountLimitFalling = 0xFF,
FilterDelayCountLimitFalling = 0x01,
ElectrodeTouchThreshold = 0x0F,
ElectrodeReleaseThreshold = 0x0A,
ChargeDischargeTimeConfiguration = 0x04,
ElectrodeConfiguration = 0x0C
};
}
private void InitializeController(Mpr121Configuration configuration)
{
SetRegister(Registers.MHDR, configuration.MaxHalfDeltaRising);
SetRegister(Registers.NHDR, configuration.NoiseHalfDeltaRising);
SetRegister(Registers.NCLR, configuration.NoiseCountLimitRising);
SetRegister(Registers.FDLR, configuration.FilterDelayCountLimitRising);
SetRegister(Registers.MHDF, configuration.MaxHalfDeltaFalling);
SetRegister(Registers.NHDF, configuration.NoiseHalfDeltaFalling);
SetRegister(Registers.NCLF, configuration.NoiseCountLimitFalling);
SetRegister(Registers.FDLF, configuration.FilterDelayCountLimitFalling);
SetRegister(Registers.E0TTH, configuration.ElectrodeTouchThreshold);
SetRegister(Registers.E0RTH, configuration.ElectrodeReleaseThreshold);
SetRegister(Registers.E1TTH, configuration.ElectrodeTouchThreshold);
SetRegister(Registers.E1RTH, configuration.ElectrodeReleaseThreshold);
SetRegister(Registers.E2TTH, configuration.ElectrodeTouchThreshold);
SetRegister(Registers.E2RTH, configuration.ElectrodeReleaseThreshold);
SetRegister(Registers.E3TTH, configuration.ElectrodeTouchThreshold);
SetRegister(Registers.E3RTH, configuration.ElectrodeReleaseThreshold);
SetRegister(Registers.E4TTH, configuration.ElectrodeTouchThreshold);
SetRegister(Registers.E4RTH, configuration.ElectrodeReleaseThreshold);
SetRegister(Registers.E5TTH, configuration.ElectrodeTouchThreshold);
SetRegister(Registers.E5RTH, configuration.ElectrodeReleaseThreshold);
SetRegister(Registers.E6TTH, configuration.ElectrodeTouchThreshold);
SetRegister(Registers.E6RTH, configuration.ElectrodeReleaseThreshold);
SetRegister(Registers.E7TTH, configuration.ElectrodeTouchThreshold);
SetRegister(Registers.E7RTH, configuration.ElectrodeReleaseThreshold);
SetRegister(Registers.E8TTH, configuration.ElectrodeTouchThreshold);
SetRegister(Registers.E8RTH, configuration.ElectrodeReleaseThreshold);
SetRegister(Registers.E9TTH, configuration.ElectrodeTouchThreshold);
SetRegister(Registers.E9RTH, configuration.ElectrodeReleaseThreshold);
SetRegister(Registers.E10TTH, configuration.ElectrodeTouchThreshold);
SetRegister(Registers.E10RTH, configuration.ElectrodeReleaseThreshold);
SetRegister(Registers.E11TTH, configuration.ElectrodeTouchThreshold);
SetRegister(Registers.E11RTH, configuration.ElectrodeReleaseThreshold);
SetRegister(Registers.CDTC, configuration.ChargeDischargeTimeConfiguration);
SetRegister(Registers.ELECONF, configuration.ElectrodeConfiguration);
}
/// <summary>
/// The callback function for timer to refresh channels statuses.
/// </summary>
private void RefreshChannelStatuses(object state)
{
RefreshChannelStatuses();
}
/// <summary>
/// Refresh the channel statuses.
/// </summary>
private void RefreshChannelStatuses()
{
// Pause the auto-refresh to prevent possible collisions.
var periodRefresh = PeriodRefresh;
PeriodRefresh = 0;
Span<byte> buffer = stackalloc byte[2];
_device.Read(buffer);
short rawStatus = BinaryPrimitives.ReadInt16LittleEndian(buffer);
bool isStatusChanged = false;
for (var i = 0; i < CHANNELS_NUMBER; i++)
{
bool status = ((1 << i) & rawStatus) > 0;
if (_statuses[(Channels)i] != status)
{
_statuses[(Channels)i] = status;
isStatusChanged = true;
}
}
if (isStatusChanged)
{
OnChannelStatusesChanged();
}
// Resume the auto-refresh.
PeriodRefresh = periodRefresh;
}
private void SetRegister(Registers register, byte value)
{
Span<byte> data = stackalloc byte[] { (byte)register, value };
_device.Write(data);
}
private void OnChannelStatusesChanged()
{
ChannelStatusesChanged?.Invoke(this, new ChannelStatusesChangedEventArgs(_statuses.ToImmutableDictionary()));
}
}
}
<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>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<ItemGroup>
<Compile Include="Channels.cs" />
<Compile Include="ChannelStatusesChangedEventArgs.cs" />
<Compile Include="Mpr121.cs" />
<Compile Include="Mpr121Configuration.cs" />
<Compile Include="Registers.cs" />
<None Include="README.md" />
<PackageReference Include="System.Device.Gpio" Version="0.1.0-prerelease*" />
</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.
namespace Iot.Device.Mpr121
{
/// <summary>
/// Configuration for registers listed on datasheet page 8.
/// </summary>
public class Mpr121Configuration
{
/// <summary>
/// Determines the largest magnitude of variation to pass through the baseline filter (rising).
/// </summary>
public byte MaxHalfDeltaRising { get; set; }
/// <summary>
/// Determines the incremental change when non-noise drift is detected (rising).
/// </summary>
public byte NoiseHalfDeltaRising { get; set; }
/// <summary>
/// Determines the number of samples consecutively greater than the Max Half Delta value (rising).
/// This is necessary to determine that it is not noise.
/// </summary>
public byte NoiseCountLimitRising { get; set; }
/// <summary>
/// Determines the operation rate of the filter. A larger count limit means the filter delay is operating more slowly (rising).
/// </summary>
public byte FilterDelayCountLimitRising { get; set; }
/// <summary>
/// Determines the largest magnitude of variation to pass through the baseline filter (falling).
/// </summary>
public byte MaxHalfDeltaFalling { get; set; }
/// <summary>
/// Determines the incremental change when non-noise drift is detected (falling).
/// </summary>
public byte NoiseHalfDeltaFalling { get; set; }
/// <summary>
/// Determines the number of samples consecutively greater than the Max Half Delta value (falling).
/// This is necessary to determine that it is not noise.
/// </summary>
public byte NoiseCountLimitFalling { get; set; }
/// <summary>
/// Determines the operation rate of the filter. A larger count limit means the filter delay is operating more slowly (falling).
/// </summary>
public byte FilterDelayCountLimitFalling { get; set; }
/// <summary>
/// Electrode touch threshold.
/// </summary>
/// <remark>
/// Threshold settings are dependant on the touch/release signal strength, system sensitivity and noise immunity requirements.
/// In a typical touch detection application, threshold is typically in the range 0x04~0x10.
/// The touch threshold is several counts larger than the release threshold. This is to provide hysteresis and to prevent noise and jitter.
/// </remark>
public byte ElectrodeTouchThreshold { get; set; }
/// <summary>
/// Electrode release threshold.
/// </summary>
/// <remark>
/// Threshold settings are dependant on the touch/release signal strength, system sensitivity and noise immunity requirements.
/// In a typical touch detection application, threshold is typically in the range 0x04~0x10.
/// The touch threshold is several counts larger than the release threshold. This is to provide hysteresis and to prevent noise and jitter.
/// </remark>
public byte ElectrodeReleaseThreshold { get; set; }
/// <summary>
/// Filter/Global Charge Discharge Time Configuration (datasheet page 14).
/// </summary>
public byte ChargeDischargeTimeConfiguration { get; set; }
/// <summary>
/// Electrode Configuration (datasheet page 15).
/// </summary>
public byte ElectrodeConfiguration { get; set; }
}
}
\ No newline at end of file
# MPR121
## Summary
The 12-channels I2C proximity capacitive touch sensor controller.
## Device Family
**MPR121**: https://www.sparkfun.com/datasheets/Components/MPR121.pdf
## Binding Notes
The binding provides different options of device configuration. The device can be configured to update the channel statuses periodically. Also it supports custom configuration of controller registers.
#### Default configuration with manually updating of channel statuses
```csharp
var i2cDevice = new UnixI2cDevice(new I2cConnectionSettings(busId: 1, deviceAddress: Mpr121.DefaultI2cAddress));
var mpr121 = new Mpr121(device: i2cDevice);
var statuses = mpr121.ReadChannelStatuses();
var status = statuses[Channels.Channel01]
? "pressed"
: "released";
Console.WriteLine($"The 1st channel is {status}");
```
#### Channel statuses auto refresh
```csharp
var i2cDevice = new UnixI2cDevice(new I2cConnectionSettings(busId: 1, deviceAddress: Mpr121.DefaultI2cAddress));
// Initialize controller with default configuration and auto-refresh the channel statuses every 100 ms.
var mpr121 = new Mpr121(device: i2cDevice, periodRefresh: 100);
// Subscribe to channel statuses updates.
mpr121.ChannelStatusesChanged += (object sender, ChannelStatusesChangedEventArgs e) =>
{
var channelStatuses = e.ChannelStatuses;
// do something.
};
```
#### Custom MPR121 registers configuration
```csharp
var i2cDevice = new UnixI2cDevice(new I2cConnectionSettings(busId: 1, deviceAddress: Mpr121.DefaultI2cAddress));
var config = new Mpr121Configuration
{
MaxHalfDeltaRising = 0x01,
NoiseHalfDeltaRising = 0x01,
NoiseCountLimitRising = 0x00,
FilterDelayCountLimitRising = 0x00,
MaxHalfDeltaFalling = 0x01,
NoiseHalfDeltaFalling = 0x01,
NoiseCountLimitFalling = 0xFF,
FilterDelayCountLimitFalling = 0x01,
ElectrodeTouchThreshold = 0x0F,
ElectrodeReleaseThreshold = 0x0A,
ChargeDischargeTimeConfiguration = 0x04,
ElectrodeConfiguration = 0x0C
};
var mpr121 = new Mpr121(device: i2cDevice, configuration: config);
```
\ No newline at end of file
// 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.Mpr121
{
// Registers of Mpr121. Please see datasheet page 8 for details.
internal enum Registers : byte
{
/// <summary>
/// MHD Rising.
/// </summary>
MHDR = 0x2B,
/// <summary>
/// NHD Amount Rising.
/// </summary>
NHDR = 0x2C,
/// <summary>
/// NCL Rising.
/// </summary>
NCLR = 0x2D,
/// <summary>
/// FDL Rising.
/// </summary>
FDLR = 0x2E,
/// <summary>
/// MHD Falling.
/// </summary>
MHDF = 0x2F,
/// <summary>
/// NHD Amount Falling.
/// </summary>
NHDF = 0x30,
/// <summary>
/// NCL Falling.
/// </summary>
NCLF = 0x31,
/// <summary>
/// FDL Falling.
/// </summary>
FDLF = 0x32,
/// <summary>
/// ELE0 Touch Threshold.
/// </summary>
E0TTH = 0x41,
/// <summary>
/// ELE0 Release Threshold.
/// </summary>
E0RTH = 0x42,
/// <summary>
/// ELE1 Touch Threshold.
/// </summary>
E1TTH = 0x43,
/// <summary>
/// ELE1 Release Threshold.
/// </summary>
E1RTH = 0x44,
/// <summary>
/// ELE2 Touch Threshold.
/// </summary>
E2TTH = 0x45,
/// <summary>
/// ELE2 Release Threshold.
/// </summary>
E2RTH = 0x46,
/// <summary>
/// ELE3 Touch Threshold.
/// </summary>
E3TTH = 0x47,
/// <summary>
/// ELE3 Release Threshold.
/// </summary>
E3RTH = 0x48,
/// <summary>
/// ELE4 Touch Threshold.
/// </summary>
E4TTH = 0x49,
/// <summary>
/// ELE4 Release Threshold.
/// </summary>
E4RTH = 0x4A,
/// <summary>
/// ELE5 Touch Threshold.
/// </summary>
E5TTH = 0x4B,
/// <summary>
/// ELE5 Release Threshold.
/// </summary>
E5RTH = 0x4C,
/// <summary>
/// ELE6 Touch Threshold.
/// </summary>
E6TTH = 0x4D,
/// <summary>
/// ELE6 Release Threshold.
/// </summary>
E6RTH = 0x4E,
/// <summary>
/// ELE7 Touch Threshold.
/// </summary>
E7TTH = 0x4F,
/// <summary>
/// ELE7 Release Threshold.
/// </summary>
E7RTH = 0x50,
/// <summary>
/// ELE8 Touch Threshold.
/// </summary>
E8TTH = 0x51,
/// <summary>
/// ELE8 Release Threshold.
/// </summary>
E8RTH = 0x52,
/// <summary>
/// ELE9 Touch Threshold.
/// </summary>
E9TTH = 0x53,
/// <summary>
/// ELE9 Release Threshold.
/// </summary>
E9RTH = 0x54,
/// <summary>
/// ELE10 Touch Threshold.
/// </summary>
E10TTH = 0x55,
/// <summary>
/// ELE10 Release Threshold.
/// </summary>
E10RTH = 0x56,
/// <summary>
///
/// </summary>
E11TTH = 0x57,
/// <summary>
/// ELE11 Touch Threshold.
/// </summary>
E11RTH = 0x58,
/// <summary>
/// ELE11 Release Threshold.
/// </summary>
CDTC = 0x5D,
/// <summary>
/// Electrode Configuration.
/// </summary>
ELECONF = 0x5E
}
}
\ No newline at end of file
// 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;
namespace Iot.Device.Mpr121.Samples
{
class Program
{
static void Main(string[] args)
{
var i2cDevice = new UnixI2cDevice(new I2cConnectionSettings(busId: 1, deviceAddress: Mpr121.DefaultI2cAddress));
// Initialize controller with default configuration and auto-refresh the channel statuses every 100 ms.
var mpr121 = new Mpr121(device: i2cDevice, periodRefresh: 100);
Console.Clear();
Console.CursorVisible = false;
PrintChannelsTable();
Console.WriteLine("Press Enter to exit.");
// Subscribe to channel statuses updates.
mpr121.ChannelStatusesChanged += (object sender, ChannelStatusesChangedEventArgs e) =>
{
var channelStatuses = e.ChannelStatuses;
foreach (var channel in channelStatuses.Keys)
{
Console.SetCursorPosition(14, (int)channel * 2 + 1);
Console.Write(channelStatuses[channel] ? "#" : " ");
}
};
using (mpr121)
{
Console.ReadLine();
Console.Clear();
Console.CursorVisible = true;
}
}
private static void PrintChannelsTable()
{
Console.WriteLine("-----------------");
foreach (var channel in Enum.GetValues(typeof(Channels)))
{
Console.WriteLine("| " + Enum.GetName(typeof(Channels), channel) + " | |");
Console.WriteLine("-----------------");
}
}
}
}
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.1</TargetFramework>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="../Mpr121.csproj" />
</ItemGroup>
</Project>
# MPR121 Sample
This sample demonstrates how to read channel statuses using auto-refresh configuration.
### Handling the channel statuses changes
```csharp
mpr121.ChannelStatusesChanged += (object sender, ChannelStatusesChangedEventArgs e) =>
{
var channelStatuses = e.ChannelStatuses;
// do something.
};
```
### Channel statuses table
The following status means that the **channel 1** and **channel 3** are pressed:
![](./ChannelStatuses.png)
\ No newline at end of file
......@@ -22,6 +22,7 @@ Our vision: the majority of .NET bindings are written completely in .NET languag
* [MAX44009 -- Ambient Light Sensor](Max44009/README.md)
* [Mcp23xxx -- I/O Expander](Mcp23xxx/README.md)
* [Mcp3008 -- Analog-to-Digital Converter](Mcp3008/README.md)
* [Mpr121 -- Proximity Capacitive Touch Sensor Controller](Mpr121/README.md)
* [nRF24L01 -- Single chip 2.4 GHz Transceiver](Nrf24l01/README.md)
* [Pca95x4 -- I2C GPIO Expander](Pca95x4/README.md)
* [Pca9685 -- I2C PWM Driver](Pca9685/README.md)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册