Skip to content

Commit 164b4fc

Browse files
authored
Adding KeyMatrix (#37)
1 parent 3d44754 commit 164b4fc

21 files changed

+876
-0
lines changed

Diff for: devices/KeyMatrix/KeyMatrix.cs

+233
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System;
6+
using System.Device.Gpio;
7+
using System.Threading;
8+
9+
namespace Iot.Device.KeyMatrix
10+
{
11+
/// <summary>
12+
/// GPIO key matrix Driver
13+
/// </summary>
14+
public class KeyMatrix : IDisposable
15+
{
16+
/// <summary>
17+
/// Get output pins
18+
/// </summary>
19+
public int[] OutputPins => _outputPins;
20+
21+
/// <summary>
22+
/// Get input pins
23+
/// </summary>
24+
public int[] InputPins => _inputPins;
25+
26+
/// <summary>
27+
/// Get all buttons' values
28+
/// </summary>
29+
public SpanPinValue Values => new SpanPinValue(_buttonValues);
30+
31+
/// <summary>
32+
/// Get or set interval in milliseconds
33+
/// </summary>
34+
public TimeSpan ScanInterval { get; set; }
35+
36+
/// <summary>
37+
/// Get buttons' values by output
38+
/// </summary>
39+
/// <param name="output">Output index</param>
40+
public SpanPinValue this[int output] => new SpanPinValue(_buttonValues, output * _outputPins.Length, _inputPins.Length);
41+
42+
private int[] _outputPins;
43+
private int[] _inputPins;
44+
private GpioController? _gpioController;
45+
private PinValue[] _buttonValues;
46+
private bool _pinsOpened;
47+
private int _currentOutput = 0;
48+
private bool _shouldDispose;
49+
private bool _isRunning = false;
50+
private KeyMatrixEvent? _lastKeyEvent;
51+
52+
/// <summary>
53+
/// Fire an event when a key is pressed or released
54+
/// </summary>
55+
/// <param name="sender">The sender KeyMatrix</param>
56+
/// <param name="keyMatrixEvent">The key event</param>
57+
public delegate void KeyEventHandler(object sender, KeyMatrixEvent keyMatrixEvent);
58+
59+
/// <summary>
60+
/// The raised event
61+
/// </summary>
62+
public event KeyEventHandler? KeyEvent;
63+
64+
/// <summary>
65+
/// Initialize key matrix
66+
/// </summary>
67+
/// <param name="outputPins">Output pins</param>
68+
/// <param name="inputPins">Input pins</param>
69+
/// <param name="scanInterval">Scanning interval in milliseconds</param>
70+
/// <param name="gpioController">GPIO controller</param>
71+
/// <param name="shouldDispose">True to dispose the GpioController</param>
72+
public KeyMatrix(int[] outputPins, int[] inputPins, TimeSpan scanInterval, GpioController? gpioController = null, bool shouldDispose = true)
73+
{
74+
_shouldDispose = shouldDispose || gpioController == null;
75+
_gpioController = gpioController ?? new();
76+
77+
if (outputPins == null)
78+
{
79+
throw new ArgumentNullException(nameof(outputPins));
80+
}
81+
82+
if (outputPins is null or { Length:0 })
83+
{
84+
throw new ArgumentOutOfRangeException(nameof(outputPins), "The number of outputs must be at least 1");
85+
}
86+
87+
if (inputPins == null)
88+
{
89+
throw new ArgumentNullException(nameof(inputPins));
90+
}
91+
92+
if (inputPins is null or { Length: 0 })
93+
{
94+
throw new ArgumentOutOfRangeException(nameof(inputPins), "The number of inputs must be at least 1");
95+
}
96+
97+
_outputPins = outputPins;
98+
_inputPins = inputPins;
99+
_buttonValues = new PinValue[_outputPins.Length * _inputPins.Length];
100+
for (int i = 0; i < _buttonValues.Length; i++)
101+
{
102+
_buttonValues[i] = PinValue.Low;
103+
}
104+
105+
_pinsOpened = false;
106+
ScanInterval = scanInterval;
107+
OpenPins();
108+
}
109+
110+
/// <summary>
111+
/// Start listening to key events
112+
/// </summary>
113+
public void StartListeningKeyEvent()
114+
{
115+
if (_isRunning)
116+
{
117+
return;
118+
}
119+
120+
_isRunning = true;
121+
new Thread(() =>
122+
{
123+
LoopReadKey();
124+
}).Start();
125+
}
126+
127+
/// <summary>
128+
/// Stop listening to key events
129+
/// </summary>
130+
public void StopListeningKeyEvent()
131+
{
132+
_isRunning = false;
133+
}
134+
135+
private void LoopReadKey()
136+
{
137+
do
138+
{
139+
Thread.Sleep(ScanInterval);
140+
141+
_currentOutput = (_currentOutput + 1) % _outputPins.Length;
142+
_gpioController!.Write(_outputPins[_currentOutput], PinValue.High);
143+
144+
for (var i = 0; i < _inputPins.Length; i++)
145+
{
146+
int index = _currentOutput * _inputPins.Length + i;
147+
148+
PinValue oldValue = _buttonValues[index];
149+
PinValue newValue = _gpioController.Read(_inputPins[i]);
150+
_buttonValues[index] = newValue;
151+
152+
if (newValue != oldValue)
153+
{
154+
KeyEvent?.Invoke(this, new KeyMatrixEvent(newValue == PinValue.High ? PinEventTypes.Rising : PinEventTypes.Falling, _currentOutput, i));
155+
}
156+
}
157+
158+
_gpioController.Write(_outputPins[_currentOutput], PinValue.Low);
159+
}
160+
while (_pinsOpened && _isRunning);
161+
}
162+
163+
/// <summary>
164+
/// Blocks execution until a key event is received
165+
/// </summary>
166+
public KeyMatrixEvent? ReadKey()
167+
{
168+
_currentOutput--;
169+
KeyEvent += KeyMatrixKeyEvent;
170+
_isRunning = true;
171+
LoopReadKey();
172+
KeyEvent -= KeyMatrixKeyEvent;
173+
return _lastKeyEvent;
174+
}
175+
176+
private void KeyMatrixKeyEvent(object sender, KeyMatrixEvent keyMatrixEvent)
177+
{
178+
_isRunning = false;
179+
_lastKeyEvent = keyMatrixEvent;
180+
}
181+
182+
/// <inheritdoc/>
183+
public void Dispose()
184+
{
185+
ClosePins();
186+
187+
if (_shouldDispose)
188+
{
189+
_gpioController?.Dispose();
190+
_gpioController = null;
191+
}
192+
else
193+
{
194+
if (_gpioController is object && _pinsOpened)
195+
{
196+
ClosePins();
197+
}
198+
}
199+
}
200+
201+
private void OpenPins()
202+
{
203+
for (int i = 0; i < _outputPins.Length; i++)
204+
{
205+
_gpioController!.OpenPin(_outputPins[i], PinMode.Output);
206+
}
207+
208+
for (int i = 0; i < _inputPins.Length; i++)
209+
{
210+
_gpioController!.OpenPin(_inputPins[i], PinMode.Input);
211+
}
212+
213+
_pinsOpened = true;
214+
}
215+
216+
private void ClosePins()
217+
{
218+
_isRunning = false;
219+
_pinsOpened = false;
220+
Thread.Sleep(ScanInterval); // wait for current scan to complete
221+
222+
for (int i = 0; i < _outputPins.Length; i++)
223+
{
224+
_gpioController!.ClosePin(_outputPins[i]);
225+
}
226+
227+
for (int i = 0; i < _inputPins.Length; i++)
228+
{
229+
_gpioController!.ClosePin(_inputPins[i]);
230+
}
231+
}
232+
}
233+
}

Diff for: devices/KeyMatrix/KeyMatrix.nfproj

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<Project ToolsVersion="Current" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3+
<PropertyGroup Label="Globals">
4+
<NanoFrameworkProjectSystemPath>$(MSBuildToolsPath)..\..\..\nanoFramework\v1.0\</NanoFrameworkProjectSystemPath>
5+
</PropertyGroup>
6+
<Import Project="$(NanoFrameworkProjectSystemPath)NFProjectSystem.Default.props" Condition="Exists('$(NanoFrameworkProjectSystemPath)NFProjectSystem.Default.props')" />
7+
<PropertyGroup>
8+
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
9+
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
10+
<ProjectTypeGuids>{11A8DD76-328B-46DF-9F39-F559912D0360};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
11+
<ProjectGuid>{A364A5A4-B98B-4D0D-9B62-84EDFE654EDF}</ProjectGuid>
12+
<OutputType>Library</OutputType>
13+
<AppDesignerFolder>Properties</AppDesignerFolder>
14+
<FileAlignment>512</FileAlignment>
15+
<RootNamespace>Iot.Device.KeyMatrix</RootNamespace>
16+
<AssemblyName>Iot.Device.KeyMatrix</AssemblyName>
17+
<TargetFrameworkVersion>v1.0</TargetFrameworkVersion>
18+
<DocumentationFile>bin\$(Configuration)\Iot.Device.KeyMatrix.xml</DocumentationFile>
19+
<LangVersion>9.0</LangVersion>
20+
</PropertyGroup>
21+
<Import Project="$(NanoFrameworkProjectSystemPath)NFProjectSystem.props" Condition="Exists('$(NanoFrameworkProjectSystemPath)NFProjectSystem.props')" />
22+
<ItemGroup>
23+
<Reference Include="mscorlib">
24+
<HintPath>packages\nanoFramework.CoreLibrary.1.10.4-preview.11\lib\mscorlib.dll</HintPath>
25+
<Private>True</Private>
26+
</Reference>
27+
<Reference Include="nanoFramework.Runtime.Events, Version=1.9.0.0, Culture=neutral, PublicKeyToken=c07d481e9758c731">
28+
<HintPath>packages\nanoFramework.Runtime.Events.1.9.0-preview.26\lib\nanoFramework.Runtime.Events.dll</HintPath>
29+
<Private>True</Private>
30+
<SpecificVersion>True</SpecificVersion>
31+
</Reference>
32+
<Reference Include="System.Device.Gpio, Version=1.0.0.0, Culture=neutral, PublicKeyToken=c07d481e9758c731">
33+
<HintPath>packages\nanoFramework.System.Device.Gpio.1.0.0-preview.40\lib\System.Device.Gpio.dll</HintPath>
34+
<Private>True</Private>
35+
<SpecificVersion>True</SpecificVersion>
36+
</Reference>
37+
</ItemGroup>
38+
<ItemGroup>
39+
<Compile Include="SpanPinValue.cs" />
40+
<None Include="packages.config" />
41+
<Compile Include="KeyMatrix.cs" />
42+
<Compile Include="KeyMatrixEvent.cs" />
43+
<None Include="README.md" />
44+
</ItemGroup>
45+
<ItemGroup>
46+
<Compile Include="Properties\AssemblyInfo.cs" />
47+
<None Include="*.md" />
48+
</ItemGroup>
49+
<Import Project="$(NanoFrameworkProjectSystemPath)NFProjectSystem.CSharp.targets" Condition="Exists('$(NanoFrameworkProjectSystemPath)NFProjectSystem.CSharp.targets')" />
50+
<!-- INSERT IMPORTS HERE -->
51+
<ProjectExtensions>
52+
<ProjectCapabilities>
53+
<ProjectConfigurationsDeclaredAsItems />
54+
</ProjectCapabilities>
55+
</ProjectExtensions>
56+
<Import Project="packages\Nerdbank.GitVersioning.3.4.194\build\Nerdbank.GitVersioning.targets" Condition="Exists('packages\Nerdbank.GitVersioning.3.4.194\build\Nerdbank.GitVersioning.targets')" />
57+
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
58+
<PropertyGroup>
59+
<ErrorText> This project references NuGet package(s) that are missing on this computer.Enable NuGet Package Restore to download them.For more information, see http://go.microsoft.com/fwlink/?LinkID=322105.The missing file is {0}.</ErrorText>
60+
</PropertyGroup>
61+
<Error Condition="!Exists('packages\Nerdbank.GitVersioning.3.4.194\build\Nerdbank.GitVersioning.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\Nerdbank.GitVersioning.3.4.194\build\Nerdbank.GitVersioning.targets'))" />
62+
</Target>
63+
</Project>

Diff for: devices/KeyMatrix/KeyMatrix.nuspec

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<package xmlns="http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd">
3+
<metadata>
4+
<id>nanoFramework.Iot.Device.KeyMatrix</id>
5+
<version>$version$</version>
6+
<title>nanoFramework.Iot.Device.KeyMatrix</title>
7+
<authors>nanoFramework project contributors</authors>
8+
<owners>nanoFramework project contributors,dotnetfoundation</owners>
9+
<requireLicenseAcceptance>false</requireLicenseAcceptance>
10+
<license type="file">LICENSE.md</license>
11+
<releaseNotes>
12+
</releaseNotes>
13+
<developmentDependency>false</developmentDependency>
14+
<projectUrl>https://github.com/nanoframework/nanoFramework.IoT.Device</projectUrl>
15+
<iconUrl>https://secure.gravatar.com/avatar/97d0e092247f0716db6d4b47b7d1d1ad</iconUrl>
16+
<icon>images\nf-logo.png</icon>
17+
<repository type="git" url="https://github.com/nanoframework/nanoFramework.IoT.Device" commit="$commit$" />
18+
<copyright>Copyright (c) .NET Foundation and Contributors</copyright>
19+
<description>This package includes the .NET IoT Core binding Iot.Device.KeyMatrix for .NET nanoFramework C# projects.</description>
20+
<summary>Iot.Device.KeyMatrix assembly for .NET nanoFramework C# projects</summary>
21+
<tags>nanoFramework C# csharp netmf netnf Iot.Device.KeyMatrix</tags>
22+
<dependencies>
23+
<dependency id="nanoFramework.CoreLibrary" version="1.10.4-preview.11" />
24+
<dependency id="nanoFramework.Runtime.Events" version="1.9.0-preview.26" />
25+
<dependency id="nanoFramework.System.Device.Gpio" version="1.0.0-preview.40" />
26+
</dependencies>
27+
</metadata>
28+
<files>
29+
<file src="bin\Release\Iot.Device.KeyMatrix.dll" target="lib\Iot.Device.KeyMatrix.dll" />
30+
<file src="bin\Release\Iot.Device.KeyMatrix.pdb" target="lib\Iot.Device.KeyMatrix.pdb" />
31+
<file src="bin\Release\Iot.Device.KeyMatrix.pdbx" target="lib\Iot.Device.KeyMatrix.pdbx" />
32+
<file src="bin\Release\Iot.Device.KeyMatrix.pe" target="lib\Iot.Device.KeyMatrix.pe" />
33+
<file src="bin\Release\Iot.Device.KeyMatrix.xml" target="lib\Iot.Device.KeyMatrix.xml" />
34+
<file src="readme.md" target="" />
35+
<file src="..\..\assets\nf-logo.png" target="images" />
36+
<file src="..\..\LICENSE.md" target="" />
37+
</files>
38+
</package>

Diff for: devices/KeyMatrix/KeyMatrix.sln

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
Microsoft Visual Studio Solution File, Format Version 12.00
2+
# Visual Studio Version 16
3+
VisualStudioVersion = 16.0.30413.136
4+
MinimumVisualStudioVersion = 10.0.40219.1
5+
Project("{11A8DD76-328B-46DF-9F39-F559912D0360}") = "KeyMatrix", "KeyMatrix.nfproj", "{A364A5A4-B98B-4D0D-9B62-84EDFE654EDF}"
6+
EndProject
7+
Project("{11A8DD76-328B-46DF-9F39-F559912D0360}") = "KeyMatrix.Sample", "samples\KeyMatrix.Sample.nfproj", "{3FA2777B-29D6-4830-AB85-241C43AC1E62}"
8+
EndProject
9+
Global
10+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
11+
Debug|Any CPU = Debug|Any CPU
12+
Release|Any CPU = Release|Any CPU
13+
EndGlobalSection
14+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
15+
{A364A5A4-B98B-4D0D-9B62-84EDFE654EDF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
16+
{A364A5A4-B98B-4D0D-9B62-84EDFE654EDF}.Debug|Any CPU.Build.0 = Debug|Any CPU
17+
{A364A5A4-B98B-4D0D-9B62-84EDFE654EDF}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
18+
{A364A5A4-B98B-4D0D-9B62-84EDFE654EDF}.Release|Any CPU.ActiveCfg = Release|Any CPU
19+
{A364A5A4-B98B-4D0D-9B62-84EDFE654EDF}.Release|Any CPU.Build.0 = Release|Any CPU
20+
{A364A5A4-B98B-4D0D-9B62-84EDFE654EDF}.Release|Any CPU.Deploy.0 = Release|Any CPU
21+
{3FA2777B-29D6-4830-AB85-241C43AC1E62}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
22+
{3FA2777B-29D6-4830-AB85-241C43AC1E62}.Debug|Any CPU.Build.0 = Debug|Any CPU
23+
{3FA2777B-29D6-4830-AB85-241C43AC1E62}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
24+
{3FA2777B-29D6-4830-AB85-241C43AC1E62}.Release|Any CPU.ActiveCfg = Release|Any CPU
25+
{3FA2777B-29D6-4830-AB85-241C43AC1E62}.Release|Any CPU.Build.0 = Release|Any CPU
26+
{3FA2777B-29D6-4830-AB85-241C43AC1E62}.Release|Any CPU.Deploy.0 = Release|Any CPU
27+
EndGlobalSection
28+
GlobalSection(SolutionProperties) = preSolution
29+
HideSolutionNode = FALSE
30+
EndGlobalSection
31+
GlobalSection(ExtensibilityGlobals) = postSolution
32+
SolutionGuid = {BCA9EAA0-A90B-49C3-90F4-A1472EA7701D}
33+
EndGlobalSection
34+
EndGlobal

0 commit comments

Comments
 (0)