diff --git a/Resources/images/SPIWS2812b/Adafruit_NeoPixel8x8.jpg b/Resources/images/SPIWS2812b/Adafruit_NeoPixel8x8.jpg new file mode 100644 index 000000000..efa8df461 Binary files /dev/null and b/Resources/images/SPIWS2812b/Adafruit_NeoPixel8x8.jpg differ diff --git a/Resources/images/SPIWS2812b/RaspberryPi_WS2812b.png b/Resources/images/SPIWS2812b/RaspberryPi_WS2812b.png new file mode 100644 index 000000000..eab7f1c90 Binary files /dev/null and b/Resources/images/SPIWS2812b/RaspberryPi_WS2812b.png differ diff --git a/Resources/images/SPIWS2812b/RaspberryPi_WS2812b_Schematics.png b/Resources/images/SPIWS2812b/RaspberryPi_WS2812b_Schematics.png new file mode 100644 index 000000000..21f932162 Binary files /dev/null and b/Resources/images/SPIWS2812b/RaspberryPi_WS2812b_Schematics.png differ diff --git a/Resources/images/SPIWS2812b/WS2812B_CompositionData.PNG b/Resources/images/SPIWS2812b/WS2812B_CompositionData.PNG new file mode 100644 index 000000000..f3877ba6b Binary files /dev/null and b/Resources/images/SPIWS2812b/WS2812B_CompositionData.PNG differ diff --git a/Resources/images/SPIWS2812b/WS2812B_DataTransferTime.PNG b/Resources/images/SPIWS2812b/WS2812B_DataTransferTime.PNG new file mode 100644 index 000000000..c61a1897a Binary files /dev/null and b/Resources/images/SPIWS2812b/WS2812B_DataTransferTime.PNG differ diff --git a/Resources/images/SPIWS2812b/WS2812B_SequenceChart.PNG b/Resources/images/SPIWS2812b/WS2812B_SequenceChart.PNG new file mode 100644 index 000000000..3ef5ac579 Binary files /dev/null and b/Resources/images/SPIWS2812b/WS2812B_SequenceChart.PNG differ diff --git a/Samples/SPIWS2812b/CS/.gitignore b/Samples/SPIWS2812b/CS/.gitignore new file mode 100644 index 000000000..62e4e8f2e --- /dev/null +++ b/Samples/SPIWS2812b/CS/.gitignore @@ -0,0 +1,352 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ diff --git a/Samples/SPIWS2812b/CS/App.xaml b/Samples/SPIWS2812b/CS/App.xaml new file mode 100644 index 000000000..213f6aab5 --- /dev/null +++ b/Samples/SPIWS2812b/CS/App.xaml @@ -0,0 +1,7 @@ + + + diff --git a/Samples/SPIWS2812b/CS/App.xaml.cs b/Samples/SPIWS2812b/CS/App.xaml.cs new file mode 100644 index 000000000..1e716b331 --- /dev/null +++ b/Samples/SPIWS2812b/CS/App.xaml.cs @@ -0,0 +1,100 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices.WindowsRuntime; +using Windows.ApplicationModel; +using Windows.ApplicationModel.Activation; +using Windows.Foundation; +using Windows.Foundation.Collections; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Controls.Primitives; +using Windows.UI.Xaml.Data; +using Windows.UI.Xaml.Input; +using Windows.UI.Xaml.Media; +using Windows.UI.Xaml.Navigation; + +namespace IoT.Windows10.ws2812b +{ + /// + /// Provides application-specific behavior to supplement the default Application class. + /// + sealed partial class App : Application + { + /// + /// Initializes the singleton application object. This is the first line of authored code + /// executed, and as such is the logical equivalent of main() or WinMain(). + /// + public App() + { + this.InitializeComponent(); + this.Suspending += OnSuspending; + } + + /// + /// Invoked when the application is launched normally by the end user. Other entry points + /// will be used such as when the application is launched to open a specific file. + /// + /// Details about the launch request and process. + protected override void OnLaunched(LaunchActivatedEventArgs e) + { + Frame rootFrame = Window.Current.Content as Frame; + + // Do not repeat app initialization when the Window already has content, + // just ensure that the window is active + if (rootFrame == null) + { + // Create a Frame to act as the navigation context and navigate to the first page + rootFrame = new Frame(); + + rootFrame.NavigationFailed += OnNavigationFailed; + + if (e.PreviousExecutionState == ApplicationExecutionState.Terminated) + { + //TODO: Load state from previously suspended application + } + + // Place the frame in the current Window + Window.Current.Content = rootFrame; + } + + if (e.PrelaunchActivated == false) + { + if (rootFrame.Content == null) + { + // When the navigation stack isn't restored navigate to the first page, + // configuring the new page by passing required information as a navigation + // parameter + rootFrame.Navigate(typeof(MainPage), e.Arguments); + } + // Ensure the current window is active + Window.Current.Activate(); + } + } + + /// + /// Invoked when Navigation to a certain page fails + /// + /// The Frame which failed navigation + /// Details about the navigation failure + void OnNavigationFailed(object sender, NavigationFailedEventArgs e) + { + throw new Exception("Failed to load Page " + e.SourcePageType.FullName); + } + + /// + /// Invoked when application execution is being suspended. Application state is saved + /// without knowing whether the application will be terminated or resumed with the contents + /// of memory still intact. + /// + /// The source of the suspend request. + /// Details about the suspend request. + private void OnSuspending(object sender, SuspendingEventArgs e) + { + var deferral = e.SuspendingOperation.GetDeferral(); + //TODO: Save application state and stop any background activity + deferral.Complete(); + } + } +} diff --git a/Samples/SPIWS2812b/CS/Assets/LockScreenLogo.scale-200.png b/Samples/SPIWS2812b/CS/Assets/LockScreenLogo.scale-200.png new file mode 100644 index 000000000..735f57adb Binary files /dev/null and b/Samples/SPIWS2812b/CS/Assets/LockScreenLogo.scale-200.png differ diff --git a/Samples/SPIWS2812b/CS/Assets/SplashScreen.scale-200.png b/Samples/SPIWS2812b/CS/Assets/SplashScreen.scale-200.png new file mode 100644 index 000000000..023e7f1fe Binary files /dev/null and b/Samples/SPIWS2812b/CS/Assets/SplashScreen.scale-200.png differ diff --git a/Samples/SPIWS2812b/CS/Assets/Square150x150Logo.scale-200.png b/Samples/SPIWS2812b/CS/Assets/Square150x150Logo.scale-200.png new file mode 100644 index 000000000..af49fec1a Binary files /dev/null and b/Samples/SPIWS2812b/CS/Assets/Square150x150Logo.scale-200.png differ diff --git a/Samples/SPIWS2812b/CS/Assets/Square44x44Logo.scale-200.png b/Samples/SPIWS2812b/CS/Assets/Square44x44Logo.scale-200.png new file mode 100644 index 000000000..ce342a2ec Binary files /dev/null and b/Samples/SPIWS2812b/CS/Assets/Square44x44Logo.scale-200.png differ diff --git a/Samples/SPIWS2812b/CS/Assets/Square44x44Logo.targetsize-24_altform-unplated.png b/Samples/SPIWS2812b/CS/Assets/Square44x44Logo.targetsize-24_altform-unplated.png new file mode 100644 index 000000000..f6c02ce97 Binary files /dev/null and b/Samples/SPIWS2812b/CS/Assets/Square44x44Logo.targetsize-24_altform-unplated.png differ diff --git a/Samples/SPIWS2812b/CS/Assets/StoreLogo.png b/Samples/SPIWS2812b/CS/Assets/StoreLogo.png new file mode 100644 index 000000000..7385b56c0 Binary files /dev/null and b/Samples/SPIWS2812b/CS/Assets/StoreLogo.png differ diff --git a/Samples/SPIWS2812b/CS/Assets/Wide310x150Logo.scale-200.png b/Samples/SPIWS2812b/CS/Assets/Wide310x150Logo.scale-200.png new file mode 100644 index 000000000..288995b39 Binary files /dev/null and b/Samples/SPIWS2812b/CS/Assets/Wide310x150Logo.scale-200.png differ diff --git a/Samples/SPIWS2812b/CS/IWS2812bDriver.cs b/Samples/SPIWS2812b/CS/IWS2812bDriver.cs new file mode 100644 index 000000000..284dba5af --- /dev/null +++ b/Samples/SPIWS2812b/CS/IWS2812bDriver.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace IoT.Windows10.ws2812b +{ + public interface IWS2812bDriver + { + bool Write(int pixel, byte red, byte green, byte blue); + bool Write(int pixel, Color color); + bool Clean(); + void RefreshLeds(); + } +} diff --git a/Samples/SPIWS2812b/CS/IoT.Windows10.ws2812b.csproj b/Samples/SPIWS2812b/CS/IoT.Windows10.ws2812b.csproj new file mode 100644 index 000000000..7556b1cbe --- /dev/null +++ b/Samples/SPIWS2812b/CS/IoT.Windows10.ws2812b.csproj @@ -0,0 +1,170 @@ + + + + + Debug + x86 + {ECAA6297-0270-431B-B5D8-619D169442D6} + AppContainerExe + Properties + IoT.Windows10.ws2812b + IoT.Windows10.ws2812b + en-US + UAP + 10.0.18362.0 + 10.0.17763.0 + 14 + 512 + {A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + true + false + + + true + bin\x86\Debug\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP + ;2008 + full + x86 + false + prompt + true + + + bin\x86\Release\ + TRACE;NETFX_CORE;WINDOWS_UWP + true + ;2008 + pdbonly + x86 + false + prompt + true + true + + + true + bin\ARM\Debug\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP + ;2008 + full + ARM + false + prompt + true + + + bin\ARM\Release\ + TRACE;NETFX_CORE;WINDOWS_UWP + true + ;2008 + pdbonly + ARM + false + prompt + true + true + + + true + bin\ARM64\Debug\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP + ;2008 + full + ARM64 + false + prompt + true + true + + + bin\ARM64\Release\ + TRACE;NETFX_CORE;WINDOWS_UWP + true + ;2008 + pdbonly + ARM64 + false + prompt + true + true + + + true + bin\x64\Debug\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP + ;2008 + full + x64 + false + prompt + true + + + bin\x64\Release\ + TRACE;NETFX_CORE;WINDOWS_UWP + true + ;2008 + pdbonly + x64 + false + prompt + true + true + + + PackageReference + + + + App.xaml + + + + MainPage.xaml + + + + + + + Designer + + + + + + + + + + + + + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + + + 6.2.8 + + + + 14.0 + + + + \ No newline at end of file diff --git a/Samples/SPIWS2812b/CS/IoT.Windows10.ws2812b.sln b/Samples/SPIWS2812b/CS/IoT.Windows10.ws2812b.sln new file mode 100644 index 000000000..3afc5c6a5 --- /dev/null +++ b/Samples/SPIWS2812b/CS/IoT.Windows10.ws2812b.sln @@ -0,0 +1,51 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29123.89 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IoT.Windows10.ws2812b", "IoT.Windows10.ws2812b.csproj", "{ECAA6297-0270-431B-B5D8-619D169442D6}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|ARM = Debug|ARM + Debug|ARM64 = Debug|ARM64 + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|ARM = Release|ARM + Release|ARM64 = Release|ARM64 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {ECAA6297-0270-431B-B5D8-619D169442D6}.Debug|ARM.ActiveCfg = Debug|ARM + {ECAA6297-0270-431B-B5D8-619D169442D6}.Debug|ARM.Build.0 = Debug|ARM + {ECAA6297-0270-431B-B5D8-619D169442D6}.Debug|ARM.Deploy.0 = Debug|ARM + {ECAA6297-0270-431B-B5D8-619D169442D6}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {ECAA6297-0270-431B-B5D8-619D169442D6}.Debug|ARM64.Build.0 = Debug|ARM64 + {ECAA6297-0270-431B-B5D8-619D169442D6}.Debug|ARM64.Deploy.0 = Debug|ARM64 + {ECAA6297-0270-431B-B5D8-619D169442D6}.Debug|x64.ActiveCfg = Debug|x64 + {ECAA6297-0270-431B-B5D8-619D169442D6}.Debug|x64.Build.0 = Debug|x64 + {ECAA6297-0270-431B-B5D8-619D169442D6}.Debug|x64.Deploy.0 = Debug|x64 + {ECAA6297-0270-431B-B5D8-619D169442D6}.Debug|x86.ActiveCfg = Debug|x86 + {ECAA6297-0270-431B-B5D8-619D169442D6}.Debug|x86.Build.0 = Debug|x86 + {ECAA6297-0270-431B-B5D8-619D169442D6}.Debug|x86.Deploy.0 = Debug|x86 + {ECAA6297-0270-431B-B5D8-619D169442D6}.Release|ARM.ActiveCfg = Release|ARM + {ECAA6297-0270-431B-B5D8-619D169442D6}.Release|ARM.Build.0 = Release|ARM + {ECAA6297-0270-431B-B5D8-619D169442D6}.Release|ARM.Deploy.0 = Release|ARM + {ECAA6297-0270-431B-B5D8-619D169442D6}.Release|ARM64.ActiveCfg = Release|ARM64 + {ECAA6297-0270-431B-B5D8-619D169442D6}.Release|ARM64.Build.0 = Release|ARM64 + {ECAA6297-0270-431B-B5D8-619D169442D6}.Release|ARM64.Deploy.0 = Release|ARM64 + {ECAA6297-0270-431B-B5D8-619D169442D6}.Release|x64.ActiveCfg = Release|x64 + {ECAA6297-0270-431B-B5D8-619D169442D6}.Release|x64.Build.0 = Release|x64 + {ECAA6297-0270-431B-B5D8-619D169442D6}.Release|x64.Deploy.0 = Release|x64 + {ECAA6297-0270-431B-B5D8-619D169442D6}.Release|x86.ActiveCfg = Release|x86 + {ECAA6297-0270-431B-B5D8-619D169442D6}.Release|x86.Build.0 = Release|x86 + {ECAA6297-0270-431B-B5D8-619D169442D6}.Release|x86.Deploy.0 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {EA368887-97AC-4150-B8D1-83CA2AB39F38} + EndGlobalSection +EndGlobal diff --git a/Samples/SPIWS2812b/CS/MainPage.xaml b/Samples/SPIWS2812b/CS/MainPage.xaml new file mode 100644 index 000000000..6b2dc6b8d --- /dev/null +++ b/Samples/SPIWS2812b/CS/MainPage.xaml @@ -0,0 +1,14 @@ + + + + + + diff --git a/Samples/SPIWS2812b/CS/MainPage.xaml.cs b/Samples/SPIWS2812b/CS/MainPage.xaml.cs new file mode 100644 index 000000000..a407b1852 --- /dev/null +++ b/Samples/SPIWS2812b/CS/MainPage.xaml.cs @@ -0,0 +1,56 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Windows.Devices.Gpio; +using Windows.UI.Xaml.Controls; + +// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409 + +namespace IoT.Windows10.ws2812b +{ + /// + /// An empty page that can be used on its own or navigated to within a Frame. + /// + public sealed partial class MainPage : Page + { + private int delay = 80; + + public MainPage() + { + InitializeComponent(); + + var ws2812bDriver = new WS2812bDriver(); + + Task.Run(async () => + { + await ws2812bDriver.InitializeAsync(64); + Random random = new Random(); + //while (true) + //{ + // ws2812bDriver.Clean(); + // ws2812bDriver.Write(random.Next(0, 63), (byte)random.Next(0, 255), (byte)random.Next(0, 255), (byte)random.Next(0, 255)); + // ws2812bDriver.Write(random.Next(0, 63) , (byte)random.Next(0, 255), (byte)random.Next(0, 255), (byte)random.Next(0, 255)); + // ws2812bDriver.Write(random.Next(0, 63), (byte)random.Next(0, 255), (byte)random.Next(0, 255), (byte)random.Next(0, 255)); + // ws2812bDriver.Write(random.Next(0, 63) , (byte)random.Next(0, 255), (byte)random.Next(0, 255), (byte)random.Next(0, 255)); + // ws2812bDriver.RefreshLeds(); + // Thread.Sleep(1); + //} + + while (true) + { + var r = (byte)random.Next(0, 255); + var g = (byte)random.Next(0, 255); + var b = (byte)random.Next(0, 255); + for (int i = 0; i < 64; i++) + ws2812bDriver.Write(i, r, g, b); + + ws2812bDriver.RefreshLeds(); + Thread.Sleep(delay); + ws2812bDriver.Clean(); + ws2812bDriver.RefreshLeds(); + Thread.Sleep(delay + 20); + } + }); + } + } +} diff --git a/Samples/SPIWS2812b/CS/Package.appxmanifest b/Samples/SPIWS2812b/CS/Package.appxmanifest new file mode 100644 index 000000000..ed22b5ae9 --- /dev/null +++ b/Samples/SPIWS2812b/CS/Package.appxmanifest @@ -0,0 +1,49 @@ + + + + + + + + + + IoT.Windows10.ws2812b + feoku + Assets\StoreLogo.png + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Samples/SPIWS2812b/CS/Properties/AssemblyInfo.cs b/Samples/SPIWS2812b/CS/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..0d369572d --- /dev/null +++ b/Samples/SPIWS2812b/CS/Properties/AssemblyInfo.cs @@ -0,0 +1,29 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("IoT.Windows10.ws2812b")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("IoT.Windows10.ws2812b")] +[assembly: AssemblyCopyright("Copyright © 2019")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: ComVisible(false)] \ No newline at end of file diff --git a/Samples/SPIWS2812b/CS/Properties/Default.rd.xml b/Samples/SPIWS2812b/CS/Properties/Default.rd.xml new file mode 100644 index 000000000..af00722cd --- /dev/null +++ b/Samples/SPIWS2812b/CS/Properties/Default.rd.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/Samples/SPIWS2812b/CS/README.md b/Samples/SPIWS2812b/CS/README.md new file mode 100644 index 000000000..8d058da12 Binary files /dev/null and b/Samples/SPIWS2812b/CS/README.md differ diff --git a/Samples/SPIWS2812b/CS/WS2812bDriver.cs b/Samples/SPIWS2812b/CS/WS2812bDriver.cs new file mode 100644 index 000000000..fc57ee66b --- /dev/null +++ b/Samples/SPIWS2812b/CS/WS2812bDriver.cs @@ -0,0 +1,114 @@ +using System; +using System.Drawing; +using System.Threading; +using System.Threading.Tasks; +using Windows.Devices.Enumeration; +using Windows.Devices.Spi; + +namespace IoT.Windows10.ws2812b +{ + public class WS2812bDriver : IWS2812bDriver + { + private SpiDevice spiDevice; + private byte[] bufferLedSPI; + private const byte ledBitOn = 0b1110; + private const byte ledBitOff = 0b1100; + + public async Task InitializeAsync(int pixels, string spiFriendlyName = "SPI0", int chipSelectLine = 0) + { + //we use 4bits for each byte of ws2812b, so we need 12 bytes to set the 24 bytes of led + var lenghtBuffer = pixels * 12; + bufferLedSPI = new byte[lenghtBuffer]; + + var settings = new SpiConnectionSettings(chipSelectLine); + settings.ClockFrequency = 4000000; + + var spi = SpiDevice.GetDeviceSelector(spiFriendlyName); + var deviceInformation = await DeviceInformation.FindAllAsync(spi); + spiDevice = await SpiDevice.FromIdAsync(deviceInformation[0].Id, settings); + } + + public bool Write(int pixel, byte red, byte green, byte blue) + { + byte[] Colors = new byte[] { green, red, blue }; + + if (spiDevice != null) + { + int indexPixel = pixel * 12; + + foreach (byte Color in Colors) + { + byte[] EncodedBytes = Encode(Color); + + foreach (byte Data in EncodedBytes) + { + bufferLedSPI[indexPixel++] = Data; + } + } + return true; + } + + return false; + } + + private byte[] Encode(byte ColorByte) + { + + byte[] byteArray = new byte[4]; + int indexBit = 0; + int indexByte = 3; + byte mask = 0x80; + + while(indexBit < 8) + { + mask = (byte)(0x80 >> indexBit); + indexBit++; + + if ((ColorByte & mask) > 0) + byteArray[indexByte] = ledBitOn << 4; + else + byteArray[indexByte] = ledBitOff << 4; + + + mask = (byte)(1 >> indexBit); + indexBit++; + + if ((ColorByte & mask) > 0) + byteArray[indexByte] |= ledBitOn; + else + byteArray[indexByte] |= ledBitOff; + + indexByte--; + } + + return byteArray; + } + + public bool Write(int pixel, Color color) + { + throw new NotImplementedException(); + } + + public void RefreshLeds() + { + spiDevice.Write(bufferLedSPI); + Thread.Sleep(1); + } + + public bool Clean() + { + byte cleanByteContent = ledBitOff << 4 | ledBitOff; + try + { + for (int indexLedByte = 0; indexLedByte < bufferLedSPI.Length; indexLedByte++) + bufferLedSPI[indexLedByte] = cleanByteContent; + } + catch(Exception ex) + { + return false; + } + + return true; + } + } +} diff --git a/Samples/SPIWS2812b/README.md b/Samples/SPIWS2812b/README.md new file mode 100644 index 000000000..9b2a9c833 --- /dev/null +++ b/Samples/SPIWS2812b/README.md @@ -0,0 +1,168 @@ +# ws2812b drive with SPI + +We'll connect an SPI accelerometer to your Raspberry Pi 3 and create a simple app to read data from it. We'll walk you through step-by-step, so no background knowledge of SPI is needed. +However, if you're curious, SparkFun provides a great [tutorial on SPI](https://learn.sparkfun.com/tutorials/serial-peripheral-interface-spi). + +This is a headed sample. To better understand what headed mode is and how to configure your device to be headed, follow the instructions [here](https://docs.microsoft.com/en-us/windows/iot-core/learn-about-hardware/headlessmode). + +### Load the project in Visual Studio + +You can find the source code for this sample by downloading a zip of all of our samples [here](https://github.com/Microsoft/Windows-iotcore-samples/archive/master.zip) and navigating to the `samples\SPIWS2812b`. Make a copy of the folder on your disk and open the project from Visual Studio. + +### The WS2812B Led + +WS2812B is a intelligent control LED light source that the control circuit and RGB chip. + +View the [WS2812B Datasheet](https://cdn-shop.adafruit.com/datasheets/WS2812B.pdf) + +### Connect the ws2812b matrix board to your device + +You'll need a few components: + +* an [NeoPixel NeoMatrix 8x8 - 64 RGB LED](https://www.sparkfun.com/products/12662) with pin headers soldered on + +* a breadboard and a couple of female-to-female connector wires + +Visit the **Raspberry Pi 2 or 3/MinnowBoard Max** sections below depending on which device you have: + +![Electrical Components](../../Resources/images/SPIWS2812b/Adafruit_NeoPixel8x8.jpg) + +#### Raspberry Pi 2 or 3 +If you have a Raspberry Pi 2 or 3, we need to hook up power, ground, and the SPI lines to the accelerometer. + See the [Raspberry Pi 2 and 3 pin mapping page](https://docs.microsoft.com/en-us/windows/iot-core/learn-about-hardware/pinmappings/pinmappingsrpi) for more details on the RPi2 and RPi3 IO pins. + +**Note: Make sure to power off the RPi2 or RPi3 when connecting your circuit. This is good practice to reduce the chance of an accidental short circuit during construction.** + +The WS2812b breakout board has 3 IO pins, connect them as follows: + +1. **GND:** Connect to ground on the RPi2 or RPi3 (Pin 6) +2. **VCC:** Connect to 3.3V on the RPi2 or RPi3 (Pin 1) +7. **SDA:** Connect to SPI0 MOSI on the RPi2 or RPi3 (Pin 19) + +Here are the connections shown on a breadboard: + +![Breadboard connections](../../Resources/images/SPIWS2812b/RaspberryPi_WS2812b.png) + +*Image made with [Fritzing](http://fritzing.org/)* + +Here are the schematics: + +![WS2812b schematics](../../Resources/images/SPIWS2812b/RaspberryPi_WS2812b_Schematics.png) + +### About the implementation + +The WS2812B include intelligent digital port data latch and signal reshaping amplification drive circuit. To set the RGB value there is a time specification that define what is a 1 or 0 value. + +![WS2812B_DataTransferTime](../../Resources/images/SPIWS2812b/WS2812B_DataTransferTime.PNG) + +And the sequence chart that explains how to set each bit of RGB data + +![WS2812B_SequenceChart](../../Resources/images/SPIWS2812b/WS2812B_SequenceChart.PNG) + +...and we need 24 of this patterns to set the entire RGB data of the led + +![WS2812B_CompositonData](../../Resources/images/SPIWS2812b/WS2812B_CompositionData.PNG) + +#### Initialize the SPI Bus + +```csharp +public async Task InitializeAsync(int pixels, string spiFriendlyName = "SPI0", int chipSelectLine = 0) +{ + //we use 4bits for each byte of ws2812b, so we need 12 bytes to set the 24 bytes of led + var lenghtBuffer = pixels * 12; + bufferLedSPI = new byte[lenghtBuffer]; + + var settings = new SpiConnectionSettings(chipSelectLine); + settings.ClockFrequency = 4000000; + + var spi = SpiDevice.GetDeviceSelector(spiFriendlyName); + var deviceInformation = await DeviceInformation.FindAllAsync(spi); + spiDevice = await SpiDevice.FromIdAsync(deviceInformation[0].Id, settings); +} +``` + +We initialize the SPI with 4MHz. It means that each bit sent will have 0.25us signal width. (1s/40000000) = 0.25us + +So using the **Sequence Chart** to send a zero bit we need to send two bits high and two bits low. So we can create the constants of these sequences of bits. + +```csharp +private const byte ledBitOn = 0b1110; +private const byte ledBitOff = 0b1100; +``` + +TH = 0b1110 = 0.25us + 0.25us + 0.25us + 0s = 0.85us + +TL = 0b1100 = 0.25us + 0.25us + 0s + 0s = 0.5us + +```csharp +private byte[] Encode(byte ColorByte) +{ + byte[] byteArray = new byte[4]; + int indexBit = 0; + int indexByte = 3; + byte mask = 0x80; + + while(indexBit < 8) + { + mask = (byte)(0x80 >> indexBit); + indexBit++; + + if ((ColorByte & mask) > 0) + byteArray[indexByte] = ledBitOn << 4; + else + byteArray[indexByte] = ledBitOff << 4; + + + mask = (byte)(1 >> indexBit); + indexBit++; + + if ((ColorByte & mask) > 0) + byteArray[indexByte] |= ledBitOn; + else + byteArray[indexByte] |= ledBitOff; + + indexByte--; + + + return byteArray; +} +``` + +Each color of RGB is represented by a byte. How we seen, each bit of WS2812B needs 4 bits of the data sent by the SPI, so to send 8 bits of WS2812B we'll use 4 bytes (two bits on each byte). + +And finally, we build this bytes for all colors. + +```csharp +public bool Write(int pixel, byte red, byte green, byte blue) +{ + byte[] Colors = new byte[] { green, red, blue }; + + if (spiDevice != null) + { + int indexPixel = pixel * 12; + + foreach (byte Color in Colors) + { + byte[] EncodedBytes = Encode(Color); + + foreach (byte Data in EncodedBytes) + { + bufferLedSPI[indexPixel++] = Data; + } + } + return true; + } + + return false; +} +``` + +Just write data to SPI + +```csharp +public void RefreshLeds() +{ + spiDevice.Write(bufferLedSPI); + Thread.Sleep(1); +} +```