| 294 | template <fl::u8 DATA_PIN, typename TIMING, EOrder RGB_ORDER = RGB, |
| 295 | int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 280> |
| 296 | class ClocklessController : public CPixelLEDController<RGB_ORDER> { |
| 297 | typedef typename FastPin<DATA_PIN>::port_ptr_t data_ptr_t; |
| 298 | typedef typename FastPin<DATA_PIN>::port_t data_t; |
| 299 | |
| 300 | data_t mPinMask; |
| 301 | data_ptr_t mPort; |
| 302 | CMinWait<WAIT_TIME> mWait; |
| 303 | bool mPluReady; |
| 304 | |
| 305 | public: |
| 306 | ClocklessController() FL_NOEXCEPT : mPinMask(0), mPort(0), mPluReady(false) {} |
| 307 | |
| 308 | virtual void init() FL_NOEXCEPT { |
| 309 | // The CPU still owns the data pin - the PLU samples it via SWM, but |
| 310 | // the pin itself is a normal GPIO that the CPU writes to. |
| 311 | FastPin<DATA_PIN>::setOutput(); |
| 312 | mPinMask = FastPin<DATA_PIN>::mask(); |
| 313 | mPort = FastPin<DATA_PIN>::port(); |
| 314 | |
| 315 | // Program the PLU LUT graph once. Idempotent. |
| 316 | lpc_plu_detail::initPluGraph(); |
| 317 | mPluReady = true; |
| 318 | } |
| 319 | |
| 320 | virtual fl::u16 getMaxRefreshRate() const { return 400; } |
| 321 | |
| 322 | virtual void showPixels(PixelController<RGB_ORDER>& pixels) FL_NOEXCEPT { |
| 323 | mWait.wait(); |
| 324 | |
| 325 | // The PLU drives the wire shape; the CPU's only job is to clock data |
| 326 | // bits out at the bit-rate cadence. We disable interrupts during the |
| 327 | // transfer because a long ISR could stretch a single bit past the |
| 328 | // WS2812 reset threshold (>50us) and end the frame prematurely. |
| 329 | cli(); |
| 330 | feedPixels(pixels); |
| 331 | sei(); |
| 332 | |
| 333 | mWait.mark(); |
| 334 | } |
| 335 | |
| 336 | private: |
| 337 | // Feed every bit of every pixel into the PLU's data-input GPIO. |
| 338 | // |
| 339 | // Per-bit cadence: |
| 340 | // 1. The PLU's timing counter is free-running on its own clock domain. |
| 341 | // 2. The CPU samples the next data bit, writes it to the data GPIO. |
| 342 | // 3. The PLU latches the resulting bit-window output on each counter |
| 343 | // rollover. We must guarantee the new DATA value is stable BEFORE |
| 344 | // the rollover edge that begins the bit. |
| 345 | // |
| 346 | // Synchronisation strategy: |
| 347 | // We do not poll the timing counter; we just push bits at slightly |
| 348 | // below the bit-rate. The PLU samples DATA at each rising edge of its |
| 349 | // internal bit-period clock; as long as we update DATA between |
| 350 | // rollovers, the LED stream is correct. At 15 MHz CPU and ~1.25us |
| 351 | // per WS2812 bit, that's ~18 cycles of slack per bit - plenty. |
| 352 | // |
| 353 | // TODO(2841): once the SCT-based timing source is integrated, expose a |
nothing calls this directly
no test coverage detected