diff options
author | David Crocker <dcrocker@eschertech.com> | 2021-03-21 12:46:59 +0300 |
---|---|---|
committer | David Crocker <dcrocker@eschertech.com> | 2021-03-21 12:46:59 +0300 |
commit | 7b10730a5fee550e1d7b248631e900b88cd5b792 (patch) | |
tree | d6b6892968c535966e9ca70e50221d34b2447c41 /src/Fans | |
parent | e898ae08700eb6caea54ff10a0f8618823c433eb (diff) |
Added support for RGBW NeoPixel LED strips
Diffstat (limited to 'src/Fans')
-rw-r--r-- | src/Fans/LedStripDriver.cpp | 107 |
1 files changed, 79 insertions, 28 deletions
diff --git a/src/Fans/LedStripDriver.cpp b/src/Fans/LedStripDriver.cpp index 3e84a91a..df619b85 100644 --- a/src/Fans/LedStripDriver.cpp +++ b/src/Fans/LedStripDriver.cpp @@ -49,15 +49,48 @@ namespace LedStripDriver { constexpr uint32_t DefaultDotStarSpiClockFrequency = 100000; // try sending at 100kHz constexpr uint32_t DefaultNeoPixelSpiClockFrequency = 2500000; // must be between about 2MHz and about 4MHz - constexpr uint32_t DefaultSpiFrequencies[2] = { DefaultDotStarSpiClockFrequency, DefaultNeoPixelSpiClockFrequency }; constexpr uint32_t MinNeoPixelResetTicks = (250 * StepTimer::StepClockRate)/1000000; // 250us minimum Neopixel reset time on later chips - constexpr size_t ChunkBufferSize = 720; // the size of our DMA buffer. DotStar LEDs use 4 bytes/LED, NeoPixels use 12 bytes/LED. + constexpr size_t ChunkBufferSize = 60 * 16; // the size of our DMA buffer. DotStar LEDs use 4 bytes/LED, NeoPixel RGBW use 16 bytes/LED. constexpr unsigned int MaxDotStarChunkSize = ChunkBufferSize/4; // maximum number of DotStarLEDs we DMA to in one go. Most strips have 30 LEDs/metre. - constexpr unsigned int MaxNeoPixelChunkSize = ChunkBufferSize/12; // maximum number of NeoPixels we can support. A full ring contains 60. + constexpr unsigned int MaxNeoPixelRGBChunkSize = ChunkBufferSize/12; // maximum number of NeoPixels we can support. A full ring contains 60. + constexpr unsigned int MaxNeoPixelRGBWChunkSize = ChunkBufferSize/16; // maximum number of NeoPixels we can support. A full ring contains 60. + + enum class LedType : unsigned int + { + dotstar, + neopixelRGB, + neopixelBitBang, + neopixelRGBW + }; + + constexpr const char *LedTypeNames[] = + { + "DotStar on LED port", + "NeoPixel RGB on LED port", + "NeoPixel RGB on 12864 display", + "NeoPixel RGBW on LED port" + }; + constexpr unsigned int NumSupportedLedTypes = ARRAY_SIZE(LedTypeNames); + + constexpr unsigned int MaxChunkSize[] = + { + MaxDotStarChunkSize, + MaxNeoPixelRGBChunkSize, + MaxNeoPixelRGBChunkSize, + MaxNeoPixelRGBWChunkSize + }; + + constexpr uint32_t DefaultSpiFrequencies[] = + { + DefaultDotStarSpiClockFrequency, + DefaultNeoPixelSpiClockFrequency, + DefaultNeoPixelSpiClockFrequency, + DefaultNeoPixelSpiClockFrequency + }; static uint32_t currentFrequency; // the SPI frequency we are using - static unsigned int ledType = 1; // 0 = DotStar (not supported on Duet 3 Mini), 1 = NeoPixel, 2 = NeoPixel on Mini 12864 display (Duet 3 Mini only) + static LedType ledType = LedType::neopixelRGB; // 0 = DotStar (not supported on Duet 3 Mini), 1 = NeoPixel, 2 = NeoPixel on Mini 12864 display (Duet 3 Mini only). 4 = NeoPixel RGBW static uint32_t whenDmaFinished = 0; // the time in step clocks when we determined that the DMA had finished static unsigned int numRemaining = 0; // how much of the current request remains after the current transfer (DotStar only) static unsigned int totalSent = 0; // total amount of data sent since the start frame (DotStar only) @@ -72,15 +105,6 @@ namespace LedStripDriver alignas(4) static uint8_t chunkBuffer[ChunkBufferSize]; // buffer for sending data to LEDs #endif - constexpr const char *LedTypeNames[] = - { - "DotStar", - "NeoPixel", -#if SUPPORT_BITBANG_NEOPIXEL - "NeoPixel on 12864 display" -#endif - }; - constexpr unsigned int NumSupportedLedTypes = ARRAY_SIZE(LedTypeNames); // DMA the data. Must be a multiple of 2 bytes if USE_16BIT_SPI is true. static void DmaSendChunkBuffer(size_t numBytes) noexcept @@ -192,7 +216,7 @@ namespace LedStripDriver # endif QSPI->QSPI_SCR = QSPI_SCR_CPOL | QSPI_SCR_CPHA | QSPI_SCR_SCBR(SystemPeripheralClock()/currentFrequency - 1); QSPI->QSPI_CR = QSPI_CR_QSPIEN; - if (ledType == 1) + if (ledType == LedType::neopixelRGB || ledType == LedType::neopixelRGBW) { QSPI->QSPI_TDR = 0; // send a word of zeros to set the data line low } @@ -261,7 +285,7 @@ namespace LedStripDriver static constexpr uint8_t EncodedByte[4] = { 0b10001000, 0b10001110, 0b11101000, 0b11101110 }; #if USE_16BIT_SPI - if (ledType == 1) + if (ledType == LedType::neopixelRGB || ledType == LedType::neopixelRGBW) { // Swap bytes for 16-bit DMA *p++ = EncodedByte[(val >> 4) & 3]; @@ -280,7 +304,7 @@ namespace LedStripDriver } // Send data to NeoPixel LEDs by DMA to SPI - static GCodeResult SpiSendNeoPixelData(uint8_t red, uint8_t green, uint8_t blue, uint32_t numLeds, bool following) noexcept + static GCodeResult SpiSendNeoPixelData(uint8_t red, uint8_t green, uint8_t blue, uint8_t white, uint32_t numLeds, bool includeWhite, bool following) noexcept { uint8_t *p = chunkBuffer + (12 * numAlreadyInBuffer); while (numLeds != 0 && p <= chunkBuffer + ARRAY_SIZE(chunkBuffer) - 12) @@ -291,13 +315,18 @@ namespace LedStripDriver p += 4; EncodeNeoPixelByte(p, blue); p += 4; + if (includeWhite) + { + EncodeNeoPixelByte(p, white); + p += 4; + } --numLeds; ++numAlreadyInBuffer; } if (!following) { - DmaSendChunkBuffer(12 * numAlreadyInBuffer); // send data by DMA to SPI + DmaSendChunkBuffer(((includeWhite) ? 16 : 12) * numAlreadyInBuffer); // send data by DMA to SPI numAlreadyInBuffer = 0; needStartFrame = true; } @@ -382,7 +411,7 @@ void LedStripDriver::Init() noexcept pmc_enable_periph_clk(DotStarClockId); #endif - currentFrequency = DefaultSpiFrequencies[ledType]; + currentFrequency = DefaultSpiFrequencies[(unsigned int)ledType]; SetupSpi(); // Initialise variables @@ -406,7 +435,7 @@ GCodeResult LedStripDriver::SetColours(GCodeBuffer& gb, const StringRef& reply) return GCodeResult::notFinished; } - if (needStartFrame && (ledType == 1 || ledType == 2) && StepTimer::GetTimerTicks() - whenDmaFinished < MinNeoPixelResetTicks) + if (needStartFrame && (ledType != LedType::neopixelBitBang) && StepTimer::GetTimerTicks() - whenDmaFinished < MinNeoPixelResetTicks) { return GCodeResult::notFinished; // give the NeoPixels time to reset } @@ -424,9 +453,17 @@ GCodeResult LedStripDriver::SetColours(GCodeBuffer& gb, const StringRef& reply) #endif NumSupportedLedTypes ); - const bool typeChanged = (newType != ledType); + const bool typeChanged = (newType != (unsigned int)ledType); +#if SUPPORT_BITBANG_NEOPIXEL if (newType != 2) +#else + if (newType == 2) + { + reply.copy("Unsupported LED strip type"); + return GCodeResult::error; + } +#endif { bool setFrequency = typeChanged; currentFrequency = DefaultSpiFrequencies[newType]; @@ -439,7 +476,7 @@ GCodeResult LedStripDriver::SetColours(GCodeBuffer& gb, const StringRef& reply) if (typeChanged) { - ledType = newType; + ledType = (LedType)newType; needInit = true; } } @@ -463,13 +500,15 @@ GCodeResult LedStripDriver::SetColours(GCodeBuffer& gb, const StringRef& reply) } // Get the RGB and brightness values - uint32_t red = 0, green = 0, blue = 0, brightness = 128, numLeds = MaxDotStarChunkSize; + uint32_t red = 0, green = 0, blue = 0, white = 0, brightness = 128; + uint32_t numLeds = MaxChunkSize[(unsigned int)ledType]; bool following = false; bool seenColours = false; gb.TryGetLimitedUIValue('R', red, seenColours, 256); gb.TryGetLimitedUIValue('U', green, seenColours, 256); gb.TryGetLimitedUIValue('B', blue, seenColours, 256); + gb.TryGetLimitedUIValue('W', white, seenColours, 256); // W value is used by RGBW NeoPixels only if (gb.Seen('P')) { @@ -488,7 +527,7 @@ GCodeResult LedStripDriver::SetColours(GCodeBuffer& gb, const StringRef& reply) if (!seenType) { // Report the current configuration - reply.printf("Led type is %s, frequency %.2fMHz", LedTypeNames[ledType], (double)((float)currentFrequency * 0.000001)); + reply.printf("Led type is %s, frequency %.2fMHz", LedTypeNames[(unsigned int)ledType], (double)((float)currentFrequency * 0.000001)); } return GCodeResult::ok; } @@ -496,7 +535,7 @@ GCodeResult LedStripDriver::SetColours(GCodeBuffer& gb, const StringRef& reply) // If there are no LEDs to set, we have finished unless we need to send a start frame to DotStar LEDs if (numLeds == 0 #if SUPPORT_DOTSTAR - && (ledType != 0 || (!needStartFrame && !following)) + && (ledType != LedType::dotstar || (!needStartFrame && !following)) #endif ) { @@ -506,7 +545,7 @@ GCodeResult LedStripDriver::SetColours(GCodeBuffer& gb, const StringRef& reply) switch (ledType) { #if SUPPORT_DOTSTAR - case 0: // DotStar + case LedType::dotstar: { if (numRemaining != 0) { @@ -523,16 +562,17 @@ GCodeResult LedStripDriver::SetColours(GCodeBuffer& gb, const StringRef& reply) } #endif - case 1: // NeoPixel + case LedType::neopixelRGB: // Scale RGB by the brightness return SpiSendNeoPixelData( (uint8_t)((red * brightness + 255) >> 8), (uint8_t)((green * brightness + 255) >> 8), (uint8_t)((blue * brightness + 255) >> 8), - numLeds, following + 0, + numLeds, false, following ); + case LedType::neopixelBitBang: #if SUPPORT_BITBANG_NEOPIXEL - case 2: // Interrupts are disabled while bit-banging the data, so make sure movement has stopped if (!reprap.GetGCodes().LockMovementAndWaitForStandstill(gb)) { @@ -545,7 +585,18 @@ GCodeResult LedStripDriver::SetColours(GCodeBuffer& gb, const StringRef& reply) (uint8_t)((blue * brightness + 255) >> 8), numLeds, following ); +#else + break; #endif + + case LedType::neopixelRGBW: + // Scale RGBW by the brightness + return SpiSendNeoPixelData( (uint8_t)((red * brightness + 255) >> 8), + (uint8_t)((green * brightness + 255) >> 8), + (uint8_t)((blue * brightness + 255) >> 8), + (uint8_t)((white * brightness + 255) >> 8), + numLeds, true, following + ); } return GCodeResult::ok; // should never get here } |