mirror of
https://github.com/skogaby/OpeNITHM.git
synced 2026-09-22 22:58:16 +03:00
321 lines
9.5 KiB
C++
321 lines
9.5 KiB
C++
/* WS2812Serial - Non-blocking WS2812 LED Display Library
|
|
https://github.com/PaulStoffregen/WS2812Serial
|
|
Copyright (c) 2017 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.
|
|
*/
|
|
|
|
#include "WS2812Serial.h"
|
|
|
|
bool WS2812Serial::begin()
|
|
{
|
|
#if defined(__IMXRT1062__) // Teensy 3.x
|
|
uint32_t hwtrigger;
|
|
#else
|
|
uint32_t divisor, portconfig, hwtrigger;
|
|
KINETISK_UART_t *uart;
|
|
#endif
|
|
switch (pin) {
|
|
#if defined(KINETISK) // Teensy 3.x
|
|
case 1: // Serial1
|
|
case 5:
|
|
#if defined(__MK64FX512__) || defined(__MK66FX1M0__)
|
|
case 26:
|
|
#endif
|
|
uart = &KINETISK_UART0;
|
|
divisor = BAUD2DIV(4000000);
|
|
portconfig = PORT_PCR_DSE | PORT_PCR_SRE | PORT_PCR_MUX(3);
|
|
hwtrigger = DMAMUX_SOURCE_UART0_TX;
|
|
SIM_SCGC4 |= SIM_SCGC4_UART0;
|
|
break;
|
|
|
|
case 10: // Serial2
|
|
#if defined(__MK20DX128__) || defined(__MK20DX256__)
|
|
case 31:
|
|
#elif defined(__MK64FX512__) || defined(__MK66FX1M0__)
|
|
case 58:
|
|
#endif
|
|
uart = &KINETISK_UART1;
|
|
divisor = BAUD2DIV2(4000000);
|
|
portconfig = PORT_PCR_DSE | PORT_PCR_SRE | PORT_PCR_MUX(3);
|
|
hwtrigger = DMAMUX_SOURCE_UART1_TX;
|
|
SIM_SCGC4 |= SIM_SCGC4_UART1;
|
|
break;
|
|
|
|
case 8: // Serial3
|
|
uart = &KINETISK_UART2;
|
|
divisor = BAUD2DIV3(4000000);
|
|
portconfig = PORT_PCR_DSE | PORT_PCR_SRE | PORT_PCR_MUX(3);
|
|
hwtrigger = DMAMUX_SOURCE_UART2_TX;
|
|
SIM_SCGC4 |= SIM_SCGC4_UART2;
|
|
break;
|
|
|
|
#if defined(__MK64FX512__) || defined(__MK66FX1M0__)
|
|
case 32: // Serial4
|
|
case 62:
|
|
uart = &KINETISK_UART3;
|
|
divisor = BAUD2DIV3(4000000);
|
|
portconfig = PORT_PCR_DSE | PORT_PCR_SRE | PORT_PCR_MUX(3);
|
|
hwtrigger = DMAMUX_SOURCE_UART3_TX;
|
|
SIM_SCGC4 |= SIM_SCGC4_UART3;
|
|
break;
|
|
|
|
case 33: // Serial5
|
|
uart = &KINETISK_UART4;
|
|
divisor = BAUD2DIV3(4000000);
|
|
portconfig = PORT_PCR_DSE | PORT_PCR_SRE | PORT_PCR_MUX(3);
|
|
hwtrigger = DMAMUX_SOURCE_UART4_RXTX;
|
|
SIM_SCGC1 |= SIM_SCGC1_UART4;
|
|
break;
|
|
#endif
|
|
#if defined(__MK64FX512__)
|
|
case 48: // Serial6
|
|
uart = &KINETISK_UART5;
|
|
divisor = BAUD2DIV3(4000000);
|
|
portconfig = PORT_PCR_DSE | PORT_PCR_SRE | PORT_PCR_MUX(3);
|
|
hwtrigger = DMAMUX_SOURCE_UART5_RXTX;
|
|
SIM_SCGC1 |= SIM_SCGC1_UART5;
|
|
break;
|
|
#endif
|
|
|
|
#elif defined(KINETISL) // Teensy LC
|
|
case 1: // Serial1
|
|
case 5:
|
|
// NOT SURE HOW THIS WORKS ON LC?????
|
|
uart = &KINETISK_UART0;
|
|
divisor = 1;
|
|
portconfig = PORT_PCR_DSE | PORT_PCR_SRE | PORT_PCR_MUX(3);
|
|
hwtrigger = DMAMUX_SOURCE_UART0_TX;
|
|
SIM_SCGC4 |= SIM_SCGC4_UART0;
|
|
break;
|
|
case 4:
|
|
uart = &KINETISK_UART0;
|
|
divisor = 1;
|
|
portconfig = PORT_PCR_DSE | PORT_PCR_SRE | PORT_PCR_MUX(2);
|
|
hwtrigger = DMAMUX_SOURCE_UART0_TX;
|
|
SIM_SCGC4 |= SIM_SCGC4_UART0;
|
|
break;
|
|
case 24:
|
|
uart = &KINETISK_UART0;
|
|
divisor = 1;
|
|
portconfig = PORT_PCR_DSE | PORT_PCR_SRE | PORT_PCR_MUX(4);
|
|
hwtrigger = DMAMUX_SOURCE_UART0_TX;
|
|
SIM_SCGC4 |= SIM_SCGC4_UART0;
|
|
break;
|
|
|
|
#elif defined(__IMXRT1062__)
|
|
case 1: // Serial1
|
|
uart = &IMXRT_LPUART6;
|
|
CCM_CCGR3 |= CCM_CCGR3_LPUART6(CCM_CCGR_ON);
|
|
hwtrigger = DMAMUX_SOURCE_LPUART6_TX;
|
|
break;
|
|
case 8: // Serial2
|
|
uart = &IMXRT_LPUART4;
|
|
CCM_CCGR1 |= CCM_CCGR1_LPUART4(CCM_CCGR_ON);
|
|
hwtrigger = DMAMUX_SOURCE_LPUART4_TX;
|
|
break;
|
|
case 14: // Serial3
|
|
uart = &IMXRT_LPUART2;
|
|
CCM_CCGR0 |= CCM_CCGR0_LPUART2(CCM_CCGR_ON);
|
|
hwtrigger = DMAMUX_SOURCE_LPUART2_TX;
|
|
break;
|
|
case 17: // Serial4
|
|
uart = &IMXRT_LPUART3;
|
|
CCM_CCGR0 |= CCM_CCGR0_LPUART3(CCM_CCGR_ON);
|
|
hwtrigger = DMAMUX_SOURCE_LPUART3_TX;
|
|
break;
|
|
case 20: // Serial5
|
|
case 39: // Serial5 alt
|
|
uart = &IMXRT_LPUART8;
|
|
CCM_CCGR6 |= CCM_CCGR6_LPUART8(CCM_CCGR_ON);
|
|
hwtrigger = DMAMUX_SOURCE_LPUART8_TX;
|
|
break;
|
|
case 24: // Serial6
|
|
uart = &IMXRT_LPUART1;
|
|
CCM_CCGR5 |= CCM_CCGR5_LPUART1(CCM_CCGR_ON);
|
|
hwtrigger = DMAMUX_SOURCE_LPUART1_TX;
|
|
break;
|
|
case 29: // Serial7
|
|
uart = &IMXRT_LPUART7;
|
|
CCM_CCGR5 |= CCM_CCGR5_LPUART7(CCM_CCGR_ON);
|
|
hwtrigger = DMAMUX_SOURCE_LPUART7_TX;
|
|
break;
|
|
#endif
|
|
default:
|
|
return false; // pin not supported
|
|
}
|
|
if (!dma) {
|
|
dma = new DMAChannel;
|
|
if (!dma) return false; // unable to allocate DMA channel
|
|
}
|
|
#if defined(__IMXRT1062__)
|
|
// Convert Baud
|
|
// Computed values for 4mhz
|
|
uart->CTRL = 0; // clear everything
|
|
uart->BAUD = LPUART_BAUD_OSR(5) | LPUART_BAUD_SBR(1) | LPUART_BAUD_TDMAE; // set baud configure for transfer DMA
|
|
uart->PINCFG = 0;
|
|
uint16_t tx_fifo_size = (((uart->FIFO >> 4) & 0x7) << 2);
|
|
uint8_t tx_water = (tx_fifo_size < 16) ? tx_fifo_size >> 1 : 7;
|
|
// uart->WATER = LPUART_WATER_TXWATER(1); // guessing here?
|
|
// uart->FIFO = 0; // disable the fifo.
|
|
uart->WATER = LPUART_WATER_TXWATER(tx_water);
|
|
uart->FIFO |= LPUART_FIFO_TXFE;
|
|
|
|
uart->CTRL = (LPUART_CTRL_TE /*| LPUART_CTRL_TIE */ | LPUART_CTRL_TXINV); // enable transmitter and invert
|
|
// We need to configure the TX pin now.
|
|
*(portControlRegister(pin)) = IOMUXC_PAD_SRE | IOMUXC_PAD_DSE(3) | IOMUXC_PAD_SPEED(3);
|
|
*(portConfigRegister(pin)) = 2; // from hardware table for each one, but I think they are all 2...
|
|
|
|
dma->destination((volatile uint8_t&)uart->DATA);
|
|
//Serial.printf("HWTrigger: %x\n", hwtrigger);
|
|
#else
|
|
#if defined(KINETISK)
|
|
if (divisor < 32) divisor = 32;
|
|
uart->BDH = (divisor >> 13) & 0x1F;
|
|
uart->BDL = (divisor >> 5) & 0xFF;
|
|
uart->C4 = divisor & 0x1F;
|
|
#elif defined(KINETISL)
|
|
uart->BDH = (divisor >> 8) & 0x1F;
|
|
uart->BDL = divisor & 0xFF;
|
|
uart->C4 = 11;
|
|
#endif
|
|
uart->C1 = 0;
|
|
uart->C2 = UART_C2_TE | UART_C2_TIE;
|
|
uart->C3 = UART_C3_TXINV;
|
|
uart->C5 = UART_C5_TDMAS;
|
|
#if defined(KINETISK)
|
|
uart->PFIFO = 0; // TODO: is this ok for Serial3-6?
|
|
#endif
|
|
*(portConfigRegister(pin)) = portconfig;
|
|
dma->destination(uart->D);
|
|
#endif
|
|
|
|
dma->triggerAtHardwareEvent(hwtrigger);
|
|
memset(drawBuffer, 0, numled * 3);
|
|
return true;
|
|
}
|
|
|
|
void WS2812Serial::busy()
|
|
{
|
|
#if defined(KINETISK)
|
|
while ((DMA_ERQ & (1 << dma->channel))) {
|
|
yield();
|
|
}
|
|
#elif defined(KINETISL)
|
|
while ((dma->CFG->DCR & DMA_DCR_ERQ)) {
|
|
yield();
|
|
}
|
|
#elif defined(__IMXRT1062__)
|
|
//Serial.println("Show called");
|
|
while ((DMA_ERQ & (1 << dma->channel))) {
|
|
yield();
|
|
}
|
|
//Serial.println("After Yield");
|
|
#endif
|
|
}
|
|
|
|
void WS2812Serial::show()
|
|
{
|
|
// wait if prior DMA still in progress
|
|
#if defined(KINETISK)
|
|
while ((DMA_ERQ & (1 << dma->channel))) {
|
|
yield();
|
|
}
|
|
#elif defined(KINETISL)
|
|
while ((dma->CFG->DCR & DMA_DCR_ERQ)) {
|
|
yield();
|
|
}
|
|
#elif defined(__IMXRT1062__)
|
|
//Serial.println("Show called");
|
|
while ((DMA_ERQ & (1 << dma->channel))) {
|
|
yield();
|
|
}
|
|
//Serial.println("After Yield");
|
|
#endif
|
|
// copy drawing buffer to frame buffer
|
|
const uint8_t *p = drawBuffer;
|
|
const uint8_t *end = p + (numled * 3);
|
|
uint8_t *fb = frameBuffer;
|
|
while (p < end) {
|
|
uint8_t b = *p++;
|
|
uint8_t g = *p++;
|
|
uint8_t r = *p++;
|
|
uint32_t n=0;
|
|
switch (config) {
|
|
case WS2812_RGB: n = (r << 16) | (g << 8) | b; break;
|
|
case WS2812_RBG: n = (r << 16) | (b << 8) | g; break;
|
|
case WS2812_GRB: n = (g << 16) | (r << 8) | b; break;
|
|
case WS2812_GBR: n = (g << 16) | (b << 8) | r; break;
|
|
case WS2812_BRG: n = (b << 16) | (r << 8) | g; break;
|
|
case WS2812_BGR: n = (b << 16) | (g << 8) | r; break;
|
|
}
|
|
const uint8_t *stop = fb + 12;
|
|
do {
|
|
uint8_t x = 0x08;
|
|
if (!(n & 0x00800000)) x |= 0x07;
|
|
if (!(n & 0x00400000)) x |= 0xE0;
|
|
n <<= 2;
|
|
*fb++ = x;
|
|
} while (fb < stop);
|
|
}
|
|
// wait 300us WS2812 reset time
|
|
uint32_t min_elapsed = (numled * 30) + 300;
|
|
if (min_elapsed < 2500) min_elapsed = 2500;
|
|
uint32_t m;
|
|
while (1) {
|
|
m = micros();
|
|
if ((m - prior_micros) > min_elapsed) break;
|
|
yield();
|
|
}
|
|
prior_micros = m;
|
|
// start DMA transfer to update LEDs :-)
|
|
#if defined(KINETISK)
|
|
dma->sourceBuffer(frameBuffer, numled * 12);
|
|
dma->transferSize(1);
|
|
dma->transferCount(numled * 12);
|
|
dma->disableOnCompletion();
|
|
dma->enable();
|
|
#elif defined(KINETISL)
|
|
dma->CFG->SAR = frameBuffer;
|
|
dma->CFG->DSR_BCR = 0x01000000;
|
|
dma->CFG->DSR_BCR = numled * 12;
|
|
dma->CFG->DCR = DMA_DCR_ERQ | DMA_DCR_CS | DMA_DCR_SSIZE(1) |
|
|
DMA_DCR_SINC | DMA_DCR_DSIZE(1) | DMA_DCR_D_REQ;
|
|
#elif defined(__IMXRT1062__)
|
|
// See if we need to muck with DMA cache...
|
|
if ((uint32_t)frameBuffer >= 0x20200000u) arm_dcache_flush(frameBuffer, numled * 12);
|
|
|
|
dma->sourceBuffer(frameBuffer, numled * 12);
|
|
// dma->transferSize(1);
|
|
dma->transferCount(numled * 12);
|
|
dma->disableOnCompletion();
|
|
|
|
/* Serial.printf("%x %x:", (uint32_t)dma, (uint32_t)dma->TCD);
|
|
|
|
Serial.printf("SA:%x SO:%d AT:%x NB:%x SL:%d DA:%x DO: %d CI:%x DL:%x CS:%x BI:%x\n", (uint32_t)dma->TCD->SADDR,
|
|
dma->TCD->SOFF, dma->TCD->ATTR, dma->TCD->NBYTES, dma->TCD->SLAST, (uint32_t)dma->TCD->DADDR,
|
|
dma->TCD->DOFF, dma->TCD->CITER, dma->TCD->DLASTSGA, dma->TCD->CSR, dma->TCD->BITER);
|
|
*/
|
|
uart->STAT = 0; // try clearing out the status
|
|
dma->enable();
|
|
|
|
#endif
|
|
}
|
|
|