From c27d9bf05508e9f629f06a88ee02c02e8b57cda8 Mon Sep 17 00:00:00 2001 From: Phillip Burgess Date: Fri, 26 Jun 2015 20:45:56 -0700 Subject: [PATCH] ESP8266 support (based on Makuna's NeoPixelBus lib) --- Adafruit_NeoPixel.cpp | 54 +++++++++++++++++------- esp8266.c | 67 ++++++++++++++++++++++++++++++ examples/strandtest/strandtest.ino | 2 +- library.properties | 2 +- 4 files changed, 108 insertions(+), 17 deletions(-) create mode 100644 esp8266.c diff --git a/Adafruit_NeoPixel.cpp b/Adafruit_NeoPixel.cpp index af6386c4..b562fa44 100644 --- a/Adafruit_NeoPixel.cpp +++ b/Adafruit_NeoPixel.cpp @@ -7,7 +7,8 @@ (possible exception with upper PORT registers on the Arduino Mega). Written by Phil Burgess / Paint Your Dragon for Adafruit Industries, - contributions by PJRC and other members of the open source community. + contributions by PJRC, Michael Miller and other members of the open + source community. Adafruit invests time and resources providing this open source code, please support Adafruit and open-source hardware by purchasing products @@ -70,6 +71,13 @@ void Adafruit_NeoPixel::begin(void) { digitalWrite(pin, LOW); } + +#ifdef ESP8266 +// ESP8266 show() is external to enforce ICACHE_RAM_ATTR execution +extern "C" void ICACHE_RAM_ATTR espShow( + uint8_t pin, uint8_t *pixels, uint32_t numBytes, uint8_t type); +#endif // ESP8266 + void Adafruit_NeoPixel::show(void) { if(!pixels) return; @@ -97,7 +105,9 @@ void Adafruit_NeoPixel::show(void) { noInterrupts(); // Need 100% focus on instruction timing + #ifdef __AVR__ +// AVR MCUs -- ATmega & ATtiny (no XMEGA) --------------------------------- volatile uint16_t i = numBytes; // Loop counter @@ -377,7 +387,7 @@ void Adafruit_NeoPixel::show(void) { [lo] "r" (lo), [ptr] "e" (ptr)); } -#endif +#endif // NEO_KHZ400 // 12 MHz(ish) AVR -------------------------------------------------------- #elif (F_CPU >= 11100000UL) && (F_CPU <= 14300000UL) @@ -557,7 +567,7 @@ void Adafruit_NeoPixel::show(void) { [lo] "r" (lo), [ptr] "e" (ptr)); } -#endif +#endif // NEO_KHZ400 // 16 MHz(ish) AVR -------------------------------------------------------- #elif (F_CPU >= 15400000UL) && (F_CPU <= 19000000L) @@ -669,14 +679,19 @@ void Adafruit_NeoPixel::show(void) { [hi] "r" (hi), [lo] "r" (lo)); } -#endif +#endif // NEO_KHZ400 #else #error "CPU SPEED NOT SUPPORTED" -#endif +#endif // end F_CPU ifdefs on __AVR__ + +// END AVR ---------------------------------------------------------------- + #elif defined(__arm__) +// ARM MCUs -- Teensy 3.0, 3.1, LC, Arduino Due --------------------------- + #if defined(__MK20DX128__) || defined(__MK20DX256__) // Teensy 3.0 & 3.1 #define CYCLES_800_T0H (F_CPU / 4000000) #define CYCLES_800_T1H (F_CPU / 1250000) @@ -732,11 +747,7 @@ void Adafruit_NeoPixel::show(void) { } while(ARM_DWT_CYCCNT - cyc < CYCLES_400); } -#endif - - - - +#endif // NEO_KHZ400 #elif defined(__MKL26Z64__) // Teensy-LC @@ -822,10 +833,9 @@ void Adafruit_NeoPixel::show(void) { ); #else #error "Sorry, only 48 MHz is supported, please set Tools > CPU Speed to 48 MHz" -#endif +#endif // F_CPU == 48000000 - -#else // Arduino Due +#else // Other ARM architecture -- Presumed Arduino Due #define SCALE VARIANT_MCK / 2UL / 1000000UL #define INST (2UL * F_CPU / VARIANT_MCK) @@ -888,9 +898,23 @@ void Adafruit_NeoPixel::show(void) { while(*timeValue < period); // Wait for last bit TC_Stop(TC1, 0); -#endif // end Arduino Due +#endif // end Due + +// END ARM ---------------------------------------------------------------- + + +#elif defined(ESP8266) + +// ESP8266 ---------------------------------------------------------------- + + // ESP8266 show() is external to enforce ICACHE_RAM_ATTR execution + espShow(pin, pixels, numBytes, type); + +#endif // ESP8266 + + +// END ARCHITECTURE SELECT ------------------------------------------------ -#endif // end Architecture select interrupts(); endTime = micros(); // Save EOD time for latch on next call diff --git a/esp8266.c b/esp8266.c new file mode 100644 index 00000000..4814ca0a --- /dev/null +++ b/esp8266.c @@ -0,0 +1,67 @@ +// This is a mash-up of the Due show() code + insights from Michael Miller's +// ESP8266 work for the NeoPixelBus library: github.com/Makuna/NeoPixelBus +// Needs to be a separate .c file to enforce ICACHE_RAM_ATTR execution. + +#ifdef ESP8266 + +#include +#include + +static uint32_t _getCycleCount(void) __attribute__((always_inline)); +static inline uint32_t _getCycleCount(void) { + uint32_t ccount; + __asm__ __volatile__("rsr %0,ccount":"=a" (ccount)); + return ccount; +} + +void ICACHE_RAM_ATTR espShow( + uint8_t pin, uint8_t *pixels, uint32_t numBytes, uint8_t type) { + +#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) // 0.5uS +#define CYCLES_400_T1H (F_CPU / 833333) // 1.2us +#define CYCLES_400 (F_CPU / 400000) // 2.5us per bit + + uint8_t *p, *end, pix, mask; + uint32_t t, time0, time1, period, c, startTime, pinMask; + + pinMask = _BV(pin); + p = pixels; + end = p + numBytes; + pix = *p++; + mask = 0x80; + startTime = 0; + +#ifdef NEO_KHZ400 + if((type & NEO_SPDMASK) == NEO_KHZ800) { // 800 KHz bitstream +#endif + time0 = CYCLES_800_T0H; + time1 = CYCLES_800_T1H; + period = CYCLES_800; +#ifdef NEO_KHZ400 + } else { // 400 KHz bitstream + time0 = CYCLES_400_T0H; + time1 = CYCLES_400_T1H; + period = CYCLES_800; + } +#endif + + for(t = time0;; t = time0) { + if(pix & mask) t = time1; // Bit high duration + while(((c = _getCycleCount()) - startTime) < period); // Wait for bit start + GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, pinMask); // Set high + startTime = c; // Save start time + while(((c = _getCycleCount()) - startTime) < t); // Wait high duration + GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, pinMask); // Set low + if(!(mask >>= 1)) { // Next bit/byte + if(p >= end) break; + pix = *p++; + mask = 0x80; + } + } + while((_getCycleCount() - startTime) < period); // Wait for last bit +} + +#endif // ESP8266 diff --git a/examples/strandtest/strandtest.ino b/examples/strandtest/strandtest.ino index 74772147..14d8ecb7 100644 --- a/examples/strandtest/strandtest.ino +++ b/examples/strandtest/strandtest.ino @@ -1,5 +1,5 @@ #include -#include +#include // Comment out this line for non-AVR boards (Arduino Due, etc.) #define PIN 6 diff --git a/library.properties b/library.properties index 77a88328..e2be3c4b 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=Adafruit NeoPixel -version=1.0 +version=1.0.1 author=Adafruit maintainer=Adafruit sentence=Arduino library for controlling single-wire-based LED pixels and strip.