├── .gitignore
├── AdafruitClassLibrary.sln
├── AdafruitClassLibrary
├── AdafruitClassLibrary.csproj
├── BNO055.cs
├── CharLCDPlate.cs
├── DotStar.cs
├── GPS.cs
├── I2CBase.cs
├── MCP23017.cs
├── MotorHat.cs
├── PCA9685.cs
├── Properties
│ ├── AdafruitClassLibrary.rd.xml
│ └── AssemblyInfo.cs
└── project.json
├── LICENSE.txt
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.userosscache
8 | *.sln.docstates
9 |
10 | # User-specific files (MonoDevelop/Xamarin Studio)
11 | *.userprefs
12 |
13 | # Build results
14 | [Dd]ebug/
15 | [Dd]ebugPublic/
16 | [Rr]elease/
17 | [Rr]eleases/
18 | [Xx]64/
19 | [Xx]86/
20 | [Bb]uild/
21 | bld/
22 | [Bb]in/
23 | [Oo]bj/
24 |
25 | # Visual Studio 2015 cache/options directory
26 | .vs/
27 | # Uncomment if you have tasks that create the project's static files in wwwroot
28 | #wwwroot/
29 |
30 | # MSTest test Results
31 | [Tt]est[Rr]esult*/
32 | [Bb]uild[Ll]og.*
33 |
34 | # NUNIT
35 | *.VisualState.xml
36 | TestResult.xml
37 |
38 | # Build Results of an ATL Project
39 | [Dd]ebugPS/
40 | [Rr]eleasePS/
41 | dlldata.c
42 |
43 | # DNX
44 | project.lock.json
45 | artifacts/
46 |
47 | *_i.c
48 | *_p.c
49 | *_i.h
50 | *.ilk
51 | *.meta
52 | *.obj
53 | *.pch
54 | *.pdb
55 | *.pgc
56 | *.pgd
57 | *.rsp
58 | *.sbr
59 | *.tlb
60 | *.tli
61 | *.tlh
62 | *.tmp
63 | *.tmp_proj
64 | *.log
65 | *.vspscc
66 | *.vssscc
67 | .builds
68 | *.pidb
69 | *.svclog
70 | *.scc
71 |
72 | # Chutzpah Test files
73 | _Chutzpah*
74 |
75 | # Visual C++ cache files
76 | ipch/
77 | *.aps
78 | *.ncb
79 | *.opendb
80 | *.opensdf
81 | *.sdf
82 | *.cachefile
83 | *.VC.db
84 |
85 | # Visual Studio profiler
86 | *.psess
87 | *.vsp
88 | *.vspx
89 | *.sap
90 |
91 | # TFS 2012 Local Workspace
92 | $tf/
93 |
94 | # Guidance Automation Toolkit
95 | *.gpState
96 |
97 | # ReSharper is a .NET coding add-in
98 | _ReSharper*/
99 | *.[Rr]e[Ss]harper
100 | *.DotSettings.user
101 |
102 | # JustCode is a .NET coding add-in
103 | .JustCode
104 |
105 | # TeamCity is a build add-in
106 | _TeamCity*
107 |
108 | # DotCover is a Code Coverage Tool
109 | *.dotCover
110 |
111 | # NCrunch
112 | _NCrunch_*
113 | .*crunch*.local.xml
114 | nCrunchTemp_*
115 |
116 | # MightyMoose
117 | *.mm.*
118 | AutoTest.Net/
119 |
120 | # Web workbench (sass)
121 | .sass-cache/
122 |
123 | # Installshield output folder
124 | [Ee]xpress/
125 |
126 | # DocProject is a documentation generator add-in
127 | DocProject/buildhelp/
128 | DocProject/Help/*.HxT
129 | DocProject/Help/*.HxC
130 | DocProject/Help/*.hhc
131 | DocProject/Help/*.hhk
132 | DocProject/Help/*.hhp
133 | DocProject/Help/Html2
134 | DocProject/Help/html
135 |
136 | # Click-Once directory
137 | publish/
138 |
139 | # Publish Web Output
140 | *.[Pp]ublish.xml
141 | *.azurePubxml
142 |
143 | # TODO: Un-comment the next line if you do not want to checkin
144 | # your web deploy settings because they may include unencrypted
145 | # passwords
146 | #*.pubxml
147 | *.publishproj
148 |
149 | # NuGet Packages
150 | *.nupkg
151 | # The packages folder can be ignored because of Package Restore
152 | **/packages/*
153 | # except build/, which is used as an MSBuild target.
154 | !**/packages/build/
155 | # Uncomment if necessary however generally it will be regenerated when needed
156 | #!**/packages/repositories.config
157 | # NuGet v3's project.json files produces more ignoreable files
158 | *.nuget.props
159 | *.nuget.targets
160 |
161 | # Microsoft Azure Build Output
162 | csx/
163 | *.build.csdef
164 |
165 | # Microsoft Azure Emulator
166 | ecf/
167 | rcf/
168 |
169 | # Microsoft Azure ApplicationInsights config file
170 | ApplicationInsights.config
171 |
172 | # Windows Store app package directory
173 | AppPackages/
174 | BundleArtifacts/
175 |
176 | # Visual Studio cache files
177 | # files ending in .cache can be ignored
178 | *.[Cc]ache
179 | # but keep track of directories ending in .cache
180 | !*.[Cc]ache/
181 |
182 | # Others
183 | ClientBin/
184 | [Ss]tyle[Cc]op.*
185 | ~$*
186 | *~
187 | *.dbmdl
188 | *.dbproj.schemaview
189 | *.pfx
190 | *.publishsettings
191 | node_modules/
192 | orleans.codegen.cs
193 |
194 | # RIA/Silverlight projects
195 | Generated_Code/
196 |
197 | # Backup & report files from converting an old project file
198 | # to a newer Visual Studio version. Backup files are not needed,
199 | # because we have git ;-)
200 | _UpgradeReport_Files/
201 | Backup*/
202 | UpgradeLog*.XML
203 | UpgradeLog*.htm
204 |
205 | # SQL Server files
206 | *.mdf
207 | *.ldf
208 |
209 | # Business Intelligence projects
210 | *.rdl.data
211 | *.bim.layout
212 | *.bim_*.settings
213 |
214 | # Microsoft Fakes
215 | FakesAssemblies/
216 |
217 | # GhostDoc plugin setting file
218 | *.GhostDoc.xml
219 |
220 | # Node.js Tools for Visual Studio
221 | .ntvs_analysis.dat
222 |
223 | # Visual Studio 6 build log
224 | *.plg
225 |
226 | # Visual Studio 6 workspace options file
227 | *.opt
228 |
229 | # Visual Studio LightSwitch build output
230 | **/*.HTMLClient/GeneratedArtifacts
231 | **/*.DesktopClient/GeneratedArtifacts
232 | **/*.DesktopClient/ModelManifest.xml
233 | **/*.Server/GeneratedArtifacts
234 | **/*.Server/ModelManifest.xml
235 | _Pvt_Extensions
236 |
237 | # LightSwitch generated files
238 | GeneratedArtifacts/
239 | ModelManifest.xml
240 |
241 | # Paket dependency manager
242 | .paket/paket.exe
243 |
244 | # FAKE - F# Make
245 | .fake/
246 | /nuget.exe
247 | /AdafruitClassLibrary/DiagnosticBuild.txt
248 | /AdafruitClassLibrary/DiagnosticBuild.zip
249 | /AdafruitClassLibrary/nuget.exe
250 | /AdafruitClassLibrary/oldAdafruitClassLibrary.nuspec
251 | /AdafruitClassLibrary/AdafruitClassLibrary.nuspec
252 | /nuget commands.txt
253 | /BNO055 Demo
254 |
--------------------------------------------------------------------------------
/AdafruitClassLibrary.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 14
4 | VisualStudioVersion = 14.0.25420.1
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AdafruitClassLibrary", "AdafruitClassLibrary\AdafruitClassLibrary.csproj", "{3F9D3776-590C-4881-8E27-E19571611E6C}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Debug|ARM = Debug|ARM
12 | Debug|x64 = Debug|x64
13 | Debug|x86 = Debug|x86
14 | Release|Any CPU = Release|Any CPU
15 | Release|ARM = Release|ARM
16 | Release|x64 = Release|x64
17 | Release|x86 = Release|x86
18 | EndGlobalSection
19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
20 | {3F9D3776-590C-4881-8E27-E19571611E6C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21 | {3F9D3776-590C-4881-8E27-E19571611E6C}.Debug|Any CPU.Build.0 = Debug|Any CPU
22 | {3F9D3776-590C-4881-8E27-E19571611E6C}.Debug|ARM.ActiveCfg = Debug|ARM
23 | {3F9D3776-590C-4881-8E27-E19571611E6C}.Debug|ARM.Build.0 = Debug|ARM
24 | {3F9D3776-590C-4881-8E27-E19571611E6C}.Debug|x64.ActiveCfg = Debug|x64
25 | {3F9D3776-590C-4881-8E27-E19571611E6C}.Debug|x64.Build.0 = Debug|x64
26 | {3F9D3776-590C-4881-8E27-E19571611E6C}.Debug|x86.ActiveCfg = Debug|x86
27 | {3F9D3776-590C-4881-8E27-E19571611E6C}.Debug|x86.Build.0 = Debug|x86
28 | {3F9D3776-590C-4881-8E27-E19571611E6C}.Release|Any CPU.ActiveCfg = Release|Any CPU
29 | {3F9D3776-590C-4881-8E27-E19571611E6C}.Release|Any CPU.Build.0 = Release|Any CPU
30 | {3F9D3776-590C-4881-8E27-E19571611E6C}.Release|ARM.ActiveCfg = Release|ARM
31 | {3F9D3776-590C-4881-8E27-E19571611E6C}.Release|ARM.Build.0 = Release|ARM
32 | {3F9D3776-590C-4881-8E27-E19571611E6C}.Release|x64.ActiveCfg = Release|x64
33 | {3F9D3776-590C-4881-8E27-E19571611E6C}.Release|x64.Build.0 = Release|x64
34 | {3F9D3776-590C-4881-8E27-E19571611E6C}.Release|x86.ActiveCfg = Release|x86
35 | {3F9D3776-590C-4881-8E27-E19571611E6C}.Release|x86.Build.0 = Release|x86
36 | EndGlobalSection
37 | GlobalSection(SolutionProperties) = preSolution
38 | HideSolutionNode = FALSE
39 | EndGlobalSection
40 | EndGlobal
41 |
--------------------------------------------------------------------------------
/AdafruitClassLibrary/AdafruitClassLibrary.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {3F9D3776-590C-4881-8E27-E19571611E6C}
8 | Library
9 | Properties
10 | AdafruitClassLibrary
11 | AdafruitClassLibrary
12 | en-US
13 | UAP
14 | 10.0.14393.0
15 | 10.0.10586.0
16 | 14
17 | 512
18 | {A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
19 |
20 |
21 | AnyCPU
22 | true
23 | full
24 | false
25 | bin\Debug\
26 | DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP
27 | prompt
28 | 4
29 |
30 |
31 | AnyCPU
32 | pdbonly
33 | true
34 | bin\Release\
35 | TRACE;NETFX_CORE;WINDOWS_UWP
36 | prompt
37 | 4
38 |
39 |
40 | x86
41 | true
42 | bin\x86\Debug\
43 | DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP
44 | ;2008
45 | full
46 | x86
47 | false
48 | prompt
49 |
50 |
51 | x86
52 | bin\x86\Release\
53 | TRACE;NETFX_CORE;WINDOWS_UWP
54 | true
55 | ;2008
56 | pdbonly
57 | x86
58 | false
59 | prompt
60 |
61 |
62 | ARM
63 | true
64 | bin\ARM\Debug\
65 | DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP
66 | ;2008
67 | full
68 | ARM
69 | false
70 | prompt
71 |
72 |
73 | ARM
74 | bin\ARM\Release\
75 | TRACE;NETFX_CORE;WINDOWS_UWP
76 | true
77 | ;2008
78 | full
79 | ARM
80 | false
81 | prompt
82 | true
83 |
84 |
85 | x64
86 | true
87 | bin\x64\Debug\
88 | DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP
89 | ;2008
90 | full
91 | x64
92 | false
93 | prompt
94 |
95 |
96 | x64
97 | bin\x64\Release\
98 | TRACE;NETFX_CORE;WINDOWS_UWP
99 | true
100 | ;2008
101 | pdbonly
102 | x64
103 | false
104 | prompt
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 | Windows IoT Extensions for the UWP %28WindowsIoT, Version=10.0.14393.0%29
125 |
126 |
127 |
128 | 14.0
129 |
130 |
131 | false
132 |
133 |
134 |
141 |
--------------------------------------------------------------------------------
/AdafruitClassLibrary/BNO055.cs:
--------------------------------------------------------------------------------
1 | /*------------------------------------------------------------------------
2 | Adafruit Class Library for Windows Core IoT: BNO055 IMU chip.
3 |
4 | Written by Rick Lesniak for Adafruit Industries.
5 |
6 | Adafruit invests time and resources providing this open source code,
7 | please support Adafruit and open-source hardware by purchasing products
8 | from Adafruit!
9 |
10 | ------------------------------------------------------------------------*/
11 |
12 | using System;
13 | using System.Collections.Generic;
14 | using System.Linq;
15 | using System.Threading.Tasks;
16 | using Windows.Devices.Enumeration;
17 | using Windows.Devices.Gpio;
18 | using Windows.Devices.SerialCommunication;
19 | using Windows.Storage.Streams;
20 |
21 | namespace AdafruitClassLibrary
22 | {
23 | public class Bno055
24 | {
25 | #region class definitions
26 |
27 | public class Revisions
28 | {
29 | public int Software { get; set; }
30 | public byte Bootloader { get; set; }
31 | public byte AccelID { get; set; }
32 | public byte MagID { get; set; }
33 | public byte GyroID { get; set; }
34 | }
35 |
36 | public class SystemStatus
37 | {
38 | public byte StatusReg { get; set; }
39 | public byte SelfTestResult { get; set; }
40 | public byte ErrorReg { get; set; }
41 | }
42 |
43 | public class CalibrationStatus
44 | {
45 | public byte System { get; set; }
46 | public byte Gyro { get; set; }
47 | public byte Accel { get; set; }
48 | public byte Mag { get; set; }
49 | }
50 |
51 | public class AxisRemap
52 | {
53 | public byte X { get; set; }
54 | public byte Y { get; set; }
55 | public byte Z { get; set; }
56 | public byte XSign { get; set; }
57 | public byte YSign { get; set; }
58 | public byte ZSign { get; set; }
59 | }
60 |
61 | public class Coords
62 | {
63 | public double X { get; set; }
64 | public double Y { get; set; }
65 | public double Z { get; set; }
66 | }
67 |
68 | public class Euler
69 | {
70 | public double Heading { get; set; }
71 | public double Roll { get; set; }
72 | public double Pitch { get; set; }
73 | }
74 |
75 | public class Quaternion
76 | {
77 | public double W { get; set; }
78 | public double X { get; set; }
79 | public double Y { get; set; }
80 | public double Z { get; set; }
81 | }
82 |
83 | #endregion class definitions
84 |
85 | #region Register Definitions
86 |
87 | // I2C addresses
88 | private const byte BNO055_ADDRESS_A = 0x28;
89 |
90 | private const byte BNO055_ADDRESS_B = 0x29;
91 | private const byte BNO055_ID = 0xA0;
92 |
93 | // Page id register definition
94 | private const byte BNO055_PAGE_ID_ADDR = 0X07;
95 |
96 | // PAGE0 REGISTER DEFINITION START
97 | private const byte BNO055_CHIP_ID_ADDR = 0x00;
98 |
99 | private const byte BNO055_ACCEL_REV_ID_ADDR = 0x01;
100 | private const byte BNO055_MAG_REV_ID_ADDR = 0x02;
101 | private const byte BNO055_GYRO_REV_ID_ADDR = 0x03;
102 | private const byte BNO055_SW_REV_ID_LSB_ADDR = 0x04;
103 | private const byte BNO055_SW_REV_ID_MSB_ADDR = 0x05;
104 | private const byte BNO055_BL_REV_ID_ADDR = 0X06;
105 |
106 | // Accel data register
107 | private const byte BNO055_ACCEL_DATA_X_LSB_ADDR = 0X08;
108 |
109 | private const byte BNO055_ACCEL_DATA_X_MSB_ADDR = 0X09;
110 | private const byte BNO055_ACCEL_DATA_Y_LSB_ADDR = 0X0A;
111 | private const byte BNO055_ACCEL_DATA_Y_MSB_ADDR = 0X0B;
112 | private const byte BNO055_ACCEL_DATA_Z_LSB_ADDR = 0X0C;
113 | private const byte BNO055_ACCEL_DATA_Z_MSB_ADDR = 0X0D;
114 |
115 | // Mag data register
116 | private const byte BNO055_MAG_DATA_X_LSB_ADDR = 0X0E;
117 |
118 | private const byte BNO055_MAG_DATA_X_MSB_ADDR = 0X0F;
119 | private const byte BNO055_MAG_DATA_Y_LSB_ADDR = 0X10;
120 | private const byte BNO055_MAG_DATA_Y_MSB_ADDR = 0X11;
121 | private const byte BNO055_MAG_DATA_Z_LSB_ADDR = 0X12;
122 | private const byte BNO055_MAG_DATA_Z_MSB_ADDR = 0X13;
123 |
124 | // Gyro data registers
125 | private const byte BNO055_GYRO_DATA_X_LSB_ADDR = 0X14;
126 |
127 | private const byte BNO055_GYRO_DATA_X_MSB_ADDR = 0X15;
128 | private const byte BNO055_GYRO_DATA_Y_LSB_ADDR = 0X16;
129 | private const byte BNO055_GYRO_DATA_Y_MSB_ADDR = 0X17;
130 | private const byte BNO055_GYRO_DATA_Z_LSB_ADDR = 0X18;
131 | private const byte BNO055_GYRO_DATA_Z_MSB_ADDR = 0X19;
132 |
133 | // Euler data registers
134 | private const byte BNO055_EULER_H_LSB_ADDR = 0X1A;
135 |
136 | private const byte BNO055_EULER_H_MSB_ADDR = 0X1B;
137 | private const byte BNO055_EULER_R_LSB_ADDR = 0X1C;
138 | private const byte BNO055_EULER_R_MSB_ADDR = 0X1D;
139 | private const byte BNO055_EULER_P_LSB_ADDR = 0X1E;
140 | private const byte BNO055_EULER_P_MSB_ADDR = 0X1F;
141 |
142 | // Quaternion data registers
143 | private const byte BNO055_QUATERNION_DATA_W_LSB_ADDR = 0X20;
144 |
145 | private const byte BNO055_QUATERNION_DATA_W_MSB_ADDR = 0X21;
146 | private const byte BNO055_QUATERNION_DATA_X_LSB_ADDR = 0X22;
147 | private const byte BNO055_QUATERNION_DATA_X_MSB_ADDR = 0X23;
148 | private const byte BNO055_QUATERNION_DATA_Y_LSB_ADDR = 0X24;
149 | private const byte BNO055_QUATERNION_DATA_Y_MSB_ADDR = 0X25;
150 | private const byte BNO055_QUATERNION_DATA_Z_LSB_ADDR = 0X26;
151 | private const byte BNO055_QUATERNION_DATA_Z_MSB_ADDR = 0X27;
152 |
153 | // Linear acceleration data registers
154 | private const byte BNO055_LINEAR_ACCEL_DATA_X_LSB_ADDR = 0X28;
155 |
156 | private const byte BNO055_LINEAR_ACCEL_DATA_X_MSB_ADDR = 0X29;
157 | private const byte BNO055_LINEAR_ACCEL_DATA_Y_LSB_ADDR = 0X2A;
158 | private const byte BNO055_LINEAR_ACCEL_DATA_Y_MSB_ADDR = 0X2B;
159 | private const byte BNO055_LINEAR_ACCEL_DATA_Z_LSB_ADDR = 0X2C;
160 | private const byte BNO055_LINEAR_ACCEL_DATA_Z_MSB_ADDR = 0X2D;
161 |
162 | // Gravity data registers
163 | private const byte BNO055_GRAVITY_DATA_X_LSB_ADDR = 0X2E;
164 |
165 | private const byte BNO055_GRAVITY_DATA_X_MSB_ADDR = 0X2F;
166 | private const byte BNO055_GRAVITY_DATA_Y_LSB_ADDR = 0X30;
167 | private const byte BNO055_GRAVITY_DATA_Y_MSB_ADDR = 0X31;
168 | private const byte BNO055_GRAVITY_DATA_Z_LSB_ADDR = 0X32;
169 | private const byte BNO055_GRAVITY_DATA_Z_MSB_ADDR = 0X33;
170 |
171 | // Temperature data register
172 | private const byte BNO055_TEMP_ADDR = 0X34;
173 |
174 | // Status registers
175 | private const byte BNO055_CALIB_STAT_ADDR = 0X35;
176 |
177 | private const byte BNO055_SELFTEST_RESULT_ADDR = 0X36;
178 | private const byte BNO055_INTR_STAT_ADDR = 0X37;
179 |
180 | private const byte BNO055_SYS_CLK_STAT_ADDR = 0X38;
181 | private const byte BNO055_SYS_STAT_ADDR = 0X39;
182 | private const byte BNO055_SYS_ERR_ADDR = 0X3A;
183 |
184 | // Unit selection register
185 | private const byte BNO055_UNIT_SEL_ADDR = 0X3B;
186 |
187 | private const byte BNO055_DATA_SELECT_ADDR = 0X3C;
188 |
189 | // Mode registers
190 | private const byte BNO055_OPR_MODE_ADDR = 0X3D;
191 |
192 | private const byte BNO055_PWR_MODE_ADDR = 0X3E;
193 |
194 | private const byte BNO055_SYS_TRIGGER_ADDR = 0X3F;
195 | private const byte BNO055_TEMP_SOURCE_ADDR = 0X40;
196 |
197 | // Axis remap registers
198 | private const byte BNO055_AXIS_MAP_CONFIG_ADDR = 0X41;
199 |
200 | private const byte BNO055_AXIS_MAP_SIGN_ADDR = 0X42;
201 |
202 | // Axis remap values
203 | private const byte AXIS_REMAP_X = 0x00;
204 |
205 | private const byte AXIS_REMAP_Y = 0x01;
206 | private const byte AXIS_REMAP_Z = 0x02;
207 | private const byte AXIS_REMAP_POSITIVE = 0x00;
208 | private const byte AXIS_REMAP_NEGATIVE = 0x01;
209 |
210 | // SIC registers
211 | private const byte BNO055_SIC_MATRIX_0_LSB_ADDR = 0X43;
212 |
213 | private const byte BNO055_SIC_MATRIX_0_MSB_ADDR = 0X44;
214 | private const byte BNO055_SIC_MATRIX_1_LSB_ADDR = 0X45;
215 | private const byte BNO055_SIC_MATRIX_1_MSB_ADDR = 0X46;
216 | private const byte BNO055_SIC_MATRIX_2_LSB_ADDR = 0X47;
217 | private const byte BNO055_SIC_MATRIX_2_MSB_ADDR = 0X48;
218 | private const byte BNO055_SIC_MATRIX_3_LSB_ADDR = 0X49;
219 | private const byte BNO055_SIC_MATRIX_3_MSB_ADDR = 0X4A;
220 | private const byte BNO055_SIC_MATRIX_4_LSB_ADDR = 0X4B;
221 | private const byte BNO055_SIC_MATRIX_4_MSB_ADDR = 0X4C;
222 | private const byte BNO055_SIC_MATRIX_5_LSB_ADDR = 0X4D;
223 | private const byte BNO055_SIC_MATRIX_5_MSB_ADDR = 0X4E;
224 | private const byte BNO055_SIC_MATRIX_6_LSB_ADDR = 0X4F;
225 | private const byte BNO055_SIC_MATRIX_6_MSB_ADDR = 0X50;
226 | private const byte BNO055_SIC_MATRIX_7_LSB_ADDR = 0X51;
227 | private const byte BNO055_SIC_MATRIX_7_MSB_ADDR = 0X52;
228 | private const byte BNO055_SIC_MATRIX_8_LSB_ADDR = 0X53;
229 | private const byte BNO055_SIC_MATRIX_8_MSB_ADDR = 0X54;
230 |
231 | // Accelerometer Offset registers
232 | private const byte ACCEL_OFFSET_X_LSB_ADDR = 0X55;
233 |
234 | private const byte ACCEL_OFFSET_X_MSB_ADDR = 0X56;
235 | private const byte ACCEL_OFFSET_Y_LSB_ADDR = 0X57;
236 | private const byte ACCEL_OFFSET_Y_MSB_ADDR = 0X58;
237 | private const byte ACCEL_OFFSET_Z_LSB_ADDR = 0X59;
238 | private const byte ACCEL_OFFSET_Z_MSB_ADDR = 0X5A;
239 |
240 | // Magnetometer Offset registers
241 | private const byte MAG_OFFSET_X_LSB_ADDR = 0X5B;
242 |
243 | private const byte MAG_OFFSET_X_MSB_ADDR = 0X5C;
244 | private const byte MAG_OFFSET_Y_LSB_ADDR = 0X5D;
245 | private const byte MAG_OFFSET_Y_MSB_ADDR = 0X5E;
246 | private const byte MAG_OFFSET_Z_LSB_ADDR = 0X5F;
247 | private const byte MAG_OFFSET_Z_MSB_ADDR = 0X60;
248 |
249 | // Gyroscope Offset register s
250 | private const byte GYRO_OFFSET_X_LSB_ADDR = 0X61;
251 |
252 | private const byte GYRO_OFFSET_X_MSB_ADDR = 0X62;
253 | private const byte GYRO_OFFSET_Y_LSB_ADDR = 0X63;
254 | private const byte GYRO_OFFSET_Y_MSB_ADDR = 0X64;
255 | private const byte GYRO_OFFSET_Z_LSB_ADDR = 0X65;
256 | private const byte GYRO_OFFSET_Z_MSB_ADDR = 0X66;
257 |
258 | // Radius registers
259 | private const byte ACCEL_RADIUS_LSB_ADDR = 0X67;
260 |
261 | private const byte ACCEL_RADIUS_MSB_ADDR = 0X68;
262 | private const byte MAG_RADIUS_LSB_ADDR = 0X69;
263 | private const byte MAG_RADIUS_MSB_ADDR = 0X6A;
264 |
265 | // Power modes
266 | private const byte POWER_MODE_NORMAL = 0X00;
267 |
268 | private const byte POWER_MODE_LOWPOWER = 0X01;
269 | private const byte POWER_MODE_SUSPEND = 0X02;
270 |
271 | // Operation mode settings
272 | public enum OperationMode
273 | {
274 | OPERATION_MODE_CONFIG = 0X00,
275 | OPERATION_MODE_ACCONLY = 0X01,
276 | OPERATION_MODE_MAGONLY = 0X02,
277 | OPERATION_MODE_GYRONLY = 0X03,
278 | OPERATION_MODE_ACCMAG = 0X04,
279 | OPERATION_MODE_ACCGYRO = 0X05,
280 | OPERATION_MODE_MAGGYRO = 0X06,
281 | OPERATION_MODE_AMG = 0X07,
282 | OPERATION_MODE_IMUPLUS = 0X08,
283 | OPERATION_MODE_COMPASS = 0X09,
284 | OPERATION_MODE_M4G = 0X0A,
285 | OPERATION_MODE_NDOF_FMC_OFF = 0X0B,
286 | OPERATION_MODE_NDOF = 0X0C
287 | };
288 |
289 | #endregion Register Definitions
290 |
291 | #region Properties
292 |
293 | private SerialDevice SerialPort { get; set; }
294 | private DataWriter DataWriterObject { get; set; }
295 | private DataReader DataReaderObject { get; set; }
296 | private GpioPin ResetPin { get; set; }
297 | private OperationMode OperatingMode { get; set; }
298 |
299 | #endregion Properties
300 |
301 | #region Constructor
302 |
303 | public Bno055()
304 | {
305 | SerialPort = null;
306 | DataWriterObject = null;
307 | DataReaderObject = null;
308 | }
309 |
310 | #endregion Constructor
311 |
312 | #region SerialControl
313 |
314 | ///
315 | /// Connected
316 | /// Predicate returns true if UART is connected
317 | ///
318 | /// bool
319 | public bool Connected
320 | {
321 | get { return SerialPort != null; }
322 | }
323 |
324 | ///
325 | /// ConnectToUART
326 | /// - Use SerialDevice.GetDeviceSelector to find serial device named "UART0".
327 | /// This is the built-in Raspberry Pi serial port.
328 | ///
329 | ///
330 | ///
331 | /// async Task
332 | public async Task ConnectToUARTAsync(string uartID = "UART0")
333 | {
334 | try
335 | {
336 | string aqs = SerialDevice.GetDeviceSelector(uartID);
337 | var dis = await DeviceInformation.FindAllAsync(aqs);
338 |
339 | if (dis.Count > 0)
340 | {
341 | DeviceInformation uart = dis[0];
342 |
343 | SerialPort = await SerialDevice.FromIdAsync(uart.Id);
344 | // Configure serial settings
345 | if (null != SerialPort)
346 | {
347 | SerialPort.WriteTimeout = TimeSpan.FromMilliseconds(100);
348 | SerialPort.ReadTimeout = TimeSpan.FromMilliseconds(100);
349 | SerialPort.BaudRate = 115200;
350 | SerialPort.Parity = SerialParity.None;
351 | SerialPort.StopBits = SerialStopBitCount.One;
352 | SerialPort.DataBits = 8;
353 | SerialPort.Handshake = SerialHandshake.None;
354 |
355 | // Create the DataReader object and attach to InputStream
356 | DataReaderObject = new DataReader(SerialPort.InputStream);
357 | // Create the DataWriter object and attach to OutputStream
358 | DataWriterObject = new DataWriter(SerialPort.OutputStream);
359 | }
360 | }
361 | }
362 | catch (Exception ex)
363 | {
364 | System.Diagnostics.Debug.WriteLine(string.Format("Error creating Serial Port: {0}", ex.Message));
365 | if (null != SerialPort)
366 | SerialPort.Dispose();
367 | SerialPort = null;
368 | }
369 | }
370 |
371 | ///
372 | /// DisconnectFromUART
373 | /// Disconnects from the UART
374 | ///
375 | public void DisconnectFromUART()
376 | {
377 | if (null != DataReaderObject)
378 | {
379 | DataReaderObject.Dispose();
380 | }
381 | if (null != DataWriterObject)
382 | {
383 | DataWriterObject.Dispose();
384 | }
385 | if (null != SerialPort)
386 | {
387 | SerialPort.Dispose();
388 | SerialPort = null;
389 | }
390 | }
391 |
392 | #endregion SerialControl
393 |
394 | #region Serial IO
395 |
396 | ///
397 | /// SendCommand
398 | /// Asynchronous task to write to GPS through UART
399 | ///
400 | ///
401 | /// async Task
402 | private async Task SendCommandAsync(byte[] command)
403 | {
404 | //Launch the storeAsync task to perform the write
405 | Task storeAsyncTask;
406 |
407 | if (command.Length != 0)
408 | {
409 | // Load the text from the sendText input text box to the dataWriter object
410 | DataWriterObject.WriteBytes(command);
411 |
412 | // Launch an async task to complete the write operation
413 | storeAsyncTask = DataWriterObject.StoreAsync().AsTask();
414 |
415 | UInt32 bytesWritten = await storeAsyncTask;
416 | if (bytesWritten != command.Length)
417 | throw new Exception("Bytes written does not match command length");
418 | }
419 | }
420 |
421 | public async Task LoadResponseAsync(uint length)
422 | {
423 | Task loadAsyncTask;
424 |
425 | loadAsyncTask = DataReaderObject.LoadAsync(length).AsTask();
426 | uint bytesRead = await loadAsyncTask;
427 | if (length != bytesRead)
428 | throw new Exception("ReadDataAsync timeout");
429 | }
430 |
431 | ///
432 | /// ReadData
433 | ///
434 | /// Array of data read from device
435 | ///
436 | private byte[] ReadData(byte[] command, uint readLength)
437 | {
438 | byte[] headerBuffer = new byte[2];
439 | byte[] returnBuffer = new byte[readLength];
440 |
441 | SendCommandAsync(command).Wait();
442 |
443 | LoadResponseAsync(2).Wait();
444 | DataReaderObject.ReadBytes(headerBuffer);
445 |
446 | if (headerBuffer[0] != 0xBB)
447 | throw new Exception(string.Format("ReadData error 0x{0:x2}{0:x2}", headerBuffer[0], headerBuffer[1]));
448 | if (headerBuffer[1] != readLength)
449 | throw new Exception(string.Format("ReadData error: failed to read {0} bytes. Read {1}", readLength, headerBuffer[1]));
450 |
451 | LoadResponseAsync(readLength).Wait();
452 | DataReaderObject.ReadBytes(returnBuffer);
453 |
454 | return returnBuffer;
455 | }
456 |
457 | ///
458 | /// WriteData
459 | ///
460 | /// command data to send
461 | /// true if ack is epected
462 | /// number of retries
463 | ///
464 | private byte[] WriteData(byte[] command, bool ack = true, int retries = 5)
465 | {
466 | byte[] ackBuffer = new byte[2];
467 | bool done = false;
468 |
469 | while ((!done) && (retries-- > 0))
470 | {
471 | SendCommandAsync(command).Wait();
472 |
473 | if (ack)
474 | {
475 | LoadResponseAsync(2).Wait();
476 | DataReaderObject.ReadBytes(ackBuffer);
477 | done = 0xEE07 != (ackBuffer[0] << 8 | ackBuffer[1]);
478 | }
479 | else
480 | done = true;
481 | }
482 |
483 | if (!done)
484 | throw new Exception("WriteData retries exceeded");
485 | return ackBuffer;
486 | }
487 |
488 | ///
489 | /// SendCommand
490 | ///
491 | /// command string for device
492 | private void WriteRegister(byte address, byte data, bool ack = true)
493 | {
494 | byte[] writeBuffer = new byte[] { 0xAA, 0x00, address, 1, data };
495 | byte[] response;
496 | try
497 | {
498 | response = WriteData(writeBuffer, ack);
499 | if (ack)
500 | {
501 | if (0xEE01 != (response[0] << 8 | response[1]))
502 | throw new Exception(string.Format("WriteRegister returned 0x{0:x2},0x{1:x2}", response[0], response[1]));
503 | }
504 | }
505 | catch (Exception ex)
506 | {
507 | System.Diagnostics.Debug.WriteLine(string.Format("WriteRegister error: {0}", ex.Message));
508 | }
509 | }
510 |
511 | private byte ReadRegister(byte register)
512 | {
513 | byte[] commandBuffer = new byte[] { 0xAA, 0x01, register, 1 };
514 | byte[] readBuffer = new byte[1];
515 | try
516 | {
517 | readBuffer = ReadData(commandBuffer, 1);
518 | }
519 | catch (Exception ex)
520 | {
521 | System.Diagnostics.Debug.WriteLine(string.Format("ReadRegister error: {0}", ex.Message));
522 | }
523 | return readBuffer[0];
524 | }
525 |
526 | private List ReadVector(byte address, byte length = 3)
527 | {
528 | List vector = new List();
529 | byte[] commandBuffer = new byte[] { 0xAA, 0x01, address, (byte)(length * 2) };
530 | byte[] readBuffer = new byte[length * 2];
531 | try
532 | {
533 | readBuffer = ReadData(commandBuffer, (uint)length * 2);
534 |
535 | for (int i = 0; i < length * 2; i = i + 2)
536 | {
537 | int entry = ((readBuffer[i + 1] << 8) | readBuffer[i]);
538 | if (entry > 32767)
539 | entry -= 65536;
540 | vector.Add((short)entry);
541 | }
542 | }
543 | catch (Exception ex)
544 | {
545 | System.Diagnostics.Debug.WriteLine(string.Format("ReadVector error: {0}", ex.Message));
546 | }
547 | return vector;
548 | }
549 |
550 | public void SetMode(OperationMode mode)
551 | {
552 | WriteRegister(BNO055_OPR_MODE_ADDR, (byte)mode);
553 | }
554 |
555 | #endregion Serial IO
556 |
557 | #region Initialization
558 |
559 | private void Reset(int RSTPin)
560 | {
561 | if (-1 == RSTPin)
562 | {
563 | WriteRegister(BNO055_SYS_TRIGGER_ADDR, 0x20, false); //do a soft reset
564 | }
565 | else //do a hard reset
566 | {
567 | var gpio = GpioController.GetDefault();
568 |
569 | if (gpio != null)
570 | {
571 | ResetPin = gpio.OpenPin(RSTPin);
572 | ResetPin.Write(GpioPinValue.High);
573 | ResetPin.SetDriveMode(GpioPinDriveMode.Output);
574 |
575 | ResetPin.Write(GpioPinValue.Low);
576 | Task.Delay(10).Wait();
577 | ResetPin.Write(GpioPinValue.High);
578 | }
579 | }
580 | Task.Delay(650).Wait(); //wait 650ms after reset for the device to become ready, as
581 | // suggested in the datasheet
582 | }
583 |
584 | ///
585 | /// InitBNO055Async
586 | /// Initialize BNO055 chip
587 | ///
588 | /// async Task
589 | public async Task InitBNO055Async(OperationMode mode, string uartID = "UART0", int RSTPin = -1)
590 | {
591 | OperatingMode = mode;
592 |
593 | ConnectToUARTAsync(uartID).Wait();
594 |
595 | if (Connected)
596 | {
597 | SetMode(OperationMode.OPERATION_MODE_CONFIG); //go into config mode
598 | WriteRegister(BNO055_PAGE_ID_ADDR, 0/*, false*/); //set to page 0
599 |
600 | byte chipID = ReadRegister(BNO055_CHIP_ID_ADDR);
601 |
602 | if (BNO055_ID == chipID)
603 | {
604 | Reset(RSTPin);
605 |
606 | WriteRegister(BNO055_PWR_MODE_ADDR, POWER_MODE_NORMAL); //set power mode normal
607 |
608 | WriteRegister(BNO055_SYS_TRIGGER_ADDR, 0x0); //set to use internal oscillator
609 |
610 | SetMode(OperatingMode);
611 | }
612 | }
613 | }
614 |
615 | #endregion Initialization
616 |
617 | #region Operations
618 |
619 | public Revisions GetRevision()
620 | {
621 | Revisions revisionData = new Revisions();
622 | revisionData.AccelID = ReadRegister(BNO055_ACCEL_REV_ID_ADDR);
623 | revisionData.MagID = ReadRegister(BNO055_MAG_REV_ID_ADDR);
624 | revisionData.GyroID = ReadRegister(BNO055_GYRO_REV_ID_ADDR);
625 | revisionData.Bootloader = ReadRegister(BNO055_BL_REV_ID_ADDR);
626 | revisionData.Software = (ReadRegister(BNO055_SW_REV_ID_MSB_ADDR) << 8) | ReadRegister(BNO055_SW_REV_ID_MSB_ADDR);
627 | return revisionData;
628 | }
629 |
630 | public void SetExternalCrystal(bool externalCrystal)
631 | {
632 | SetMode(OperationMode.OPERATION_MODE_CONFIG);
633 |
634 | if (externalCrystal)
635 | WriteRegister(BNO055_SYS_TRIGGER_ADDR, 0x80);
636 | else
637 | WriteRegister(BNO055_SYS_TRIGGER_ADDR, 0x00);
638 |
639 | SetMode(OperatingMode);
640 | }
641 |
642 | public SystemStatus GetSystemStatus(bool runSelfTest)
643 | {
644 | SystemStatus status = new SystemStatus();
645 |
646 | if (runSelfTest)
647 | {
648 | SetMode(OperationMode.OPERATION_MODE_CONFIG);
649 |
650 | byte sysTrigger = ReadRegister(BNO055_SYS_TRIGGER_ADDR);
651 | WriteRegister(BNO055_SYS_TRIGGER_ADDR, (byte)(sysTrigger | 0x01));
652 |
653 | Task.Delay(1000).Wait();
654 |
655 | status.SelfTestResult = ReadRegister(BNO055_SELFTEST_RESULT_ADDR);
656 |
657 | SetMode(OperatingMode);
658 | }
659 | else
660 | {
661 | status.SelfTestResult = 0;
662 | }
663 |
664 | status.StatusReg = ReadRegister(BNO055_SYS_STAT_ADDR);
665 | status.ErrorReg = ReadRegister(BNO055_SYS_ERR_ADDR);
666 |
667 | return status;
668 | }
669 |
670 | public CalibrationStatus GetCalibrationStatus()
671 | {
672 | CalibrationStatus status = new CalibrationStatus();
673 | byte cal = ReadRegister(BNO055_CALIB_STAT_ADDR);
674 |
675 | status.System = (byte)((cal >> 6) & 0x03);
676 | status.Gyro = (byte)((cal >> 4) & 0x03);
677 | status.Accel = (byte)((cal >> 2) & 0x03);
678 | status.Mag = (byte)(cal & 0x03);
679 |
680 | return status;
681 | }
682 |
683 | public byte[] GetCalibration()
684 | {
685 | byte[] commandBuffer = new byte[] { 0xAA, 0x01, ACCEL_OFFSET_X_LSB_ADDR, 22 };
686 | byte[] calib = new byte[22];
687 |
688 | SetMode(OperationMode.OPERATION_MODE_CONFIG);
689 | calib = ReadData(commandBuffer, 22);
690 | SetMode(OperatingMode);
691 |
692 | return calib;
693 | }
694 |
695 | public byte[] SetCalibration(byte[] calib)
696 | {
697 | byte[] commandBuffer = new byte[26];
698 |
699 | commandBuffer[0] = 0xAA;
700 | commandBuffer[1] = 0x00;
701 | commandBuffer[2] = ACCEL_OFFSET_X_LSB_ADDR;
702 | commandBuffer[3] = 22;
703 | for (int i = 0; i < 22; i++)
704 | commandBuffer[i + 4] = calib[i];
705 |
706 | SetMode(OperationMode.OPERATION_MODE_CONFIG);
707 | WriteData(commandBuffer);
708 | SetMode(OperatingMode);
709 |
710 | return calib;
711 | }
712 |
713 | public AxisRemap GetAxisRemap()
714 | {
715 | AxisRemap map = new AxisRemap();
716 | byte mapConfig = ReadRegister(BNO055_AXIS_MAP_CONFIG_ADDR);
717 | byte signConfig = ReadRegister(BNO055_AXIS_MAP_SIGN_ADDR);
718 |
719 | map.Z = (byte)((mapConfig >> 4) & 0x03);
720 | map.Y = (byte)((mapConfig >> 2) & 0x03);
721 | map.X = (byte)(mapConfig & 0x03);
722 |
723 | map.ZSign = (byte)((signConfig >> 2) & 0x01);
724 | map.YSign = (byte)((signConfig >> 1) & 0x01);
725 | map.XSign = (byte)(signConfig & 0x01);
726 |
727 | return map;
728 | }
729 |
730 | public void SetAxisRemap(AxisRemap map)
731 | {
732 | byte mapConfig = 0;
733 | byte signConfig = 0;
734 |
735 | mapConfig |= (byte)((map.Z & 0x03) << 4);
736 | mapConfig |= (byte)((map.Y & 0x03) << 2);
737 | mapConfig |= (byte)(map.X & 0x03);
738 |
739 | signConfig |= (byte)((map.ZSign & 0x01) << 2);
740 | signConfig |= (byte)((map.YSign & 0x01) << 1);
741 | signConfig |= (byte)(map.XSign & 0x01);
742 |
743 | SetMode(OperationMode.OPERATION_MODE_CONFIG);
744 | WriteRegister(BNO055_AXIS_MAP_CONFIG_ADDR, mapConfig);
745 | WriteRegister(BNO055_AXIS_MAP_SIGN_ADDR, signConfig);
746 | SetMode(OperatingMode);
747 | }
748 |
749 | public Euler ReadEuler()
750 | {
751 | Euler euler = new Euler();
752 |
753 | List vector = ReadVector(BNO055_EULER_H_LSB_ADDR);
754 | if (3 == vector.Count)
755 | {
756 | euler.Heading = vector[0] / 16.0;
757 | euler.Roll = vector[1] / 16.0;
758 | euler.Pitch = vector[2] / 16.0;
759 | return euler;
760 | }
761 | else
762 | return null;
763 | }
764 |
765 | public Coords ReadMagnetometer()
766 | {
767 | Coords mag = new Coords();
768 |
769 | List vector = ReadVector(BNO055_MAG_DATA_X_LSB_ADDR);
770 | if (3 == vector.Count())
771 | {
772 | mag.X = vector[0] / 16.0;
773 | mag.Y = vector[1] / 16.0;
774 | mag.Z = vector[2] / 16.0;
775 | }
776 | return mag;
777 | }
778 |
779 | public Coords ReadGyroscope()
780 | {
781 | Coords gyro = new Coords();
782 |
783 | List vector = ReadVector(BNO055_GYRO_DATA_X_LSB_ADDR);
784 | if (3 == vector.Count())
785 | {
786 | gyro.X = vector[0] / 16.0;
787 | gyro.Y = vector[1] / 16.0;
788 | gyro.Z = vector[2] / 16.0;
789 | }
790 | return gyro;
791 | }
792 |
793 | public Coords ReadAccelerometer()
794 | {
795 | Coords accel = new Coords();
796 |
797 | List vector = ReadVector(BNO055_ACCEL_DATA_X_LSB_ADDR);
798 | if (3 == vector.Count())
799 | {
800 | accel.X = vector[0] / 100.0;
801 | accel.Y = vector[1] / 100.0;
802 | accel.Z = vector[2] / 100.0;
803 | }
804 | return accel;
805 | }
806 |
807 | public Coords ReadLinearAccel()
808 | {
809 | Coords accel = new Coords();
810 |
811 | List vector = ReadVector(BNO055_LINEAR_ACCEL_DATA_X_LSB_ADDR);
812 | if (3 == vector.Count())
813 | {
814 | accel.X = vector[0] / 100.0;
815 | accel.Y = vector[1] / 100.0;
816 | accel.Z = vector[2] / 100.0;
817 | }
818 | return accel;
819 | }
820 |
821 | public Coords ReadGravity()
822 | {
823 | Coords gravity = new Coords();
824 |
825 | List vector = ReadVector(BNO055_GRAVITY_DATA_X_LSB_ADDR);
826 | if (3 == vector.Count())
827 | {
828 | gravity.X = vector[0] / 100.0;
829 | gravity.Y = vector[1] / 100.0;
830 | gravity.Z = vector[2] / 100.0;
831 | }
832 | return gravity;
833 | }
834 |
835 | public Quaternion ReadQuaternion()
836 | {
837 | Quaternion quat = new Quaternion();
838 |
839 | List vector = ReadVector(BNO055_QUATERNION_DATA_W_LSB_ADDR, 4);
840 | if (4 == vector.Count())
841 | {
842 | double scale = (1.0 / (1 << 14));
843 |
844 | quat.W = vector[0] / scale;
845 | quat.X = vector[1] / scale;
846 | quat.Y = vector[2] / scale;
847 | quat.Z = vector[3] / scale;
848 | }
849 | return quat;
850 | }
851 |
852 | public int ReadTemp()
853 | {
854 | byte temp = ReadRegister(BNO055_TEMP_ADDR);
855 |
856 | if (temp > 127)
857 | return temp - 256;
858 | else
859 | return temp;
860 | }
861 |
862 | #endregion Operations
863 | }
864 | }
--------------------------------------------------------------------------------
/AdafruitClassLibrary/CharLCDPlate.cs:
--------------------------------------------------------------------------------
1 | /*------------------------------------------------------------------------
2 | Windows IoT library to control the Adafruit Character LCD Plate.
3 |
4 | Written by Rick Lesniak for Adafruit Industries.
5 |
6 | Adafruit invests time and resources providing this open source code,
7 | please support Adafruit and open-source hardware by purchasing products
8 | from Adafruit!
9 |
10 | ------------------------------------------------------------------------*/
11 |
12 | using System;
13 | using System.Collections.Generic;
14 | using System.Diagnostics;
15 | using System.Threading.Tasks;
16 |
17 | namespace AdafruitClassLibrary
18 | {
19 | public class CharLcdPlate
20 | {
21 | // commands
22 | private const byte LCD_CLEARDISPLAY = 0x01;
23 |
24 | private const byte LCD_RETURNHOME = 0x02;
25 | private const byte LCD_ENTRYMODESET = 0x04;
26 | private const byte LCDDisplayControl = 0x08;
27 | private const byte LCD_CURSORSHIFT = 0x10;
28 | private const byte LCD_FUNCTIONSET = 0x20;
29 | private const byte LCD_SETCGRAMADDR = 0x40;
30 | private const byte LCD_SETDDRAMADDR = 0x80;
31 |
32 | // flags for display entry mode
33 | private const byte LCD_ENTRYRIGHT = 0x00;
34 |
35 | private const byte LCD_ENTRYLEFT = 0x02;
36 | private const byte LCD_ENTRYSHIFTINCREMENT = 0x01;
37 | private const byte LCD_ENTRYSHIFTDECREMENT = 0x00;
38 |
39 | // flags for display on/off control
40 | private const byte LCD_DISPLAYON = 0x04;
41 |
42 | private const byte LCD_DISPLAYOFF = 0x00;
43 | private const byte LCD_CURSORON = 0x02;
44 | private const byte LCD_CURSOROFF = 0x00;
45 | private const byte LCD_BLINKON = 0x01;
46 | private const byte LCD_BLINKOFF = 0x00;
47 |
48 | // flags for display/cursor shift
49 | private const byte LCD_DISPLAYMOVE = 0x08;
50 |
51 | private const byte LCD_CURSORMOVE = 0x00;
52 | private const byte LCD_MOVERIGHT = 0x04;
53 | private const byte LCD_MOVELEFT = 0x00;
54 |
55 | // flags for function set
56 | private const byte LCD_8BITMODE = 0x10;
57 |
58 | private const byte LCD_4BITMODE = 0x00;
59 | private const byte LCD_2LINE = 0x08;
60 | private const byte LCD_1LINE = 0x00;
61 | public const byte LCD_5x10DOTS = 0x04;
62 | public const byte LCD_5x8DOTS = 0x00;
63 |
64 | public const byte BUTTON_UP = 0x08;
65 | public const byte BUTTON_DOWN = 0x04;
66 | public const byte BUTTON_LEFT = 0x10;
67 | public const byte BUTTON_RIGHT = 0x02;
68 | public const byte BUTTON_SELECT = 0x01;
69 |
70 | private Mcp23017 MCP { get; set; }
71 |
72 | private byte DisplayFunction { get; set; }
73 | private byte DisplayControl { get; set; }
74 | private byte DisplayMode { get; set; }
75 | private int NumLines { get; set; }
76 | private int NumColumns { get; set; }
77 | private int CurrentLine { get; set; }
78 | private int CurrentCol { get; set; }
79 |
80 | // GPIO Pins
81 | private byte RSPin { get; set; }
82 |
83 | private byte RWPin { get; set; }
84 | private byte EnablePin { get; set; }
85 | private List DataPins { get; set; }
86 | private List ButtonPins { get; set; }
87 | private List ColorPins { get; set; }
88 |
89 | public const int RED = 0x1;
90 | public const int YELLOW = 0x3;
91 | public const int GREEN = 0x2;
92 | public const int TEAL = 0x6;
93 | public const int BLUE = 0x4;
94 | public const int VIOLET = 0x5;
95 | public const int WHITE = 0x07;
96 |
97 | //public enum Color { RED = 0x1,
98 | // YELLOW = 0x3,
99 | // GREEN = 0x2,
100 | // TEAL = 0x6,
101 | // BLUE = 0x4,
102 | // VIOLET = 0x5,
103 | // WHITE = 0x07 };
104 |
105 | ///
106 | /// Constructor
107 | ///
108 | public CharLcdPlate()
109 | {
110 | MCP = new Mcp23017();
111 |
112 | DisplayFunction = (byte)(LCD_4BITMODE | LCD_1LINE | LCD_5x8DOTS);
113 |
114 | // the I/O expander pinout
115 | RSPin = 15;
116 | RWPin = 14;
117 | EnablePin = 13;
118 | DataPins = new List() { 12, 11, 10, 9 }; // really d4, d5, d6, d7
119 | ButtonPins = new List() { 0, 1, 2, 3, 4 }; // select, right, up, down, left
120 | ColorPins = new List() { 6, 7, 8 }; //red, green, blue
121 | }
122 |
123 | ///
124 | /// Begin
125 | /// Initializes the Character LCD and MCP23017
126 | ///
127 | ///
128 | ///
129 | ///
130 | ///
131 | public async Task BeginAsync(int cols, int lines, int dotsize = LCD_5x8DOTS)
132 | {
133 | await MCP.InitMCP23017Async();
134 |
135 | MCP.pinMode(RWPin, Mcp23017.Direction.OUTPUT);
136 | MCP.pinMode(RSPin, Mcp23017.Direction.OUTPUT);
137 | MCP.pinMode(EnablePin, Mcp23017.Direction.OUTPUT);
138 |
139 | foreach (var pin in DataPins)
140 | {
141 | MCP.pinMode(pin, Mcp23017.Direction.OUTPUT);
142 | }
143 |
144 | foreach (var pin in ButtonPins)
145 | {
146 | MCP.pinMode(pin, Mcp23017.Direction.INPUT);
147 | MCP.pullUp(pin, Mcp23017.Level.HIGH);
148 | }
149 |
150 | foreach (var pin in ColorPins)
151 | {
152 | MCP.pinMode(pin, Mcp23017.Direction.OUTPUT);
153 | }
154 | setBacklight(WHITE);
155 |
156 | if (lines > 1)
157 | {
158 | DisplayFunction |= LCD_2LINE;
159 | }
160 | NumLines = lines;
161 | NumColumns = cols;
162 | CurrentLine = 0;
163 | CurrentCol = 0;
164 |
165 | // for some 1 line displays you can select a 10 pixel high font
166 | if ((dotsize != 0) && (lines == 1))
167 | {
168 | DisplayFunction |= LCD_5x10DOTS;
169 | }
170 |
171 | // Pull both RS and R/W low to begin commands
172 | MCP.digitalWrite(RSPin, Mcp23017.Level.LOW);
173 | MCP.digitalWrite(EnablePin, Mcp23017.Level.LOW);
174 | MCP.digitalWrite(RWPin, Mcp23017.Level.LOW);
175 |
176 | //put the LCD into 4 bit or 8 bit mode
177 | if (0 == (DisplayFunction & LCD_8BITMODE))
178 | {
179 | // this is according to the hitachi HD44780 datasheet
180 | // figure 24, pg 46
181 |
182 | // we start in 8bit mode, try to set 4 bit mode
183 | write4bits(0x03);
184 | usDelay(4500);
185 |
186 | // second try
187 | write4bits(0x03);
188 | usDelay(4500);
189 |
190 | // third go!
191 | write4bits(0x03);
192 | usDelay(150);
193 |
194 | // finally, set to 8-bit interface
195 | write4bits(0x02);
196 | }
197 | else
198 | {
199 | // this is according to the hitachi HD44780 datasheet
200 | // page 45 figure 23
201 |
202 | // Send function set command sequence
203 | command((byte)(LCD_FUNCTIONSET | DisplayFunction));
204 | usDelay(4500);
205 |
206 | // second try
207 | command((byte)(LCD_FUNCTIONSET | DisplayFunction));
208 | usDelay(150);
209 |
210 | // third go
211 | command((byte)(LCD_FUNCTIONSET | DisplayFunction));
212 | }
213 |
214 | // finally, set # lines, font size, etc.
215 | command((byte)(LCD_FUNCTIONSET | DisplayFunction));
216 |
217 | // turn the display on with no cursor or blinking default
218 | DisplayControl = (byte)(LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF);
219 | display();
220 |
221 | // clear it off
222 | clear();
223 |
224 | // Initialize to default text direction (for romance languages)
225 | DisplayMode = (byte)(LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT);
226 | // set the entry mode
227 | command((byte)(LCD_ENTRYMODESET | DisplayMode));
228 | }
229 |
230 | ///
231 | /// clear
232 | /// Clears the LCD display
233 | ///
234 | public void clear()
235 | {
236 | command(LCD_CLEARDISPLAY); // clear display, set cursor position to zero
237 | usDelay(2000);
238 | }
239 |
240 | ///
241 | /// home
242 | /// set curson postion to 0,0
243 | ///
244 | public void home()
245 | {
246 | command(LCD_RETURNHOME); // set cursor position to zero
247 | usDelay(2000);
248 | }
249 |
250 | ///
251 | /// setCursor
252 | /// Sets cursor position
253 | ///
254 | ///
255 | ///
256 | public void setCursor(byte col, byte line)
257 | {
258 | byte[] row_offsets = { 0x00, 0x40, 0x14, 0x54 };
259 | if (line > NumLines)
260 | {
261 | line = (byte)(NumLines - 1); // we count rows starting w/0
262 | }
263 | CurrentLine = line;
264 | CurrentCol = col;
265 | command((byte)(LCD_SETDDRAMADDR | (col + row_offsets[line])));
266 | }
267 |
268 | ///
269 | /// noDisplay
270 | /// Turn the display off (quickly)
271 | ///
272 | public void noDisplay()
273 | {
274 | unchecked { DisplayControl &= (byte)(~LCD_DISPLAYON); }
275 | command((byte)(LCDDisplayControl | DisplayControl));
276 | }
277 |
278 | ///
279 | /// display
280 | /// Turn the display on (quickly)
281 | ///
282 | public void display()
283 | {
284 | DisplayControl |= LCD_DISPLAYON;
285 | command((byte)(LCDDisplayControl | DisplayControl));
286 | }
287 |
288 | ///
289 | /// noCursor
290 | /// Turns the underline cursor ooff
291 | ///
292 | public void noCursor()
293 | {
294 | unchecked { DisplayControl &= (byte)~LCD_CURSORON; }
295 | command((byte)(LCDDisplayControl | DisplayControl));
296 | }
297 |
298 | ///
299 | /// cursor
300 | /// Turns the underline cursor on
301 | ///
302 | public void cursor()
303 | {
304 | DisplayControl |= LCD_CURSORON;
305 | command((byte)(LCDDisplayControl | DisplayControl));
306 | }
307 |
308 | ///
309 | /// noBLink
310 | /// Turn off the blinking cursor
311 | ///
312 | public void noBlink()
313 | {
314 | unchecked { DisplayControl &= (byte)~LCD_BLINKON; }
315 | command((byte)(LCDDisplayControl | DisplayControl));
316 | }
317 |
318 | ///
319 | /// blink
320 | /// Turn on the blinking cursor
321 | ///
322 | public void blink()
323 | {
324 | DisplayControl |= LCD_BLINKON;
325 | command((byte)(LCDDisplayControl | DisplayControl));
326 | }
327 |
328 | ///
329 | /// scrollDisplayLeft
330 | /// scroll the display left without changing the RAM
331 | ///
332 | public void scrollDisplayLeft()
333 | {
334 | command((byte)(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVELEFT));
335 | }
336 |
337 | ///
338 | /// scrollDisplayRight
339 | /// scroll the display right without changing the RAM
340 | ///
341 | public void scrollDisplayRight()
342 | {
343 | command((byte)(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVERIGHT));
344 | }
345 |
346 | ///
347 | /// leftToRight
348 | /// set text to flow Left to Right
349 | ///
350 | public void leftToRight()
351 | {
352 | DisplayMode |= LCD_ENTRYLEFT;
353 | command((byte)(LCD_ENTRYMODESET | DisplayMode));
354 | }
355 |
356 | ///
357 | /// rightToLeft
358 | /// set text to flow Right to Left
359 | ///
360 | public void rightToLeft()
361 | {
362 | unchecked { DisplayMode &= (byte)~LCD_ENTRYLEFT; }
363 | command((byte)(LCD_ENTRYMODESET | DisplayMode));
364 | }
365 |
366 | ///
367 | /// autoscroll
368 | /// 'right justify' text from the cursor
369 | ///
370 | public void autoscroll()
371 | {
372 | DisplayMode |= LCD_ENTRYSHIFTINCREMENT;
373 | command((byte)(LCD_ENTRYMODESET | DisplayMode));
374 | }
375 |
376 | ///
377 | /// noAutoscroll
378 | /// 'left justify' text from the cursor
379 | ///
380 | public void noAutoscroll()
381 | {
382 | unchecked { DisplayMode &= (byte)~LCD_ENTRYSHIFTINCREMENT; }
383 | command((byte)(LCD_ENTRYMODESET | DisplayMode));
384 | }
385 |
386 | // Allows us to fill the first 8 CGRAM locations
387 | // with custom characters
388 | ///
389 | /// createChar
390 | /// Allows you to fill the first 8 CGRAM locations
391 | /// with custom characters
392 | ///
393 | ///
394 | ///
395 | public void createChar(byte location, byte[] charmap)
396 | {
397 | location &= 0x7; // we only have 8 locations 0-7
398 | command((byte)(LCD_SETCGRAMADDR | (location << 3)));
399 | for (int i = 0; i < 8; i++)
400 | {
401 | write(charmap[i]);
402 | }
403 | command(LCD_SETDDRAMADDR); // unfortunately resets the location to 0,0
404 | }
405 |
406 | ///
407 | /// readButtons
408 | /// read the state of the 5 buttons on the plate
409 | /// returns bitmap
410 | ///
411 | /// byte
412 | public byte readButtons()
413 | {
414 | byte reply = 0x1F;
415 |
416 | for (byte i = 0; i < 5; i++)
417 | {
418 | reply &= (byte)~((byte)(MCP.digitalRead(ButtonPins[i])) << i);
419 | }
420 | return reply;
421 | }
422 |
423 | ///
424 | /// setBacklight
425 | /// set RGB colors for the backlight
426 | /// bitmap R=4, G=2, B=1
427 | ///
428 | ///
429 | public void setBacklight(int color)
430 | {
431 | MCP.digitalWrite(8, (Mcp23017.Level)(~(color >> 2) & 0x1));
432 | MCP.digitalWrite(7, (Mcp23017.Level)(~(color >> 1) & 0x1));
433 | MCP.digitalWrite(6, (Mcp23017.Level)(~color & 0x1));
434 | }
435 |
436 | ///
437 | /// print overload.
438 | /// print a string starting at cursor
439 | ///
440 | ///
441 | public void print(string str)
442 | {
443 | foreach (char c in str)
444 | {
445 | write((byte)c);
446 | CurrentCol++;
447 | if (CurrentCol == NumColumns)
448 | {
449 | Math.Min(NumLines, CurrentLine++);
450 | CurrentCol = 0;
451 | }
452 | setCursor((byte)CurrentCol, (byte)CurrentLine);
453 | }
454 | }
455 |
456 | ///
457 | /// print overload
458 | /// print an integer starting at the cursor
459 | ///
460 | ///
461 | public void print(int number)
462 | {
463 | print(number.ToString());
464 | }
465 |
466 | ///
467 | /// print overload
468 | /// print a double starting at the cursor
469 | ///
470 | ///
471 | public void print(double number)
472 | {
473 | print(number.ToString());
474 | }
475 |
476 | #region mid level commands
477 |
478 | private void command(byte value)
479 | {
480 | send(value, Mcp23017.Level.LOW);
481 | }
482 |
483 | private void write(byte value)
484 | {
485 | send(value, Mcp23017.Level.HIGH);
486 | }
487 |
488 | #endregion mid level commands
489 |
490 | #region low level commands
491 |
492 | // write either command or data, with automatic 4/8-bit selection
493 | private void send(byte value, Mcp23017.Level mode)
494 | {
495 | MCP.digitalWrite(RSPin, mode);
496 |
497 | MCP.digitalWrite(RWPin, Mcp23017.Level.LOW);
498 |
499 | write4bits((byte)(value >> 4));
500 | write4bits(value);
501 | }
502 |
503 | private void write4bits(byte value)
504 | {
505 | UInt16 registerValue;
506 |
507 | registerValue = MCP.readGPIOAB();
508 |
509 | // speed up for i2c since its sluggish
510 | for (int i = 0; i < 4; i++)
511 | {
512 | registerValue &= (UInt16)~(1 << DataPins[i]);
513 | registerValue |= (UInt16)(((value >> i) & 0x1) << DataPins[i]);
514 | }
515 |
516 | // make sure enable is low
517 | registerValue &= (UInt16)~(1 << EnablePin);
518 |
519 | MCP.writeGPIOAB(registerValue);
520 |
521 | pulseEnable();
522 | }
523 |
524 | private void pulseEnable()
525 | {
526 | MCP.digitalWrite(EnablePin, Mcp23017.Level.LOW);
527 | usDelay(2);
528 | MCP.digitalWrite(EnablePin, Mcp23017.Level.HIGH);
529 | usDelay(2); // enable pulse must be >450ns
530 | MCP.digitalWrite(EnablePin, Mcp23017.Level.LOW);
531 | usDelay(100); // commands need > 37us to settle
532 | }
533 |
534 | #endregion low level commands
535 |
536 | ///
537 | /// usDelay
538 | /// function with delay argument in microseconds
539 | ///
540 | ///
541 | private void usDelay(long duration)
542 | {
543 | // Static method to initialize and start stopwatch
544 | var sw = Stopwatch.StartNew();
545 |
546 | long nanosecPerTick = (1000L * 1000L * 1000L) / Stopwatch.Frequency;
547 | long durationTicks = (duration * 1000) / nanosecPerTick;
548 |
549 | while (sw.ElapsedTicks < durationTicks)
550 | {
551 | }
552 | }
553 | }
554 | }
--------------------------------------------------------------------------------
/AdafruitClassLibrary/DotStar.cs:
--------------------------------------------------------------------------------
1 | /*------------------------------------------------------------------------
2 | Adafruit Class Library for Windows Core IoT: Adafruit DotStar.
3 |
4 | Written by Rick Lesniak for Adafruit Industries.
5 |
6 | Adafruit invests time and resources providing this open source code,
7 | please support Adafruit and open-source hardware by purchasing products
8 | from Adafruit!
9 |
10 | ------------------------------------------------------------------------*/
11 |
12 | using System;
13 | using System.Threading.Tasks;
14 | using Windows.Devices.Enumeration;
15 | using Windows.Devices.Gpio;
16 | using Windows.Devices.Spi;
17 |
18 | namespace AdafruitClassLibrary
19 | {
20 | public class DotStar
21 | {
22 | public const int DOTSTAR_RGB = (1 | (2 << 8) | (3 << 16));
23 | public const int DOTSTAR_RBG = (1 | (3 << 8) | (2 << 16));
24 | public const int DOTSTAR_GRB = (2 | (1 << 8) | (3 << 16));
25 | public const int DOTSTAR_GBR = (3 | (1 << 8) | (2 << 16));
26 | public const int DOTSTAR_BRG = (2 | (3 << 8) | (1 << 16));
27 | public const int DOTSTAR_BGR = (3 | (2 << 8) | (1 << 16));
28 |
29 | public uint NumPixels { get; set; }
30 | private UInt32 ColorOrder { get; set; }
31 | private int DataPin { get; set; }
32 | private int ClockPin { get; set; }
33 | private int ROffset { get; set; }
34 | private int GOffset { get; set; }
35 | private int BOffset { get; set; }
36 |
37 | private byte[] Pixels { get; set; }
38 |
39 | private GpioPin SWSpiDataPin { get; set; }
40 | private GpioPin SWSpiClockPin { get; set; }
41 | private SpiDevice HWSpiDevice { get; set; }
42 | public bool HW_SPI { get; set; }
43 |
44 | // Stored brightness value is different than what's passed. This
45 | // optimizes the actual scaling math later, allowing an 8x8-bit
46 | // multiply and taking the MSB. 'brightness' is a byte; adding 1
47 | // here may (intentionally) roll over...so 0 = max brightness (color
48 | // values are interpreted literally; no scaling), 1 = min brightness
49 | // (off), 255 = just below max brightness.
50 | private byte m_Brightness = 255;
51 |
52 | public byte Brightness
53 | {
54 | get
55 | {
56 | return (byte)(m_Brightness - 1);
57 | }
58 | set
59 | {
60 | m_Brightness = (byte)(value + 1);
61 | }
62 | }
63 |
64 | public DotStar(uint numPixels, UInt32 colorOrder = DOTSTAR_BRG) : this(numPixels, 255, 255, colorOrder)
65 | {
66 | }
67 |
68 | public DotStar(uint numPixels, int dataPin, int clockPin, UInt32 colorOrder = DOTSTAR_BRG)
69 | {
70 | UpdateLength(numPixels);
71 | ColorOrder = colorOrder;
72 | DataPin = dataPin;
73 | ClockPin = clockPin;
74 | Brightness = 255;
75 |
76 | ROffset = (int)(ColorOrder & 255);
77 | GOffset = (int)((ColorOrder >> 8) & 255);
78 | BOffset = (int)((ColorOrder >> 16) & 255);
79 |
80 | HW_SPI = (dataPin == 255);
81 | }
82 |
83 | public async Task BeginAsync()
84 | {
85 | if (HW_SPI)
86 | await hw_spi_initAsync();
87 | else
88 | sw_spi_init();
89 |
90 | ScopeTriggerInit(6);
91 | }
92 |
93 | public void End()
94 | {
95 | if (HW_SPI)
96 | hw_spi_end();
97 | else
98 | sw_spi_end();
99 |
100 | ScopeTriggerEnd();
101 | }
102 |
103 | #region Software Spi
104 |
105 | private void sw_spi_init()
106 | {
107 | var gpio = GpioController.GetDefault();
108 |
109 | if (gpio != null)
110 | {
111 | SWSpiDataPin = gpio.OpenPin(DataPin);
112 | SWSpiDataPin.Write(GpioPinValue.Low);
113 | SWSpiDataPin.SetDriveMode(GpioPinDriveMode.Output);
114 |
115 | SWSpiClockPin = gpio.OpenPin(ClockPin);
116 | SWSpiClockPin.Write(GpioPinValue.Low);
117 | SWSpiClockPin.SetDriveMode(GpioPinDriveMode.Output);
118 | }
119 | }
120 |
121 | private void sw_spi_out(byte n)
122 | { // Bitbang SPI write
123 | for (int i = 8; 0 != i--; n <<= 1)
124 | {
125 | if (0x80 == (n & 0x80))
126 | SWSpiDataPin.Write(GpioPinValue.High);
127 | else
128 | SWSpiDataPin.Write(GpioPinValue.Low);
129 |
130 | SWSpiClockPin.Write(GpioPinValue.High);
131 | SWSpiClockPin.Write(GpioPinValue.Low);
132 | }
133 | }
134 |
135 | private void sw_spi_out(byte[] pixels)
136 | {
137 | foreach (byte p in pixels)
138 | sw_spi_out(p);
139 | }
140 |
141 | private void sw_spi_end()
142 | { // Stop 'soft' SPI
143 | SWSpiDataPin.SetDriveMode(GpioPinDriveMode.Input);
144 | SWSpiClockPin.SetDriveMode(GpioPinDriveMode.Input);
145 | }
146 |
147 | #endregion Software Spi
148 |
149 | #region Hardware Spi
150 |
151 | private async Task hw_spi_initAsync()
152 | {
153 | // Get a selector string for bus "SPI0"
154 | string aqs = SpiDevice.GetDeviceSelector("SPI0");
155 |
156 | // Find the SPI bus controller device with our selector string
157 | var dis = await DeviceInformation.FindAllAsync(aqs);
158 | if (dis.Count == 0)
159 | return; // "SPI0" not found on this system
160 |
161 | // Use chip select line CS0
162 | var settings = new SpiConnectionSettings(0);
163 |
164 | // Create an SpiDevice with our bus controller and SPI settings
165 | HWSpiDevice = await SpiDevice.FromIdAsync(dis[0].Id, settings);
166 | }
167 |
168 | private void hw_spi_out(byte n)
169 | {
170 | byte[] writeBuf = { n };
171 | HWSpiDevice.Write(writeBuf);
172 | }
173 |
174 | private void hw_spi_out(byte[] pixels)
175 | {
176 | HWSpiDevice.Write(pixels);
177 | }
178 |
179 | private void hw_spi_end()
180 | {
181 | HWSpiDevice.Dispose();
182 | }
183 |
184 | #endregion Hardware Spi
185 |
186 | #region DotStarOptimized functions
187 |
188 | public void UpdateLength(uint numPixels)
189 | {
190 | NumPixels = numPixels;
191 | Pixels = new byte[NumPixels * 4];
192 | Clear();
193 | }
194 |
195 | public void Clear()
196 | {
197 | for (int pixel = 0; pixel < NumPixels; pixel++)
198 | {
199 | Pixels[pixel * 4] = 0xFF;
200 | Pixels[pixel * 4 + ROffset] = 0;
201 | Pixels[pixel * 4 + GOffset] = 0;
202 | Pixels[pixel * 4 + BOffset] = 0;
203 | }
204 | }
205 |
206 | // Set pixel color, separate R,G,B values (0-255 ea.)
207 | public void SetPixelColor(int pixel, byte r, byte g, byte b)
208 | {
209 | if ((pixel >= 0) && (pixel < NumPixels))
210 | {
211 | Pixels[pixel * 4] = 0xFF;
212 | Pixels[pixel * 4 + ROffset] = r;
213 | Pixels[pixel * 4 + GOffset] = g;
214 | Pixels[pixel * 4 + BOffset] = b;
215 | }
216 | }
217 |
218 | // Set pixel color, 'packed' RGB value (0x00000000 - 0x00FFFFFF)
219 | public void SetPixelColor(int pixel, UInt32 c)
220 | {
221 | if ((pixel >= 0) && (pixel < NumPixels))
222 | {
223 | byte[] colors = { 0xFF, (byte)((c >> 16) & 0xFF), (byte)((c >> 8) & 0xFF), (byte)(c & 0xFF) };
224 | SetPixelColor(pixel, colors[1], colors[2], colors[3]);
225 | }
226 | }
227 |
228 | // Get pixel color, 'packed' RGB value (0x00000000 - 0x00FFFFFF)
229 | public UInt32 GetPixelColor(int pixel)
230 | {
231 | UInt32 color = 0;
232 | if ((pixel >= 0) && (pixel < NumPixels))
233 | {
234 | color = (UInt32)(Pixels[pixel * 4 + ROffset] << 16 | Pixels[pixel * 4 + GOffset] << 8 | Pixels[pixel * 4 + BOffset]);
235 | }
236 | return color;
237 | }
238 |
239 | // Convert separate R,G,B to packed value
240 | public UInt32 Color(uint r, uint g, uint b)
241 | {
242 | byte[] colors = { 0xFF, 0, 0, 0 };
243 | colors[ROffset] = (byte)r;
244 | colors[GOffset] = (byte)g;
245 | colors[BOffset] = (byte)b;
246 |
247 | return (UInt32)(colors[ROffset] << 16 | colors[GOffset] << 8 | colors[BOffset]);
248 | }
249 |
250 | public void Show()
251 | {
252 | TriggerScopePin(true);
253 | byte[] m_startFrame = { 0, 0, 0, 0 };
254 | byte[] m_endFrame = { 0xFF, 0xFF, 0xFF, 0xFF };
255 |
256 | if (null != Pixels)
257 | {
258 | if (HW_SPI)
259 | {
260 | hw_spi_out(m_startFrame); // Start-frame marker
261 | if (255 != Brightness)
262 | { // Scale pixel brightness on output
263 | int brite = Brightness + 1;
264 | byte[] adjusted = new byte[NumPixels * 4];
265 | for (int pixel = 0; pixel < NumPixels; pixel++)
266 | { // For each pixel...
267 | adjusted[pixel * 4] = 0xFF;
268 | adjusted[pixel * 4 + 1] = (byte)(Pixels[pixel * 4 + 1] * brite / 256);
269 | adjusted[pixel * 4 + 2] = (byte)(Pixels[pixel * 4 + 2] * brite / 256);
270 | adjusted[pixel * 4 + 3] = (byte)(Pixels[pixel * 4 + 3] * brite / 256);
271 | }
272 | hw_spi_out(adjusted);
273 | }
274 | else
275 | { // Full brightness (no scaling)
276 | // TriggerScopePin(true);
277 | hw_spi_out(Pixels);
278 | // TriggerScopePin(false);
279 | }
280 |
281 | // Four end-frame bytes are seemingly indistinguishable from a white
282 | // pixel, and empirical testing suggests it can be left out...but it's
283 | // always a good idea to follow the datasheet, in case future hardware
284 | // revisions are more strict (e.g. might mandate use of end-frame
285 | // before start-frame marker). i.e. let's not remove this.
286 |
287 | hw_spi_out(m_endFrame); // End-frame marker (see note above)
288 | }
289 | else //sw spi
290 | {
291 | sw_spi_out(m_startFrame); // Start-frame marker
292 |
293 | if (255 != Brightness)
294 | { // Scale pixel brightness on output
295 | int brite = Brightness + 1;
296 | byte[] adjusted = new byte[NumPixels * 4];
297 | for (int pixel = 0; pixel < NumPixels; pixel++)
298 | { // For each pixel...
299 | adjusted[pixel * 4] = 0xFF;
300 | adjusted[pixel * 4 + 1] = (byte)(Pixels[pixel * 4 + 1] * brite / 256);
301 | adjusted[pixel * 4 + 2] = (byte)(Pixels[pixel * 4 + 2] * brite / 256);
302 | adjusted[pixel * 4 + 3] = (byte)(Pixels[pixel * 4 + 3] * brite / 256);
303 | }
304 | sw_spi_out(adjusted);
305 | }
306 | else
307 | { // Full brightness (no scaling)
308 | sw_spi_out(Pixels);
309 | }
310 |
311 | sw_spi_out(m_endFrame); // End-frame marker (see note above)
312 | }
313 | }
314 |
315 | TriggerScopePin(false);
316 | }
317 |
318 | #endregion DotStarOptimized functions
319 |
320 | #region debugging
321 |
322 | private GpioPin ScopePin { get; set; }
323 |
324 | private void ScopeTriggerInit(int scopePin)
325 | {
326 | var gpio = GpioController.GetDefault();
327 |
328 | // Show an error if there is no GPIO controller
329 | if (gpio == null)
330 | {
331 | return;
332 | }
333 | ScopePin = gpio.OpenPin(scopePin);
334 | ScopePin.Write(GpioPinValue.Low);
335 | ScopePin.SetDriveMode(GpioPinDriveMode.Output);
336 | }
337 |
338 | private void ScopeTriggerEnd()
339 | {
340 | ScopePin.SetDriveMode(GpioPinDriveMode.Input);
341 | ScopePin.Dispose();
342 | }
343 |
344 | private void TriggerScopePin(bool triggerOn)
345 | {
346 | if (triggerOn)
347 | ScopePin.Write(GpioPinValue.High);
348 | else
349 | ScopePin.Write(GpioPinValue.Low);
350 | }
351 |
352 | #endregion debugging
353 | }
354 | }
--------------------------------------------------------------------------------
/AdafruitClassLibrary/GPS.cs:
--------------------------------------------------------------------------------
1 | /*------------------------------------------------------------------------
2 | Adafruit Class Library for Windows Core IoT: Adafruit Ultimate GPS.
3 |
4 | Written by Rick Lesniak for Adafruit Industries.
5 |
6 | Adafruit invests time and resources providing this open source code,
7 | please support Adafruit and open-source hardware by purchasing products
8 | from Adafruit!
9 |
10 | ------------------------------------------------------------------------*/
11 |
12 | using System;
13 | using System.Collections.Generic;
14 | using System.Globalization;
15 | using System.Threading;
16 | using System.Threading.Tasks;
17 | using Windows.Devices.Enumeration;
18 | using Windows.Devices.SerialCommunication;
19 | using Windows.Storage.Streams;
20 |
21 | namespace AdafruitClassLibrary
22 | {
23 | public class Gps
24 | {
25 | #region Properties
26 |
27 | private SerialDevice SerialPort { get; set; }
28 | private DataWriter DataWriterObject { get; set; }
29 | private DataReader DataReaderObject { get; set; }
30 | private Task ReadTask { get; set; }
31 |
32 | private uint BaudRate { get; set; }
33 |
34 | private GPSRMC m_RMC = new GPSRMC();
35 | private GPSGGA m_GGA = new GPSGGA();
36 | private GPSGLL m_GLL = new GPSGLL();
37 | private GPSVTG m_VTG = new GPSVTG();
38 | private GPSGSA m_GSA = new GPSGSA();
39 | private GPSGSV m_GSV = new GPSGSV();
40 |
41 | private GPSRMC RMC
42 | {
43 | get
44 | {
45 | lock (m_RMC)
46 | {
47 | return m_RMC;
48 | }
49 | }
50 | set
51 | {
52 | lock (m_RMC)
53 | {
54 | m_RMC = value;
55 | }
56 | }
57 | }
58 |
59 | private GPSGGA GGA
60 | {
61 | get
62 | {
63 | lock (m_GGA)
64 | {
65 | return m_GGA;
66 | }
67 | }
68 | set
69 | {
70 | lock (m_GGA)
71 | {
72 | m_GGA = value;
73 | }
74 | }
75 | }
76 |
77 | private GPSGLL GLL
78 | {
79 | get
80 | {
81 | lock (m_GLL)
82 | {
83 | return m_GLL;
84 | }
85 | }
86 | set
87 | {
88 | lock (m_GLL)
89 | {
90 | m_GLL = value;
91 | }
92 | }
93 | }
94 |
95 | private GPSVTG VTG
96 | {
97 | get
98 | {
99 | lock (m_VTG)
100 | {
101 | return m_VTG;
102 | }
103 | }
104 | set
105 | {
106 | lock (m_VTG)
107 | {
108 | m_VTG = value;
109 | }
110 | }
111 | }
112 |
113 | private GPSGSA GSA
114 | {
115 | get
116 | {
117 | lock (m_GSA)
118 | {
119 | return m_GSA;
120 | }
121 | }
122 | set
123 | {
124 | lock (m_GSA)
125 | {
126 | m_GSA = value;
127 | }
128 | }
129 | }
130 |
131 | private GPSGSV GSV
132 | {
133 | get
134 | {
135 | lock (m_GSV)
136 | {
137 | return m_GSV;
138 | }
139 | }
140 | set
141 | {
142 | lock (m_GSV)
143 | {
144 | m_GSV = value;
145 | }
146 | }
147 | }
148 |
149 | #endregion Properties
150 |
151 | private CancellationTokenSource ReadCancellationTokenSource;
152 |
153 | public Gps()
154 | {
155 | SerialPort = null;
156 | DataWriterObject = null;
157 | DataReaderObject = null;
158 | ReadTask = null;
159 | GLLEvent += (o, e) => { };
160 | RMCEvent += (o, e) => { };
161 | VTGEvent += (o, e) => { };
162 | GGAEvent += (o, e) => { };
163 | GSAEvent += (o, e) => { };
164 | GSVEvent += (o, e) => { };
165 | }
166 |
167 | #region GPS Commands
168 |
169 | ///
170 | /// SetSentencesReporting: Set which sentences to report and their individual reporting frequency in fix intervals
171 | /// 0: for no sentence reporting
172 | /// 1: once every position fix
173 | /// 2: once every two position fixes
174 | /// 3..5: once every 3 to 5 position fixes
175 | ///
176 | ///
177 | ///
178 | ///
179 | ///
180 | ///
181 | ///
182 | /// async Task
183 | public async Task SetSentencesReportingAsync(int GLLfreq, int RMCfreq, int VTGfreq, int GGAfreq, int GSAfreq, int GSVfreq)
184 | {
185 | string cmdString = "PMTK314";
186 | cmdString = string.Concat(string.Concat(cmdString, ","), Math.Min(5, GLLfreq).ToString());
187 | cmdString = string.Concat(string.Concat(cmdString, ","), Math.Min(5, RMCfreq).ToString());
188 | cmdString = string.Concat(string.Concat(cmdString, ","), Math.Min(5, VTGfreq).ToString());
189 | cmdString = string.Concat(string.Concat(cmdString, ","), Math.Min(5, GGAfreq).ToString());
190 | cmdString = string.Concat(string.Concat(cmdString, ","), Math.Min(5, GSAfreq).ToString());
191 | cmdString = string.Concat(string.Concat(cmdString, ","), Math.Min(5, GSVfreq).ToString());
192 |
193 | cmdString = string.Concat(cmdString, ",0,0,0,0,0,0,0,0,0,0,0,0,0");
194 | uint checksum = 0;
195 | for (int index = 0; index < cmdString.Length; index++)
196 | checksum ^= cmdString[index];
197 |
198 | cmdString = string.Concat(string.Concat("$", cmdString), "*");
199 | cmdString = string.Concat(cmdString, checksum.ToString("X2"));
200 | cmdString = string.Concat(cmdString, "\r\n");
201 |
202 | await SendCommandAsync(cmdString);
203 | }
204 |
205 | ///
206 | /// SetUpdateFrequency
207 | /// Set postion reporting frwquency in Hz
208 | ///
209 | ///
210 | /// async Task
211 | public async Task SetUpdateFrequencyAsync(double freqHz)
212 | {
213 | string cmdString = "PMTK220";
214 | cmdString = string.Concat(string.Concat(cmdString, ","), (1000 / freqHz).ToString());
215 |
216 | uint checksum = 0;
217 | for (int index = 0; index < cmdString.Length; index++)
218 | checksum ^= cmdString[index];
219 |
220 | cmdString = string.Concat(string.Concat("$", cmdString), "*");
221 | cmdString = string.Concat(cmdString, checksum.ToString("X2"));
222 | cmdString = string.Concat(cmdString, "\r\n");
223 |
224 | await SendCommandAsync(cmdString);
225 | }
226 |
227 | ///
228 | /// SetBaudRate
229 | /// Set GPS UART Baud Rate
230 | ///
231 | ///
232 | /// async Task
233 | public async Task SetBaudRateAsync(uint baudrate)
234 | {
235 | string cmdString = "PMTK251";
236 | cmdString = string.Concat(string.Concat(cmdString, ","), baudrate.ToString());
237 |
238 | uint checksum = 0;
239 | for (int index = 0; index < cmdString.Length; index++)
240 | checksum ^= cmdString[index];
241 |
242 | cmdString = string.Concat(string.Concat("$", cmdString), "*");
243 | cmdString = string.Concat(cmdString, checksum.ToString("X2"));
244 | cmdString = string.Concat(cmdString, "\r\n");
245 |
246 | await SendCommandAsync(cmdString);
247 | }
248 |
249 | ///
250 | /// SetPMTKCommand
251 | /// Generic send for a PMTK command. Argument string should include leading '$', checksum,
252 | /// and trailing CR/LF
253 | ///
254 | ///
255 | /// async Task
256 | public async Task SendPMTKCommandAsync(string pmtk)
257 | {
258 | await SendCommandAsync(pmtk);
259 | }
260 |
261 | #endregion GPS Commands
262 |
263 | #region Serial Control
264 |
265 | ///
266 | /// Connected
267 | /// Predicate returns true if UART is connected
268 | ///
269 | /// bool
270 | public bool Connected
271 | {
272 | get { return SerialPort != null; }
273 | }
274 |
275 | ///
276 | /// ConnectToUART
277 | /// - Use SerialDevice.GetDeviceSelector to find serial device named "UART0".
278 | /// This is the built-in Raspberry Pi serial port.
279 | ///
280 | ///
281 | ///
282 | /// async Task
283 | public async Task ConnectToUARTAsync(uint baudRate = 9600, string uartID = "UART0")
284 | {
285 | BaudRate = (uint)baudRate;
286 | try
287 | {
288 | string aqs = SerialDevice.GetDeviceSelector(uartID);
289 | var dis = await DeviceInformation.FindAllAsync(aqs);
290 |
291 | if (dis.Count > 0)
292 | {
293 | DeviceInformation uart = dis[0];
294 |
295 | SerialPort = await SerialDevice.FromIdAsync(uart.Id);
296 | // Configure serial settings
297 | if (null != SerialPort)
298 | {
299 | SerialPort.WriteTimeout = TimeSpan.FromMilliseconds(1000);
300 | SerialPort.ReadTimeout = TimeSpan.FromMilliseconds(100);
301 | SerialPort.BaudRate = BaudRate;
302 | SerialPort.Parity = SerialParity.None;
303 | SerialPort.StopBits = SerialStopBitCount.One;
304 | SerialPort.DataBits = 8;
305 | SerialPort.Handshake = SerialHandshake.None;
306 |
307 | // Create the DataReader object and attach to InputStream
308 | DataReaderObject = new DataReader(SerialPort.InputStream);
309 | // Create the DataWriter object and attach to OutputStream
310 | DataWriterObject = new DataWriter(SerialPort.OutputStream);
311 |
312 | ReadCancellationTokenSource = new CancellationTokenSource();
313 | }
314 | }
315 | }
316 | catch (Exception ex)
317 | {
318 | System.Diagnostics.Debug.WriteLine(string.Format("Error creating Serial Port: {0}", ex.Message));
319 | if (null != SerialPort)
320 | SerialPort.Dispose();
321 | SerialPort = null;
322 | }
323 | }
324 |
325 | ///
326 | /// StartReading
327 | /// Starts process to read NMEA sentences from UART
328 | ///
329 | public void StartReading()
330 | {
331 | if (null == ReadTask)
332 | {
333 | ReadCancellationTokenSource = new CancellationTokenSource();
334 | ReadTask = ReadAsync(ReadCancellationTokenSource.Token);
335 | }
336 | }
337 |
338 | ///
339 | /// StopReading
340 | /// Terminates NMEA reading process
341 | ///
342 | public void StopReading()
343 | {
344 | ReadCancellationTokenSource.Cancel();
345 | if (null != ReadTask)
346 | {
347 | ReadTask.Wait();
348 | ReadTask = null;
349 | }
350 | }
351 |
352 | ///
353 | /// DisconnectFromUART
354 | /// Disconnects from the UART
355 | ///
356 | public void DisconnectFromUART()
357 | {
358 | StopReading();
359 | if (null != DataReaderObject)
360 | {
361 | //DataReaderObject.DetachStream();
362 | //DataReaderObject.DetachBuffer();
363 | DataReaderObject.Dispose();
364 | }
365 | if (null != DataWriterObject)
366 | {
367 | DataWriterObject.Dispose();
368 | }
369 | if (null != SerialPort)
370 | {
371 | SerialPort.Dispose();
372 | SerialPort = null;
373 | }
374 | }
375 |
376 | ///
377 | /// ReadAsync
378 | /// Task to read PMTK sentences.
379 | /// Performs sentence assembly, calls parsing routine
380 | ///
381 | ///
382 | /// async Task
383 | private async Task ReadAsync(CancellationToken cancellationToken)
384 | {
385 | Task loadAsyncTask;
386 |
387 | uint ReadBufferLength = 128;
388 | UInt32 bytesRead = 0;
389 |
390 | // Set InputStreamOptions to complete the asynchronous read operation when one or more bytes is available
391 | DataReaderObject.InputStreamOptions = InputStreamOptions.Partial;
392 |
393 | int retries = 5;
394 |
395 | string rawSentence = "";
396 | string leftovers = "";
397 |
398 | while ((!cancellationToken.IsCancellationRequested) && (retries > 0))
399 | {
400 | try
401 | {
402 | while (true)
403 | {
404 | // If task cancellation was requested, comply
405 | cancellationToken.ThrowIfCancellationRequested();
406 |
407 | // Create a task object to wait for data on the serialPort.InputStream
408 | loadAsyncTask = DataReaderObject.LoadAsync(ReadBufferLength).AsTask(cancellationToken);
409 | // Launch the task and wait
410 | bytesRead = await loadAsyncTask;
411 | if (bytesRead > 0)
412 | {
413 | rawSentence = DataReaderObject.ReadString(bytesRead);
414 | rawSentence = string.Concat(leftovers, rawSentence);
415 | string[] splitSentences = rawSentence.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
416 | leftovers = "";
417 |
418 | foreach (string str in splitSentences)
419 | {
420 | if ((str.Length >= 3) && (str.LastIndexOf('*') == str.Length - 3))
421 | {
422 | System.Diagnostics.Debug.WriteLine(str);
423 | DispatchSentence(str);
424 | }
425 | else
426 | {
427 | leftovers = str;
428 | }
429 | }
430 | }
431 | retries = 5;
432 | }
433 | }
434 | catch (OperationCanceledException ex)
435 | {
436 | System.Diagnostics.Debug.WriteLine(string.Format("Exiting GPS read task"));
437 | }
438 | catch (ArgumentOutOfRangeException ex)
439 | {
440 | //do nothing. this tends to sort itself out eventually
441 | //System.Diagnostics.Debug.WriteLine(string.Format("ArgumentOutOfRangeException: {0}", ex.Message));
442 | for (int b = 0; b < bytesRead; b++)
443 | {
444 | DataReaderObject.ReadByte();
445 | // bytesRead--;
446 | }
447 | }
448 | catch (Exception ex)
449 | {
450 | System.Diagnostics.Debug.WriteLine(string.Format("Error reading from GPS: {0}", ex.Message));
451 | leftovers = "";
452 | retries--;
453 | }
454 | }
455 | SerialPort.Dispose();
456 | SerialPort = null;
457 | }
458 |
459 | ///
460 | /// SendCommand
461 | /// Asynchronous task to write to GPS through UART
462 | ///
463 | ///
464 | /// async Task
465 | private async Task SendCommandAsync(string cmdString)
466 | {
467 | try
468 | {
469 | //Launch the WriteAsync task to perform the write
470 | Task storeAsyncTask;
471 |
472 | if (cmdString.Length != 0)
473 | {
474 | // Load the text from the sendText input text box to the dataWriter object
475 | DataWriterObject.WriteString(cmdString);
476 |
477 | // Launch an async task to complete the write operation
478 | storeAsyncTask = DataWriterObject.StoreAsync().AsTask();
479 |
480 | UInt32 bytesWritten = await storeAsyncTask;
481 | if (bytesWritten != cmdString.Length)
482 | throw new Exception("Bytes written does not match command length");
483 | }
484 | }
485 | catch (Exception ex)
486 | {
487 | System.Diagnostics.Debug.WriteLine(string.Format("Error writing to GPS: {0}", ex.Message));
488 | }
489 | }
490 |
491 | #endregion Serial Control
492 |
493 | #region Parsing
494 |
495 | ///
496 | /// IsValid
497 | /// Checks PMTK sentence, including checksum. Returns true if sentence is valid
498 | ///
499 | ///
500 | /// bool
501 | private bool IsValid(string sentence)
502 | {
503 | bool validSentence = false;
504 | if (null != sentence)
505 | {
506 | try
507 | {
508 | if ('$' == sentence[0])
509 | {
510 | uint checksum = 0;
511 | for (int index = 1; index < sentence.Length - 3; index++)
512 | checksum ^= sentence[index];
513 | uint targetChecksum = Convert.ToUInt16(sentence.Substring((sentence.LastIndexOf('*') + 1), 2), 16);
514 | validSentence = (checksum == targetChecksum);
515 | }
516 | else
517 | throw new Exception("Missing '$'");
518 | }
519 | catch (Exception ex)
520 | {
521 | System.Diagnostics.Debug.WriteLine(string.Format("Invalid RMC Sentence: {0}\r\n {1}", sentence, ex.Message));
522 | }
523 | }
524 | if (!validSentence)
525 | System.Diagnostics.Debug.WriteLine(string.Format("Invalid RMC Sentence: {0}", sentence));
526 |
527 | return validSentence;
528 | }
529 |
530 | // GPGLL - Geographic Position - Latitude longitude
531 | // GPRMC - Recommended Minimum Specific GNSS Sentence
532 | // GPVTG - Course over Ground and Ground Speed
533 | // GPGGA - GPS Fix Data
534 | // GPGSA - GNSS DOPS and Active Satellites
535 | // GPGSV - GNSS Satellites in View
536 |
537 | ///
538 | /// DispatchSentnce
539 | /// Sends sentence to a parser based on sentence type
540 | ///
541 | ///
542 | private void DispatchSentence(string sentence)
543 | {
544 | if (IsValid(sentence))
545 | {
546 | string sentenceType = sentence.Substring(1, 5);
547 | switch (sentenceType)
548 | {
549 | case "GPRMC":
550 | RMC = new GPSRMC(sentence.Substring(sentence.IndexOf(',') + 1));
551 | OnRMCEvent(RMC);
552 | break;
553 |
554 | case "GPGGA":
555 | GGA = new GPSGGA(sentence.Substring(sentence.IndexOf(',') + 1));
556 | OnGGAEvent(GGA);
557 | break;
558 |
559 | case "GPGLL":
560 | GLL = new GPSGLL(sentence.Substring(sentence.IndexOf(',') + 1));
561 | OnGLLEvent(GLL);
562 | break;
563 |
564 | case "GPVTG":
565 | VTG = new GPSVTG(sentence.Substring(sentence.IndexOf(',') + 1));
566 | OnVTGEvent(VTG);
567 | break;
568 |
569 | case "GPGSA":
570 | GSA = new GPSGSA(sentence.Substring(sentence.IndexOf(',') + 1));
571 | OnGSAEvent(GSA);
572 | break;
573 |
574 | case "GPGSV":
575 | GSV = new GPSGSV(sentence.Substring(sentence.IndexOf(',') + 1));
576 | OnGSVEvent(GSV);
577 | break;
578 |
579 | default:
580 | break;
581 | }
582 | }
583 | }
584 |
585 | ///
586 | /// CheckPMTKAck
587 | /// Parses PMTK ACK sentence
588 | ///
589 | ///
590 | ///
591 | ///
592 | private bool CheckPMTKAck(string sentence, string cmdNumber)
593 | {
594 | bool isAck = false;
595 | if (IsValid(sentence))
596 | {
597 | string[] sections = sentence.Split(new char[] { ',', '*' });
598 | isAck = ("$PMTK001" == sections[0]) && (cmdNumber == sections[1]) && ("3" == sections[2]);
599 | }
600 | return isAck;
601 | }
602 |
603 | #endregion Parsing
604 |
605 | #region Data Classes
606 |
607 | public class GPSRMC : SentenceBase
608 | {
609 | public DateTime TimeStamp { get; set; }
610 | public bool Valid { get; set; }
611 | public double? Latitude { get; set; }
612 | public string LatHemisphere { get; set; }
613 | public double? Longitude { get; set; }
614 | public string LonHemisphere { get; set; }
615 | public double? Speed { get; set; }
616 | public double? Course { get; set; }
617 | public DateTime DateStamp { get; set; }
618 | public double? MagVariation { get; set; }
619 | public string VarDirection { get; set; }
620 | public double? LatDegrees { get; set; }
621 | public double? LonDegrees { get; set; }
622 |
623 | public GPSRMC()
624 | {
625 | }
626 |
627 | ///
628 | /// GPSRMC
629 | /// Parses RMC sentence
630 | ///
631 | ///
632 | public GPSRMC(string sentence)
633 | {
634 | string[] splitString = sentence.Split(new char[] { ',', '*' });
635 |
636 | TimeStamp = ParseTimeStamp(splitString[0]); // Time Stamp
637 | Valid = splitString[1] == "A" ? true : false; // validity -A - ok, V - invalid
638 | Latitude = ParseDouble(splitString[2]); // current Latitude
639 | LatHemisphere = splitString[3]; // North/ South
640 | Longitude = ParseDouble(splitString[4]); // current Longitude
641 | LonHemisphere = splitString[5]; // East/ West
642 | Speed = ParseDouble(splitString[6]); // Speed in knots
643 | Course = ParseDouble(splitString[7]); // True course
644 | DateStamp = ParseDateStamp(splitString[8]); // Date Stamp
645 | MagVariation = ParseDouble(splitString[9]); // Magnetic Variation
646 | VarDirection = splitString[10]; // East/ West
647 |
648 | LatDegrees = ToDegrees(Latitude);
649 | LonDegrees = ToDegrees(Longitude);
650 | }
651 | }
652 |
653 | public class GPSGGA : SentenceBase
654 | {
655 | public enum FixQuality { noFix = 0, gpsFix = 1, dgpsFix = 2 }
656 |
657 | public DateTime TimeStamp { get; set; }
658 | public double? Latitude { get; set; }
659 | public string LatHemisphere { get; set; }
660 | public double? Longitude { get; set; }
661 | public string LonHemisphere { get; set; }
662 | public FixQuality Quality { get; set; }
663 | public int? Satellites { get; set; }
664 | public double? Dilution { get; set; }
665 | public double? Altitude { get; set; }
666 | public string AltUnits { get; set; }
667 | public double? Geoidal { get; set; }
668 | public string GeoidalUnits { get; set; }
669 | public double? DGPSAge { get; set; }
670 | public int? DGPS_ID { get; set; }
671 | public double? LatDegrees { get; set; }
672 | public double? LonDegrees { get; set; }
673 |
674 | public GPSGGA()
675 | {
676 | }
677 |
678 | ///
679 | /// GPSGGA
680 | /// Parses GGA sentence
681 | ///
682 | ///
683 | public GPSGGA(string sentence)
684 | {
685 | string[] splitString = sentence.Split(new char[] { ',', '*' });
686 |
687 | TimeStamp = ParseTimeStamp(splitString[0]); // UTC of Position
688 | Latitude = ParseDouble(splitString[1]); // Latitude
689 | LatHemisphere = splitString[2]; // N or S
690 | Longitude = ParseDouble(splitString[3]); // Longitude
691 | LonHemisphere = splitString[4]; // E or W
692 | Quality = (FixQuality)ParseInt(splitString[5]); // GPS quality indicator(0=invalid; 1=GPS fix; 2=Diff.GPS fix)
693 | Satellites = ParseInt(splitString[6]); // Number of satellites in use[not those in view]
694 | Dilution = ParseDouble(splitString[7]); // Horizontal dilution of position
695 | Altitude = ParseDouble(splitString[8]); // Antenna altitude above/below mean sea level(geoid)
696 | AltUnits = splitString[9]; // Meters(Antenna height unit)
697 | Geoidal = ParseDouble(splitString[10]); // Geoidal separation(Diff.between WGS - 84 earth ellipsoid and mean sea level. -=geoid is below WGS-84 ellipsoid)
698 | GeoidalUnits = splitString[11]; // Meters(Units of geoidal separation)
699 | DGPSAge = ParseDouble(splitString[12]); // Age in seconds since last update from diff.reference station
700 | DGPS_ID = ParseInt(splitString[13]); // Diff.reference station ID#
701 |
702 | LatDegrees = ToDegrees(Latitude);
703 | LonDegrees = ToDegrees(Longitude);
704 | }
705 | }
706 |
707 | public class GPSGLL : SentenceBase
708 | {
709 | public DateTime TimeStamp { get; set; }
710 | public bool Valid { get; set; }
711 | public double? Latitude { get; set; }
712 | public string LatHemisphere { get; set; }
713 | public double? Longitude { get; set; }
714 | public string LonHemisphere { get; set; }
715 | public double? LatDegrees { get; set; }
716 | public double? LonDegrees { get; set; }
717 |
718 | public GPSGLL()
719 | {
720 | }
721 |
722 | ///
723 | /// GPSGLL
724 | /// Parses GLL sentence
725 | ///
726 | ///
727 | public GPSGLL(string sentence)
728 | {
729 | string[] splitString = sentence.Split(new char[] { ',', '*' });
730 |
731 | Latitude = ParseDouble(splitString[0]); // current Latitude
732 | LatHemisphere = splitString[1]; // North/ South
733 | Longitude = ParseDouble(splitString[2]); // current Longitude
734 | LonHemisphere = splitString[3]; // East/ West
735 | TimeStamp = ParseTimeStamp(splitString[4]); // Time Stamp
736 | Valid = splitString[5] == "A" ? true : false; // validity -A - ok, V - invalid
737 |
738 | LatDegrees = ToDegrees(Latitude);
739 | LonDegrees = ToDegrees(Longitude);
740 | }
741 | }
742 |
743 | public class GPSVTG : SentenceBase
744 | {
745 | public double? TrackTrue { get; set; }
746 | public string TT { get; set; }
747 | public double? TrackMag { get; set; }
748 | public string TM { get; set; }
749 | public double? SpeedKnots { get; set; }
750 | public string SKn { get; set; }
751 | public double? SpeedKm { get; set; }
752 | public string SKm { get; set; }
753 | public string Mode { get; set; }
754 |
755 | public GPSVTG()
756 | {
757 | }
758 |
759 | ///
760 | /// GPSVTG
761 | /// Parses VTG sentence
762 | ///
763 | ///
764 | public GPSVTG(string sentence)
765 | {
766 | string[] splitString = sentence.Split(new char[] { ',', '*' });
767 |
768 | TrackTrue = ParseDouble(splitString[0]); // Track true north
769 | TT = splitString[1]; // "T"
770 | TrackMag = ParseDouble(splitString[2]); // Track magnetic north
771 | TM = splitString[3]; // "M"
772 | SpeedKnots = ParseDouble(splitString[4]); // Speed in knots
773 | SKn = splitString[5]; // "N"
774 | SpeedKm = ParseDouble(splitString[6]); // Speed in km/h
775 | SKm = splitString[7]; // "K"
776 | Mode = splitString[8];
777 | }
778 | }
779 |
780 | public class GPSGSA : SentenceBase
781 | {
782 | public enum FixType { noFix = 1, fix2D = 2, fix3D = 3 }
783 |
784 | public string Mode { get; set; }
785 | public FixType Fix { get; set; }
786 | public List SVIDs { get; set; }
787 | public double? PDOP { get; set; }
788 | public double? HDOP { get; set; }
789 | public double? VDOP { get; set; }
790 |
791 | public GPSGSA()
792 | {
793 | }
794 |
795 | ///
796 | /// GPSGSA
797 | /// Parses GSA sentence
798 | ///
799 | ///
800 | public GPSGSA(string sentence)
801 | {
802 | string[] splitString = sentence.Split(new char[] { ',', '*' });
803 |
804 | Mode = splitString[0]; // "M" - Manual, "A" - Automatic
805 | Fix = (FixType)ParseInt(splitString[1]); // 0: no fix, 1 : 2D, 2 : 3D
806 | SVIDs = new List(); //IDs of atellites in view
807 | for (int i = 2; i < 14; i++)
808 | SVIDs.Add(ParseInt(splitString[i]));
809 | PDOP = ParseDouble(splitString[14]); // PDOP
810 | HDOP = ParseDouble(splitString[15]); // HDOP
811 | VDOP = ParseDouble(splitString[16]); // VDOP
812 | }
813 | }
814 |
815 | public class GPSGSV : SentenceBase
816 | {
817 | public class SVRecord
818 | {
819 | public int? PRN { get; set; }
820 | public int? Elevation { get; set; }
821 | public int? Azimuth { get; set; }
822 | public int? SNR { get; set; }
823 | }
824 |
825 | public int? MsgCount { get; set; }
826 | public int? MsgNumber { get; set; }
827 | public int? Satellites { get; set; }
828 | public List SVList { get; set; }
829 |
830 | public GPSGSV()
831 | {
832 | }
833 |
834 | ///
835 | /// GPSGSV
836 | /// Parses GSV sentence
837 | ///
838 | ///
839 | public GPSGSV(string sentence)
840 | {
841 | string[] splitString = sentence.Split(new char[] { ',', '*' });
842 |
843 | MsgCount = ParseInt(splitString[0]); // 1 = Total number of messages of this type in this cycle
844 | MsgNumber = ParseInt(splitString[1]); // 2 = Message number
845 | Satellites = ParseInt(splitString[2]); // 3 = Total number of SVs in view
846 | SVList = new List();
847 | int stringIndex = 3;
848 | int recordCount = (MsgNumber < MsgCount) ? 4 : (((int)Satellites - 1) % 4) + 1;
849 | for (int svNum = 1; svNum <= recordCount; svNum++)
850 | {
851 | SVRecord record = new SVRecord();
852 | record.PRN = ParseInt(splitString[stringIndex++]); // 4 = SV PRN number
853 | record.Elevation = ParseInt(splitString[stringIndex++]); // 5 = Elevation in degrees, 90 maximum
854 | record.Azimuth = ParseInt(splitString[stringIndex++]); // 6 = Azimuth, degrees from true north, 000 to 359
855 | record.SNR = ParseInt(splitString[stringIndex++]); // 7 = SNR, 00-99 dB (null when not tracking)
856 | SVList.Add(record); // 8-11 = Information about second SV, same as field 4-7
857 | } // 12-15= Information about third SV, same as field 4-7
858 | } // 16-19= Information about fourth SV, same as field 4-7
859 | }
860 |
861 | public class SentenceBase : EventArgs
862 | {
863 | protected double? ParseDouble(string str)
864 | {
865 | double? result = null;
866 | try
867 | {
868 | if ("" != str)
869 | result = Convert.ToDouble(str);
870 | }
871 | catch (Exception ex)
872 | {
873 | System.Diagnostics.Debug.WriteLine(string.Format("Error parsing double: {0}\r\n {1}", str, ex.Message));
874 | }
875 | return result;
876 | }
877 |
878 | protected int? ParseInt(string str)
879 | {
880 | int? result = null;
881 | try
882 | {
883 | if ("" != str)
884 | result = Convert.ToInt32(str);
885 | }
886 | catch (Exception ex)
887 | {
888 | System.Diagnostics.Debug.WriteLine(string.Format("Error parsing integer: {0}\r\n {1}", str, ex.Message));
889 | }
890 | return result;
891 | }
892 |
893 | protected DateTime ParseTimeStamp(string timeStr)
894 | {
895 | timeStr = timeStr.Insert(4, ":");
896 | timeStr = timeStr.Insert(2, ":");
897 |
898 | DateTime stamp = DateTime.Parse(timeStr, CultureInfo.InvariantCulture);
899 | return stamp;
900 | }
901 |
902 | protected DateTime ParseDateStamp(string dateStr)
903 | {
904 | dateStr = dateStr.Insert(4, "-");
905 | dateStr = dateStr.Insert(2, "-");
906 |
907 | //this wretched kludge exists because DateTime.ParseExact doesn't appear to work.
908 | // so, we have to rearrange the month and day to make Parse happy
909 | string[] components = dateStr.Split(new char[] { '-' });
910 | dateStr = components[1] + '-' + components[0] + '-' + components[2];
911 |
912 | DateTime stamp = DateTime.Parse(dateStr, CultureInfo.InvariantCulture);
913 | return stamp;
914 | }
915 |
916 | ///
917 | /// ToDegrees
918 | /// Converts dddmm.mmmm lat/long format to decimal degrees
919 | ///
920 | ///
921 | ///
922 | protected double? ToDegrees(double? latlong)
923 | {
924 | double? degrees = null;
925 | if (null != latlong)
926 | {
927 | degrees = (int)(latlong / 100);
928 | double? minutes = (latlong % 100) / 60;
929 | degrees = degrees + minutes;
930 | }
931 | return degrees;
932 | }
933 | }
934 |
935 | #endregion Data Classes
936 |
937 | #region Events
938 |
939 | // Delegate declarations
940 | //
941 | public delegate void RMCEventHandler(object sender, GPSRMC e);
942 |
943 | public delegate void GLLEventHandler(object sender, GPSGLL e);
944 |
945 | public delegate void VTGEventHandler(object sender, GPSVTG e);
946 |
947 | public delegate void GGAEventHandler(object sender, GPSGGA e);
948 |
949 | public delegate void GSAEventHandler(object sender, GPSGSA e);
950 |
951 | public delegate void GSVEventHandler(object sender, GPSGSV e);
952 |
953 | public event RMCEventHandler RMCEvent;
954 |
955 | public event GLLEventHandler GLLEvent;
956 |
957 | public event VTGEventHandler VTGEvent;
958 |
959 | public event GGAEventHandler GGAEvent;
960 |
961 | public event GSAEventHandler GSAEvent;
962 |
963 | public event GSVEventHandler GSVEvent;
964 |
965 | protected virtual void OnRMCEvent(GPSRMC e)
966 | {
967 | RMCEvent(this, e);
968 | }
969 |
970 | protected virtual void OnGLLEvent(GPSGLL e)
971 | {
972 | GLLEvent(this, e);
973 | }
974 |
975 | protected virtual void OnVTGEvent(GPSVTG e)
976 | {
977 | VTGEvent(this, e);
978 | }
979 |
980 | protected virtual void OnGGAEvent(GPSGGA e)
981 | {
982 | GGAEvent(this, e);
983 | }
984 |
985 | protected virtual void OnGSAEvent(GPSGSA e)
986 | {
987 | GSAEvent(this, e);
988 | }
989 |
990 | protected virtual void OnGSVEvent(GPSGSV e)
991 | {
992 | GSVEvent(this, e);
993 | }
994 |
995 | #endregion Events
996 | }
997 | }
--------------------------------------------------------------------------------
/AdafruitClassLibrary/I2CBase.cs:
--------------------------------------------------------------------------------
1 | /*------------------------------------------------------------------------
2 | Adafruit Class Library for Windows Core IoT: I2C base class.
3 |
4 | Written by Rick Lesniak for Adafruit Industries.
5 |
6 | Adafruit invests time and resources providing this open source code,
7 | please support Adafruit and open-source hardware by purchasing products
8 | from Adafruit!
9 |
10 | ------------------------------------------------------------------------*/
11 |
12 | using System;
13 | using System.Threading.Tasks;
14 | using Windows.Devices.Enumeration;
15 | using Windows.Devices.I2c;
16 |
17 | namespace AdafruitClassLibrary
18 | {
19 | public class I2CBase
20 | {
21 | public enum I2CSpeed { I2C_100kHz, I2C_400kHz };
22 |
23 | #region Properties
24 |
25 | private int I2CAddr { get; set; }
26 | protected I2cDevice Device { get; set; }
27 |
28 | #endregion Properties
29 |
30 | #region Constructor
31 |
32 | protected I2CBase(int addr)
33 | {
34 | I2CAddr = addr;
35 | }
36 |
37 | #endregion Constructor
38 |
39 | #region Initialization
40 |
41 | ///
42 | /// InitI2C
43 | /// Initialize I2C Communications
44 | ///
45 | /// async Task
46 | public async Task InitI2CAsync(I2CSpeed i2cSpeed = I2CSpeed.I2C_100kHz)
47 | {
48 | // initialize I2C communications
49 | try
50 | {
51 | I2cConnectionSettings i2cSettings = new I2cConnectionSettings(I2CAddr);
52 | if (i2cSpeed == I2CSpeed.I2C_400kHz)
53 | i2cSettings.BusSpeed = I2cBusSpeed.FastMode;
54 | else
55 | i2cSettings.BusSpeed = I2cBusSpeed.StandardMode;
56 |
57 | string deviceSelector = I2cDevice.GetDeviceSelector();
58 | var i2cDeviceControllers = await DeviceInformation.FindAllAsync(deviceSelector);
59 | Device = await I2cDevice.FromIdAsync(i2cDeviceControllers[0].Id, i2cSettings);
60 | }
61 | catch (Exception e)
62 | {
63 | System.Diagnostics.Debug.WriteLine("Exception: {0}", e.Message);
64 | return;
65 | }
66 | }
67 |
68 | #endregion Initialization
69 |
70 | #region I2C primitives
71 |
72 | ///
73 | /// WriteRead
74 | /// writes to I2C and reads back the result
75 | ///
76 | ///
77 | ///
78 | protected void WriteRead(byte[] writeBuffer, byte[] readBuffer)
79 | {
80 | try
81 | {
82 | lock (Device)
83 | {
84 | Device.WriteRead(writeBuffer, readBuffer);
85 | }
86 | }
87 | catch (Exception ex)
88 | {
89 | System.Diagnostics.Debug.WriteLine("I2C WriteRead Exception: {0}", ex.Message);
90 | }
91 | }
92 |
93 | ///
94 | /// Read
95 | /// Reads a PCA9685 register
96 | ///
97 | ///
98 | protected void Read(byte[] readBuffer)
99 | {
100 | try
101 | {
102 | lock (Device)
103 | {
104 | Device.Read(readBuffer);
105 | }
106 | }
107 | catch (Exception ex)
108 | {
109 | System.Diagnostics.Debug.WriteLine("I2C Read Exception: {0}", ex.Message);
110 | }
111 | }
112 |
113 | ///
114 | /// Write
115 | /// Writes to a PCA9685 register
116 | ///
117 | ///
118 | protected void Write(byte[] writeBuffer)
119 | {
120 | try
121 | {
122 | lock (Device)
123 | {
124 | Device.Write(writeBuffer);
125 | }
126 | }
127 | catch (Exception ex)
128 | {
129 | System.Diagnostics.Debug.WriteLine("I2C Write Exception: {0}", ex.Message);
130 | }
131 | }
132 |
133 | #endregion I2C primitives
134 | }
135 | }
--------------------------------------------------------------------------------
/AdafruitClassLibrary/MCP23017.cs:
--------------------------------------------------------------------------------
1 | /*------------------------------------------------------------------------
2 | Adafruit Class Library for Windows Core IoT: MCP23017 I2C expander chip.
3 |
4 | Written by Rick Lesniak for Adafruit Industries.
5 |
6 | Adafruit invests time and resources providing this open source code,
7 | please support Adafruit and open-source hardware by purchasing products
8 | from Adafruit!
9 |
10 | ------------------------------------------------------------------------*/
11 |
12 | using System;
13 | using System.Threading.Tasks;
14 |
15 | namespace AdafruitClassLibrary
16 | {
17 | public class Mcp23017 : I2CBase
18 | {
19 | #region Constants
20 |
21 | //default I2C address
22 | private const int MCP23017_ADDRESS = 0x20;
23 |
24 | // port A registers
25 | private const byte MCP23017_IODIRA = 0x00;
26 |
27 | private const byte MCP23017_IPOLA = 0x02;
28 | private const byte MCP23017_GPINTENA = 0x04;
29 | private const byte MCP23017_DEFVALA = 0x06;
30 | private const byte MCP23017_INTCONA = 0x08;
31 | private const byte MCP23017_IOCONA = 0x0A;
32 | private const byte MCP23017_GPPUA = 0x0C;
33 | private const byte MCP23017_INTFA = 0x0E;
34 | private const byte MCP23017_INTCAPA = 0x10;
35 | private const byte MCP23017_GPIOA = 0x12;
36 | private const byte MCP23017_OLATA = 0x14;
37 |
38 | //Port B registers
39 | private const byte MCP23017_IODIRB = 0x01;
40 |
41 | private const byte MCP23017_IPOLB = 0x03;
42 | private const byte MCP23017_GPINTENB = 0x05;
43 | private const byte MCP23017_DEFVALB = 0x07;
44 | private const byte MCP23017_INTCONB = 0x09;
45 | private const byte MCP23017_IOCONB = 0x0B;
46 | private const byte MCP23017_GPPUB = 0x0D;
47 | private const byte MCP23017_INTFB = 0x0F;
48 | private const byte MCP23017_INTCAPB = 0x11;
49 | private const byte MCP23017_GPIOB = 0x13;
50 | private const byte MCP23017_OLATB = 0x15;
51 |
52 | public enum Direction { INPUT = 0, OUTPUT = 1 };
53 |
54 | public enum Level { LOW = 0, HIGH = 1 };
55 |
56 | #endregion Constants
57 |
58 | #region Constructor
59 |
60 | public Mcp23017(int addr = MCP23017_ADDRESS) : base(addr)
61 | {
62 | }
63 |
64 | #endregion Constructor
65 |
66 | #region Initialization
67 |
68 | ///
69 | /// InitMCP23017
70 | /// Initialize MCP23017 chip
71 | ///
72 | /// async Task
73 | public async Task InitMCP23017Async(I2CSpeed i2cSpeed = I2CSpeed.I2C_100kHz)
74 | {
75 | byte[] writeBuffer;
76 |
77 | await InitI2CAsync(i2cSpeed);
78 |
79 | // set defaults!
80 | // all outputs on Port A
81 | writeBuffer = new byte[] { MCP23017_IODIRA, 0xFF };
82 | Write(writeBuffer);
83 |
84 | // all outputs on Port B
85 | writeBuffer = new byte[] { MCP23017_IODIRB, 0xFF };
86 | Write(writeBuffer);
87 | }
88 |
89 | #endregion Initialization
90 |
91 | #region Interrupts
92 |
93 | ///
94 | /// Enables the interrupts for pin p.
95 | ///
96 | /// Pin id (0..15).
97 | public void EnableInterrupts(int p)
98 | {
99 | byte gppuAddr, gppu, gpintenAddr, gpinten;
100 |
101 | // only 16 bits!
102 | if (p > 15)
103 | return;
104 | if (p < 8)
105 | {
106 | gppuAddr = MCP23017_GPPUA;
107 | gpintenAddr = MCP23017_GPINTENA;
108 | }
109 | else
110 | {
111 | gppuAddr = MCP23017_GPPUB;
112 | gpintenAddr = MCP23017_GPINTENB;
113 | p -= 8;
114 | }
115 |
116 | byte[] readBuffer = new byte[1];
117 |
118 | // Enable pull-up resistor for pin p
119 | WriteRead(new byte[] { gppuAddr }, readBuffer);
120 | gppu = readBuffer[0];
121 | gppu |= (byte)(1 << p);
122 | Write(new byte[] { gppuAddr, gppu });
123 |
124 | // Enable interrup on pin p
125 | WriteRead(new byte[] { gpintenAddr }, readBuffer);
126 | gpinten = readBuffer[0];
127 | gpinten |= (byte)(1 << p);
128 | Write(new byte[] { gpintenAddr, gpinten });
129 | }
130 |
131 | ///
132 | /// Enables interrupts mirroring.
133 | /// Signalises both INTA and INTB on input change.
134 | ///
135 | public void EnableInterruptsMirroring()
136 | {
137 | byte[] ReadBuffer = new byte[1];
138 |
139 | WriteRead(new byte[] { MCP23017_IOCONA }, ReadBuffer); // 0x0A IOCONA
140 | byte NewValues = ReadBuffer[0];
141 | NewValues |= (byte)(1 << 6);
142 | Write(new byte[] { MCP23017_IOCONA, NewValues });
143 | }
144 |
145 | #endregion
146 |
147 | #region Operations
148 |
149 | ///
150 | /// pinMode
151 | /// Set pin direction, input or output
152 | ///
153 | ///
154 | ///
155 | public void pinMode(int p, Direction d)
156 | {
157 | byte[] readBuffer;
158 | byte[] writeBuffer;
159 | byte iodir;
160 | byte iodiraddr;
161 |
162 | // only 16 bits!
163 | if (p > 15)
164 | return;
165 |
166 | if (p < 8)
167 | iodiraddr = MCP23017_IODIRA;
168 | else
169 | {
170 | iodiraddr = MCP23017_IODIRB;
171 | p -= 8;
172 | }
173 |
174 | // read the current IODIR
175 | readBuffer = new byte[1];
176 |
177 | WriteRead(new byte[] { iodiraddr }, readBuffer);
178 | iodir = readBuffer[0];
179 |
180 | // set the pin and direction
181 | if (d == Direction.INPUT)
182 | {
183 | iodir |= (byte)(1 << p);
184 | }
185 | else
186 | {
187 | iodir &= (byte)~((1 << p));
188 | }
189 |
190 | // write the new IODIR
191 | writeBuffer = new byte[] { iodiraddr, iodir };
192 | Write(writeBuffer);
193 | }
194 |
195 | ///
196 | /// digitalWrite
197 | /// Sets the state of an output pin
198 | ///
199 | ///
200 | ///
201 | public void digitalWrite(int pin, Level d)
202 | {
203 | byte[] readBuffer;
204 | byte[] writeBuffer;
205 | byte pinRegister;
206 | byte registerAddr;
207 | byte olatAddr;
208 |
209 | // only 16 bits!
210 | if (pin > 15)
211 | return;
212 |
213 | if (pin < 8)
214 | {
215 | olatAddr = MCP23017_OLATA;
216 | registerAddr = MCP23017_GPIOA;
217 | }
218 | else
219 | {
220 | olatAddr = MCP23017_OLATB;
221 | registerAddr = MCP23017_GPIOB;
222 | pin -= 8;
223 | }
224 |
225 | lock (Device)
226 | {
227 | // read the current GPIO output latches
228 | readBuffer = new byte[1];
229 | WriteRead(new byte[] { olatAddr }, readBuffer);
230 | pinRegister = readBuffer[0];
231 |
232 | // set the pin and direction
233 | if (d == Level.HIGH)
234 | {
235 | pinRegister |= (byte)(1 << pin);
236 | }
237 | else
238 | {
239 | pinRegister &= (byte)~(1 << pin);
240 | }
241 |
242 | // write the new GPIO
243 | writeBuffer = new byte[] { registerAddr, pinRegister };
244 | Write(writeBuffer);
245 | }
246 | }
247 |
248 | ///
249 | /// pullUp
250 | /// Sets the pullup resistor on a gpio pin
251 | ///
252 | ///
253 | ///
254 | public void pullUp(int pin, Level d)
255 | {
256 | byte[] readBuffer;
257 | byte[] writeBuffer;
258 | byte pullupRegister;
259 | byte pullupAddr;
260 |
261 | // only 16 bits!
262 | if (pin > 15)
263 | return;
264 |
265 | if (pin < 8)
266 | pullupAddr = MCP23017_GPPUA;
267 | else
268 | {
269 | pullupAddr = MCP23017_GPPUB;
270 | pin -= 8;
271 | }
272 |
273 | lock (Device)
274 | {
275 | // read the current pullup register set
276 | readBuffer = new byte[1];
277 | WriteRead(new byte[] { pullupAddr }, readBuffer);
278 | pullupRegister = readBuffer[0];
279 |
280 | // set the pin and direction
281 | if (d == Level.HIGH)
282 | {
283 | pullupRegister |= (byte)(1 << pin);
284 | }
285 | else
286 | {
287 | pullupRegister &= (byte)~(1 << pin);
288 | }
289 |
290 | // write the new pullup
291 | writeBuffer = new byte[] { pullupAddr, pullupRegister };
292 | Write(writeBuffer);
293 | }
294 | }
295 |
296 | ///
297 | /// digitalRead
298 | /// returns the input state of a gpio pin
299 | ///
300 | ///
301 | /// Level
302 | public Level digitalRead(int pin)
303 | {
304 | byte[] readBuffer;
305 | byte registerAddr;
306 |
307 | // only 16 bits!
308 | if (pin > 15)
309 | return 0;
310 |
311 | if (pin < 8)
312 | registerAddr = MCP23017_GPIOA;
313 | else
314 | {
315 | registerAddr = MCP23017_GPIOB;
316 | pin -= 8;
317 | }
318 |
319 | // read the current GPIO
320 | readBuffer = new byte[1];
321 | WriteRead(new byte[] { registerAddr }, readBuffer);
322 | return ((readBuffer[0] >> pin) & 0x1) == 0 ? Level.LOW : Level.HIGH;
323 | }
324 |
325 | ///
326 | /// writeGPIOAB
327 | /// writes a 16-bit value to the A and B gpio registers
328 | ///
329 | ///
330 | public void writeGPIOAB(UInt16 ba)
331 | {
332 | byte[] writeBuffer;
333 | writeBuffer = new byte[] { MCP23017_GPIOA, (byte)(ba & 0xFF), (byte)(ba >> 8) };
334 | Write(writeBuffer);
335 | }
336 |
337 | ///
338 | /// readGPIOAB
339 | /// Reads a 16-bit value from the GPIO a and B registers
340 | ///
341 | /// UInt16
342 | public UInt16 readGPIOAB()
343 | {
344 | byte[] readBuffer;
345 |
346 | // read the current GPIO output latches
347 | readBuffer = new byte[2];
348 | WriteRead(new byte[] { MCP23017_GPIOA }, readBuffer);
349 | return (UInt16)((readBuffer[1] << 8) | readBuffer[0]);
350 | }
351 |
352 | #endregion Operations
353 | }
354 | }
--------------------------------------------------------------------------------
/AdafruitClassLibrary/MotorHat.cs:
--------------------------------------------------------------------------------
1 | /*------------------------------------------------------------------------
2 | Adafruit Class Library for Windows Core IoT: Adafruit Motor Hat.
3 |
4 | Written by Rick Lesniak for Adafruit Industries.
5 |
6 | Adafruit invests time and resources providing this open source code,
7 | please support Adafruit and open-source hardware by purchasing products
8 | from Adafruit!
9 |
10 | ------------------------------------------------------------------------*/
11 |
12 | using System.Collections.Generic;
13 | using System.Diagnostics;
14 | using System.Threading.Tasks;
15 |
16 | namespace AdafruitClassLibrary
17 | {
18 | public class MotorHat : Pca9685
19 | {
20 | #region Subordinate Classes
21 |
22 | public class DCMotor
23 | {
24 | public enum Command { FORWARD, BACKWARD, RELEASE };
25 |
26 | private MotorHat Hat { get; set; }
27 | private int Index { get; set; }
28 | private int PWMPin { get; set; }
29 | private int In1Pin { get; set; }
30 | private int In2Pin { get; set; }
31 |
32 | public DCMotor(MotorHat hat, int index)
33 | {
34 | Hat = hat;
35 | Index = index;
36 | switch (index)
37 | {
38 | case 0:
39 | PWMPin = 8;
40 | In2Pin = 9;
41 | In1Pin = 10;
42 | break;
43 |
44 | case 1:
45 | PWMPin = 13;
46 | In2Pin = 12;
47 | In1Pin = 11;
48 | break;
49 |
50 | case 2:
51 | PWMPin = 2;
52 | In2Pin = 3;
53 | In1Pin = 4;
54 | break;
55 |
56 | case 3:
57 | PWMPin = 7;
58 | In2Pin = 6;
59 | In1Pin = 5;
60 | break;
61 |
62 | default:
63 | break;
64 | }
65 | }
66 |
67 | public void Run(Command cmd)
68 | {
69 | switch (cmd)
70 | {
71 | case Command.FORWARD:
72 | Hat.SetPin(In2Pin, PinState.LOW);
73 | Hat.SetPin(In1Pin, PinState.HIGH);
74 | break;
75 |
76 | case Command.BACKWARD:
77 | Hat.SetPin(In1Pin, PinState.LOW);
78 | Hat.SetPin(In2Pin, PinState.HIGH);
79 | break;
80 |
81 | case Command.RELEASE:
82 | Hat.SetPin(In1Pin, PinState.LOW);
83 | Hat.SetPin(In2Pin, PinState.LOW);
84 | break;
85 |
86 | default:
87 | break;
88 | }
89 | }
90 |
91 | public void SetSpeed(uint speed)
92 | {
93 | Hat.SetPWM(PWMPin, (ushort)(speed * 16));
94 | }
95 | }
96 |
97 | public class Stepper
98 | {
99 | public enum Command { FORWARD, BACKWARD };
100 |
101 | public enum Style { SINGLE, DOUBLE, INTERLEAVE, MICROSTEP };
102 |
103 | private MotorHat Hat { get; set; }
104 | private int Index { get; set; }
105 | private int PWMAPin { get; set; }
106 | private int AIn1Pin { get; set; }
107 | private int AIn2Pin { get; set; }
108 | private int PWMBPin { get; set; }
109 | private int BIn1Pin { get; set; }
110 | private int BIn2Pin { get; set; }
111 |
112 | private ushort RevSteps { get; set; } //steps per revolution
113 | private ushort CurrentStep { get; set; }
114 | private uint MicrosecondsPerStep { get; set; }
115 |
116 | private const int MICROSTEPS = 16; // 8 or 16
117 |
118 | private List MicrostepCurve;
119 |
120 | public Stepper(MotorHat hat, ushort steps, int index)
121 | {
122 | Hat = hat;
123 | Index = index;
124 | RevSteps = steps;
125 |
126 | if (8 == MICROSTEPS)
127 | MicrostepCurve = new List() { 0, 50, 98, 142, 180, 212, 236, 250, 255 };
128 | else
129 | MicrostepCurve = new List() { 0, 25, 50, 74, 98, 120, 141, 162, 180, 197, 212, 225, 236, 244, 250, 253, 255 };
130 |
131 | switch (index)
132 | {
133 | case 0:
134 | PWMAPin = 8;
135 | AIn2Pin = 9;
136 | AIn1Pin = 10;
137 | PWMBPin = 13;
138 | BIn2Pin = 12;
139 | BIn1Pin = 11;
140 | break;
141 |
142 | case 1:
143 | PWMAPin = 2;
144 | AIn2Pin = 3;
145 | AIn1Pin = 4;
146 | PWMBPin = 7;
147 | BIn2Pin = 6;
148 | BIn1Pin = 5;
149 | break;
150 |
151 | default:
152 | break;
153 | }
154 | }
155 |
156 | public void SetSpeed(uint rpm)
157 | {
158 | MicrosecondsPerStep = 60000000 / (RevSteps * rpm);
159 | }
160 |
161 | public void Release()
162 | {
163 | Hat.SetPin(AIn1Pin, PinState.LOW);
164 | Hat.SetPin(AIn2Pin, PinState.LOW);
165 | Hat.SetPin(BIn1Pin, PinState.LOW);
166 | Hat.SetPin(BIn2Pin, PinState.LOW);
167 | Hat.SetPWM(PWMAPin, 0);
168 | Hat.SetPWM(PWMBPin, 0);
169 | }
170 |
171 | public void step(ushort steps, Command direction, Style style)
172 | {
173 | uint usPerStep = MicrosecondsPerStep;
174 | int ret;
175 |
176 | if (style == Style.INTERLEAVE)
177 | {
178 | usPerStep /= 2;
179 | }
180 | else if (style == Style.MICROSTEP)
181 | {
182 | usPerStep /= MICROSTEPS;
183 | steps *= MICROSTEPS;
184 | }
185 |
186 | while (0 != steps--)
187 | {
188 | ret = OneStep(direction, style);
189 | Hat.usDelay(usPerStep);
190 | }
191 | }
192 |
193 | public int OneStep(Command direction, Style style)
194 | {
195 | int ocrb, ocra;
196 |
197 | ocra = ocrb = 255;
198 |
199 | if (style == Style.SINGLE)
200 | {
201 | if (1 == ((CurrentStep / (MICROSTEPS / 2)) % 2))
202 | { // we're at an odd step, weird
203 | if (direction == Command.FORWARD)
204 | {
205 | CurrentStep += MICROSTEPS / 2;
206 | }
207 | else
208 | {
209 | CurrentStep -= MICROSTEPS / 2;
210 | }
211 | }
212 | else
213 | { // go to the next even step
214 | if (direction == Command.FORWARD)
215 | {
216 | CurrentStep += MICROSTEPS;
217 | }
218 | else
219 | {
220 | CurrentStep -= MICROSTEPS;
221 | }
222 | }
223 | }
224 | else if (style == Style.DOUBLE)
225 | {
226 | if (0 == (CurrentStep / (MICROSTEPS / 2) % 2))
227 | { // we're at an even step, weird
228 | if (direction == Command.FORWARD)
229 | {
230 | CurrentStep += MICROSTEPS / 2;
231 | }
232 | else
233 | {
234 | CurrentStep -= MICROSTEPS / 2;
235 | }
236 | }
237 | else
238 | { // go to the next odd step
239 | if (direction == Command.FORWARD)
240 | {
241 | CurrentStep += MICROSTEPS;
242 | }
243 | else
244 | {
245 | CurrentStep -= MICROSTEPS;
246 | }
247 | }
248 | }
249 | else if (style == Style.INTERLEAVE)
250 | {
251 | if (direction == Command.FORWARD)
252 | {
253 | CurrentStep += MICROSTEPS / 2;
254 | }
255 | else
256 | {
257 | CurrentStep -= MICROSTEPS / 2;
258 | }
259 | }
260 |
261 | if (style == Style.MICROSTEP)
262 | {
263 | if (direction == Command.FORWARD)
264 | {
265 | CurrentStep++;
266 | }
267 | else
268 | {
269 | // BACKWARDS
270 | CurrentStep--;
271 | }
272 |
273 | CurrentStep += MICROSTEPS * 4;
274 | CurrentStep %= MICROSTEPS * 4;
275 |
276 | ocra = ocrb = 0;
277 | if ((CurrentStep >= 0) && (CurrentStep < MICROSTEPS))
278 | {
279 | ocra = MicrostepCurve[MICROSTEPS - CurrentStep];
280 | ocrb = MicrostepCurve[CurrentStep];
281 | }
282 | else if ((CurrentStep >= MICROSTEPS) && (CurrentStep < MICROSTEPS * 2))
283 | {
284 | ocra = MicrostepCurve[CurrentStep - MICROSTEPS];
285 | ocrb = MicrostepCurve[MICROSTEPS * 2 - CurrentStep];
286 | }
287 | else if ((CurrentStep >= MICROSTEPS * 2) && (CurrentStep < MICROSTEPS * 3))
288 | {
289 | ocra = MicrostepCurve[MICROSTEPS * 3 - CurrentStep];
290 | ocrb = MicrostepCurve[CurrentStep - MICROSTEPS * 2];
291 | }
292 | else if ((CurrentStep >= MICROSTEPS * 3) && (CurrentStep < MICROSTEPS * 4))
293 | {
294 | ocra = MicrostepCurve[CurrentStep - MICROSTEPS * 3];
295 | ocrb = MicrostepCurve[MICROSTEPS * 4 - CurrentStep];
296 | }
297 | }
298 |
299 | CurrentStep += MICROSTEPS * 4;
300 | CurrentStep %= MICROSTEPS * 4;
301 |
302 | Hat.SetPWM(PWMAPin, (ushort)(ocra * 16));
303 | Hat.SetPWM(PWMBPin, (ushort)(ocrb * 16));
304 |
305 | uint latch_state = 0; // all motor pins to 0
306 |
307 | if (style == Style.MICROSTEP)
308 | {
309 | if ((CurrentStep >= 0) && (CurrentStep < MICROSTEPS))
310 | latch_state |= 0x03;
311 | if ((CurrentStep >= MICROSTEPS) && (CurrentStep < MICROSTEPS * 2))
312 | latch_state |= 0x06;
313 | if ((CurrentStep >= MICROSTEPS * 2) && (CurrentStep < MICROSTEPS * 3))
314 | latch_state |= 0x0C;
315 | if ((CurrentStep >= MICROSTEPS * 3) && (CurrentStep < MICROSTEPS * 4))
316 | latch_state |= 0x09;
317 | }
318 | else
319 | {
320 | switch (CurrentStep / (MICROSTEPS / 2))
321 | {
322 | case 0:
323 | latch_state |= 0x1; // energize coil 1 only
324 | break;
325 |
326 | case 1:
327 | latch_state |= 0x3; // energize coil 1+2
328 | break;
329 |
330 | case 2:
331 | latch_state |= 0x2; // energize coil 2 only
332 | break;
333 |
334 | case 3:
335 | latch_state |= 0x6; // energize coil 2+3
336 | break;
337 |
338 | case 4:
339 | latch_state |= 0x4; // energize coil 3 only
340 | break;
341 |
342 | case 5:
343 | latch_state |= 0xC; // energize coil 3+4
344 | break;
345 |
346 | case 6:
347 | latch_state |= 0x8; // energize coil 4 only
348 | break;
349 |
350 | case 7:
351 | latch_state |= 0x9; // energize coil 1+4
352 | break;
353 | }
354 | }
355 |
356 | if (0x01 == (latch_state & 0x1))
357 | {
358 | Hat.SetPin(AIn2Pin, PinState.HIGH);
359 | }
360 | else
361 | {
362 | Hat.SetPin(AIn2Pin, PinState.LOW);
363 | }
364 | if (0x02 == (latch_state & 0x2))
365 | {
366 | Hat.SetPin(BIn1Pin, PinState.HIGH);
367 | }
368 | else
369 | {
370 | Hat.SetPin(BIn1Pin, PinState.LOW);
371 | }
372 | if (0x04 == (latch_state & 0x4))
373 | {
374 | Hat.SetPin(AIn1Pin, PinState.HIGH);
375 | }
376 | else
377 | {
378 | Hat.SetPin(AIn1Pin, PinState.LOW);
379 | }
380 | if (0x08 == (latch_state & 0x8))
381 | {
382 | Hat.SetPin(BIn2Pin, PinState.HIGH);
383 | }
384 | else
385 | {
386 | Hat.SetPin(BIn2Pin, PinState.LOW);
387 | }
388 |
389 | return CurrentStep;
390 | }
391 |
392 | public void usDelay(long duration)
393 | {
394 | // Static method to initialize and start stopwatch
395 | var sw = Stopwatch.StartNew();
396 |
397 | long nanosecPerTick = (1000L * 1000L * 1000L) / Stopwatch.Frequency;
398 | long durationTicks = (duration * 1000) / nanosecPerTick;
399 |
400 | while (sw.ElapsedTicks < durationTicks)
401 | {
402 | }
403 | }
404 | }
405 |
406 | #endregion Subordinate Classes
407 |
408 | #region Main class
409 |
410 | public enum PinState { LOW, HIGH };
411 |
412 | private List MotorList;
413 | private List StepperList;
414 |
415 | public MotorHat(int i2cAddr = 0x60) : base(i2cAddr)
416 | {
417 | MotorList = new List() { null, null, null, null };
418 | StepperList = new List() { null, null };
419 | }
420 |
421 | public async Task InitAsync(uint freq)
422 | {
423 | await InitPCA9685Async();
424 | SetPWMFrequency(freq);
425 | SetAllPWM(0, 0);
426 | }
427 |
428 | public void SetPWM(int pin, ushort value)
429 | {
430 | if (value > 4096)
431 | {
432 | SetPWM(pin, 4096, 0);
433 | }
434 | else
435 | {
436 | SetPWM(pin, 0, value);
437 | }
438 | }
439 |
440 | public void SetPin(int pin, PinState state)
441 | {
442 | if (state == PinState.LOW)
443 | {
444 | SetPWM(pin, 0, 0);
445 | }
446 | else
447 | {
448 | SetPWM(pin, 4096, 0);
449 | }
450 | }
451 |
452 | public DCMotor GetMotor(int index)
453 | {
454 | if (index > 4)
455 | return null;
456 | index--;
457 |
458 | if (null == MotorList[index])
459 | {
460 | MotorList[index] = new DCMotor(this, index);
461 | }
462 |
463 | return MotorList[index];
464 | }
465 |
466 | public Stepper GetStepper(ushort steps, int index)
467 | {
468 | if (index > 2)
469 | return null;
470 | index--;
471 |
472 | if (null == StepperList[index])
473 | {
474 | StepperList[index] = new Stepper(this, steps, index);
475 | }
476 |
477 | return StepperList[index];
478 | }
479 |
480 | #endregion Main class
481 | }
482 | }
--------------------------------------------------------------------------------
/AdafruitClassLibrary/PCA9685.cs:
--------------------------------------------------------------------------------
1 | /*------------------------------------------------------------------------
2 | Adafruit Class Library for Windows Core IoT: PCA968 PWM chip.
3 |
4 | Written by Rick Lesniak for Adafruit Industries.
5 |
6 | Adafruit invests time and resources providing this open source code,
7 | please support Adafruit and open-source hardware by purchasing products
8 | from Adafruit!
9 |
10 | ------------------------------------------------------------------------*/
11 |
12 | using System;
13 | using System.Diagnostics;
14 | using System.Threading.Tasks;
15 |
16 | namespace AdafruitClassLibrary
17 | {
18 | public class Pca9685 : I2CBase
19 | {
20 | #region Constants
21 |
22 | private const byte PCA9685_ADDRESS = 0x40;
23 | private const byte PCA9685_MODE1 = 0x00;
24 | private const byte PCA9685_MODE2 = 0x01;
25 | private const byte PCA9685_SUBADR1 = 0x02;
26 | private const byte PCA9685_SUBADR2 = 0x03;
27 | private const byte PCA9685_SUBADR3 = 0x04;
28 | private const byte PCA9685_PRESCALE = 0xFE;
29 | private const byte LED0_ON_L = 0x06;
30 | private const byte LED0_ON_H = 0x07;
31 | private const byte LED0_OFF_L = 0x08;
32 | private const byte LED0_OFF_H = 0x09;
33 | private const byte ALL_LED_ON_L = 0xFA;
34 | private const byte ALL_LED_ON_H = 0xFB;
35 | private const byte ALL_LED_OFF_L = 0xFC;
36 | private const byte ALL_LED_OFF_H = 0xFD;
37 |
38 | // Bits:
39 | private const byte RESTART = 0x80;
40 |
41 | private const byte SLEEP = 0x10;
42 | private const byte ALLCALL = 0x01;
43 | private const byte INVRT = 0x10;
44 | private const byte OUTDRV = 0x04;
45 |
46 | #endregion Constants
47 |
48 | #region Constructor
49 |
50 | public Pca9685(int addr = PCA9685_ADDRESS) : base(addr)
51 | {
52 | }
53 |
54 | #endregion Constructor
55 |
56 | #region Initialization
57 |
58 | ///
59 | /// InitPCA9685Async
60 | /// Initialize PCA9685 chip
61 | ///
62 | /// async Task
63 | public async Task InitPCA9685Async(I2CSpeed i2cSpeed = I2CSpeed.I2C_100kHz)
64 | {
65 | try
66 | {
67 | await InitI2CAsync(i2cSpeed);
68 |
69 | Reset();
70 | }
71 | catch (Exception e)
72 | {
73 | System.Diagnostics.Debug.WriteLine("Exception: {0}", e.Message);
74 | return;
75 | }
76 | }
77 |
78 | ///
79 | /// Reset
80 | /// Issue Reset command to PCA9685
81 | ///
82 | public void Reset()
83 | {
84 | byte[] writeBuffer;
85 | // set defaults!
86 | // all outputs on Port A
87 | writeBuffer = new byte[] { PCA9685_MODE1, 0x0 };
88 | Write(writeBuffer);
89 | SetPWMFrequency(1000); //default frequency
90 | }
91 |
92 | #endregion Initialization
93 |
94 | #region Operations
95 |
96 | public void SetPWMFrequency(double freq)
97 | {
98 | byte[] readBuffer;
99 | byte[] writeBuffer;
100 |
101 | freq *= 0.9; // Correct for overshoot in the frequency setting
102 |
103 | double preScaleVal = 25000000;
104 | preScaleVal /= 4096;
105 | preScaleVal /= freq;
106 | preScaleVal -= 1;
107 | byte prescale = (byte)Math.Floor(preScaleVal + 0.5);
108 |
109 | lock (Device)
110 | {
111 | writeBuffer = new byte[] { PCA9685_MODE1 };
112 | readBuffer = new byte[1];
113 | WriteRead(writeBuffer, readBuffer);
114 | byte oldmode = readBuffer[0];
115 | byte newmode = (byte)((oldmode & 0x7F) | 0x10); // sleep
116 |
117 | writeBuffer = new byte[] { PCA9685_MODE1, newmode };
118 | Write(writeBuffer); // go to sleep
119 |
120 | writeBuffer = new byte[] { PCA9685_PRESCALE, prescale };
121 | Write(writeBuffer); // set the prescaler
122 |
123 | writeBuffer = new byte[] { PCA9685_MODE1, oldmode };
124 | Write(writeBuffer); // wake
125 |
126 | Task.Delay(5).Wait();
127 |
128 | writeBuffer = new byte[] { PCA9685_MODE1, (byte)(oldmode | 0xa1) };
129 | Write(writeBuffer); // turn on auto mode
130 | }
131 | }
132 |
133 | ///
134 | /// Set PWM on a specified pin
135 | ///
136 | ///
137 | ///
138 | ///
139 | public void SetPWM(int num, ushort on, ushort off)
140 | {
141 | byte[] writeBuffer;
142 | writeBuffer = new byte[] { (byte)(LED0_ON_L + 4 * num), (byte)on, (byte)(on >> 8), (byte)off, (byte)(off >> 8) };
143 | Write(writeBuffer);
144 | }
145 |
146 | ///
147 | /// Set PWM on all pins
148 | ///
149 | ///
150 | ///
151 | public void SetAllPWM(ushort on, ushort off)
152 | {
153 | byte[] writeBuffer;
154 | writeBuffer = new byte[] { (byte)(ALL_LED_ON_L), (byte)on, (byte)(on >> 8), (byte)off, (byte)(off >> 8) };
155 | Write(writeBuffer);
156 | }
157 |
158 | ///
159 | /// Sets pin without having to deal with on/off tick placement and properly handles
160 | /// a zero value as completely off. Optional invert parameter supports inverting
161 | /// the pulse for sinking to ground. Val should be a value from 0 to 4095 inclusive
162 | ///
163 | ///
164 | ///
165 | ///
166 | public void SetPin(int num, ushort val, bool invert)
167 | {
168 | // Clamp value between 0 and 4095 inclusive.
169 | val = Math.Min(val, (ushort)4095);
170 | if (invert)
171 | {
172 | if (val == 0)
173 | {
174 | // Special value for signal fully on.
175 | SetPWM(num, 4096, 0);
176 | }
177 | else if (val == 4095)
178 | {
179 | // Special value for signal fully off.
180 | SetPWM(num, 0, 4096);
181 | }
182 | else
183 | {
184 | SetPWM(num, 0, (ushort)(4095 - val));
185 | }
186 | }
187 | else
188 | {
189 | if (val == 4095)
190 | {
191 | // Special value for signal fully on.
192 | SetPWM(num, 4096, 0);
193 | }
194 | else if (val == 0)
195 | {
196 | // Special value for signal fully off.
197 | SetPWM(num, 0, 4096);
198 | }
199 | else
200 | {
201 | SetPWM(num, 0, val);
202 | }
203 | }
204 | }
205 |
206 | #endregion Operations
207 |
208 | ///
209 | /// usDelay
210 | /// function with delay argument in microseconds
211 | ///
212 | ///
213 | protected void usDelay(long duration)
214 | {
215 | // Static method to initialize and start stopwatch
216 | var sw = Stopwatch.StartNew();
217 |
218 | long nanosecPerTick = (1000L * 1000L * 1000L) / Stopwatch.Frequency;
219 | long durationTicks = (duration * 1000) / nanosecPerTick;
220 |
221 | while (sw.ElapsedTicks < durationTicks)
222 | {
223 | }
224 | }
225 | }
226 | }
--------------------------------------------------------------------------------
/AdafruitClassLibrary/Properties/AdafruitClassLibrary.rd.xml:
--------------------------------------------------------------------------------
1 |
2 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/AdafruitClassLibrary/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Resources;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("AdafruitClassLibrary")]
9 | [assembly: AssemblyDescription("Library for Adafruit hardware under Windows IoT Core")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("Adafruit Industries")]
12 | [assembly: AssemblyProduct("AdafruitClassLibrary")]
13 | [assembly: AssemblyCopyright("2017 Adafruit Industries")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Version information for an assembly consists of the following four values:
18 | //
19 | // Major Version
20 | // Minor Version
21 | // Build Number
22 | // Revision
23 | //
24 | // You can specify all the values or you can default the Build and Revision Numbers
25 | // by using the '*' as shown below:
26 | // [assembly: AssemblyVersion("1.0.*")]
27 | [assembly: AssemblyVersion("1.0.7.0")]
28 | [assembly: AssemblyFileVersion("1.0.7.0")]
29 | [assembly: ComVisible(false)]
30 | [assembly: NeutralResourcesLanguage("en-US")]
--------------------------------------------------------------------------------
/AdafruitClassLibrary/project.json:
--------------------------------------------------------------------------------
1 | {
2 | "dependencies": {
3 | "Microsoft.NETCore.UniversalWindowsPlatform": "5.2.2"
4 | },
5 | "frameworks": {
6 | "uap10.0": {}
7 | },
8 | "runtimes": {
9 | "win10-arm": {},
10 | "win10-arm-aot": {},
11 | "win10-x86": {},
12 | "win10-x86-aot": {},
13 | "win10-x64": {},
14 | "win10-x64-aot": {}
15 | }
16 | }
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright 2016 Adafruit Industries, LLC
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4 |
5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 |
7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # AdafruitClassLibrary
2 | Windows IoT Core libraries for Raspberry Pi
3 |
4 | Sources for AdafruitClassLibrary. Executable for the libary is published in NuGet.
5 |
6 | You can get the library through the Visual Studio package manager command line, available in the View Menu under "Other Windows..."
7 | PM> Install-Package AdafruitClassLibrary
8 | Or you can use the NuGet Package Manager GUI window, available through the Tools menu. Search for "AdafruitClassLibrary"
9 |
10 | The library requires Visual Studio 2015 and Windows 10
11 |
12 |
--------------------------------------------------------------------------------