32#if defined(FL_IS_ARM_LPC_845) && FASTLED_LPC_UART_DMA
34#if defined(FL_LPC_UART_DMA_CLOCKLESS_TEST)
44#include "platforms/arm/lpc/uart_arm_lpc_dma.h"
49#if !defined(FL_LPC_UART_DMA_CLOCKLESS_TEST)
55#if !defined(FASTLED_LPC_UART_DMA_HARNESS_TX_PIN)
56#define FASTLED_LPC_UART_DMA_HARNESS_TX_PIN 9
58#if !defined(FASTLED_LPC_UART_DMA_HARNESS_BAUD)
59#define FASTLED_LPC_UART_DMA_HARNESS_BAUD 1000000
62using HarnessDriver = fl::ARMHardwareUARTOutputDMA<
63 static_cast<fl::u8>(FASTLED_LPC_UART_DMA_HARNESS_TX_PIN),
64 static_cast<fl::u32
>(FASTLED_LPC_UART_DMA_HARNESS_BAUD)>;
69#if !defined(FASTLED_LPC_UART_DMA_HARNESS_BYTES)
70#define FASTLED_LPC_UART_DMA_HARNESS_BYTES 2048
73struct UartDmaHarnessState {
74 fl::u8 buffer[FASTLED_LPC_UART_DMA_HARNESS_BYTES];
78inline UartDmaHarnessState& uartDmaHarnessState()
FL_NO_EXCEPT {
83 return uartDmaHarnessState().buffer;
87 UartDmaHarnessState&
state = uartDmaHarnessState();
94 const int cap =
static_cast<int>(FASTLED_LPC_UART_DMA_HARNESS_BYTES);
95 return (n > cap) ? cap : n;
102inline bool spinUntilStreamDone(fl::sstream& diag)
FL_NO_EXCEPT {
104 while (!HarnessDriver::streamDone()) {
105 if (++spins > 4000000u) {
107 << DMA0->COMMON[0].ACTIVE <<
','
108 << DMA0->CHANNEL[3].XFERCFG <<
','
109 << DMA0->CHANNEL[3].CTLSTAT <<
','
110 << USART1->STAT <<
','
111 << DMA0->COMMON[0].ERRINT;
119inline fl::string streamOnceHandler(
int byte_count,
int byte_pattern)
FL_NO_EXCEPT {
120 const int len = clampByteCount(byte_count);
121 if (len == 0)
return fl::string(
"0,0,0");
122 fl::u8* buf = harnessBuffer();
123 const fl::u8 pat =
static_cast<fl::u8>(byte_pattern & 0xFF);
124 for (
int i = 0; i < len; ++i) buf[i] = pat;
126 HarnessDriver& drv = harnessDriver();
129 const fl::u32 t_start =
micros();
130 if (!HarnessDriver::kickDmaStreamAsync(buf,
static_cast<fl::u32
>(len))) {
131 return fl::string(
"0,kick_refused,0");
134 if (!spinUntilStreamDone(diag))
return diag.
str();
135 const fl::u32 total_us =
micros() - t_start;
138 s << 1 <<
',' << total_us <<
',' << len;
143inline fl::string streamOverlapHandler(
int byte_count,
int byte_pattern)
FL_NO_EXCEPT {
144 const int len = clampByteCount(byte_count);
145 if (len == 0)
return fl::string(
"0,0,0");
146 fl::u8* buf = harnessBuffer();
147 const fl::u8 pat =
static_cast<fl::u8>(byte_pattern & 0xFF);
148 for (
int i = 0; i < len; ++i) buf[i] = pat;
150 HarnessDriver& drv = harnessDriver();
153 const fl::u32 t_start =
micros();
154 if (!HarnessDriver::kickDmaStreamAsync(buf,
static_cast<fl::u32
>(len))) {
155 return fl::string(
"0,kick_refused,0");
161 volatile fl::u32 toggle_count = 0;
163 while (!HarnessDriver::streamDone()) {
165 if (++spins > 4000000u) {
167 d << 0 <<
',' << DMA0->COMMON[0].ACTIVE <<
','
168 << DMA0->CHANNEL[3].CTLSTAT <<
',' << USART1->STAT;
172 const fl::u32 total_us =
micros() - t_start;
175 s << 1 <<
',' << total_us <<
',' << toggle_count;
184inline int argInt(
const fl::json&
args, fl::size i,
int fallback)
FL_NO_EXCEPT {
185 return static_cast<int>(
args[i].as_int().value_or(fallback));
188#if defined(FL_LPC_UART_DMA_CLOCKLESS_TEST)
190#if !defined(FL_LPC_UART_DMA_CLOCKLESS_TX_PIN)
191#define FL_LPC_UART_DMA_CLOCKLESS_TX_PIN 10
193#if !defined(FL_LPC_UART_DMA_CLOCKLESS_LEDS)
194#define FL_LPC_UART_DMA_CLOCKLESS_LEDS 8
197constexpr fl::size kClocklessEncodedBytes =
198 FL_LPC_UART_DMA_CLOCKLESS_LEDS * 3u * 4u;
200struct UartDmaClocklessState {
202 fl::u8 expected[kClocklessEncodedBytes];
203 fl::u8 received[kClocklessEncodedBytes];
206 UartDmaClocklessState() : registered(false) {}
209inline UartDmaClocklessState& uartDmaClocklessState()
FL_NO_EXCEPT {
214 return uartDmaClocklessState().leds;
217inline bool ensureClocklessFastLedRegistered()
FL_NO_EXCEPT {
218 UartDmaClocklessState&
state = uartDmaClocklessState();
219 if (!
state.registered) {
221 static_cast<fl::u8>(FL_LPC_UART_DMA_CLOCKLESS_TX_PIN),
222 GRB>(clocklessLeds(), FL_LPC_UART_DMA_CLOCKLESS_LEDS);
223 state.registered =
true;
225 return state.registered;
229#if FASTLED_LPC_UART_DMA_INSTANCE == 2
237 return *
reinterpret_cast<volatile fl::u32*
>(0x50008000u +
offset);
241 USART_Type* u = clocklessUartBlock();
242 const fl::u32 osr = (u->OSR & 0xFu) + 1u;
243 const fl::u32 brg = (u->BRG & 0xFFFFu) + 1u;
244 return F_CPU / (osr * brg);
247inline void clocklessUartDrainRx(fl::u32& errors)
FL_NO_EXCEPT {
248 USART_Type* u = clocklessUartBlock();
249 constexpr fl::u32 kRxRdy = (1u << 0);
250 constexpr fl::u32 kOverrun = (1u << 8);
251 constexpr fl::u32 kFrame = (1u << 13);
252 constexpr fl::u32 kParity = (1u << 14);
253 while ((u->STAT & kRxRdy) != 0u) {
254 errors |= (u->RXDATSTAT & (kOverrun | kFrame | kParity));
256 u->STAT = kOverrun | kFrame | kParity;
259inline fl::u32 clocklessAbsDiff(fl::u32 a, fl::u32 b)
FL_NO_EXCEPT {
260 return a > b ? a - b : b - a;
264 if (pulse_ns == 0u) {
267 const fl::u8 lo =
static_cast<fl::u8>(timing_ns / pulse_ns);
269 const fl::u32 err_lo = clocklessAbsDiff(
static_cast<fl::u32
>(lo) * pulse_ns, timing_ns);
270 const fl::u32 err_hi = clocklessAbsDiff(
static_cast<fl::u32
>(hi) * pulse_ns, timing_ns);
271 return err_hi < err_lo ? hi : lo;
276 for (
int i = 0; i < 8; ++i) {
277 const int pulse = i + 1;
278 const bool wire_high =
279 (pulse < 5 && pulse < static_cast<int>(pulses_a)) ||
280 (pulse >= 5 && (pulse - 5) <
static_cast<int>(pulses_b));
282 value |=
static_cast<fl::u8>(1u << i);
289 const fl::ChipsetTimingConfig timing =
291 constexpr fl::u8 kPulsesPerBit = 5;
293 if (period_ns == 0u) {
297 const fl::u32 pulse_ns = period_ns / kPulsesPerBit;
298 const fl::u8 pulses0 = clocklessPulseCount(timing.
t1_ns, pulse_ns);
299 const fl::u8 pulses1 = clocklessPulseCount(timing.
t1_ns + timing.
t2_ns, pulse_ns);
300 if (pulses0 < 1u || pulses0 > 4u || pulses1 < 1u || pulses1 > 4u ||
301 pulses0 == pulses1) {
305 lut[0] = clocklessBuildUartByte(pulses0, pulses0);
306 lut[1] = clocklessBuildUartByte(pulses0, pulses1);
307 lut[2] = clocklessBuildUartByte(pulses1, pulses0);
308 lut[3] = clocklessBuildUartByte(pulses1, pulses1);
309 baud_out =
static_cast<fl::u32
>(
310 static_cast<fl::u64
>(kPulsesPerBit) * 1000000000ULL / period_ns);
314inline fl::size clocklessEncodeExpectedUartBytes(
315 int led_count, fl::u32 rgb,
fl::u8* out, fl::size out_cap,
317 fl::u8 lut[4] = {0, 0, 0, 0};
318 if (!clocklessBuildUartLut(lut, expected_baud)) {
322 const fl::size byte_count =
static_cast<fl::size
>(led_count) * 3u;
323 const fl::size encoded_count = byte_count * 4u;
324 if (encoded_count > out_cap) {
328 const fl::u8 r =
static_cast<fl::u8>((rgb >> 16) & 0xFF);
332 for (
int i = 0; i < led_count; ++i) {
333 const fl::u8 grb[3] = {
g, r, b};
335 out[o++] = lut[(
byte >> 6) & 0x03u];
336 out[o++] = lut[(
byte >> 4) & 0x03u];
337 out[o++] = lut[(
byte >> 2) & 0x03u];
338 out[o++] = lut[
byte & 0x03u];
341 return encoded_count;
346inline fl::string clocklessLoopbackSelfHandler(
347 int led_count, fl::u32 rgb,
int data_pin,
int rx_pin,
349 if (led_count != FL_LPC_UART_DMA_CLOCKLESS_LEDS ||
350 data_pin != FL_LPC_UART_DMA_CLOCKLESS_TX_PIN ||
351 rx_pin < 0 || rx_pin > 0x35 ||
352 !fl::lpc::LpcUartDmaRuntime::isMovableUartPin(
353 static_cast<fl::u8>(rx_pin)) ||
354 capture_ms <= 0 || capture_ms > 200) {
355 return fl::string(
"0,args");
358 ensureClocklessFastLedRegistered();
361 const fl::u8 r =
static_cast<fl::u8>((rgb >> 16) & 0xFF);
364 for (
int i = 0; i < led_count; ++i) {
368 UartDmaClocklessState&
state = uartDmaClocklessState();
371 fl::u32 expected_baud = 0;
372 const fl::size expected_count = clocklessEncodeExpectedUartBytes(
373 led_count, rgb, expected, kClocklessEncodedBytes, expected_baud);
374 if (expected_count == 0u) {
375 return fl::string(
"0,encode");
379 clocklessUartDrainRx(errors);
380 for (fl::size i = 0; i < expected_count; ++i) {
383 fl::lpc::LpcUartDmaRuntime::prepareLoopbackRxBuffer(
384 received,
static_cast<fl::u32
>(expected_count),
385 static_cast<fl::u8>(rx_pin));
389 USART_Type* u = clocklessUartBlock();
390 auto& driver = fl::BusTraits<fl::Bus::UART>::instance();
391 bool stream_done =
false;
392 const fl::u32 start =
millis();
393 while ((
millis() - start) <
static_cast<fl::u32
>(capture_ms)) {
394 const auto state = driver.poll();
397 fl::lpc::LpcUartDmaRuntime::loopbackRxReceivedCount() >= expected_count) {
402 stream_done = driver.waitForReady(1000);
403 const fl::size rx_count =
404 static_cast<fl::size
>(fl::lpc::LpcUartDmaRuntime::loopbackRxReceivedCount());
405 errors |= (u->STAT & ((1u << 8) | (1u << 13) | (1u << 14)));
406 constexpr fl::u32 kLoopbackRxDmaChannel =
407 (FASTLED_LPC_UART_DMA_INSTANCE == 2) ? 4u : 2u;
408 const fl::u32 rx_dma_base = 0x400u + 16u * kLoopbackRxDmaChannel;
409 const fl::u32 rx_dma_cfg = clocklessDmaReg(rx_dma_base + 0x0u);
410 const fl::u32 rx_dma_ctlstat = clocklessDmaReg(rx_dma_base + 0x4u);
411 const fl::u32 rx_dma_xfercfg = clocklessDmaReg(rx_dma_base + 0x8u);
412 const fl::u32 rx_dma_active = clocklessDmaReg(0x030u);
413 const fl::u32 rx_dma_busy = clocklessDmaReg(0x038u);
414 const fl::u32 rx_dma_err = clocklessDmaReg(0x040u);
415 const fl::u32 rx_requested_len = g_fastled_lpc_uart_loopback_rx_len;
416 fl::lpc::LpcUartDmaRuntime::clearLoopbackRxBuffer();
418 fl::size mismatch = expected_count;
419 const fl::size compare_count =
420 rx_count < expected_count ? rx_count : expected_count;
421 for (fl::size i = 0; i < compare_count; ++i) {
422 if (received[i] != expected[i]) {
428 if (rx_count != expected_count || mismatch != expected_count || errors != 0u) {
430 d << 0 <<
",loopback," <<
static_cast<fl::u32
>(rx_count) <<
','
431 <<
static_cast<fl::u32
>(expected_count) <<
','
432 << (stream_done ? 1 : 0) <<
',' << u->STAT <<
',' << u->CFG <<
','
433 << errors <<
',' << expected_baud <<
',' << clocklessUartActualBaud()
434 <<
',' << rx_dma_cfg <<
',' << rx_dma_ctlstat <<
','
435 << rx_dma_xfercfg <<
',' << rx_dma_active <<
','
436 << rx_dma_busy <<
',' << rx_dma_err <<
','
437 << rx_requested_len <<
','
438 << g_fastled_lpc_uart_loopback_rx_arm_status <<
','
439 << g_fastled_lpc_uart_loopback_rx_arm_tx_len <<
','
440 << g_fastled_lpc_uart_loopback_rx_arm_xfer_readback <<
','
441 << g_fastled_lpc_uart_loopback_rx_arm_desc0_readback <<
',';
442 if (mismatch != expected_count) {
443 d << static_cast<fl::u32>(mismatch) <<
','
444 <<
static_cast<fl::u32
>(expected[mismatch]) <<
','
445 <<
static_cast<fl::u32
>(received[mismatch]);
447 d << static_cast<fl::u32>(expected_count) <<
",0,0";
452 const fl::u32 n_decoded =
static_cast<fl::u32
>(led_count * 3);
454 s << 1 <<
',' << n_decoded;
455 for (
int i = 0; i < led_count; ++i) {
456 s << ',' << static_cast<fl::u32>(g)
457 <<
',' <<
static_cast<fl::u32
>(r)
458 <<
',' <<
static_cast<fl::u32
>(b);
463inline fl::string clocklessProbeHandler(
465 if (data_pin != FL_LPC_UART_DMA_CLOCKLESS_TX_PIN ||
466 rx_pin < 0 || rx_pin > 0x35 ||
467 !fl::lpc::LpcUartDmaRuntime::isMovableUartPin(
468 static_cast<fl::u8>(rx_pin))) {
469 return fl::string(
"0,args");
472 return fl::string(
"1,entry");
475 s << 1 <<
",uart_rx_dma," << clocklessUartBlock()->CFG;
482#if !defined(FL_LPC_UART_DMA_CLOCKLESS_TEST)
483 remote.bind(
"uartDmaStreamOnce",
484 [](
const fl::json&
args) -> fl::string {
485 return streamOnceHandler(argInt(
args, 0, 0), argInt(
args, 1, 0));
487 remote.bind(
"uartDmaStreamOverlap",
488 [](
const fl::json&
args) -> fl::string {
489 return streamOverlapHandler(argInt(
args, 0, 0), argInt(
args, 1, 0));
492#if defined(FL_LPC_UART_DMA_CLOCKLESS_TEST)
493 remote.bind(
"uartDmaClocklessProbe",
494 [](
const fl::json&
args) -> fl::string {
495 return clocklessProbeHandler(
497 argInt(
args, 1, FL_LPC_UART_DMA_CLOCKLESS_TX_PIN),
498 argInt(
args, 2, 11));
500 remote.bind(
"uartDmaClocklessLoopbackSelf",
501 [](
const fl::json&
args) -> fl::string {
502 return clocklessLoopbackSelfHandler(
503 argInt(
args, 0, FL_LPC_UART_DMA_CLOCKLESS_LEDS),
504 static_cast<fl::u32
>(argInt(
args, 1, 0)),
505 argInt(
args, 2, FL_LPC_UART_DMA_CLOCKLESS_TX_PIN),
507 argInt(
args, 4, 30));
FL_DISABLE_WARNING_PUSH FL_DISABLE_WARNING_GLOBAL_CONSTRUCTORS CFastLED FastLED
Global LED strip management instance.
fl::unique_ptr< fl::Remote > remote
Portable compile-time identifiers for LED channel buses.
Per-driver traits keyed on fl::Bus.
Runtime chipset timing configuration for clockless LED drivers.
void show(fl::u8 scale)
Update all our controllers with the current led colors, using the passed in brightness.
void setBrightness(fl::u8 scale)
Set the global brightness scaling.
::CLEDController & addLeds(::CLEDController *pLed, CRGB *data, int nLedsOrOffset, int nLedsIfOffset=0)
Add a CLEDController instance to the world.
static T & instance() FL_NO_EXCEPT
string str() const FL_NO_EXCEPT
fl::UISlider offset("Offset", 0.0f, 0.0f, 1.0f, 0.01f)
constexpr ChipsetTimingConfig makeTimingConfig() FL_NO_EXCEPT
Convert compile-time CHIPSET type to runtime timing config.
fl::u32 millis()
Universal millisecond timer - returns milliseconds since system startup.
fl::u32 micros()
Universal microsecond timer - returns microseconds since system startup.
u32 t1_ns
T0H: High time for bit 0 (nanoseconds)
u32 t2_ns
T1H-T0H: Additional high time for bit 1 (nanoseconds)
constexpr u32 total_period_ns() const FL_NO_EXCEPT
Get total bit period (T1 + T2 + T3)
@ READY
Hardware idle; ready to accept new transmissions.
Compile-time timing extraction from ChipsetTiming structs.