diff --git a/NeoPixelBus.cpp b/NeoPixelBus.cpp index ab34610..1bba22f 100644 --- a/NeoPixelBus.cpp +++ b/NeoPixelBus.cpp @@ -31,6 +31,13 @@ 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); +#endif + NeoPixelBus::NeoPixelBus(uint16_t n, uint8_t p, uint8_t t) : _countPixels(n), _sizePixels(n * 3), @@ -73,122 +80,6 @@ void NeoPixelBus::Begin(void) Dirty(); } -#if defined(ESP8266) - - -#define CYCLES_800_T0H (F_CPU / 2500000 - 4) // 0.4us -#define CYCLES_800_T1H (F_CPU / 1250000 - 4) // 0.8us -#define CYCLES_800 (F_CPU / 800000 - 4) // 1.25us per bit -#define CYCLES_400_T0H (F_CPU / 2000000 - 4) -#define CYCLES_400_T1H (F_CPU / 833333 - 4) -#define CYCLES_400 (F_CPU / 400000 - 4) - -static inline void send_pixels_800(uint8_t* pixels, uint8_t* end, uint8_t pin) -{ - const uint32_t pinRegister = _BV(pin); - uint8_t mask; - uint8_t subpix; - uint32_t cyclesStart; - - // this set low will help cleanup the first bit - GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, pinRegister); - __asm__ __volatile__("isync"); - - cyclesStart = ESP.getCycleCount() + CYCLES_800; - while (pixels < end) - { - subpix = *pixels++; - for (mask = 0x80; mask; mask >>= 1) - { - // do the check here while we are waiting on time to pass - bool nextBit = (subpix & mask); - uint32_t cyclesNext = cyclesStart; - - // after we have done as much work as needed for this next bit - // now wait for the HIGH - do - { - // cache and use this count so we don't incur another - // instruction before we turn the bit high - cyclesStart = ESP.getCycleCount(); - } - while ((cyclesStart - cyclesNext) < CYCLES_800); - - // set high - GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, pinRegister); - - // wait for the LOW - if (nextBit) - { - while ((ESP.getCycleCount() - cyclesStart) < CYCLES_800_T1H); - } - else - { - while ((ESP.getCycleCount() - cyclesStart) < CYCLES_800_T0H); - } - - // set low - GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, pinRegister); - } - } - - // while accurate, this isn't needed due to the delays at the - // top of Show() to enforce between update timing - // while ((ESP.getCycleCount() - cyclesStart) < CYCLES_800); -} - -static inline void send_pixels_400(uint8_t* pixels, uint8_t* end, uint8_t pin) -{ - const uint32_t pinRegister = _BV(pin); - uint8_t mask; - uint8_t subpix; - uint32_t cyclesStart; - - // this set low will help cleanup the first bit - GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, pinRegister); - __asm__ __volatile__("isync"); - - cyclesStart = ESP.getCycleCount() + CYCLES_400; - while (pixels < end) - { - subpix = *pixels++; - for (mask = 0x80; mask; mask >>= 1) - { - bool nextBit = (subpix & mask); - uint32_t cyclesNext = cyclesStart; - - // after we have done as much work as needed for this next bit - // now wait for the HIGH - do - { - cyclesStart = ESP.getCycleCount(); - } while ((cyclesStart - cyclesNext) < CYCLES_400); - - - GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, pinRegister); - - // wait for the LOW - if (nextBit) - { - while ((ESP.getCycleCount() - cyclesStart) < CYCLES_400_T1H); - } - else - { - while ((ESP.getCycleCount() - cyclesStart) < CYCLES_400_T0H); - } - - GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, pinRegister); - } - } - - // while accurate, this isn't needed due to the delays at the - // top of Show() to enforce between update timing - // while ((ESP.getCycleCount() - cyclesStart) < CYCLES_400); -} - -#endif - - void NeoPixelBus::Show(void) { if (!_pixels) @@ -823,7 +714,6 @@ void NeoPixelBus::Show(void) #endif #elif defined(ESP8266) - uint8_t* p = _pixels; uint8_t* end = p + _sizePixels; diff --git a/NeoPixelesp8266.c b/NeoPixelesp8266.c new file mode 100644 index 0000000..3766067 --- /dev/null +++ b/NeoPixelesp8266.c @@ -0,0 +1,131 @@ +/* +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 + +#if defined(ESP8266) + +inline uint32_t _getCycleCount() +{ + uint32_t ccount; + __asm__ __volatile__("rsr %0,ccount":"=a" (ccount)); + return ccount; +} + +#define CYCLES_800_T0H (F_CPU / 2500000) // 0.4us +#define CYCLES_800_T1H (F_CPU / 1250000) // 0.8us +#define CYCLES_800 (F_CPU / 800000) // 1.25us per bit +#define CYCLES_400_T0H (F_CPU / 2000000) +#define CYCLES_400_T1H (F_CPU / 833333) +#define CYCLES_400 (F_CPU / 400000) + +void ICACHE_RAM_ATTR send_pixels_800(uint8_t* pixels, uint8_t* end, uint8_t pin) +{ + const uint32_t pinRegister = _BV(pin); + uint8_t mask; + uint8_t subpix; + uint32_t cyclesStart; + + // trigger emediately + cyclesStart = _getCycleCount() - CYCLES_800; + do + { + subpix = *pixels++; + for (mask = 0x80; mask != 0; mask >>= 1) + { + // do the checks here while we are waiting on time to pass + uint32_t cyclesBit = ((subpix & mask)) ? CYCLES_800_T1H : CYCLES_800_T0H; + uint32_t cyclesNext = cyclesStart; + uint32_t delta; + + // after we have done as much work as needed for this next bit + // now wait for the HIGH + do + { + // cache and use this count so we don't incur another + // instruction before we turn the bit high + cyclesStart = _getCycleCount(); + } while ((cyclesStart - cyclesNext) < CYCLES_800); + + // set high + GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, pinRegister); + + // wait for the LOW + do + { + cyclesNext = _getCycleCount(); + } while ((cyclesNext - cyclesStart) < cyclesBit); + + // set low + GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, pinRegister); + } + } while (pixels < end); + + // while accurate, this isn't needed due to the delays at the + // top of Show() to enforce between update timing + // while ((_getCycleCount() - cyclesStart) < CYCLES_800); +} + +void ICACHE_RAM_ATTR send_pixels_400(uint8_t* pixels, uint8_t* end, uint8_t pin) +{ + const uint32_t pinRegister = _BV(pin); + uint8_t mask; + uint8_t subpix; + uint32_t cyclesStart; + + // trigger emediately + cyclesStart = _getCycleCount() - CYCLES_400; + while (pixels < end) + { + subpix = *pixels++; + for (mask = 0x80; mask; mask >>= 1) + { + uint32_t cyclesBit = ((subpix & mask)) ? CYCLES_400_T1H : CYCLES_400_T0H; + uint32_t cyclesNext = cyclesStart; + + // after we have done as much work as needed for this next bit + // now wait for the HIGH + do + { + // cache and use this count so we don't incur another + // instruction before we turn the bit high + cyclesStart = _getCycleCount(); + } while ((cyclesStart - cyclesNext) < CYCLES_400); + + // set high + GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, pinRegister); + + // wait for the LOW + do + { + cyclesNext = _getCycleCount(); + } while ((cyclesNext - cyclesStart) < cyclesBit); + + // set low + GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, pinRegister); + } + } + + // while accurate, this isn't needed due to the delays at the + // top of Show() to enforce between update timing + // while ((_getCycleCount() - cyclesStart) < CYCLES_400); +} + +#endif \ No newline at end of file