未验证 提交 5809972a 编写于 作者: K Krzysztof Wicher 提交者: GitHub

Add LIS3DH sensor (#1954)

* Add LIS3DH sensor

* fix category.txt

* Add secondary I2C address
上级 08664fa6
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
namespace Iot.Device.Lis3DhAccelerometer
{
/// <summary>
/// Acceleration scale
/// </summary>
public enum AccelerationScale : byte
{
/// <summary>
/// Acceleration 2G
/// </summary>
Scale02G = 0b00,
/// <summary>
/// Acceleration 4G
/// </summary>
Scale04G = 0b01,
/// <summary>
/// Acceleration 8G
/// </summary>
Scale08G = 0b10,
/// <summary>
/// Acceleration 16G
/// </summary>
Scale16G = 0b11,
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
namespace Iot.Device.Lis3DhAccelerometer
{
/// <summary>
/// Data rate selection
/// </summary>
public enum DataRate : byte
{
/// <summary>
/// Power down mode
/// </summary>
PowerDownMode = 0b0000,
/// <summary>
/// Data rate 1Hz
/// </summary>
DataRate1Hz = 0b0001,
/// <summary>
/// Data rate 10Hz
/// </summary>
DataRate10Hz = 0b0010,
/// <summary>
/// Data rate 25Hz
/// </summary>
DataRate25Hz = 0b0011,
/// <summary>
/// Data rate 50Hz
/// </summary>
DataRate50Hz = 0b0100,
/// <summary>
/// Data rate 100Hz
/// </summary>
DataRate100Hz = 0b0101,
/// <summary>
/// Data rate 200Hz
/// </summary>
DataRate200Hz = 0b0110,
/// <summary>
/// Data rate 400Hz
/// </summary>
DataRate400Hz = 0b0111,
/// <summary>
/// Data rate 1.6kHz - this data rate mode is only applicable for LowPowerMode
/// </summary>
LowPowerMode1600Hz = 0b1000,
/// <summary>
/// Data rate, for HighResolutionMode or NormalMode 1.344kHz, for LowPowerMode 5.376kHz
/// </summary>
HighResolutionNormal1344HzOrLowPowerMode5376Hz = 0b1001,
}
}
// 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.Buffers.Binary;
using System.Device.Gpio;
using System.Device.I2c;
using System.Device.Model;
using System.Numerics;
namespace Iot.Device.Lis3DhAccelerometer
{
/// <summary>
/// LIS3DH accelerometer
/// </summary>
[Interface("LIS3DH accelerometer")]
public abstract class Lis3Dh : IDisposable
{
/// <summary>
/// Default I2C address (SDO/SA0 pin low)
/// </summary>
public const int DefaultI2cAddress = 0x18;
/// <summary>
/// Secondary I2C address (SDO/SA0 pin high)
/// </summary>
public const int SecondaryI2cAddress = 0x18;
private const int Max = (1 << 15);
private DataRate _dataRate;
private OperatingMode _operatingMode;
private AccelerationScale _accelerationScale;
/// <summary>
/// Data rate
/// </summary>
[Property]
public DataRate DataRate
{
get => _dataRate;
set => ChangeSettings(value, _operatingMode, _accelerationScale);
}
/// <summary>
/// Operating mode
/// </summary>
[Property]
public OperatingMode OperatingMode
{
get => _operatingMode;
set => ChangeSettings(_dataRate, value, _accelerationScale);
}
/// <summary>
/// Acceleration scale
/// </summary>
[Property]
public AccelerationScale AccelerationScale
{
get => _accelerationScale;
set => ChangeSettings(_dataRate, _operatingMode, value);
}
/// <summary>
/// Gets I2C address depending on SDO/SA0 pin.
/// </summary>
/// <param name="sdoPinValue">SDO pin value. Pin may be also called SA0</param>
/// <returns>I2C address</returns>
public static int GetI2cAddress(PinValue sdoPinValue)
=> sdoPinValue == PinValue.High ? SecondaryI2cAddress : DefaultI2cAddress;
/// <summary>
/// Creates Lis3Dh instance using I2cDevice
/// </summary>
/// <param name="i2cDevice">I2C device</param>
/// <param name="dataRate">Data rate</param>
/// <param name="operatingMode">Operating mode</param>
/// <param name="accelerationScale">Acceleration scale</param>
/// <returns>Lis3Dh instance</returns>
public static Lis3Dh Create(I2cDevice i2cDevice, DataRate dataRate = DataRate.DataRate100Hz, OperatingMode operatingMode = OperatingMode.HighResolutionMode, AccelerationScale accelerationScale = AccelerationScale.Scale04G)
=> new Lis3DhI2c(i2cDevice).Initialize(dataRate, operatingMode, accelerationScale);
private protected Lis3Dh()
{
}
private protected abstract void WriteRegister(Register register, byte data, bool autoIncrement = true);
private protected abstract void ReadRegister(Register register, Span<byte> data, bool autoIncrement = true);
/// <summary>
/// Acceleration measured in gravitational force
/// </summary>
[Telemetry(displayName: "Acceleration measured in gravitational force")]
public Vector3 Acceleration => Vector3.Divide(ReadRawAcceleration(), GetAccelerationDivisor());
/// <inheritdoc/>
public abstract void Dispose();
private void ResetUnusedSettings()
{
// Reset to default values
// msb: pull-up on SA0, remainder must be hard-coded
this[Register.CTRL_REG0] = 0b00010000;
// disable temperature sensor and ADC
this[Register.TEMP_CFG_REG] = 0b00000000;
// CTRL_REG1 is handled by ChangeSettings (DataRate and low-power mode bit)
// high-pass filter settings default
this[Register.CTRL_REG2] = 0b00000000;
// interrupts settings to default
this[Register.CTRL_REG3] = 0b00000000;
// CTRL_REG4 is handled by ChangeSettings (AccelerationScale and high-resolution mode bit)
// reboot, FIFO, interrupt settings to default
this[Register.CTRL_REG5] = 0b00000000;
// other boot and interrupt settings to default
this[Register.CTRL_REG6] = 0b00000000;
}
private void ChangeSettings(DataRate dataRate, OperatingMode operatingMode, AccelerationScale accelerationScale)
{
byte dataRateBits = (byte)((byte)dataRate << 4);
byte lowPowerModeBitAndAxesEnable = (byte)(operatingMode == OperatingMode.LowPowerMode ? 0b1111 : 0b0111);
this[Register.CTRL_REG1] = (byte)(dataRateBits | lowPowerModeBitAndAxesEnable);
// remainder of the bits (block data update/endianess/self-test/SPI 3 or 4-wire setting) set to default
byte fullScaleBits = (byte)((byte)accelerationScale << 4);
byte highResolutionModeBit = (byte)(operatingMode == OperatingMode.HighResolutionMode ? 0b1000 : 0b0000);
this[Register.CTRL_REG4] = (byte)(fullScaleBits | highResolutionModeBit);
_dataRate = dataRate;
_operatingMode = operatingMode;
_accelerationScale = accelerationScale;
}
private Lis3Dh Initialize(DataRate dataRate, OperatingMode operatingMode, AccelerationScale accelerationScale)
{
ResetUnusedSettings();
ChangeSettings(dataRate, operatingMode, accelerationScale);
// return this to simplify syntax
return this;
}
private Vector3 ReadRawAcceleration()
{
Span<byte> vec = stackalloc byte[6];
ReadRegister(Register.OUT_X_L, vec);
short x = BinaryPrimitives.ReadInt16LittleEndian(vec.Slice(0, 2));
short y = BinaryPrimitives.ReadInt16LittleEndian(vec.Slice(2, 2));
short z = BinaryPrimitives.ReadInt16LittleEndian(vec.Slice(4, 2));
return new Vector3(x, y, z);
}
private float GetAccelerationDivisor() => _accelerationScale switch
{
// Max is power of 2 so we don't lose any data here by integer division
AccelerationScale.Scale02G => Max / 2,
AccelerationScale.Scale04G => Max / 4,
AccelerationScale.Scale08G => Max / 8,
// Note: While we theoretically should divide by 16 here,
// according to my measurements the actual divisor is around 24:
// When 24 is used measurements match other scales and 1G is closer to 1G rather than 0.64G.
// I was not able to find section in datasheet explaining why this is 24 (exactly 50% more).
AccelerationScale.Scale16G => Max / 24,
_ => throw new ArgumentException("Value is unknown.", nameof(_accelerationScale)),
};
private byte this[Register register]
{
get
{
Span<byte> reg = stackalloc byte[1];
ReadRegister(register, reg);
return reg[0];
}
set
{
WriteRegister(register, value);
}
}
}
}
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>$(DefaultBindingTfms)</TargetFrameworks>
<EnableDefaultItems>false</EnableDefaultItems>
</PropertyGroup>
<ItemGroup>
<Compile Include="*.cs" />
<None Include="README.md" />
</ItemGroup>
</Project>
\ No newline at end of file

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.5.33002.27
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lis3Dh", "Lis3Dh.csproj", "{4FCCDD79-2AE3-49D4-AA02-32BAB267EC6F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lis3Dh.Samples", "samples\Lis3Dh.Samples.csproj", "{3E7C8A76-E7FD-426E-8901-697950D05141}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{4FCCDD79-2AE3-49D4-AA02-32BAB267EC6F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4FCCDD79-2AE3-49D4-AA02-32BAB267EC6F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4FCCDD79-2AE3-49D4-AA02-32BAB267EC6F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4FCCDD79-2AE3-49D4-AA02-32BAB267EC6F}.Release|Any CPU.Build.0 = Release|Any CPU
{3E7C8A76-E7FD-426E-8901-697950D05141}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3E7C8A76-E7FD-426E-8901-697950D05141}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3E7C8A76-E7FD-426E-8901-697950D05141}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3E7C8A76-E7FD-426E-8901-697950D05141}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {5AE9D65D-E65D-458E-8A7C-B1936EAE9A68}
EndGlobalSection
EndGlobal
// 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.Buffers.Binary;
using System.Device.I2c;
using System.Device.Model;
using System.Numerics;
namespace Iot.Device.Lis3DhAccelerometer
{
internal sealed class Lis3DhI2c : Lis3Dh
{
private I2cDevice _i2c;
public Lis3DhI2c(I2cDevice i2cDevice)
{
_i2c = i2cDevice ?? throw new ArgumentNullException(nameof(i2cDevice));
}
/// <inheritdoc/>
public override void Dispose()
{
_i2c?.Dispose();
_i2c = null!;
}
private protected override void ReadRegister(Register register, Span<byte> data, bool autoIncrement = true)
{
Span<byte> reg = stackalloc byte[1];
reg[0] = GetRegister(register, autoIncrement);
_i2c.WriteRead(reg, data);
}
private protected override void WriteRegister(Register register, byte data, bool autoIncrement = true)
{
Span<byte> reg = stackalloc byte[2];
reg[0] = GetRegister(register, autoIncrement);
reg[1] = data;
_i2c.Write(reg);
}
private static byte GetRegister(Register register, bool autoIncrement)
=> (byte)((byte)register | (autoIncrement ? (byte)Register.I2cAutoIncrement : (byte)0));
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
namespace Iot.Device.Lis3DhAccelerometer
{
/// <summary>
/// Operating mode selection
/// </summary>
public enum OperatingMode : byte
{
/// <summary>
/// Power down mode
/// </summary>
HighResolutionMode,
/// <summary>
/// Normal mode
/// </summary>
NormalMode,
/// <summary>
/// Low power mode
/// </summary>
LowPowerMode,
}
}
# LIS3DH - ultra-low-power high-performance 3-axis nano accelerometer
The LIS3DH is an ultra-low-power high-performance three-axis linear accelerometer belonging to the "nano" family.
LIS3DH supports two serial interfaces: I2C and SPI but only I2C is currently supported.
## Documentation
- You can find the datasheet [here](https://www.st.com/resource/en/datasheet/lis3dh.pdf)
## Usage
### Accelerometer example (using FT4222)
```csharp
using System;
using System.Collections.Generic;
using System.Device.I2c;
using System.Numerics;
using System.Threading;
using Iot.Device.Ft4222;
using Iot.Device.Lis3DhAccelerometer;
List<Ft4222Device> ft4222s = Ft4222Device.GetFt4222();
if (ft4222s.Count == 0)
{
Console.WriteLine("FT4222 not plugged in");
return;
}
Ft4222Device ft4222 = ft4222s[0];
using var accelerometer = Lis3Dh.Create(ft4222.CreateI2cDevice(new I2cConnectionSettings(0, Lis3Dh.DefaultI2cAddress)), dataRate: DataRate.DataRate10Hz);
Console.WriteLine("If you orient sensor so that two axes are close to 0");
Console.WriteLine("the remaining one should equal close to 1 or -1 (1G) which is gravitational force");
while (!Console.KeyAvailable || Console.ReadKey().Key != ConsoleKey.Enter)
{
Vector3 acceleration = accelerometer.Acceleration;
// Clear causes flickering when updated frequently
Console.SetCursorPosition(0, 2);
Console.WriteLine($"{acceleration.X:0.0000} {acceleration.Y:0.0000} {acceleration.Z:0.0000}".PadRight(30));
Thread.Sleep(20);
}
```
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
namespace Iot.Device.Lis3DhAccelerometer
{
// Register for Accelerometer and Gyroscope
internal enum Register : byte
{
// register modifiers
I2cAutoIncrement = 0x80,
CTRL_REG0 = 0x1E, // SDO_PULL_UP_DISCONNECT, 0, 0, 1, 0, 0, 0, 0
TEMP_CFG_REG = 0x1F, // ADC_ENABLE, TEMP_ENABLE, 0, 0, ...
CTRL_REG1 = 0x20,
CTRL_REG2 = 0x21,
CTRL_REG3 = 0x22,
CTRL_REG4 = 0x23,
CTRL_REG5 = 0x24,
CTRL_REG6 = 0x25,
REFERENCE = 0x26,
STATUS_REG = 0x27,
OUT_X_L = 0x28,
OUT_X_H = 0x29,
OUT_Y_L = 0x2A,
OUT_Y_H = 0x2B,
OUT_Z_L = 0x2C,
OUT_Z_H = 0x2D,
}
}
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>$(DefaultSampleTfms)</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="../Lis3Dh.csproj" />
<ProjectReference Include="../../Board/Board.csproj" />
<ProjectReference Include="../../Ft4222/Ft4222.csproj" />
</ItemGroup>
</Project>
\ 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.
using System;
using System.Collections.Generic;
using System.Device.I2c;
using System.Numerics;
using System.Threading;
using Iot.Device.Ft4222;
using Iot.Device.Lis3DhAccelerometer;
List<Ft4222Device> ft4222s = Ft4222Device.GetFt4222();
if (ft4222s.Count == 0)
{
Console.WriteLine("FT4222 not plugged in");
return;
}
Ft4222Device ft4222 = ft4222s[0];
using var accelerometer = Lis3Dh.Create(ft4222.CreateI2cDevice(new I2cConnectionSettings(0, Lis3Dh.DefaultI2cAddress)), dataRate: DataRate.DataRate10Hz);
Console.WriteLine("If you orient sensor so that two axes are close to 0");
Console.WriteLine("the remaining one should equal close to 1 or -1 (1G) which is gravitational force");
while (!Console.KeyAvailable || Console.ReadKey().Key != ConsoleKey.Enter)
{
Vector3 acceleration = accelerometer.Acceleration;
// Clear causes flickering when updated frequently
Console.SetCursorPosition(0, 2);
Console.WriteLine($"{acceleration.X:0.0000} {acceleration.Y:0.0000} {acceleration.Z:0.0000}".PadRight(30));
Thread.Sleep(20);
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册