From bca827145d63ab4f7ac693ae03881a0b35c42d92 Mon Sep 17 00:00:00 2001 From: Andrew Melvin Date: Mon, 24 Aug 2015 18:30:50 +0100 Subject: [PATCH 1/5] UART driven neopixels using Serial1 --- NeoPixelBus.cpp | 77 ++++++++++++++++++++++++++++++++++++++++++------- NeoPixelBus.h | 8 +++++ 2 files changed, 75 insertions(+), 10 deletions(-) diff --git a/NeoPixelBus.cpp b/NeoPixelBus.cpp index 1bba22f..f1de042 100644 --- a/NeoPixelBus.cpp +++ b/NeoPixelBus.cpp @@ -32,12 +32,28 @@ License along with NeoPixel. If not, see #include "NeoPixelBus.h" #if defined(ESP8266) -// due to linker overriding the ICACHE_RAM_ATTR for cpp files, these methods are -// moved into a C file so the attribute will be applied correctly -extern "C" void ICACHE_RAM_ATTR send_pixels_800(uint8_t* pixels, uint8_t* end, uint8_t pin); -extern "C" void ICACHE_RAM_ATTR send_pixels_400(uint8_t* pixels, uint8_t* end, uint8_t pin); + + #ifdef ESPUARTWS2812 + extern "C" { + #include "eagle_soc.h" + #include "uart_register.h" + #include + } + #define UART_INV_MASK (0x3f<<19) + #define UART 1 + + #else + // due to linker overriding the ICACHE_RAM_ATTR for cpp files, these methods are + // moved into a C file so the attribute will be applied correctly + extern "C" void ICACHE_RAM_ATTR send_pixels_800(uint8_t* pixels, uint8_t* end, uint8_t pin); + extern "C" void ICACHE_RAM_ATTR send_pixels_400(uint8_t* pixels, uint8_t* end, uint8_t pin); + #endif + #endif + + + NeoPixelBus::NeoPixelBus(uint16_t n, uint8_t p, uint8_t t) : _countPixels(n), _sizePixels(n * 3), @@ -46,7 +62,11 @@ NeoPixelBus::NeoPixelBus(uint16_t n, uint8_t p, uint8_t t) : _activeAnimations(0), _flagsPixels(t) { + +#ifndef ESPUARTWS2812 setPin(p); +#endif + _pixels = (uint8_t *)malloc(_sizePixels); if (_pixels) @@ -69,16 +89,28 @@ NeoPixelBus::~NeoPixelBus() if (_animations) free(_animations); +#ifndef ESPUARTWS2812 pinMode(_pin, INPUT); +#endif + } -void NeoPixelBus::Begin(void) -{ - pinMode(_pin, OUTPUT); - digitalWrite(_pin, LOW); + void NeoPixelBus::Begin(void) + { + +#ifdef ESPUARTWS2812 + /* Serial rate is 4x 800KHz for WS2811 */ + Serial1.begin(3200000, SERIAL_6N1, SERIAL_TX_ONLY); + CLEAR_PERI_REG_MASK(UART_CONF0(UART), UART_INV_MASK); + //SET_PERI_REG_MASK(UART_CONF0(UART), UART_TXD_INV); + SET_PERI_REG_MASK(UART_CONF0(UART), (BIT(22))); +#else + pinMode(_pin, OUTPUT); + digitalWrite(_pin, LOW); +#endif + Dirty(); + } - Dirty(); -} void NeoPixelBus::Show(void) { @@ -111,7 +143,9 @@ void NeoPixelBus::Show(void) // state, computes 'pin high' and 'pin low' values, and writes these back // to the PORT register as needed. +#ifndef ESPUARTWS2812 noInterrupts(); // Need 100% focus on instruction timing +#endif #ifdef __AVR__ @@ -724,8 +758,28 @@ void NeoPixelBus::Show(void) if ((_flagsPixels & NEO_SPDMASK) == NEO_KHZ800) { #endif +#ifndef ESPUARTWS2812 // 800 KHz bitstream send_pixels_800(p, end, _pin); +#else + + char buff[4]; + + do + { + uint8_t subpix = *p++; + + buff[0] = data[(subpix >> 6) & 3]; + buff[1] = data[(subpix >> 4) & 3]; + buff[2] = data[(subpix >> 2) & 3]; + buff[3] = data[subpix & 3]; + Serial1.write(buff, sizeof(buff)); + + } while (p < end); + + + +#endif #ifdef INCLUDE_NEO_KHZ400_SUPPORT } @@ -973,7 +1027,10 @@ void NeoPixelBus::Show(void) #endif // end Architecture select +#ifndef ESPUARTWS2812 interrupts(); +#endif + ResetDirty(); _endTime = micros(); // Save EOD time for latch on next call } diff --git a/NeoPixelBus.h b/NeoPixelBus.h index bbb778a..b29e349 100644 --- a/NeoPixelBus.h +++ b/NeoPixelBus.h @@ -20,6 +20,8 @@ License along with NeoPixel. If not, see #include #include "RgbColor.h" +#define ESPUARTWS2812 // drive neopixels by UART on GPIO2 on ESP8266 thanks to Forkineye. https://github.com/forkineye/ESPixelStick + // '_flagsPixels' flags for LED _pixels (third parameter to constructor): #define NEO_RGB 0x00 // Wired for RGB data order #define NEO_GRB 0x01 // Wired for GRB data order @@ -120,6 +122,12 @@ private: uint8_t _pinMask; // Output PORT bitmask #endif +#ifdef ESPUARTWS2812 + /* 6 bit UART lookup table, first 2 bits ignored. Start and stop bits are part of the pixel stream. */ +const char data[4] = { 0b00110111, 0b00000111, 0b00110100, 0b00000100 }; +#endif + + struct FadeAnimation { uint16_t time; From 6a669f9390ca8dd9cf6bb87dbb15e23d632757e9 Mon Sep 17 00:00:00 2001 From: Andrew Melvin Date: Mon, 24 Aug 2015 19:06:34 +0100 Subject: [PATCH 2/5] Attempted 400Khz support --- NeoPixelBus.cpp | 42 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/NeoPixelBus.cpp b/NeoPixelBus.cpp index f1de042..a3971bc 100644 --- a/NeoPixelBus.cpp +++ b/NeoPixelBus.cpp @@ -100,7 +100,25 @@ NeoPixelBus::~NeoPixelBus() #ifdef ESPUARTWS2812 /* Serial rate is 4x 800KHz for WS2811 */ + +#ifdef INCLUDE_NEO_KHZ400_SUPPORT + + + if ((_flagsPixels & NEO_SPDMASK) == NEO_KHZ800) + { + // 800 Support + Serial1.begin(3200000, SERIAL_6N1, SERIAL_TX_ONLY); + } else { + // 400 Support + Serial1.begin(1600000, SERIAL_6N1, SERIAL_TX_ONLY); + } + +#else + Serial1.begin(3200000, SERIAL_6N1, SERIAL_TX_ONLY); + +#endif + CLEAR_PERI_REG_MASK(UART_CONF0(UART), UART_INV_MASK); //SET_PERI_REG_MASK(UART_CONF0(UART), UART_TXD_INV); SET_PERI_REG_MASK(UART_CONF0(UART), (BIT(22))); @@ -758,6 +776,7 @@ void NeoPixelBus::Show(void) if ((_flagsPixels & NEO_SPDMASK) == NEO_KHZ800) { #endif + #ifndef ESPUARTWS2812 // 800 KHz bitstream send_pixels_800(p, end, _pin); @@ -777,16 +796,33 @@ void NeoPixelBus::Show(void) } while (p < end); - - #endif #ifdef INCLUDE_NEO_KHZ400_SUPPORT } else { - // 400 kHz bitstream + +#ifndef ESPUARTWS2812 + // 800 KHz bitstream send_pixels_400(p, end, _pin); +#else + + char buff[4]; + + do + { + uint8_t subpix = *p++; + + buff[0] = data[(subpix >> 6) & 3]; + buff[1] = data[(subpix >> 4) & 3]; + buff[2] = data[(subpix >> 2) & 3]; + buff[3] = data[subpix & 3]; + Serial1.write(buff, sizeof(buff)); + + } while (p < end); + +#endif } #endif From e620850707cdc4c20f048216a061f2c6a4eb1828 Mon Sep 17 00:00:00 2001 From: Andrew Melvin Date: Wed, 26 Aug 2015 17:36:32 +0100 Subject: [PATCH 3/5] Moved to external cpp file --- NeoPixelBus.cpp | 5 ++++ NeoPixelBus.h | 1 + NeoPixelesp8266.c | 12 +++++++++ NeoPixelesp8266uart.cpp | 54 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 72 insertions(+) create mode 100644 NeoPixelesp8266uart.cpp diff --git a/NeoPixelBus.cpp b/NeoPixelBus.cpp index a3971bc..4d08f24 100644 --- a/NeoPixelBus.cpp +++ b/NeoPixelBus.cpp @@ -42,11 +42,14 @@ License along with NeoPixel. If not, see #define UART_INV_MASK (0x3f<<19) #define UART 1 + extern void ICACHE_RAM_ATTR send_pixels_UART(uint8_t* pixels, uint8_t* end); + #else // due to linker overriding the ICACHE_RAM_ATTR for cpp files, these methods are // moved into a C file so the attribute will be applied correctly extern "C" void ICACHE_RAM_ATTR send_pixels_800(uint8_t* pixels, uint8_t* end, uint8_t pin); extern "C" void ICACHE_RAM_ATTR send_pixels_400(uint8_t* pixels, uint8_t* end, uint8_t pin); + #endif #endif @@ -782,6 +785,8 @@ void NeoPixelBus::Show(void) send_pixels_800(p, end, _pin); #else + send_pixels_UART(p, end); + char buff[4]; do diff --git a/NeoPixelBus.h b/NeoPixelBus.h index b29e349..0d9bc93 100644 --- a/NeoPixelBus.h +++ b/NeoPixelBus.h @@ -37,6 +37,7 @@ License along with NeoPixel. If not, see // NeoPixelBus library include to support the slower bus speeds //#define INCLUDE_NEO_KHZ400_SUPPORT + class NeoPixelBus { public: diff --git a/NeoPixelesp8266.c b/NeoPixelesp8266.c index 3766067..91cd2bc 100644 --- a/NeoPixelesp8266.c +++ b/NeoPixelesp8266.c @@ -36,6 +36,18 @@ inline uint32_t _getCycleCount() #define CYCLES_400_T1H (F_CPU / 833333) #define CYCLES_400 (F_CPU / 400000) +#define UART_INV_MASK (0x3f<<19) +#define UART 1 + +#include "eagle_soc.h" +#include "uart_register.h" + + +const char data[4] = { 0b00110111, 0b00000111, 0b00110100, 0b00000100 }; + + + + void ICACHE_RAM_ATTR send_pixels_800(uint8_t* pixels, uint8_t* end, uint8_t pin) { const uint32_t pinRegister = _BV(pin); diff --git a/NeoPixelesp8266uart.cpp b/NeoPixelesp8266uart.cpp new file mode 100644 index 0000000..3be509b --- /dev/null +++ b/NeoPixelesp8266uart.cpp @@ -0,0 +1,54 @@ +/* +NeoPixelEsp8266.h - NeoPixel library helper functions for Esp8266 using cycle count +Copyright (c) 2015 Michael C. Miller. All right reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include "uart_register.h" + + +#if defined(ESP8266) + + +#define UART_INV_MASK (0x3f<<19) +#define UART 1 + + + +const char data[4] = { 0b00110111, 0b00000111, 0b00110100, 0b00000100 }; + + +void ICACHE_RAM_ATTR send_pixels_UART(uint8_t* pixels, uint8_t* end) +{ + char buff[4]; + + do + { + uint8_t subpix = *pixels++; + + buff[0] = data[(subpix >> 6) & 3]; + buff[1] = data[(subpix >> 4) & 3]; + buff[2] = data[(subpix >> 2) & 3]; + buff[3] = data[subpix & 3]; + Serial1.write(buff, sizeof(buff)); + + } while (pixels < end); + +} + +#endif \ No newline at end of file From cf00e8a41b75e7ffb778cd0c48802a1f65281da3 Mon Sep 17 00:00:00 2001 From: Andrew Melvin Date: Thu, 27 Aug 2015 07:30:16 +0100 Subject: [PATCH 4/5] not working --- NeoPixelBus.cpp | 35 ++++++++++++++++++---------- NeoPixelBus.h | 2 +- NeoPixelesp8266uart.cpp | 20 ++++++++++++---- examples/NeoPixelFun/NeoPixelFun.pde | 17 ++++++++++++-- 4 files changed, 55 insertions(+), 19 deletions(-) diff --git a/NeoPixelBus.cpp b/NeoPixelBus.cpp index 4d08f24..aae3e05 100644 --- a/NeoPixelBus.cpp +++ b/NeoPixelBus.cpp @@ -42,7 +42,7 @@ License along with NeoPixel. If not, see #define UART_INV_MASK (0x3f<<19) #define UART 1 - extern void ICACHE_RAM_ATTR send_pixels_UART(uint8_t* pixels, uint8_t* end); + extern void ICACHE_RAM_ATTR send_pixels_UART(uint8_t* pixels, uint8_t* end, bool force); #else // due to linker overriding the ICACHE_RAM_ATTR for cpp files, these methods are @@ -98,6 +98,17 @@ NeoPixelBus::~NeoPixelBus() } + +void NeoPixelBus::FillUart(void) { + + uint8_t* p = _pixels; + uint8_t* end = p + _sizePixels; + + send_pixels_UART(p, end, false); + +} + + void NeoPixelBus::Begin(void) { @@ -785,21 +796,21 @@ void NeoPixelBus::Show(void) send_pixels_800(p, end, _pin); #else - send_pixels_UART(p, end); + send_pixels_UART(p, end,true); - char buff[4]; + // char buff[4]; - do - { - uint8_t subpix = *p++; + // do + // { + // uint8_t subpix = *p++; - buff[0] = data[(subpix >> 6) & 3]; - buff[1] = data[(subpix >> 4) & 3]; - buff[2] = data[(subpix >> 2) & 3]; - buff[3] = data[subpix & 3]; - Serial1.write(buff, sizeof(buff)); + // buff[0] = data[(subpix >> 6) & 3]; + // buff[1] = data[(subpix >> 4) & 3]; + // buff[2] = data[(subpix >> 2) & 3]; + // buff[3] = data[subpix & 3]; + // Serial1.write(buff, sizeof(buff)); - } while (p < end); + // } while (p < end); #endif diff --git a/NeoPixelBus.h b/NeoPixelBus.h index 0d9bc93..f0b2f79 100644 --- a/NeoPixelBus.h +++ b/NeoPixelBus.h @@ -61,7 +61,7 @@ public: { ClearTo(c.R, c.G, c.B); } - + void FillUart(); bool IsDirty() { return (_flagsPixels & NEO_DIRTY); diff --git a/NeoPixelesp8266uart.cpp b/NeoPixelesp8266uart.cpp index 3be509b..57b9c37 100644 --- a/NeoPixelesp8266uart.cpp +++ b/NeoPixelesp8266uart.cpp @@ -33,21 +33,33 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA const char data[4] = { 0b00110111, 0b00000111, 0b00110100, 0b00000100 }; -void ICACHE_RAM_ATTR send_pixels_UART(uint8_t* pixels, uint8_t* end) +void ICACHE_RAM_ATTR send_pixels_UART(uint8_t* pixels, uint8_t* end, bool force) { char buff[4]; - + uint8_t count = 0; + static uint8_t* position = NULL; + + + if (position != NULL && !force ) { + pixels = position; + } else if (position == NULL && !force ) { + return; + } + do { uint8_t subpix = *pixels++; - + position = pixels; + count++; buff[0] = data[(subpix >> 6) & 3]; buff[1] = data[(subpix >> 4) & 3]; buff[2] = data[(subpix >> 2) & 3]; buff[3] = data[subpix & 3]; Serial1.write(buff, sizeof(buff)); - } while (pixels < end); + } while (pixels < end || count < 156 ); + + if ( pixels == end) { position = NULL; } } diff --git a/examples/NeoPixelFun/NeoPixelFun.pde b/examples/NeoPixelFun/NeoPixelFun.pde index b97ec40..89a2afe 100644 --- a/examples/NeoPixelFun/NeoPixelFun.pde +++ b/examples/NeoPixelFun/NeoPixelFun.pde @@ -1,8 +1,8 @@ #include -#define pixelCount 4 +#define pixelCount 300 -NeoPixelBus strip = NeoPixelBus(pixelCount, 8); +NeoPixelBus strip = NeoPixelBus(pixelCount, 2); uint16_t effectState = 0; @@ -13,6 +13,7 @@ void setup() SetRandomSeed(); } +uint32_t update_strip_time; void loop() { @@ -33,6 +34,18 @@ void loop() strip.Show(); delay(31); // ~30hz change cycle } + + if ( (millis() - update_strip_time > 30) ) { + + strip.UpdateAnimations(); + + strip.Show(); // takes 6ms with 200, take 12ms with 400 ----> so 100 takes 3ms. + // one LED takes 30uS of nointeruppts, 100 takes 3ms. + update_strip_time = millis(); + + } + + strip.FillUart(); } From e4847e69913b882ccbcac1501f900941599ae5f8 Mon Sep 17 00:00:00 2001 From: Andrew Melvin Date: Thu, 27 Aug 2015 18:54:15 +0100 Subject: [PATCH 5/5] working separate files --- NeoPixelBus.cpp | 2 +- NeoPixelesp8266.c | 6 ------ NeoPixelesp8266uart.cpp | 24 +++++++++++------------- 3 files changed, 12 insertions(+), 20 deletions(-) diff --git a/NeoPixelBus.cpp b/NeoPixelBus.cpp index aae3e05..a244c8d 100644 --- a/NeoPixelBus.cpp +++ b/NeoPixelBus.cpp @@ -796,7 +796,7 @@ void NeoPixelBus::Show(void) send_pixels_800(p, end, _pin); #else - send_pixels_UART(p, end,true); + send_pixels_UART(p, end, true); // char buff[4]; diff --git a/NeoPixelesp8266.c b/NeoPixelesp8266.c index 91cd2bc..4a2cbae 100644 --- a/NeoPixelesp8266.c +++ b/NeoPixelesp8266.c @@ -36,17 +36,11 @@ inline uint32_t _getCycleCount() #define CYCLES_400_T1H (F_CPU / 833333) #define CYCLES_400 (F_CPU / 400000) -#define UART_INV_MASK (0x3f<<19) -#define UART 1 #include "eagle_soc.h" #include "uart_register.h" -const char data[4] = { 0b00110111, 0b00000111, 0b00110100, 0b00000100 }; - - - void ICACHE_RAM_ATTR send_pixels_800(uint8_t* pixels, uint8_t* end, uint8_t pin) { diff --git a/NeoPixelesp8266uart.cpp b/NeoPixelesp8266uart.cpp index 57b9c37..038478a 100644 --- a/NeoPixelesp8266uart.cpp +++ b/NeoPixelesp8266uart.cpp @@ -29,37 +29,35 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #define UART 1 - const char data[4] = { 0b00110111, 0b00000111, 0b00110100, 0b00000100 }; - void ICACHE_RAM_ATTR send_pixels_UART(uint8_t* pixels, uint8_t* end, bool force) { char buff[4]; - uint8_t count = 0; - static uint8_t* position = NULL; + //uint8_t count = 0; + // static uint8_t* position = NULL; - if (position != NULL && !force ) { - pixels = position; - } else if (position == NULL && !force ) { - return; - } + // if (position != NULL && !force ) { + // pixels = position; + // } else if (position == NULL && !force ) { + // return; + // } do { uint8_t subpix = *pixels++; - position = pixels; - count++; + // position = pixels; + // count++; buff[0] = data[(subpix >> 6) & 3]; buff[1] = data[(subpix >> 4) & 3]; buff[2] = data[(subpix >> 2) & 3]; buff[3] = data[subpix & 3]; Serial1.write(buff, sizeof(buff)); - } while (pixels < end || count < 156 ); + } while (pixels < end ); - if ( pixels == end) { position = NULL; } + // if ( pixels == end) { position = NULL; } }