| 542 | } |
| 543 | |
| 544 | RxWaitResult LpcSctRxChannel::wait(u32 timeout_ms) FL_NOEXCEPT { |
| 545 | #if defined(FASTLED_LPC_RX_SCT_DMA) && defined(FL_IS_ARM_LPC_845) |
| 546 | // DMA capture path. The two DMA channels are autonomously latching |
| 547 | // SCT->CAP[0..1] into the rings via the SCT_DMAn trigger lines — |
| 548 | // CPU just sleeps for the capture window, then halts the SCT and |
| 549 | // merges the two rings by timestamp into mEdges. |
| 550 | // |
| 551 | // ---- 1. Sleep for the capture window. ---- |
| 552 | const fl::u32 start_ms = millis(); |
| 553 | while ((millis() - start_ms) < timeout_ms) { |
| 554 | const fl::u32 rising_done = dmaChannelEdgeCount(0); |
| 555 | const fl::u32 falling_done = dmaChannelEdgeCount(1); |
| 556 | if (rising_done >= kDmaRingSize && falling_done >= kDmaRingSize) { |
| 557 | break; // both rings full — no point waiting longer |
| 558 | } |
| 559 | // No need to spin tight; the DMA work happens in hardware. |
| 560 | // A small idle keeps wakelet contention down. (millis() based |
| 561 | // throttle — we don't need precise pacing here.) |
| 562 | } |
| 563 | |
| 564 | // ---- 2. Halt SCT to stop further captures. ---- |
| 565 | sct(kOffCTRL) |= kCtrlHaltL; |
| 566 | |
| 567 | // ---- 3. Snapshot how many entries each ring received. ---- |
| 568 | const fl::u32 n_rising = dmaChannelEdgeCount(0); |
| 569 | const fl::u32 n_falling = dmaChannelEdgeCount(1); |
| 570 | |
| 571 | // Disable channels so SETVALID can re-arm in the next begin(). |
| 572 | dma(kOffDmaEnableClr0) = (1u << 0) | (1u << 1); |
| 573 | |
| 574 | // ---- 4. Merge the two rings by timestamp into mEdges. ---- |
| 575 | // The rings hold raw 32-bit SCT counter ticks. Walk both in parallel |
| 576 | // (they're already monotonic per-direction), pick the earlier tick, |
| 577 | // emit `EdgeTime{level, ns-delta-from-prior}`. |
| 578 | fl::u32 ir = 0, jf = 0; |
| 579 | fl::u32 prev_tick = 0; |
| 580 | bool prev_seen = false; |
| 581 | bool last_was_rising = false; |
| 582 | while (ir < n_rising || jf < n_falling) { |
| 583 | if (mEdges.size() >= mCapacity) break; |
| 584 | |
| 585 | bool pick_rising; |
| 586 | if (ir >= n_rising) { |
| 587 | pick_rising = false; |
| 588 | } else if (jf >= n_falling) { |
| 589 | pick_rising = true; |
| 590 | } else { |
| 591 | // Both rings have unread entries; pick the earlier tick. |
| 592 | // Unsigned subtract treats wrap as modulo — assume neither |
| 593 | // ring's outstanding entries span a full 32-bit wrap (~143 s |
| 594 | // at 30 MHz, way beyond our test windows). |
| 595 | pick_rising = (g_dma_rising[ir] - g_dma_falling[jf]) >= 0x80000000u; |
| 596 | } |
| 597 | |
| 598 | const fl::u32 tick = pick_rising ? g_dma_rising[ir] : g_dma_falling[jf]; |
| 599 | if (prev_seen) { |
| 600 | const fl::u32 delta = tick - prev_tick; |
| 601 | mEdges.push_back(EdgeTime(last_was_rising, ticksToNs(delta))); |
no test coverage detected