From ed0265bbcb6423e87ccb97ba0d2e6e27c6224299 Mon Sep 17 00:00:00 2001 From: fat-fred Date: Sun, 17 May 2020 10:02:39 +0200 Subject: [PATCH 1/8] Include Test over serial + Update for newer octoWS2811 lib --- TeensyStripController.ino | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/TeensyStripController.ino b/TeensyStripController.ino index 61ba512..3c1f65b 100644 --- a/TeensyStripController.ino +++ b/TeensyStripController.ino @@ -56,6 +56,9 @@ #define FirmwareVersionMajor 1 #define FirmwareVersionMinor 3 +const int numPins = 8; +byte pinList[numPins] = {2, 14, 7, 8, 6, 20, 21, 5}; + //Defines the max number of leds which is allowed per ledstrip. //This number is fine for Teensy 3.2, 3.1. For newer Teensy versions (they dont exists yet) it might be possible to increase this number. #define MaxLedsPerStrip 1100 @@ -68,8 +71,8 @@ #define TestPin 17 //Memory buffers for the OctoWS2811 lib -DMAMEM int displayMemory[MaxLedsPerStrip*6]; -int drawingMemory[MaxLedsPerStrip*6]; +DMAMEM int displayMemory[MaxLedsPerStrip * numPins * 3 / 4]; +int drawingMemory[MaxLedsPerStrip * numPins * 3 / 4]; //Variable used to control the blinking and flickering of the led of the Teensy elapsedMillis BlinkTimer; @@ -79,7 +82,7 @@ elapsedMillis BlinkModeTimeoutTimer; //Config definition for the OctoWS2811 lib const int config = WS2811_RGB | WS2811_800kHz; //Dont change the color order (even if your strip are GRB). DOF takes care of this issue (see config of ledstrip toy) -OctoWS2811Ext leds(MaxLedsPerStrip, displayMemory, drawingMemory, config); +OctoWS2811Ext leds(MaxLedsPerStrip, displayMemory, drawingMemory, config, numPins, pinList); word configuredStripLength=144; @@ -142,6 +145,10 @@ void loop() { //Get max number of leds per strip SendMaxNumberOfLeds(); break; + case 'T': + //initiate Test over serial + Test(); + break; default: // no unknown commands allowed. Send NACK (N) Nack(); From d5e1b43117362c2b46fb76eae34011233e68ffd2 Mon Sep 17 00:00:00 2001 From: fat-fred Date: Sun, 17 May 2020 10:03:45 +0200 Subject: [PATCH 2/8] Update OctoWS2811Ext.h --- OctoWS2811Ext.h | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/OctoWS2811Ext.h b/OctoWS2811Ext.h index ede5417..7cf97a0 100644 --- a/OctoWS2811Ext.h +++ b/OctoWS2811Ext.h @@ -56,8 +56,20 @@ class OctoWS2811Ext { public: - OctoWS2811Ext(uint32_t numPerStrip, void *frameBuf, void *drawBuf, uint8_t config = WS2811_GRB); - void begin(void); +#if defined(__IMXRT1062__) + // Teensy 4.x can use any arbitrary group of pins! + OctoWS2811Ext(uint32_t numPerStrip, void *frameBuf, void *drawBuf, uint8_t config = WS2811_GRB, uint8_t numPins = 8, const uint8_t *pinList = defaultPinList); + void begin(uint32_t numPerStrip, void *frameBuf, void *drawBuf, uint8_t config = WS2811_GRB, uint8_t numPins = 8, const uint8_t *pinList = defaultPinList); + int numPixels(void); +#else + // Teensy 3.x is fixed to 8 pins: 2, 14, 7, 8, 6, 20, 21, 5 + OctoWS2811Ext(uint32_t numPerStrip, void *frameBuf, void *drawBuf, uint8_t config = WS2811_GRB); + void begin(uint32_t numPerStrip, void *frameBuf, void *drawBuf, uint8_t config = WS2811_GRB); + int numPixels(void) { + return stripLen * 8; + } +#endif + void begin(void); void setStripLength(uint16_t length); void setPixel(uint32_t num, int color); @@ -69,9 +81,6 @@ class OctoWS2811Ext { void show(void); int busy(void); - int numPixels(void) { - return stripLen * 8; - } int color(uint8_t red, uint8_t green, uint8_t blue) { return (red << 16) | (green << 8) | blue; } @@ -84,6 +93,7 @@ class OctoWS2811Ext { static uint8_t params; static DMAChannel dma1, dma2, dma3; static void isr(void); + static uint8_t defaultPinList[8]; }; #endif From 8b8dc59dfbe91d2c5cbb5546a9540d237d945ae6 Mon Sep 17 00:00:00 2001 From: fat-fred Date: Sun, 17 May 2020 10:05:27 +0200 Subject: [PATCH 3/8] Create OctoWS2811Ext_imxrt.cpp --- OctoWS2811Ext_imxrt.cpp | 446 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 446 insertions(+) create mode 100644 OctoWS2811Ext_imxrt.cpp diff --git a/OctoWS2811Ext_imxrt.cpp b/OctoWS2811Ext_imxrt.cpp new file mode 100644 index 0000000..8bdd4a4 --- /dev/null +++ b/OctoWS2811Ext_imxrt.cpp @@ -0,0 +1,446 @@ +/* OctoWS2811 - High Performance WS2811 LED Display Library + http://www.pjrc.com/teensy/td_libs_OctoWS2811.html + Copyright (c) 2013 Paul Stoffregen, PJRC.COM, LLC + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +/* Remark: + This is a slightly extended version of the original OctoWS2811 lib. It contains + a extra method to set the number of leds per strip dynamically. + + Thanks to the author of the lib for his excellent work. +*/ + +#include +#include "OctoWS2811Ext.h" + +#if defined(__IMXRT1062__) + +#define TH_TL 1.25e-6 +#define T0H 0.30e-6 +#define T1H 0.75e-6 + +// Ordinary RGB data is converted to GPIO bitmasks on-the-fly using +// a transmit buffer sized for 2 DMA transfers. The larger this setting, +// the more interrupt latency OctoWS2811 can tolerate, but the transmit +// buffer grows in size. For good performance, the buffer should be kept +// smaller than the half the Cortex-M7 data cache. +#define BYTES_PER_DMA 40 + +uint8_t OctoWS2811Ext::defaultPinList[8] = {2, 14, 7, 8, 6, 20, 21, 5}; +uint16_t OctoWS2811Ext::stripLen; +void * OctoWS2811Ext::frameBuffer; +void * OctoWS2811Ext::drawBuffer; +uint8_t OctoWS2811Ext::params; +DMAChannel OctoWS2811Ext::dma1; +DMAChannel OctoWS2811Ext::dma2; +DMAChannel OctoWS2811Ext::dma3; +static DMASetting dma2next; +static uint32_t numbytes; + +static uint8_t numpins; +static uint8_t pinlist[NUM_DIGITAL_PINS]; // = {2, 14, 7, 8, 6, 20, 21, 5}; +static uint8_t pin_bitnum[NUM_DIGITAL_PINS]; +static uint8_t pin_offset[NUM_DIGITAL_PINS]; + +static uint16_t comp1load[3]; +DMAMEM static uint32_t bitmask[4] __attribute__ ((used, aligned(32))); +DMAMEM static uint32_t bitdata[BYTES_PER_DMA*64] __attribute__ ((used, aligned(32))); +volatile uint32_t framebuffer_index = 0; +volatile bool dma_first; + +static uint32_t update_begin_micros = 0; + +OctoWS2811Ext::OctoWS2811Ext(uint32_t numPerStrip, void *frameBuf, void *drawBuf, uint8_t config, uint8_t numPins, const uint8_t *pinList) +{ + stripLen = numPerStrip; + frameBuffer = frameBuf; + drawBuffer = drawBuf; + params = config; + if (numPins > NUM_DIGITAL_PINS) numPins = NUM_DIGITAL_PINS; + numpins = numPins; + memcpy(pinlist, pinList, numpins); +} + + +// Discussion about timing and flicker & color shift issues: +// http://forum.pjrc.com/threads/23877-WS2812B-compatible-with-OctoWS2811Ext-library?p=38190&viewfull=1#post38190 + +void OctoWS2811Ext::begin(uint32_t numPerStrip, void *frameBuf, void *drawBuf, uint8_t config, uint8_t numPins, const uint8_t *pinList) +{ + stripLen = numPerStrip; + frameBuffer = frameBuf; + drawBuffer = drawBuf; + params = config; + if (numPins > NUM_DIGITAL_PINS) numPins = NUM_DIGITAL_PINS; + numpins = numPins; + memcpy(pinlist, pinList, numpins); + begin(); +} + +int OctoWS2811Ext::numPixels(void) +{ + return stripLen * numpins; +} + +extern "C" void xbar_connect(unsigned int input, unsigned int output); // in pwm.c +static volatile uint32_t *standard_gpio_addr(volatile uint32_t *fastgpio) { + return (volatile uint32_t *)((uint32_t)fastgpio - 0x01E48000); +} + +void OctoWS2811Ext::begin(void) +{ + numbytes = stripLen * 3; // TODO: 4 if RGBW + + // configure which pins to use + memset(bitmask, 0, sizeof(bitmask)); + for (uint32_t i=0; i < numpins; i++) { + uint8_t pin = pinlist[i]; + if (pin >= NUM_DIGITAL_PINS) continue; // ignore illegal pins + uint8_t bit = digitalPinToBit(pin); + uint8_t offset = ((uint32_t)portOutputRegister(pin) - (uint32_t)&GPIO6_DR) >> 14; + if (offset > 3) continue; // ignore unknown pins + pin_bitnum[i] = bit; + pin_offset[i] = offset; + uint32_t mask = 1 << bit; + bitmask[offset] |= mask; + *(&IOMUXC_GPR_GPR26 + offset) &= ~mask; + *standard_gpio_addr(portModeRegister(pin)) |= mask; + } + arm_dcache_flush_delete(bitmask, sizeof(bitmask)); + + // Set up 3 timers to create waveform timing events + comp1load[0] = (uint16_t)((float)F_BUS_ACTUAL * (float)TH_TL); + comp1load[1] = (uint16_t)((float)F_BUS_ACTUAL * (float)T0H); + comp1load[2] = (uint16_t)((float)F_BUS_ACTUAL * (float)T1H); + if ((params & 0xF0) == WS2811_400kHz) { + comp1load[0] *= 2; + comp1load[1] *= 2; + comp1load[2] *= 2; + } + TMR4_ENBL &= ~7; + TMR4_SCTRL0 = TMR_SCTRL_OEN | TMR_SCTRL_FORCE | TMR_SCTRL_MSTR; + TMR4_CSCTRL0 = TMR_CSCTRL_CL1(1) | TMR_CSCTRL_TCF1EN; + TMR4_CNTR0 = 0; + TMR4_LOAD0 = 0; + TMR4_COMP10 = comp1load[0]; + TMR4_CMPLD10 = comp1load[0]; + TMR4_CTRL0 = TMR_CTRL_CM(1) | TMR_CTRL_PCS(8) | TMR_CTRL_LENGTH | TMR_CTRL_OUTMODE(3); + TMR4_SCTRL1 = TMR_SCTRL_OEN | TMR_SCTRL_FORCE; + TMR4_CNTR1 = 0; + TMR4_LOAD1 = 0; + TMR4_COMP11 = comp1load[1]; // T0H + TMR4_CMPLD11 = comp1load[1]; + TMR4_CTRL1 = TMR_CTRL_CM(1) | TMR_CTRL_PCS(8) | TMR_CTRL_COINIT | TMR_CTRL_OUTMODE(3); + TMR4_SCTRL2 = TMR_SCTRL_OEN | TMR_SCTRL_FORCE; + TMR4_CNTR2 = 0; + TMR4_LOAD2 = 0; + TMR4_COMP12 = comp1load[2]; // T1H + TMR4_CMPLD12 = comp1load[2]; + TMR4_CTRL2 = TMR_CTRL_CM(1) | TMR_CTRL_PCS(8) | TMR_CTRL_COINIT | TMR_CTRL_OUTMODE(3); + + // route the timer outputs through XBAR to edge trigger DMA request + CCM_CCGR2 |= CCM_CCGR2_XBAR1(CCM_CCGR_ON); + xbar_connect(XBARA1_IN_QTIMER4_TIMER0, XBARA1_OUT_DMA_CH_MUX_REQ30); + xbar_connect(XBARA1_IN_QTIMER4_TIMER1, XBARA1_OUT_DMA_CH_MUX_REQ31); + xbar_connect(XBARA1_IN_QTIMER4_TIMER2, XBARA1_OUT_DMA_CH_MUX_REQ94); + XBARA1_CTRL0 = XBARA_CTRL_STS1 | XBARA_CTRL_EDGE1(3) | XBARA_CTRL_DEN1 | + XBARA_CTRL_STS0 | XBARA_CTRL_EDGE0(3) | XBARA_CTRL_DEN0; + XBARA1_CTRL1 = XBARA_CTRL_STS0 | XBARA_CTRL_EDGE0(3) | XBARA_CTRL_DEN0; + + // configure DMA channels + dma1.begin(); + dma1.TCD->SADDR = bitmask; + dma1.TCD->SOFF = 8; + dma1.TCD->ATTR = DMA_TCD_ATTR_SSIZE(3) | DMA_TCD_ATTR_SMOD(4) | DMA_TCD_ATTR_DSIZE(2); + dma1.TCD->NBYTES_MLOFFYES = DMA_TCD_NBYTES_DMLOE | + DMA_TCD_NBYTES_MLOFFYES_MLOFF(-65536) | + DMA_TCD_NBYTES_MLOFFYES_NBYTES(16); + dma1.TCD->SLAST = 0; + dma1.TCD->DADDR = &GPIO1_DR_SET; + dma1.TCD->DOFF = 16384; + dma1.TCD->CITER_ELINKNO = numbytes * 8; + dma1.TCD->DLASTSGA = -65536; + dma1.TCD->BITER_ELINKNO = numbytes * 8; + dma1.TCD->CSR = DMA_TCD_CSR_DREQ; + dma1.triggerAtHardwareEvent(DMAMUX_SOURCE_XBAR1_0); + + dma2next.TCD->SADDR = bitdata; + dma2next.TCD->SOFF = 8; + dma2next.TCD->ATTR = DMA_TCD_ATTR_SSIZE(3) | DMA_TCD_ATTR_DSIZE(2); + dma2next.TCD->NBYTES_MLOFFYES = DMA_TCD_NBYTES_DMLOE | + DMA_TCD_NBYTES_MLOFFYES_MLOFF(-65536) | + DMA_TCD_NBYTES_MLOFFYES_NBYTES(16); + dma2next.TCD->SLAST = 0; + dma2next.TCD->DADDR = &GPIO1_DR_CLEAR; + dma2next.TCD->DOFF = 16384; + dma2next.TCD->CITER_ELINKNO = BYTES_PER_DMA * 8; + dma2next.TCD->DLASTSGA = (int32_t)(dma2next.TCD); + dma2next.TCD->BITER_ELINKNO = BYTES_PER_DMA * 8; + dma2next.TCD->CSR = 0; + + dma2.begin(); + dma2 = dma2next; // copies TCD + dma2.triggerAtHardwareEvent(DMAMUX_SOURCE_XBAR1_1); + dma2.attachInterrupt(isr); + + dma3.begin(); + dma3.TCD->SADDR = bitmask; + dma3.TCD->SOFF = 8; + dma3.TCD->ATTR = DMA_TCD_ATTR_SSIZE(3) | DMA_TCD_ATTR_SMOD(4) | DMA_TCD_ATTR_DSIZE(2); + dma3.TCD->NBYTES_MLOFFYES = DMA_TCD_NBYTES_DMLOE | + DMA_TCD_NBYTES_MLOFFYES_MLOFF(-65536) | + DMA_TCD_NBYTES_MLOFFYES_NBYTES(16); + dma3.TCD->SLAST = 0; + dma3.TCD->DADDR = &GPIO1_DR_CLEAR; + dma3.TCD->DOFF = 16384; + dma3.TCD->CITER_ELINKNO = numbytes * 8; + dma3.TCD->DLASTSGA = -65536; + dma3.TCD->BITER_ELINKNO = numbytes * 8; + dma3.TCD->CSR = DMA_TCD_CSR_DREQ | DMA_TCD_CSR_DONE; + dma3.triggerAtHardwareEvent(DMAMUX_SOURCE_XBAR1_2); + + // set up the buffers + uint32_t bufsize = numbytes * numpins; + memset(frameBuffer, 0, bufsize); + if (drawBuffer) { + memset(drawBuffer, 0, bufsize); + } else { + drawBuffer = frameBuffer; + } +} + +static void fillbits(uint32_t *dest, const uint8_t *pixels, int n, uint32_t mask) +{ + do { + uint8_t pix = *pixels++; + if (!(pix & 0x80)) *dest |= mask; + dest += 4; + if (!(pix & 0x40)) *dest |= mask; + dest += 4; + if (!(pix & 0x20)) *dest |= mask; + dest += 4; + if (!(pix & 0x10)) *dest |= mask; + dest += 4; + if (!(pix & 0x08)) *dest |= mask; + dest += 4; + if (!(pix & 0x04)) *dest |= mask; + dest += 4; + if (!(pix & 0x02)) *dest |= mask; + dest += 4; + if (!(pix & 0x01)) *dest |= mask; + dest += 4; + } while (--n > 0); +} + +void OctoWS2811Ext::show(void) +{ + // wait for any prior DMA operation + while (!dma3.complete()) ; // wait + + // it's ok to copy the drawing buffer to the frame buffer + // during the 50us WS2811 reset time + if (drawBuffer != frameBuffer) { + memcpy(frameBuffer, drawBuffer, numbytes * numpins); + } + + // disable timers + uint16_t enable = TMR4_ENBL; + TMR4_ENBL = enable & ~7; + + // force all timer outputs to logic low + TMR4_SCTRL0 = TMR_SCTRL_OEN | TMR_SCTRL_FORCE | TMR_SCTRL_MSTR; + TMR4_SCTRL1 = TMR_SCTRL_OEN | TMR_SCTRL_FORCE; + TMR4_SCTRL2 = TMR_SCTRL_OEN | TMR_SCTRL_FORCE; + + // clear any prior pending DMA requests + XBARA1_CTRL0 |= XBARA_CTRL_STS1 | XBARA_CTRL_STS0; + XBARA1_CTRL1 |= XBARA_CTRL_STS0; + + // fill the DMA transmit buffer + //digitalWriteFast(12, HIGH); + memset(bitdata, 0, sizeof(bitdata)); + uint32_t count = numbytes; + if (count > BYTES_PER_DMA*2) count = BYTES_PER_DMA*2; + framebuffer_index = count; + for (uint32_t i=0; i < numpins; i++) { + fillbits(bitdata + pin_offset[i], (uint8_t *)frameBuffer + i*numbytes, + count, 1<SADDR = bitdata; + dma2.TCD->DADDR = &GPIO1_DR_CLEAR; + dma2.TCD->CITER_ELINKNO = count * 8; + dma2.TCD->CSR = DMA_TCD_CSR_DREQ; + } else { + dma2.TCD->SADDR = bitdata; + dma2.TCD->DADDR = &GPIO1_DR_CLEAR; + dma2.TCD->CITER_ELINKNO = BYTES_PER_DMA * 8; + dma2.TCD->CSR = 0; + dma2.TCD->CSR = DMA_TCD_CSR_INTMAJOR | DMA_TCD_CSR_ESG; + dma2next.TCD->SADDR = bitdata + BYTES_PER_DMA*32; + dma2next.TCD->CITER_ELINKNO = BYTES_PER_DMA * 8; + if (numbytes <= BYTES_PER_DMA*3) { + dma2next.TCD->CSR = DMA_TCD_CSR_ESG; + } else { + dma2next.TCD->CSR = DMA_TCD_CSR_ESG | DMA_TCD_CSR_INTMAJOR; + } + dma_first = true; + } + dma3.clearComplete(); + dma1.enable(); + dma2.enable(); + dma3.enable(); + + // initialize timers + TMR4_CNTR0 = 0; + TMR4_CNTR1 = comp1load[0] + 1; + TMR4_CNTR2 = comp1load[0] + 1; + + // wait for WS2812 reset + while (micros() - update_begin_micros < numbytes * 10 + 300) ; + + // start everything running! + TMR4_ENBL = enable | 7; + update_begin_micros = micros(); +} + +void OctoWS2811Ext::isr(void) +{ + // first ack the interrupt + dma2.clearInterrupt(); + + // fill (up to) half the transmit buffer with new data + //digitalWriteFast(12, HIGH); + uint32_t *dest; + if (dma_first) { + dma_first = false; + dest = bitdata; + } else { + dma_first = true; + dest = bitdata + BYTES_PER_DMA*32; + } + memset(dest, 0, sizeof(bitdata)/2); + uint32_t index = framebuffer_index; + uint32_t count = numbytes - framebuffer_index; + if (count > BYTES_PER_DMA) count = BYTES_PER_DMA; + framebuffer_index = index + count; + for (int i=0; i < numpins; i++) { + fillbits(dest + pin_offset[i], (uint8_t *)frameBuffer + index + i*numbytes, + count, 1<SADDR = dest; + dma2next.TCD->CITER_ELINKNO = count * 8; + uint32_t remain = numbytes - (index + count); + if (remain == 0) { + dma2next.TCD->CSR = DMA_TCD_CSR_DREQ; + } else if (remain <= BYTES_PER_DMA) { + dma2next.TCD->CSR = DMA_TCD_CSR_ESG; + } else { + dma2next.TCD->CSR = DMA_TCD_CSR_ESG | DMA_TCD_CSR_INTMAJOR; + } +} + +int OctoWS2811Ext::busy(void) +{ + if (!dma3.complete()) ; // DMA still running + if (micros() - update_begin_micros < numbytes * 10 + 300) return 1; // WS2812 reset + return 0; +} + +// For Teensy 4.x, the pixel data is stored in ordinary RGB format. Translation +// from 24 bit color to GPIO bitmasks is done on-the-fly by fillbits(). This is +// different from Teensy 3.x, where the data was stored as bytes to write directly +// to the GPIO output register. + + +void OctoWS2811Ext::setStripLength(uint16_t length) +{ + stripLen=length; +} + + +// For Teensy 4.x, the pixel data is stored in ordinary RGB format. Translation +// from 24 bit color to GPIO bitmasks is done on-the-fly by fillbits(). This is +// different from Teensy 3.x, where the data was stored as bytes to write directly +// to the GPIO output register. + +void OctoWS2811Ext::setPixel(uint32_t num, int color) +{ + switch (params & 7) { + case WS2811_RBG: + color = (color&0xFF0000) | ((color<<8)&0x00FF00) | ((color>>8)&0x0000FF); + break; + case WS2811_GRB: + color = ((color<<8)&0xFF0000) | ((color>>8)&0x00FF00) | (color&0x0000FF); + break; + case WS2811_GBR: + color = ((color<<16)&0xFF0000) | ((color>>8)&0x00FFFF); + break; + case WS2811_BRG: + color = ((color<<8)&0xFFFF00) | ((color>>16)&0x0000FF); + break; + case WS2811_BGR: + color = ((color<<16)&0xFF0000) | (color&0x00FF00) | ((color>>16)&0x0000FF); + break; + default: + break; + } + uint8_t *dest = (uint8_t *)drawBuffer + num * 3; + *dest++ = color >> 16; + *dest++ = color >> 8; + *dest++ = color; +} + +int OctoWS2811Ext::getPixel(uint32_t num) +{ + const uint8_t *p = (uint8_t *)drawBuffer + num * 3; + int color = p[2] | (p[1] << 8) | (p[0] << 16); + switch (params & 7) { + case WS2811_RBG: + color = (color&0xFF0000) | ((color<<8)&0x00FF00) | ((color>>8)&0x0000FF); + break; + case WS2811_GRB: + color = ((color<<8)&0xFF0000) | ((color>>8)&0x00FF00) | (color&0x0000FF); + break; + case WS2811_GBR: + color = ((color<<8)&0xFFFF00) | ((color>>16)&0x0000FF); + break; + case WS2811_BRG: + color = ((color<<16)&0xFF0000) | ((color>>8)&0x00FFFF); + break; + case WS2811_BGR: + color = ((color<<16)&0xFF0000) | (color&0x00FF00) | ((color>>16)&0x0000FF); + break; + default: + break; + } + return color; +} + +#endif // __IMXRT1062__ From db8835f3894726baaf3adaaccc77b77f7879b0b1 Mon Sep 17 00:00:00 2001 From: fat-fred Date: Sun, 17 May 2020 10:10:59 +0200 Subject: [PATCH 4/8] Update OctoWS2811Ext.cpp --- OctoWS2811Ext.cpp | 564 +++++++++++++++++++++++++++++----------------- 1 file changed, 358 insertions(+), 206 deletions(-) diff --git a/OctoWS2811Ext.cpp b/OctoWS2811Ext.cpp index 04265e4..8a7f2eb 100644 --- a/OctoWS2811Ext.cpp +++ b/OctoWS2811Ext.cpp @@ -1,17 +1,14 @@ /* OctoWS2811 - High Performance WS2811 LED Display Library http://www.pjrc.com/teensy/td_libs_OctoWS2811.html Copyright (c) 2013 Paul Stoffregen, PJRC.COM, LLC - Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -24,15 +21,14 @@ /* Remark: This is a slightly extended version of the original OctoWS2811 lib. It contains a extra method to set the number of leds per strip dynamically. - Thanks to the author of the lib for his excellent work. */ - -#include +#include #include "OctoWS2811Ext.h" +#if defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__) || defined(__MKL26Z64__) uint16_t OctoWS2811Ext::stripLen; void * OctoWS2811Ext::frameBuffer; @@ -46,7 +42,6 @@ static uint8_t ones = 0xFF; static volatile uint8_t update_in_progress = 0; static uint32_t update_completed_at = 0; - OctoWS2811Ext::OctoWS2811Ext(uint32_t numPerStrip, void *frameBuf, void *drawBuf, uint8_t config) { stripLen = numPerStrip; @@ -74,8 +69,16 @@ OctoWS2811Ext::OctoWS2811Ext(uint32_t numPerStrip, void *frameBuf, void *drawBuf #define WS2811_TIMING_T1H 176 // Discussion about timing and flicker & color shift issues: -// http://forum.pjrc.com/threads/23877-WS2812B-compatible-with-OctoWS2811Ext-library?p=38190&viewfull=1#post38190 +// http://forum.pjrc.com/threads/23877-WS2812B-compatible-with-OctoWS2811-library?p=38190&viewfull=1#post38190 +void OctoWS2811Ext::begin(uint32_t numPerStrip, void *frameBuf, void *drawBuf, uint8_t config) +{ + stripLen = numPerStrip; + frameBuffer = frameBuf; + drawBuffer = drawBuf; + params = config; + begin(); +} void OctoWS2811Ext::begin(void) { @@ -101,134 +104,283 @@ void OctoWS2811Ext::begin(void) pinMode(21, OUTPUT); // strip #7 pinMode(5, OUTPUT); // strip #8 - // create the two waveforms for WS2811 low and high bits - switch (params & 0xF0) { - case WS2811_400kHz: - frequency = 400000; - break; - case WS2811_800kHz: - frequency = 800000; - break; - case WS2813_800kHz: - frequency = 800000; - break; - default: - frequency = 800000; - } - - FTM2_SC = 0; - FTM2_CNT = 0; - uint32_t mod = (F_BUS + frequency / 2) / frequency; - FTM2_MOD = mod - 1; - FTM2_SC = FTM_SC_CLKS(1) | FTM_SC_PS(0); - FTM2_C0SC = 0x69; - FTM2_C1SC = 0x69; - FTM2_C0V = (mod * WS2811_TIMING_T0H) >> 8; - FTM2_C1V = (mod * WS2811_TIMING_T1H) >> 8; - // pin 32 is FTM2_CH0, PTB18, triggers DMA(port B) on rising edge - // pin 25 is FTM2_CH1, PTB19 - CORE_PIN32_CONFIG = PORT_PCR_IRQC(1)|PORT_PCR_MUX(3); - //CORE_PIN25_CONFIG = PORT_PCR_MUX(3); // testing only - - // DMA channel #1 sets WS2811 high at the beginning of each cycle - dma1.source(ones); - dma1.destination(GPIOD_PSOR); - dma1.transferSize(1); - dma1.transferCount(bufsize); - dma1.disableOnCompletion(); - - // DMA channel #2 writes the pixel data at 23% of the cycle - dma2.sourceBuffer((uint8_t *)frameBuffer, bufsize); - dma2.destination(GPIOD_PDOR); - dma2.transferSize(1); - dma2.transferCount(bufsize); - dma2.disableOnCompletion(); - - // DMA channel #3 clear all the pins low at 69% of the cycle - dma3.source(ones); - dma3.destination(GPIOD_PCOR); - dma3.transferSize(1); - dma3.transferCount(bufsize); - dma3.disableOnCompletion(); - dma3.interruptAtCompletion(); - - // route the edge detect interrupts to trigger the 3 channels - dma1.triggerAtHardwareEvent(DMAMUX_SOURCE_PORTB); - dma2.triggerAtHardwareEvent(DMAMUX_SOURCE_FTM2_CH0); - dma3.triggerAtHardwareEvent(DMAMUX_SOURCE_FTM2_CH1); - DMAPriorityOrder(dma3, dma2, dma1); - - // enable a done interrupts when channel #3 completes - dma3.attachInterrupt(isr); - //pinMode(9, OUTPUT); // testing: oscilloscope trigger + // create the two waveforms for WS2811 low and high bits + switch (params & 0xF0) { + case WS2811_400kHz: + frequency = 400000; + break; + case WS2811_800kHz: + frequency = 800000; + break; + case WS2813_800kHz: + frequency = 800000; + break; + default: + frequency = 800000; + } + + +#if defined(__MK20DX128__) + FTM1_SC = 0; + FTM1_CNT = 0; + uint32_t mod = (F_BUS + frequency / 2) / frequency; + FTM1_MOD = mod - 1; + FTM1_SC = FTM_SC_CLKS(1) | FTM_SC_PS(0); + FTM1_C0SC = 0x69; + FTM1_C1SC = 0x69; + FTM1_C0V = (mod * WS2811_TIMING_T0H) >> 8; + FTM1_C1V = (mod * WS2811_TIMING_T1H) >> 8; + // pin 16 triggers DMA(port B) on rising edge + CORE_PIN16_CONFIG = PORT_PCR_IRQC(1)|PORT_PCR_MUX(3); + //CORE_PIN4_CONFIG = PORT_PCR_MUX(3); // testing only + +#elif defined(__MK20DX256__) + FTM2_SC = 0; + FTM2_CNT = 0; + uint32_t mod = (F_BUS + frequency / 2) / frequency; + FTM2_MOD = mod - 1; + FTM2_SC = FTM_SC_CLKS(1) | FTM_SC_PS(0); + FTM2_C0SC = 0x69; + FTM2_C1SC = 0x69; + FTM2_C0V = (mod * WS2811_TIMING_T0H) >> 8; + FTM2_C1V = (mod * WS2811_TIMING_T1H) >> 8; + // pin 32 is FTM2_CH0, PTB18, triggers DMA(port B) on rising edge + // pin 25 is FTM2_CH1, PTB19 + CORE_PIN32_CONFIG = PORT_PCR_IRQC(1)|PORT_PCR_MUX(3); + //CORE_PIN25_CONFIG = PORT_PCR_MUX(3); // testing only + +#elif defined(__MK64FX512__) || defined(__MK66FX1M0__) + FTM2_SC = 0; + FTM2_CNT = 0; + uint32_t mod = (F_BUS + frequency / 2) / frequency; + FTM2_MOD = mod - 1; + FTM2_SC = FTM_SC_CLKS(1) | FTM_SC_PS(0); + FTM2_C0SC = 0x69; + FTM2_C1SC = 0x69; + FTM2_C0V = (mod * WS2811_TIMING_T0H) >> 8; + FTM2_C1V = (mod * WS2811_TIMING_T1H) >> 8; + // FTM2_CH0, PTA10 (not connected), triggers DMA(port A) on rising edge + PORTA_PCR10 = PORT_PCR_IRQC(1)|PORT_PCR_MUX(3); + +#elif defined(__MKL26Z64__) + FTM2_SC = 0; + FTM2_CNT = 0; + uint32_t mod = F_CPU / frequency; + FTM2_MOD = mod - 1; + FTM2_SC = FTM_SC_CLKS(1) | FTM_SC_PS(0); + FTM2_C0SC = FTM_CSC_CHF | FTM_CSC_MSB | FTM_CSC_ELSB; + FTM2_C1SC = FTM_CSC_CHF | FTM_CSC_MSB | FTM_CSC_ELSB; + TPM2_C0V = mod - ((mod * WS2811_TIMING_T1H) >> 8); + TPM2_C1V = mod - ((mod * WS2811_TIMING_T1H) >> 8) + ((mod * WS2811_TIMING_T0H) >> 8); + +#endif + + // DMA channel #1 sets WS2811 high at the beginning of each cycle + dma1.source(ones); + dma1.destination(GPIOD_PSOR); + dma1.transferSize(1); + dma1.transferCount(bufsize); + dma1.disableOnCompletion(); + + // DMA channel #2 writes the pixel data at 23% of the cycle + dma2.sourceBuffer((uint8_t *)frameBuffer, bufsize); + dma2.destination(GPIOD_PDOR); + dma2.transferSize(1); + dma2.transferCount(bufsize); + dma2.disableOnCompletion(); + + // DMA channel #3 clear all the pins low at 69% of the cycle + dma3.source(ones); + dma3.destination(GPIOD_PCOR); + dma3.transferSize(1); + dma3.transferCount(bufsize); + dma3.disableOnCompletion(); + dma3.interruptAtCompletion(); + +#if defined(__MK20DX128__) + // route the edge detect interrupts to trigger the 3 channels + dma1.triggerAtHardwareEvent(DMAMUX_SOURCE_PORTB); + dma2.triggerAtHardwareEvent(DMAMUX_SOURCE_FTM1_CH0); + dma3.triggerAtHardwareEvent(DMAMUX_SOURCE_FTM1_CH1); + DMAPriorityOrder(dma3, dma2, dma1); +#elif defined(__MK20DX256__) + // route the edge detect interrupts to trigger the 3 channels + dma1.triggerAtHardwareEvent(DMAMUX_SOURCE_PORTB); + dma2.triggerAtHardwareEvent(DMAMUX_SOURCE_FTM2_CH0); + dma3.triggerAtHardwareEvent(DMAMUX_SOURCE_FTM2_CH1); + DMAPriorityOrder(dma3, dma2, dma1); +#elif defined(__MK64FX512__) || defined(__MK66FX1M0__) + // route the edge detect interrupts to trigger the 3 channels + dma1.triggerAtHardwareEvent(DMAMUX_SOURCE_PORTA); + dma2.triggerAtHardwareEvent(DMAMUX_SOURCE_FTM2_CH0); + dma3.triggerAtHardwareEvent(DMAMUX_SOURCE_FTM2_CH1); + DMAPriorityOrder(dma3, dma2, dma1); +#elif defined(__MKL26Z64__) + // route the timer interrupts to trigger the 3 channels + dma1.triggerAtHardwareEvent(DMAMUX_SOURCE_TPM2_CH0); + dma2.triggerAtHardwareEvent(DMAMUX_SOURCE_TPM2_CH1); + dma3.triggerAtHardwareEvent(DMAMUX_SOURCE_FTM2_OV); +#endif + + // enable a done interrupts when channel #3 completes + dma3.attachInterrupt(isr); + //pinMode(9, OUTPUT); // testing: oscilloscope trigger } void OctoWS2811Ext::isr(void) { - //digitalWriteFast(9, HIGH); - //Serial1.print("."); - //Serial1.println(dma3.CFG->DCR, HEX); - //Serial1.print(dma3.CFG->DSR_BCR > 24, HEX); - dma3.clearInterrupt(); - - //Serial1.print("*"); - update_completed_at = micros(); - update_in_progress = 0; - //digitalWriteFast(9, LOW); + //digitalWriteFast(9, HIGH); + //Serial1.print("."); + //Serial1.println(dma3.CFG->DCR, HEX); + //Serial1.print(dma3.CFG->DSR_BCR > 24, HEX); + dma3.clearInterrupt(); +#if defined(__MKL26Z64__) + GPIOD_PCOR = 0xFF; +#endif + //Serial1.print("*"); + update_completed_at = micros(); + update_in_progress = 0; + //digitalWriteFast(9, LOW); } int OctoWS2811Ext::busy(void) { if (update_in_progress) return 1; - // busy for 50 us after the done interrupt, for WS2811 reset - if (micros() - update_completed_at < 50) return 1; + // busy for 50 (or 300 for ws2813) us after the done interrupt, for WS2811 reset + if (micros() - update_completed_at < 300) return 1; return 0; } void OctoWS2811Ext::show(void) { - // wait for any prior DMA operation - //Serial1.print("1"); - while (update_in_progress) ; - //Serial1.print("2"); - // it's ok to copy the drawing buffer to the frame buffer - // during the 50us WS2811 reset time - if (drawBuffer != frameBuffer) { - // TODO: this could be faster with DMA, especially if the - // buffers are 32 bit aligned... but does it matter? - memcpy(frameBuffer, drawBuffer, stripLen * 24); - } - // wait for WS2811 reset - while (micros() - update_completed_at < 50) ; - // ok to start, but we must be very careful to begin - // without any prior 3 x 800kHz DMA requests pending - - FTM2_C0SC = 0x28; - FTM2_C1SC = 0x28; - uint32_t cv = FTM2_C0V; - noInterrupts(); - // CAUTION: this code is timing critical. - while (FTM2_CNT <= cv) ; - while (FTM2_CNT > cv) ; // wait for beginning of an 800 kHz cycle - while (FTM2_CNT < cv) ; - FTM2_SC = 0; // stop FTM2 timer (hopefully before it rolls over) - FTM2_CNT = 0; - update_in_progress = 1; - //digitalWriteFast(9, HIGH); // oscilloscope trigger - PORTB_ISFR = (1<<18); // clear any prior rising edge - uint32_t tmp __attribute__((unused)); - FTM2_C0SC = 0x28; - tmp = FTM2_C0SC; // clear any prior timer DMA triggers - FTM2_C0SC = 0x69; - FTM2_C1SC = 0x28; - tmp = FTM2_C1SC; - FTM2_C1SC = 0x69; - dma1.enable(); - dma2.enable(); // enable all 3 DMA channels - dma3.enable(); - FTM2_SC = FTM_SC_CLKS(1) | FTM_SC_PS(0); // restart FTM2 timer - //digitalWriteFast(9, LOW); - + // wait for any prior DMA operation + //Serial1.print("1"); + while (update_in_progress) ; + //Serial1.print("2"); + // it's ok to copy the drawing buffer to the frame buffer + // during the 50us WS2811 reset time + if (drawBuffer != frameBuffer) { + // TODO: this could be faster with DMA, especially if the + // buffers are 32 bit aligned... but does it matter? + memcpy(frameBuffer, drawBuffer, stripLen * 24); + } + // wait for WS2811 reset + while (micros() - update_completed_at < 300) ; + // ok to start, but we must be very careful to begin + // without any prior 3 x 800kHz DMA requests pending + +#if defined(__MK20DX128__) + uint32_t cv = FTM1_C0V; + noInterrupts(); + // CAUTION: this code is timing critical. + while (FTM1_CNT <= cv) ; + while (FTM1_CNT > cv) ; // wait for beginning of an 800 kHz cycle + while (FTM1_CNT < cv) ; + FTM1_SC = 0; // stop FTM1 timer (hopefully before it rolls over) + FTM1_CNT = 0; + update_in_progress = 1; + //digitalWriteFast(9, HIGH); // oscilloscope trigger + PORTB_ISFR = (1<<0); // clear any prior rising edge + uint32_t tmp __attribute__((unused)); + FTM1_C0SC = 0x28; + tmp = FTM1_C0SC; // clear any prior timer DMA triggers + FTM1_C0SC = 0x69; + FTM1_C1SC = 0x28; + tmp = FTM1_C1SC; + FTM1_C1SC = 0x69; + dma1.enable(); + dma2.enable(); // enable all 3 DMA channels + dma3.enable(); + FTM1_SC = FTM_SC_CLKS(1) | FTM_SC_PS(0); // restart FTM1 timer + //digitalWriteFast(9, LOW); + +#elif defined(__MK20DX256__) + FTM2_C0SC = 0x28; + FTM2_C1SC = 0x28; + uint32_t cv = FTM2_C0V; + noInterrupts(); + // CAUTION: this code is timing critical. + while (FTM2_CNT <= cv) ; + while (FTM2_CNT > cv) ; // wait for beginning of an 800 kHz cycle + while (FTM2_CNT < cv) ; + FTM2_SC = 0; // stop FTM2 timer (hopefully before it rolls over) + FTM2_CNT = 0; + update_in_progress = 1; + //digitalWriteFast(9, HIGH); // oscilloscope trigger + PORTB_ISFR = (1<<18); // clear any prior rising edge + uint32_t tmp __attribute__((unused)); + FTM2_C0SC = 0x28; + tmp = FTM2_C0SC; // clear any prior timer DMA triggers + FTM2_C0SC = 0x69; + FTM2_C1SC = 0x28; + tmp = FTM2_C1SC; + FTM2_C1SC = 0x69; + dma1.enable(); + dma2.enable(); // enable all 3 DMA channels + dma3.enable(); + FTM2_SC = FTM_SC_CLKS(1) | FTM_SC_PS(0); // restart FTM2 timer + //digitalWriteFast(9, LOW); + +#elif defined(__MK64FX512__) || defined(__MK66FX1M0__) + FTM2_C0SC = 0x28; + FTM2_C1SC = 0x28; + uint32_t cv = FTM2_C1V; + noInterrupts(); + // CAUTION: this code is timing critical. + while (FTM2_CNT <= cv) ; + while (FTM2_CNT > cv) ; // wait for beginning of an 800 kHz cycle + while (FTM2_CNT < cv) ; + FTM2_SC = 0; // stop FTM2 timer (hopefully before it rolls over) + FTM2_CNT = 0; + update_in_progress = 1; + //digitalWriteFast(9, HIGH); // oscilloscope trigger + #if defined(__MK64FX512__) + asm("nop"); + #endif + PORTA_ISFR = (1<<10); // clear any prior rising edge + uint32_t tmp __attribute__((unused)); + FTM2_C0SC = 0x28; + tmp = FTM2_C0SC; // clear any prior timer DMA triggers + FTM2_C0SC = 0x69; + FTM2_C1SC = 0x28; + tmp = FTM2_C1SC; + FTM2_C1SC = 0x69; + dma1.enable(); + dma2.enable(); // enable all 3 DMA channels + dma3.enable(); + FTM2_SC = FTM_SC_CLKS(1) | FTM_SC_PS(0); // restart FTM2 timer + //digitalWriteFast(9, LOW); + +#elif defined(__MKL26Z64__) + uint32_t sc __attribute__((unused)) = FTM2_SC; + uint32_t cv = FTM2_C1V; + noInterrupts(); + while (FTM2_CNT <= cv) ; + while (FTM2_CNT > cv) ; // wait for beginning of an 800 kHz cycle + while (FTM2_CNT < cv) ; + FTM2_SC = 0; // stop FTM2 timer (hopefully before it rolls over) + update_in_progress = 1; + //digitalWriteFast(9, HIGH); // oscilloscope trigger + dma1.clearComplete(); + dma2.clearComplete(); + dma3.clearComplete(); + uint32_t bufsize = stripLen*24; + dma1.transferCount(bufsize); + dma2.transferCount(bufsize); + dma3.transferCount(bufsize); + dma2.sourceBuffer((uint8_t *)frameBuffer, bufsize); + // clear any pending event flags + FTM2_SC = FTM_SC_TOF; + FTM2_C0SC = FTM_CSC_CHF | FTM_CSC_MSB | FTM_CSC_ELSB | FTM_CSC_DMA; + FTM2_C1SC = FTM_CSC_CHF | FTM_CSC_MSB | FTM_CSC_ELSB | FTM_CSC_DMA; + // clear any prior pending DMA requests + dma1.enable(); + dma2.enable(); // enable all 3 DMA channels + dma3.enable(); + FTM2_CNT = 0; // writing any value resets counter + FTM2_SC = FTM_SC_DMA | FTM_SC_CLKS(1) | FTM_SC_PS(0); + //digitalWriteFast(9, LOW); +#endif //Serial1.print("3"); interrupts(); //Serial1.print("4"); @@ -239,94 +391,94 @@ void OctoWS2811Ext::setStripLength(uint16_t length) stripLen=length; } - void OctoWS2811Ext::setPixel(uint32_t num, int color) { - uint32_t strip, offset, mask32, *p; - - switch (params & 7) { - case WS2811_RBG: - color = (color&0xFF0000) | ((color<<8)&0x00FF00) | ((color>>8)&0x0000FF); - break; - case WS2811_GRB: - color = ((color<<8)&0xFF0000) | ((color>>8)&0x00FF00) | (color&0x0000FF); - break; - case WS2811_GBR: - color = ((color<<16)&0xFF0000) | ((color>>8)&0x00FFFF); - break; - case WS2811_BRG: - color = ((color<<8)&0xFFFF00) | ((color>>16)&0x0000FF); - break; - case WS2811_BGR: - color = ((color<<16)&0xFF0000) | (color&0x00FF00) | ((color>>16)&0x0000FF); - break; - default: - break; - } - - strip = num / stripLen; // Cortex-M4 has 2 cycle unsigned divide :-) - offset = num % stripLen; - - p = ((uint32_t *) drawBuffer) + offset * 6; - - mask32 = (0x01010101) << strip; - - // Set bytes 0-3 - *p &= ~mask32; - *p |= (((color & 0x800000) >> 23) | ((color & 0x400000) >> 14) | ((color & 0x200000) >> 5) | ((color & 0x100000) << 4)) << strip; - - // Set bytes 4-7 - *++p &= ~mask32; - *p |= (((color & 0x80000) >> 19) | ((color & 0x40000) >> 10) | ((color & 0x20000) >> 1) | ((color & 0x10000) << 8)) << strip; - - // Set bytes 8-11 - *++p &= ~mask32; - *p |= (((color & 0x8000) >> 15) | ((color & 0x4000) >> 6) | ((color & 0x2000) << 3) | ((color & 0x1000) << 12)) << strip; - - // Set bytes 12-15 - *++p &= ~mask32; - *p |= (((color & 0x800) >> 11) | ((color & 0x400) >> 2) | ((color & 0x200) << 7) | ((color & 0x100) << 16)) << strip; - - // Set bytes 16-19 - *++p &= ~mask32; - *p |= (((color & 0x80) >> 7) | ((color & 0x40) << 2) | ((color & 0x20) << 11) | ((color & 0x10) << 20)) << strip; - - // Set bytes 20-23 - *++p &= ~mask32; - *p |= (((color & 0x8) >> 3) | ((color & 0x4) << 6) | ((color & 0x2) << 15) | ((color & 0x1) << 24)) << strip; + uint32_t strip, offset, mask32, *p; + + switch (params & 7) { + case WS2811_RBG: + color = (color&0xFF0000) | ((color<<8)&0x00FF00) | ((color>>8)&0x0000FF); + break; + case WS2811_GRB: + color = ((color<<8)&0xFF0000) | ((color>>8)&0x00FF00) | (color&0x0000FF); + break; + case WS2811_GBR: + color = ((color<<16)&0xFF0000) | ((color>>8)&0x00FFFF); + break; + case WS2811_BRG: + color = ((color<<8)&0xFFFF00) | ((color>>16)&0x0000FF); + break; + case WS2811_BGR: + color = ((color<<16)&0xFF0000) | (color&0x00FF00) | ((color>>16)&0x0000FF); + break; + default: + break; + } + strip = num / stripLen; // Cortex-M4 has 2 cycle unsigned divide :-) + offset = num % stripLen; + + p = ((uint32_t *) drawBuffer) + offset * 6; + + mask32 = (0x01010101) << strip; + + // Set bytes 0-3 + *p &= ~mask32; + *p |= (((color & 0x800000) >> 23) | ((color & 0x400000) >> 14) | ((color & 0x200000) >> 5) | ((color & 0x100000) << 4)) << strip; + + // Set bytes 4-7 + *++p &= ~mask32; + *p |= (((color & 0x80000) >> 19) | ((color & 0x40000) >> 10) | ((color & 0x20000) >> 1) | ((color & 0x10000) << 8)) << strip; + + // Set bytes 8-11 + *++p &= ~mask32; + *p |= (((color & 0x8000) >> 15) | ((color & 0x4000) >> 6) | ((color & 0x2000) << 3) | ((color & 0x1000) << 12)) << strip; + + // Set bytes 12-15 + *++p &= ~mask32; + *p |= (((color & 0x800) >> 11) | ((color & 0x400) >> 2) | ((color & 0x200) << 7) | ((color & 0x100) << 16)) << strip; + + // Set bytes 16-19 + *++p &= ~mask32; + *p |= (((color & 0x80) >> 7) | ((color & 0x40) << 2) | ((color & 0x20) << 11) | ((color & 0x10) << 20)) << strip; + + // Set bytes 20-23 + *++p &= ~mask32; + *p |= (((color & 0x8) >> 3) | ((color & 0x4) << 6) | ((color & 0x2) << 15) | ((color & 0x1) << 24)) << strip; } int OctoWS2811Ext::getPixel(uint32_t num) { - uint32_t strip, offset, mask; - uint8_t bit, *p; - int color=0; - - strip = num / stripLen; - offset = num % stripLen; - bit = (1<>= 1) { - if (*p++ & bit) color |= mask; - } - switch (params & 7) { - case WS2811_RBG: - color = (color&0xFF0000) | ((color<<8)&0x00FF00) | ((color>>8)&0x0000FF); - break; - case WS2811_GRB: - color = ((color<<8)&0xFF0000) | ((color>>8)&0x00FF00) | (color&0x0000FF); - break; - case WS2811_GBR: - color = ((color<<8)&0xFFFF00) | ((color>>16)&0x0000FF); - break; - case WS2811_BRG: - color = ((color<<16)&0xFF0000) | ((color>>8)&0x00FFFF); - break; - case WS2811_BGR: - color = ((color<<16)&0xFF0000) | (color&0x00FF00) | ((color>>16)&0x0000FF); - break; - default: - break; - } - return color; + uint32_t strip, offset, mask; + uint8_t bit, *p; + int color=0; + + strip = num / stripLen; + offset = num % stripLen; + bit = (1<>= 1) { + if (*p++ & bit) color |= mask; + } + switch (params & 7) { + case WS2811_RBG: + color = (color&0xFF0000) | ((color<<8)&0x00FF00) | ((color>>8)&0x0000FF); + break; + case WS2811_GRB: + color = ((color<<8)&0xFF0000) | ((color>>8)&0x00FF00) | (color&0x0000FF); + break; + case WS2811_GBR: + color = ((color<<8)&0xFFFF00) | ((color>>16)&0x0000FF); + break; + case WS2811_BRG: + color = ((color<<16)&0xFF0000) | ((color>>8)&0x00FFFF); + break; + case WS2811_BGR: + color = ((color<<16)&0xFF0000) | (color&0x00FF00) | ((color>>16)&0x0000FF); + break; + default: + break; + } + return color; } + +#endif // supported boards From 97b337d8fe39c160106071d8f991038973fc7f3b Mon Sep 17 00:00:00 2001 From: fat-fred Date: Sun, 17 May 2020 10:35:13 +0200 Subject: [PATCH 5/8] Fix for Teensy 3.1/3.2 + more Doku --- TeensyStripController.ino | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/TeensyStripController.ino b/TeensyStripController.ino index 3c1f65b..8d26558 100644 --- a/TeensyStripController.ino +++ b/TeensyStripController.ino @@ -56,7 +56,9 @@ #define FirmwareVersionMajor 1 #define FirmwareVersionMinor 3 +//For Teensy 4.0 you can define the nuber of ouput Pins - 8 should be good (for 3.1/3.2 this is only used for calculation and schould not changed) const int numPins = 8; +//for tesnsy 4.0 you can change the standard port (schould not be done) - Not used for 3.1/3.2 byte pinList[numPins] = {2, 14, 7, 8, 6, 20, 21, 5}; //Defines the max number of leds which is allowed per ledstrip. @@ -82,7 +84,11 @@ elapsedMillis BlinkModeTimeoutTimer; //Config definition for the OctoWS2811 lib const int config = WS2811_RGB | WS2811_800kHz; //Dont change the color order (even if your strip are GRB). DOF takes care of this issue (see config of ledstrip toy) -OctoWS2811Ext leds(MaxLedsPerStrip, displayMemory, drawingMemory, config, numPins, pinList); +#if defined(__IMXRT1062__) + OctoWS2811Ext leds(MaxLedsPerStrip, displayMemory, drawingMemory, config, numPins, pinList); +#else + OctoWS2811Ext leds(MaxLedsPerStrip, displayMemory, drawingMemory, config); +#endif word configuredStripLength=144; From dbb945961a5cacc955e09f31bf7b68a674f666d8 Mon Sep 17 00:00:00 2001 From: fat-fred Date: Sun, 17 May 2020 12:41:19 +0200 Subject: [PATCH 6/8] Update OctoWS2811Ext_imxrt.cpp --- OctoWS2811Ext_imxrt.cpp | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/OctoWS2811Ext_imxrt.cpp b/OctoWS2811Ext_imxrt.cpp index 8bdd4a4..646a264 100644 --- a/OctoWS2811Ext_imxrt.cpp +++ b/OctoWS2811Ext_imxrt.cpp @@ -373,18 +373,11 @@ int OctoWS2811Ext::busy(void) return 0; } -// For Teensy 4.x, the pixel data is stored in ordinary RGB format. Translation -// from 24 bit color to GPIO bitmasks is done on-the-fly by fillbits(). This is -// different from Teensy 3.x, where the data was stored as bytes to write directly -// to the GPIO output register. - - void OctoWS2811Ext::setStripLength(uint16_t length) { - stripLen=length; + stripLen=length; } - // For Teensy 4.x, the pixel data is stored in ordinary RGB format. Translation // from 24 bit color to GPIO bitmasks is done on-the-fly by fillbits(). This is // different from Teensy 3.x, where the data was stored as bytes to write directly From e204270d2cd7f111fd25faecbd70560d39596420 Mon Sep 17 00:00:00 2001 From: Fat-Fred Date: Mon, 18 May 2020 13:28:24 +0200 Subject: [PATCH 7/8] revert Timing to 50ms --- OctoWS2811Ext.cpp | 4 ++-- OctoWS2811Ext_imxrt.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/OctoWS2811Ext.cpp b/OctoWS2811Ext.cpp index 8a7f2eb..b83b577 100644 --- a/OctoWS2811Ext.cpp +++ b/OctoWS2811Ext.cpp @@ -247,7 +247,7 @@ int OctoWS2811Ext::busy(void) { if (update_in_progress) return 1; // busy for 50 (or 300 for ws2813) us after the done interrupt, for WS2811 reset - if (micros() - update_completed_at < 300) return 1; + if (micros() - update_completed_at < 50) return 1; return 0; } @@ -265,7 +265,7 @@ void OctoWS2811Ext::show(void) memcpy(frameBuffer, drawBuffer, stripLen * 24); } // wait for WS2811 reset - while (micros() - update_completed_at < 300) ; + while (micros() - update_completed_at < 50) ; // ok to start, but we must be very careful to begin // without any prior 3 x 800kHz DMA requests pending diff --git a/OctoWS2811Ext_imxrt.cpp b/OctoWS2811Ext_imxrt.cpp index 646a264..27eda34 100644 --- a/OctoWS2811Ext_imxrt.cpp +++ b/OctoWS2811Ext_imxrt.cpp @@ -319,7 +319,7 @@ void OctoWS2811Ext::show(void) TMR4_CNTR2 = comp1load[0] + 1; // wait for WS2812 reset - while (micros() - update_begin_micros < numbytes * 10 + 300) ; + while (micros() - update_begin_micros < numbytes * 10 + 50) ; // start everything running! TMR4_ENBL = enable | 7; @@ -369,7 +369,7 @@ void OctoWS2811Ext::isr(void) int OctoWS2811Ext::busy(void) { if (!dma3.complete()) ; // DMA still running - if (micros() - update_begin_micros < numbytes * 10 + 300) return 1; // WS2812 reset + if (micros() - update_begin_micros < numbytes * 10 + 50) return 1; // WS2812 reset return 0; } From 73c4f13bddf9a0f099ddc488a3823927d2c68683 Mon Sep 17 00:00:00 2001 From: Fat-Fred Date: Mon, 18 May 2020 13:32:00 +0200 Subject: [PATCH 8/8] changed Version to 1.04 --- README.md | 4 ++-- TeensyStripController.ino | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 42b6e76..06932fa 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ -TeensyStripController 1.03 +TeensyStripController 1.04 ========================== -Firmware for a Teensy 3.1 or Teensy 3.2 to control WS2811/WS2812 based ledstrips. Fully compatible with the DirectOutput Framework. +Firmware for a Teensy 3.1/3.2 or Teensy 4.0 to control WS2811/WS2812 based ledstrips. Fully compatible with the DirectOutput Framework. ![Teensy 3.1 with OctoWS2811 adaptor](http://www.pjrc.com/store/octo28_adaptor_6.jpg) diff --git a/TeensyStripController.ino b/TeensyStripController.ino index 8d26558..975a2d1 100644 --- a/TeensyStripController.ino +++ b/TeensyStripController.ino @@ -54,7 +54,7 @@ //Definiton of Major and Minor part of the firmware version. This value can be received using the V command. //If something is changed in the code the number should be increased. #define FirmwareVersionMajor 1 -#define FirmwareVersionMinor 3 +#define FirmwareVersionMinor 4 //For Teensy 4.0 you can define the nuber of ouput Pins - 8 should be good (for 3.1/3.2 this is only used for calculation and schould not changed) const int numPins = 8;