Skip to content

I80Peripheral

Source: I80Peripheral.h

I80Peripheral

class I80Peripheral
src/light/drivers/I80Peripheral.h:24

Inherits: LedPeripheral

Output driver: parallel 8-or-16-lane WS2812B over the ESP-IDF esp_lcd i80 bus, the scale path on all three i80-capable ESP32 families.

RMT gives a chip 4-8 channels; this gives 8-16 lanes for the wall time of one. The shared body lives in ParallelLedDriver and the wire format in [ParallelSlots.h].

Prior art: Adafruit's LCD_CAM discovery, hpwit's I2SClockless lineage, FastLED's S3 driver. Architecture studied and never copied, on IDF's esp_lcd rather than the raw registers.

Public Attributes

int8_t clockPin = > 0 ? -1 : 10 : The i80 bus's write strobe; the pixel clock the peripheral mandates. See the driver page.

int8_t dcPin = > 0 ? 33 : 11 : The i80 bus's data/command select; unused by WS2812 but mandated by the peripheral.

Public Methods

virtual inline uint8_t lanesAvailable() const override : The number of i80 lanes this chip provides (0 = no i80 bus on this chip); the orchestrator's.

virtual inline bool powerOfTwoBus() const override : Whether the bus width must round to a power of two. On i80 it must.

virtual inline size_t dmaBudgetBytes() const override : The largest frame whole-frame DMA can carry, 0 meaning no bound.

virtual inline bool loopbackFullWidth() const override : Whether the loopback needs the full operational bus width. On i80 it does.

virtual inline LedHwBlock hwBlock() const override : The peripheral block this backend claims, for the sibling guard.

virtual inline bool busContentionCleared() const override : Whether the shared peripheral instance this bus needs is free right now.

virtual inline const char * initFailMsg() const override : What the status says when the bus will not come up.

virtual inline uint16_t clockPinForBus() const override : The GPIO spare lanes park on, overriding the interface default.

virtual inline void addBusControls(ControlList & controls) override : Bind the sacrificial WR (clockPin) and DC pins the i80 peripheral mandates.

virtual inline bool busControlTriggersBuild(const char * name) const override : Whether changing name needs the bus rebuilt: clockPin and dcPin do.

virtual inline const char * validateBusFatal() const override : Why the bus pins are fatally wrong, or null when they are usable.

virtual inline const char * validateBusPins(const uint16_t * lanes, uint8_t n) const override : Which data lane collides with WR or DC, as a warning, or null when the set is clear.

virtual inline bool supportsPinExpander() const override : Whether a 74HCT595 pin expander can ride this bus.

virtual inline bool busInit(size_t frameBytes, bool wantSecondBuffer) override : Create the i80 bus and its DMA buffers for frameBytes on the current lanes.

virtual inline uint8_t * busBuffer(uint8_t i) override : DMA buffer i (0 or 1) the orchestrator encodes into.

virtual inline size_t busCapacity() const override : The per-buffer byte capacity (fixed at bus creation; both buffers equal).

virtual inline bool busTransmit(uint8_t i, size_t bytes) override : Start the autonomous transfer of the first bytes of DMA buffer i, returning whether it began.

virtual inline bool busWait(uint8_t i, uint32_t ms) override : Block up to ms for buffer i's in-flight transfer to complete.

virtual inline uint32_t busLastTransmitUs() const override : The most recent DMA transfer's wire time (µs): the WS2812 output floor.

virtual inline void busDeinit() override : Tear down the i80 bus and its DMA buffer.

virtual inline platform::RmtLoopbackResult busLoopback(const uint8_t * frame, size_t frameBytes, size_t dataBytes, uint8_t rowBits) override : Run the loopback self-test on a full-width private bus.

virtual inline void recordBusPins() override : Store WR and DC alongside the data pins, so editing either rebuilds the bus.

virtual inline bool extraBusPinsCurrent() const override : Whether the live bus's WR and DC pins still match the controls, so a rebuild can be skipped.

More info

One API, two peripherals. ESP-IDF exposes one public i80 API and routes it to whatever the silicon has: LCD_CAM on the S3, P4 and S31, and the I2S peripheral on the classic, its only route past 8 lanes. They are mutually exclusive per chip, so lanesAvailable() reads whichever lane count is non-zero. Named for the bus, since two peripherals serve it.

The 3-slot-per-bit wire contract. Each WS2812 bit becomes three bus slots at 2.67 MHz, a 375 ns slot: every active lane HIGH, the data bits, then all LOW. A 1 is HIGH 750 ns and a 0 375 ns, approximating RMT's 700/350 rather than the lineage's ~416 ns. That stays under the 380 ns T0H max newer WS2812B revisions spec.

Both silicon paths do whole-frame chained DMA. So the classic I2S path is WiFi-underrun-immune by construction, and needs none of the ISR-refilled ring the lineage requires.