diff --git a/src/internal/methods/11StepDMXEncoding.PNG b/src/internal/methods/11StepDMXEncoding.PNG new file mode 100644 index 0000000..0b61bc8 Binary files /dev/null and b/src/internal/methods/11StepDMXEncoding.PNG differ diff --git a/src/internal/methods/EndianDependantByteOrder.PNG b/src/internal/methods/EndianDependantByteOrder.PNG new file mode 100644 index 0000000..fcaf9da Binary files /dev/null and b/src/internal/methods/EndianDependantByteOrder.PNG differ diff --git a/src/internal/methods/NeoBits.h b/src/internal/methods/NeoBits.h index ba7ab42..2a65cb0 100644 --- a/src/internal/methods/NeoBits.h +++ b/src/internal/methods/NeoBits.h @@ -5,7 +5,7 @@ NeoPixel library helper functions for Methods. Written by Michael C. Miller. I invest time and resources providing this open source code, -please support me by donating (see https://github.com/Makuna/NeoPixelBus) +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) ------------------------------------------------------------------------- This file is part of the Makuna/NeoPixelBus library. @@ -25,73 +25,71 @@ License along with NeoPixel. If not, see . -------------------------------------------------------------------------*/ -class NeoBitsSpeedBase +// -------------------------------------------------------- +class NeoBitsSpeedDmx512 { public: - static uint16_t ByteSendTimeUs(uint16_t bitSendTimeNs) - { - return (bitSendTimeNs * 8) / 1000; - } + const static uint16_t BitSendTimeNs = 4000; //5500; // 4us, 250Kbps so 4000 * 11 / 8 + const static uint16_t ResetTimeUs = 500; // 6 x 32us equals 3 Words. }; -// -------------------------------------------------------- -class NeoBitsSpeedWs2812x : public NeoBitsSpeedBase +class NeoBitsSpeedWs2812x { public: const static uint16_t BitSendTimeNs = 1250; const static uint16_t ResetTimeUs = 300; }; -class NeoBitsSpeedWs2805 : public NeoBitsSpeedBase +class NeoBitsSpeedWs2805 { public: const static uint16_t BitSendTimeNs = 1125; const static uint16_t ResetTimeUs = 300; // spec is 280, intentionally longer for compatiblity use }; -class NeoBitsSpeedSk6812 : public NeoBitsSpeedBase +class NeoBitsSpeedSk6812 { public: const static uint16_t BitSendTimeNs = 1250; const static uint16_t ResetTimeUs = 80; }; -class NeoBitsSpeedTm1814 : public NeoBitsSpeedBase +class NeoBitsSpeedTm1814 { public: const static uint16_t BitSendTimeNs = 1250; const static uint16_t ResetTimeUs = 200; }; -class NeoBitsSpeedTm1914 : public NeoBitsSpeedBase +class NeoBitsSpeedTm1914 { public: const static uint16_t BitSendTimeNs = 1250; const static uint16_t ResetTimeUs = 200; }; -class NeoBitsSpeedTm1829 : public NeoBitsSpeedBase +class NeoBitsSpeedTm1829 { public: const static uint16_t BitSendTimeNs = 1250; const static uint16_t ResetTimeUs = 200; }; -class NeoBitsSpeed800Kbps : public NeoBitsSpeedBase +class NeoBitsSpeed800Kbps { public: const static uint16_t BitSendTimeNs = 1250; const static uint16_t ResetTimeUs = 50; }; -class NeoBitsSpeed400Kbps : public NeoBitsSpeedBase +class NeoBitsSpeed400Kbps { public: const static uint16_t BitSendTimeNs = 2500; const static uint16_t ResetTimeUs = 50; }; -class NeoBitsSpeedApa106 : public NeoBitsSpeedBase +class NeoBitsSpeedApa106 { public: const static uint16_t BitSendTimeNs = 1710; diff --git a/src/internal/methods/NeoEsp32I2sMethod.h b/src/internal/methods/NeoEsp32I2sMethod.h index 85aae16..9ed3783 100644 --- a/src/internal/methods/NeoEsp32I2sMethod.h +++ b/src/internal/methods/NeoEsp32I2sMethod.h @@ -4,7 +4,7 @@ NeoPixel library helper functions for Esp32. Written by Michael C. Miller. I invest time and resources providing this open source code, -please support me by donating (see https://github.com/Makuna/NeoPixelBus) +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) ------------------------------------------------------------------------- This file is part of the Makuna/NeoPixelBus library. @@ -65,16 +65,248 @@ class NeoEsp32I2sBusN const uint8_t I2sBusNumber; }; +//---------------------------------------------------------- // -------------------------------------------------------- +// 11 step cadences, specifically for DMX output +// + +class NeoEsp32I2sCadence11Step11BitLookup // this is the quickest, include start & stop bits in the lookup + // and use a double lookup to reduce bit-shifting +{ +//#define _USE32BIT_ // the 32_bit lookup table is faster since it omits the clearing of the lsWord + // but of course uses more memory on the stack +public: + const static size_t DmaBitsPerPixelByte = 11; + + static void EncodeIntoDma(uint8_t* dmaBuffer, const uint8_t* data, size_t sizeData) + { + uint16_t* pDma = reinterpret_cast(dmaBuffer); + uint16_t dmaValue[2] = {0, 0}; // 16-bit dual buffer, used for reading the created word(s) +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ // See 11-Step DMX Encoding.PNG for schematic explanation + const uint8_t msW = 0, lsW = 1; +#else + const uint8_t lsW = 0, msW = 1; +#endif + uint32_t* dmaV32 = reinterpret_cast(dmaValue); // pointer for the 32-bit version + // the 32-bit version is used to shift the bits so the lsBits shift into the lsWord + // and are not just discarded + const uint8_t* pSrc = data; + const uint8_t* pEnd = pSrc + sizeData; + const uint8_t BitsInSample = 16; //sizeof(dmaValue) * 8; + uint8_t destBitsLeft = BitsInSample; + *(pDma++) = 0x0000; // start the break. + *(pDma++) = 0x0000; // with the headebyte added it doesn't fit into 4 bytes anymore + *(pDma++) = 0xf803; // 0b1111 1000 0000 0011 + *pDma = 0x0; // clear the first word !! + +#ifndef _USE32BIT_ + const uint16_t LookUpMsN[16] = {0x003, 0x023, 0x013, 0x033, 0x00b, 0x02b, 0x01b, 0x03b, + 0x007, 0x027, 0x017, 0x037, 0x00f, 0x02f, 0x01f, 0x03f + }; + const uint16_t LookUpLsN[16] = {0x000, 0x200, 0x100, 0x300, 0x080, 0x280, 0x180, 0x380, + 0x040, 0x240, 0x140, 0x340, 0x0c0, 0x2c0, 0x1c0, 0x3c0 + }; +#else + const uint32_t LookUpMsN[16] = {0x0030000, 0x0230000, 0x0130000, 0x0330000, 0x00b0000, 0x02b0000, 0x01b0000, 0x03b0000, + 0x0070000, 0x0270000, 0x0170000, 0x0370000, 0x00f0000, 0x02f0000, 0x01f0000, 0x03f0000 + }; + const uint32_t LookUpLsN[16] = {0x0000000, 0x2000000, 0x1000000, 0x3000000, 0x0800000, 0x2800000, 0x1800000, 0x3800000, + 0x0400000, 0x2400000, 0x1400000, 0x3400000, 0x0c00000, 0x2c00000, 0x1c00000, 0x3c00000 + }; +#endif + + while (pSrc < pEnd) + { +#ifndef _USE32BIT_ + dmaValue[msW] = LookUpLsN[(*pSrc) & 0x0f] | LookUpMsN[(*pSrc) >> 4]; // lookup value into the msWord + // works like an automatic 16-bit Shift +#else + *dmaV32 = LookUpLsN[(*pSrc) & 0x0f] | LookUpMsN[(*pSrc) >> 4]; + // basically read it as a 32-bit (asignment clears lsWord automatically) +#endif + pSrc++; + if (destBitsLeft < 11) // Split the Bits + { + *dmaV32 = *dmaV32 >> (11 - destBitsLeft); // shift it as a 32-bit, so the rightshifted bits end up in the lsWord + *(pDma++) |= dmaValue[msW]; // the msWord & and up the 16-bit Dma buffer pointer + *pDma = dmaValue[lsW]; // lsWord aka whatever got shifted out of the variable + // instead of shifting again to obtain these bits, they are available and ready for 'or' +#ifndef _USE32BIT_ + dmaValue[lsW] = 0; // clear the lsWord after use, not needed when using 32-bit lookup +#endif + destBitsLeft += 5; // 11 bits subtracted but rolled over backwards = 5 bits added. + } + else + { + *dmaV32 = *dmaV32 << (destBitsLeft - 11); // shift to be the next bit + *pDma |= dmaValue[msW]; // 'or' the most significant Word + destBitsLeft -= 11; // substract the 11 bits added + } + } + if (destBitsLeft) + { + *pDma |= 0xffff >> (16 - destBitsLeft); + } + } +}; + + + +class NeoEsp32I2sCadence11Step8BitLookup +{ +public: + const static size_t DmaBitsPerPixelByte = 11; + + static void EncodeIntoDma(uint8_t* dmaBuffer, const uint8_t* data, size_t sizeData) + { + const uint8_t* pSrc = data; + const uint8_t* pEnd = pSrc + sizeData; + uint16_t* pDma = reinterpret_cast(dmaBuffer); + const uint8_t Reverse8BitsLookup[16] = {0x0, 0x8, 0x4, 0xc, 0x2, 0xa, 0x6, 0xe, 0x1, 0x9, 0x5, 0xd, 0x3, 0xb, 0x7, 0xf}; + // significantly quicker than calling a separate function, at least in my speed test. + + int8_t outputBit = 16; + uint16_t output = 0; + + *(pDma++) = 0x0000; // start the break. + *(pDma++) = 0x0000; // with the headebyte added it doesn't fit into 4 bytes anymore + *(pDma++) = 0xf803; // 0b1111 1000 0000 0011 + while (pSrc < pEnd) + { + uint8_t invertedByte = (Reverse8BitsLookup[*pSrc & 0xf] << 4) | Reverse8BitsLookup[*pSrc >> 4]; + pSrc++; + if (outputBit > 10) + { + outputBit -= 1; + output |= 0 << outputBit; + outputBit -= 8; + output |= invertedByte << outputBit; + outputBit -= 2; + output |= 3 << outputBit; + } + else + { + // split across an output uint16_t + // handle start bit + if (outputBit < 1) + { + *(pDma++) = output; + output = 0; + outputBit += 16; + } + outputBit -= 1; + output |= 0 << outputBit; + // handle data bits + if (outputBit < 8) + { + output |= invertedByte >> (8 - outputBit); + *(pDma++) = output; + output = 0; + outputBit += 16; + } + outputBit -= 8; + output |= invertedByte << outputBit; + // handle stop bits + if (outputBit < 2) + { + output |= 3 >> (2 - outputBit); + *(pDma++) = output; + output = 0; + outputBit += 16; + } + outputBit -= 2; + output |= 3 << outputBit; + } + } + if (outputBit > 0) + { + // padd last output uint16_t with Mtbp + output |= 0xffff >> (16 - outputBit); + *(pDma++) = output; + } + } +}; + + +class NeoEsp32I2sCadence11StepNoLookup +{ +public: + const static size_t DmaBitsPerPixelByte = 11; + + static void EncodeIntoDma(uint8_t* dmaBuffer, const uint8_t* data, size_t sizeData) + { + + uint16_t* pDma = reinterpret_cast(dmaBuffer); + uint16_t dmaValue = 0; + const uint8_t* pSrc = data; + const uint8_t* pEnd = pSrc + sizeData; + const size_t BitsInSample = sizeof(dmaValue) * 8; + + uint8_t destBitsLeft = BitsInSample; + + + *(pDma++) = 0x0000; // start the break. + *(pDma++) = 0x0000; // with the headerbyte added it doesn't fit into 4 bytes anymore + *(pDma++) = 0xf803; // 0b1111 1000 0000 0011 + + + while (pSrc < pEnd) + { + uint8_t source = *(pSrc++); + if (!destBitsLeft) + { + destBitsLeft = BitsInSample; + *(pDma++) = dmaValue; + //dmaValue = 0; // not needed and a waste of time. + } + dmaValue = dmaValue << 1; // start Bit + destBitsLeft--; + + for (uint8_t i = 0; i < 8; i++) // data bits + { + if (!destBitsLeft) + { + destBitsLeft = BitsInSample; + *(pDma++) = dmaValue; + //dmaValue = 0; // not needed all bits will have been shifted out + } + dmaValue = dmaValue << 1; + dmaValue |= (source & 1); + source = source >> 1; + destBitsLeft--; + } + + for (uint8_t i = 0; i < 2; i++) // stop bits + { + if (!destBitsLeft) + { + destBitsLeft = BitsInSample; + *(pDma++) = dmaValue; + //dmaValue = 0; + } + dmaValue = dmaValue << 1; + dmaValue |= 1; + destBitsLeft--; + } + } + if (destBitsLeft) { + dmaValue = dmaValue << destBitsLeft; // shift the significant bits fully left + dmaValue |= (0xffff >> (BitsInSample - destBitsLeft)); // fill it up with HIGh bits + *pDma++ = dmaValue; // and store the remaining bits + } + } +}; + // 4 step cadence, so pulses are 1/4 and 3/4 of pulse width // class NeoEsp32I2sCadence4Step { public: - const static size_t DmaBitsPerPixelBit = 4; // 4 step cadence, matches encoding + //const static size_t DmaBitsPerPixelBit = 4; // 4 step cadence, matches encoding + const static size_t DmaBitsPerPixelByte = 32; // 4 step cadence, matches encoding static void EncodeIntoDma(uint8_t* dmaBuffer, const uint8_t* data, size_t sizeData) { @@ -90,12 +322,140 @@ class NeoEsp32I2sCadence4Step const uint8_t* pEnd = data + sizeData; for (const uint8_t* pSrc = data; pSrc < pEnd; pSrc++) { - *(pDma++) = bitpatterns[((*pSrc) >> 4) & 0x0f]; + *(pDma++) = bitpatterns[((*pSrc) >> 4) /*& 0x0f*/]; // the mask is obsolete, an 8-bit shifted 4 + // bits doesn't have any other bits left. *(pDma++) = bitpatterns[((*pSrc) & 0x0f)]; } } }; +class NeoEsp32I2sCadence3Stepfast +{ +public: + //const static size_t DmaBitsPerPixelBit = 3; // 3 step cadence, matches encoding + const static size_t DmaBitsPerPixelByte = 24; // 3 step cadence, matches encoding + + static void EncodeIntoDma(uint8_t* dmaBuffer, const uint8_t* data, size_t sizeData) + { + const uint32_t bitpatternsLow[16] = + { + 0x000924, 0x000926, 0x000934, 0x000936, 0x0009A4, 0x0009A6, 0x0009B4, 0x0009B6, + 0x000D24, 0x000D26, 0x000D34, 0x000D36, 0x000DA4, 0x000DA6, 0x000DB4, 0x000DB6 + + }; + const uint32_t bitpatternsHigh[16] = + { + 0x924000, 0x926000, 0x934000, 0x936000, 0x9A4000, 0x9A6000, 0x9B4000, 0x9B6000, + 0xD24000, 0xD26000, 0xD34000, 0xD36000, 0xDA4000, 0xDA6000, 0xDB4000, 0xDB6000 + + }; + uint32_t output[2]; // 2x 24-bit bitPattern in consequetive location + uint8_t * output8 = reinterpret_cast(output); + + uint8_t* pDma = dmaBuffer; + const uint8_t* pEnd = data + sizeData - 1; // Encode 2 bytes at a time, make sure they are there + const uint8_t* pSrc = data; + while (pSrc < pEnd) + { + output[0] = bitpatternsHigh[((*pSrc) >> 4)] | bitpatternsLow[((*pSrc) & 0x0f)]; + pSrc++; + output[1] = bitpatternsHigh[((*pSrc) >> 4)] | bitpatternsLow[((*pSrc) & 0x0f)]; + pSrc++; // note: the mask for the bitpatternsHigh index should be obsolete + // To get the 2x 3-byte values in the right order copy them Byte by Byte +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ // see Endian Dependant Byte Order.PNG for schematic explanation + memcpy(pDma++, output8 + 1 , 1); + memcpy(pDma++, output8 + 2 , 1); + memcpy(pDma++, output8 + 3 , 1); + memcpy(pDma++, output8 + 5 , 1); + memcpy(pDma++, output8 + 6 , 1); + memcpy(pDma++, output8 + 7 , 1); +#else + memcpy(pDma++, output8 + 1 , 1); + memcpy(pDma++, output8 + 2 , 1); + memcpy(pDma++, output8 + 6 , 1); + memcpy(pDma++, output8 + 0 , 1); + memcpy(pDma++, output8 + 4 , 1); + memcpy(pDma++, output8 + 5 , 1); +#endif + } + if (pSrc == pEnd) // the last pixelbuffer byte if it exists + { + output[0] = bitpatternsHigh[((*pSrc) >> 4) & 0x0f] | bitpatternsLow[((*pSrc) & 0x0f)]; +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + memcpy(pDma++, output8 + 1 , 1); + memcpy(pDma++, output8 + 2 , 1); + memcpy(pDma++, output8 + 3 , 1); +#else + memcpy(pDma++, output8 + 1 , 1); + memcpy(pDma++, output8 + 2 , 1); + pDma++; + memcpy(pDma++, output8 + 0 , 1); +#endif + } + } +}; + +/* Not used, kept for reference, slower than the previous method +class NeoEsp32I2sCadence3Stepfast2 +{ +public: + //const static size_t DmaBitsPerPixelBit = 3; // 3 step cadence, matches encoding + const static size_t DmaBitsPerPixelByte = 24; // 3 step cadence, matches encoding + static void EncodeIntoDma(uint8_t* dmaBuffer, const uint8_t* data, size_t sizeData) + { + const uint16_t bitpatterns[6][16] = { + { + 0x9240, 0x9260, 0x9340, 0x9360, 0x9A40, 0x9A60, 0x9B40, 0x9B60, + 0xD240, 0xD260, 0xD340, 0xD360, 0xDA40, 0xDA60, 0xDB40, 0xDB60 + }, + { + 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, + 0x000D, 0x000D, 0x000D, 0x000D, 0x000D, 0x000D, 0x000D, 0x000D + }, + { + 0x0092, 0x0092, 0x0093, 0x0093, 0x009A, 0x009A, 0x009B, 0x009B, + 0x00D2, 0x00D2, 0x00D3, 0x00D3, 0x00DA, 0x00DA, 0x00DB, 0x00DB + }, + { + 0x2400, 0x2600, 0x3400, 0x3600, 0xA400, 0xA600, 0xB400, 0xB600, + 0x2400, 0x2600, 0x3400, 0x3600, 0xA400, 0xA600, 0xB400, 0xB600 + }, + { + 0x4000, 0x6000, 0x4000, 0x6000, 0x4000, 0x6000, 0x4000, 0x6000, + 0x4000, 0x6000, 0x4000, 0x6000, 0x4000, 0x6000, 0x4000, 0x6000 + }, + { + 0x0924, 0x0926, 0x0934, 0x0936, 0x09A4, 0x09A6, 0x09B4, 0x09B6, + 0x0D24, 0x0D26, 0x0D34, 0x0D36, 0x0DA4, 0x0DA6, 0x0DB4, 0x0DB6 + }}; + + + uint16_t* pDma = reinterpret_cast(dmaBuffer); + const uint8_t* pEnd = data + sizeData - 1; // Encode 2 bytes at a time, make sure they are there + const uint8_t* pSrc = data; + + while (pSrc < pEnd) + { + uint8_t msNibble = ((*pSrc) >> 4) & 0x0f, lsNibble = (*pSrc) & 0x0f; + *(pDma++) = bitpatterns[0][msNibble] | bitpatterns[1][lsNibble]; + pSrc++; + msNibble = ((*pSrc) >> 4) & 0x0f; + *(pDma++) = bitpatterns[2][msNibble] | bitpatterns[3][lsNibble]; + lsNibble = (*pSrc) & 0x0f; + *(pDma++) = bitpatterns[4][msNibble] | bitpatterns[5][lsNibble]; + pSrc++; + } + if (pSrc == pEnd) // the last pixelbuffer byte if it exists + { + uint8_t msNibble = ((*pSrc) >> 4) & 0x0f, lsNibble = (*pSrc) & 0x0f; + *(pDma++) = bitpatterns[0][msNibble] | bitpatterns[1][lsNibble]; + *(pDma++) = bitpatterns[3][lsNibble]; + } + } +}; +*/ + + // fedc ba98 7654 3210 // 0000 0000 0000 0000 // 111 @@ -104,7 +464,8 @@ class NeoEsp32I2sCadence4Step class NeoEsp32I2sCadence3Step { public: - const static size_t DmaBitsPerPixelBit = 3; // 3 step cadence, matches encoding + //const static size_t DmaBitsPerPixelBit = 3; // 3 step cadence, matches encoding + const static size_t DmaBitsPerPixelByte = 24; // 3 step cadence, matches encoding static void EncodeIntoDma(uint8_t* dmaBuffer, const uint8_t* data, size_t sizeData) { @@ -224,8 +585,9 @@ template(heap_caps_malloc(_i2sBufferSize, MALLOC_CAP_DMA)); // no need to initialize all of it, but since it contains - // "reset" bits that don't latter get overwritten we just clear it all - memset(_i2sBuffer, 0x00, _i2sBufferSize); + // "reset" bits that don't later get overwritten we just clear it all + + memset(_i2sBuffer, 0x00, _i2sBufferSize); + } +}; + +//typedef NeoEsp32I2sCadence11StepNoLookup NeoEsp32I2sCadence11Step; + +//typedef NeoEsp32I2sCadence11Step8BitLookup NeoEsp32I2sCadence11Step; + +typedef NeoEsp32I2sCadence11Step11BitLookup NeoEsp32I2sCadence11Step; + +typedef NeoEsp32I2sMethodBase NeoEsp32I2s0Dmx512Method; +typedef NeoEsp32I2sMethodBase NeoEsp32I2s0Dmx512InvertedMethod; +typedef NeoEsp32I2sMethodBase NeoEsp32I2s1Dmx512Method; +typedef NeoEsp32I2sMethodBase NeoEsp32I2s1Dmx512InvertedMethod; -}; #if defined(NPB_CONF_4STEP_CADENCE) typedef NeoEsp32I2sCadence4Step NeoEsp32I2sCadence; #else + +//typedef NeoEsp32I2sCadence3Step NeoEsp32I2sCadence; -typedef NeoEsp32I2sCadence3Step NeoEsp32I2sCadence; +typedef NeoEsp32I2sCadence3Stepfast NeoEsp32I2sCadence; #endif + typedef NeoEsp32I2sMethodBase NeoEsp32I2s0Ws2812xMethod; typedef NeoEsp32I2sMethodBase NeoEsp32I2s0Ws2805Method; typedef NeoEsp32I2sMethodBase NeoEsp32I2s0Sk6812Method; diff --git a/src/internal/methods/NeoEsp8266I2sDmx512Method.h b/src/internal/methods/NeoEsp8266I2sDmx512Method.h index 9dc717d..28de195 100644 --- a/src/internal/methods/NeoEsp8266I2sDmx512Method.h +++ b/src/internal/methods/NeoEsp8266I2sDmx512Method.h @@ -241,7 +241,7 @@ template class NeoEsp8266I2sDmx512MethodBase : NeoEsp8266I2sMe // encodes the data with start and stop bits // input buffer is bytes - // output stream is uint31_t + // output stream is uint32_t static void Encoder(const uint8_t* pSrc, const uint8_t* pSrcEnd, uint32_t* pOutput, const uint32_t* pOutputEnd) {