未验证 提交 c1e6e18d 编写于 作者: O Oliver Japes 提交者: GitHub

Added Support for SK6812-Strips (#1709)

Co-authored-by: NJose Perez Rodriguez <joperezr@microsoft.com>
上级 61d33dd5
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Drawing;
using Iot.Device.Graphics;
namespace Iot.Device.Ws28xx
{
internal class BitmapImageNeo4 : BitmapImage
{
private const int BytesPerComponent = 3;
private const int BytesPerPixel = BytesPerComponent * 4;
// The Neo Pixels require a 50us delay (all zeros) after. Since Spi freq is not exactly
// as requested 100us is used here with good practical results. 100us @ 2.4Mbps and 8bit
// data means we have to add 30 bytes of zero padding.
private const int ResetDelayInBytes = 30;
// This field defines the count within the lookup table. The length correlates to the possible values of a single byte.
private const int LookupCount = 256;
private static readonly byte[] _lookup = new byte[LookupCount * BytesPerComponent];
static BitmapImageNeo4()
{
for (int i = 0; i < LookupCount; i++)
{
int data = 0;
for (int j = 7; j >= 0; j--)
{
data = (data << 3) | 0b100 | ((i >> j) << 1) & 2;
}
_lookup[i * BytesPerComponent + 0] = unchecked((byte)(data >> 16));
_lookup[i * BytesPerComponent + 1] = unchecked((byte)(data >> 8));
_lookup[i * BytesPerComponent + 2] = unchecked((byte)(data >> 0));
}
}
public BitmapImageNeo4(int width, int height)
: base(new byte[width * height * BytesPerPixel + ResetDelayInBytes], width, height, width * BytesPerPixel)
{
}
public override void SetPixel(int x, int y, Color c)
{
// Alpha is used as white.
var offset = y * Stride + x * BytesPerPixel;
Data[offset++] = _lookup[c.G * BytesPerComponent + 0];
Data[offset++] = _lookup[c.G * BytesPerComponent + 1];
Data[offset++] = _lookup[c.G * BytesPerComponent + 2];
Data[offset++] = _lookup[c.R * BytesPerComponent + 0];
Data[offset++] = _lookup[c.R * BytesPerComponent + 1];
Data[offset++] = _lookup[c.R * BytesPerComponent + 2];
Data[offset++] = _lookup[c.B * BytesPerComponent + 0];
Data[offset++] = _lookup[c.B * BytesPerComponent + 1];
Data[offset++] = _lookup[c.B * BytesPerComponent + 2];
Data[offset++] = _lookup[c.A * BytesPerComponent + 0];
Data[offset++] = _lookup[c.A * BytesPerComponent + 1];
Data[offset++] = _lookup[c.A * BytesPerComponent + 2];
}
}
}
# Ws28xx LED drivers
# Ws28xx / SK6812 LED drivers
This binding allows you to update the RGB LEDs on Ws28xx and based strips and matrices.
This binding allows you to update the RGB LEDs on Ws28xx / SK6812 and based strips and matrices.
To see how to use the binding in code, see the [sample](samples/Program.cs).
To see how to use the binding in code, see the [sample](samples/Ws28xx_Samples/Program.cs).
## Documentation
* WS2812B: [Datasheet](https://cdn-shop.adafruit.com/datasheets/WS2812B.pdf)
* WS2808: [Datasheet](https://datasheetspdf.com/pdf-file/806051/Worldsemi/WS2801/1)
* SK6812: [Datasheet](https://cdn-shop.adafruit.com/product-files/2757/p2757_SK6812RGBW_REV01.pdf)
* [Neo pixels guide](https://learn.adafruit.com/adafruit-neopixel-uberguide)
* [Neo pixels x8 stick](https://www.adafruit.com/product/1426)
## Board
### Neo pixels
### Neo pixels / SK6812
![Raspberry Pi Breadboard diagram](rpi-neo-pixels_bb.png)
......@@ -44,7 +45,7 @@ SpiConnectionSettings settings = new(0, 0)
using SpiDevice spi = SpiDevice.Create(settings);
Ws28xx neo = new Ws2808(spi, count);
//Ws28xx neo = new Ws2812b(spi, Count);
// Ws28xx neo = new Ws2812b(spi, Count);
while (true)
{
......@@ -67,17 +68,27 @@ void Rainbow(Ws28xx neo, int count, int iterations = 1)
}
```
***Note:***
Using the SK6812 is almost the same, but the alpha channel of the color is used for the white LED. This means that the predefined color definitions (like ```System.Drawing.Color.Red```) will not work correctly as they have the alpha channel set to 255 (0xFF). That will turn the white LED always on. See the [sample](samples/SK6812_Samples/Programs.cs) for the main differences to the above code.
Because ```System.Drawing.Color``` is a readonly struct, it's not possible to change the any channel directly. In order to correctly set Red, use ```Color.FromArgb(0, 255, 0, 0)```. For setting the white LED, use ```Color.FromArgb(255, 0, 0, 0)```. It's also possible to use an existing definition and remove the white channel like this:
```csharp
var color = Color.HotPink;
var newColor = Color.FromArgb(0, color.R, color.G, color.B);
```
## Binding Notes
### Raspberry Pi setup (/boot/config.txt)
* Make sure spi is enabled
* Make sure SPI is enabled
```text
dtparam=spi=on
```
* Make sure SPI don't change speed fix the core clock:
* To make sure SPI doesn't change speed fix the core clock:
```text
core_freq=250
......
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Device.Spi;
namespace Iot.Device.Ws28xx
{
/// <summary>
/// Represents the SK6812 Driver.
/// </summary>
/// <seealso cref="Iot.Device.Ws28xx.Ws28xx" />
public class Sk6812 : Ws28xx
{
/// <summary>
/// Gets the default SPI clock frequency.
/// </summary>
/// <value>
/// The default SPI clock frequency used for SK6812-LED-Strips.
/// </value>
/// <remarks>This frequency defines how fast the data will be put on the line. The SK6812-strip requires a speed of 1.25 kHz.
/// A single byte takes 1.2 µs for the transfer, the value for a single LED of 32 bit amounts to 38.4 µs.
/// </remarks>
public static int DefaultSpiClockFrequency => 2_400_000;
/// <summary>
/// Initializes a new instance of the <see cref="Sk6812"/> class.
/// </summary>
/// <param name="spiDevice">The spi device.</param>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
public Sk6812(SpiDevice spiDevice, int width, int height = 1)
: base(spiDevice, new BitmapImageNeo4(width, height))
{
}
}
}

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26124.0
# Visual Studio Version 16
VisualStudioVersion = 16.0.31729.503
MinimumVisualStudioVersion = 15.0.26124.0
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{B2C3D1C3-4F59-4544-982C-E205F522DF27}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ws28xx.Samples", "samples\Ws28xx.Samples.csproj", "{D97F0DC4-777E-4F1A-BF18-A63DA22707ED}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ws28xx.Samples", "samples\Ws28xx_Samples\Ws28xx.Samples.csproj", "{D97F0DC4-777E-4F1A-BF18-A63DA22707ED}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ws28xx", "Ws28xx.csproj", "{D909238E-13C4-45C4-B044-CCBB08B51647}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ws28xx", "Ws28xx.csproj", "{D909238E-13C4-45C4-B044-CCBB08B51647}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sk6812.Samples", "samples\Sk6812_Samples\Sk6812.Samples.csproj", "{76D2B9B1-CBDE-445C-BE6D-B5C5C1754192}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
......@@ -18,9 +20,6 @@ Global
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{D97F0DC4-777E-4F1A-BF18-A63DA22707ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D97F0DC4-777E-4F1A-BF18-A63DA22707ED}.Debug|Any CPU.Build.0 = Debug|Any CPU
......@@ -46,8 +45,27 @@ Global
{D909238E-13C4-45C4-B044-CCBB08B51647}.Release|x64.Build.0 = Release|Any CPU
{D909238E-13C4-45C4-B044-CCBB08B51647}.Release|x86.ActiveCfg = Release|Any CPU
{D909238E-13C4-45C4-B044-CCBB08B51647}.Release|x86.Build.0 = Release|Any CPU
{76D2B9B1-CBDE-445C-BE6D-B5C5C1754192}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{76D2B9B1-CBDE-445C-BE6D-B5C5C1754192}.Debug|Any CPU.Build.0 = Debug|Any CPU
{76D2B9B1-CBDE-445C-BE6D-B5C5C1754192}.Debug|x64.ActiveCfg = Debug|Any CPU
{76D2B9B1-CBDE-445C-BE6D-B5C5C1754192}.Debug|x64.Build.0 = Debug|Any CPU
{76D2B9B1-CBDE-445C-BE6D-B5C5C1754192}.Debug|x86.ActiveCfg = Debug|Any CPU
{76D2B9B1-CBDE-445C-BE6D-B5C5C1754192}.Debug|x86.Build.0 = Debug|Any CPU
{76D2B9B1-CBDE-445C-BE6D-B5C5C1754192}.Release|Any CPU.ActiveCfg = Release|Any CPU
{76D2B9B1-CBDE-445C-BE6D-B5C5C1754192}.Release|Any CPU.Build.0 = Release|Any CPU
{76D2B9B1-CBDE-445C-BE6D-B5C5C1754192}.Release|x64.ActiveCfg = Release|Any CPU
{76D2B9B1-CBDE-445C-BE6D-B5C5C1754192}.Release|x64.Build.0 = Release|Any CPU
{76D2B9B1-CBDE-445C-BE6D-B5C5C1754192}.Release|x86.ActiveCfg = Release|Any CPU
{76D2B9B1-CBDE-445C-BE6D-B5C5C1754192}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{D97F0DC4-777E-4F1A-BF18-A63DA22707ED} = {B2C3D1C3-4F59-4544-982C-E205F522DF27}
{76D2B9B1-CBDE-445C-BE6D-B5C5C1754192} = {B2C3D1C3-4F59-4544-982C-E205F522DF27}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {F452F127-C7D8-4D14-9484-B23E8F0C3468}
EndGlobalSection
EndGlobal
\ No newline at end of file
EndGlobal
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Device.Spi;
using System.Drawing;
using System.Threading;
using Iot.Device.Graphics;
using Iot.Device.Ws28xx;
// Configure the count of pixels
const int Count = 8;
Console.Clear();
SpiConnectionSettings settings = new(0, 0)
{
ClockFrequency = Sk6812.DefaultSpiClockFrequency,
Mode = SpiMode.Mode0,
DataBitLength = 8
};
using SpiDevice spi = SpiDevice.Create(settings);
Sk6812 neo = new Sk6812(spi, Count);
Console.CancelKeyPress += (o, e) =>
{
BitmapImage img = neo.Image;
img.Clear();
neo.Update();
Console.Clear();
};
while (true)
{
Color4Wipe(neo, Color.White, Count);
Color4Wipe(neo, Color.Red, Count);
Color4Wipe(neo, Color.Green, Count);
Color4Wipe(neo, Color.Blue, Count);
Theatre4Chase(neo, Color.White, Count);
Theatre4Chase(neo, Color.Red, Count);
Theatre4Chase(neo, Color.Green, Count);
Theatre4Chase(neo, Color.Blue, Count);
Rainbow4(neo, Count);
Rainbow4Cycle(neo, Count);
TheaterChase4Rainbow(neo, Count);
}
void Color4Wipe(Sk6812 neo, Color color, int count)
{
BitmapImage img = neo.Image;
for (var i = 0; i < count; i++)
{
img.SetPixel(i, 0, color);
neo.Update();
Thread.Sleep(100);
}
}
void Theatre4Chase(Ws28xx neo, Color color, int count, int iterations = 10)
{
BitmapImage img = neo.Image;
for (var i = 0; i < iterations; i++)
{
for (var j = 0; j < 3; j++)
{
for (var k = 0; k < count; k += 3)
{
img.SetPixel(j + k, 0, color);
}
neo.Update();
Thread.Sleep(100);
for (var k = 0; k < count; k += 3)
{
img.SetPixel(j + k, 0, Color.FromArgb(0, 0, 0, 0));
}
}
}
}
Color Wheel4(int position)
{
if (position < 85)
{
return Color.FromArgb(0, position * 3, 255 - position * 3, 0);
}
else if (position < 170)
{
position -= 85;
return Color.FromArgb(0, 255 - position * 3, 0, position * 3);
}
else
{
position -= 170;
return Color.FromArgb(0, 0, position * 3, 255 - position * 3);
}
}
void Rainbow4(Sk6812 neo, int count, int iterations = 1)
{
BitmapImage img = neo.Image;
for (var i = 0; i < 255 * iterations; i++)
{
for (var j = 0; j < count; j++)
{
img.SetPixel(j, 0, Wheel4((i + j) & 255));
}
neo.Update();
Thread.Sleep(50);
}
}
void Rainbow4Cycle(Ws28xx neo, int count, int iterations = 1)
{
BitmapImage img = neo.Image;
for (var i = 0; i < 255 * iterations; i++)
{
for (var j = 0; j < count; j++)
{
img.SetPixel(j, 0, Wheel4(((int)(j * 255 / count) + i) & 255));
}
neo.Update();
Thread.Sleep(50);
}
}
void TheaterChase4Rainbow(Ws28xx neo, int count)
{
BitmapImage img = neo.Image;
for (var i = 0; i < 255; i++)
{
for (var j = 0; j < 3; j++)
{
for (var k = 0; k < count; k += 3)
{
img.SetPixel(k + j, 0, Wheel4((k + i) % 255));
}
neo.Update();
System.Threading.Thread.Sleep(100);
for (var k = 0; k < count; k += 3)
{
img.SetPixel(k + j, 0, Color.FromArgb(0, 0, 0, 0));
}
}
}
}
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="../../Ws28xx.csproj" />
</ItemGroup>
</Project>
\ No newline at end of file
......@@ -4,6 +4,6 @@
<TargetFramework>$(DefaultSampleTfms)</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="../Ws28xx.csproj" />
<ProjectReference Include="../../Ws28xx.csproj" />
</ItemGroup>
</Project>
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册