├── .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 | --------------------------------------------------------------------------------