circuitpython/esp8266/espneopixel.c
Olivier Ortigues bc4441afa7 esp8266/espneopixel.c: Solve glitching LED issues with cpu at 80MHz.
At the WS2812 driver level, a 400ns value was used for T0H (time high to
send a 0 bit) but LED specification says it should be 350ns +- 150ns.
Due to loop overhead the 400ns value could lead to T0H close to 500ns
which is too close from the limit value and gave glitches (bad data to
pixels) in some cases.  This patch makes the calculated T0H value 350ns.
2016-11-07 17:13:49 +11:00

66 lines
2.1 KiB
C

// Original version from https://github.com/adafruit/Adafruit_NeoPixel
// Modifications by dpgeorge to support auto-CPU-frequency detection
// 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.
#include "py/mpconfig.h"
#if MICROPY_ESP8266_NEOPIXEL
#include "c_types.h"
#include "eagle_soc.h"
#include "user_interface.h"
#include "espneopixel.h"
#include "esp_mphal.h"
#define NEO_KHZ400 (1)
void /*ICACHE_RAM_ATTR*/ esp_neopixel_write(uint8_t pin, uint8_t *pixels, uint32_t numBytes, bool is800KHz) {
uint8_t *p, *end, pix, mask;
uint32_t t, time0, time1, period, c, startTime, pinMask;
pinMask = 1 << pin;
p = pixels;
end = p + numBytes;
pix = *p++;
mask = 0x80;
startTime = 0;
uint32_t fcpu = system_get_cpu_freq() * 1000000;
#ifdef NEO_KHZ400
if(is800KHz) {
#endif
time0 = fcpu / 2857143; // 0.35us
time1 = fcpu / 1250000; // 0.8us
period = fcpu / 800000; // 1.25us per bit
#ifdef NEO_KHZ400
} else { // 400 KHz bitstream
time0 = fcpu / 2000000; // 0.5uS
time1 = fcpu / 833333; // 1.2us
period = fcpu / 400000; // 2.5us per bit
}
#endif
uint32_t irq_state = mp_hal_quiet_timing_enter();
for(t = time0;; t = time0) {
if(pix & mask) t = time1; // Bit high duration
while(((c = mp_hal_ticks_cpu()) - startTime) < period); // Wait for bit start
GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, pinMask); // Set high
startTime = c; // Save start time
while(((c = mp_hal_ticks_cpu()) - 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((mp_hal_ticks_cpu() - startTime) < period); // Wait for last bit
mp_hal_quiet_timing_exit(irq_state);
}
#endif // MICROPY_ESP8266_NEOPIXEL