LEDS interface I2S setup/async mode
Fixes https://github.com/qmsk/esp/issues/86
New leds -> interface_setup = true mode to replace the leds_tx() -> i2s_out_setup() with a one-time i2s_out_setup(). This only works with one leds per interface, and does not work if sharing interfaces between multiple leds instances. This is currently only supported for I2S interfaces, and has no effect for other interfaces.
This allows a new async mode of operation where leds_interface_i2s_tx() now calls i2s_out_write_*() -> i2s_out_start() instead of i2s_out_flush(), which fills the I2S DMA buffer and starts the DMA/TX as previously, but does not wait for it to complete. The next loop -> tx can then start filling up the DMA buffer as DMA descriptors are freed up by the EOF interrupt, and waits for the previous TX to complete before starting the new TX using the recycled DMA buffer/descriptors.
This significantly increases the efficiency of the LEDS I2S interface, and it can now reach framerates close to the theoretical maximum (800kbps / (24-bits-per-pixel × pixel-count)).
54.6 FPS with TEST_MODE_CHASE and 600 serial LEDs - that's 98.3% of the theoretical maxium (800000 / 24 / 600 = 55.55555555555556).
> leds stats reset
spi : open 0 count @ 0.000s = 0.0/s avg, 0.000s total, 0.0ms avg, 0.0% util
spi : tx 0 count @ 0.000s = 0.0/s avg, 0.000s total, 0.0ms avg, 0.0% util
uart : open 0 count @ 0.000s = 0.0/s avg, 0.000s total, 0.0ms avg, 0.0% util
uart : tx 0 count @ 0.000s = 0.0/s avg, 0.000s total, 0.0ms avg, 0.0% util
i2s0 : open 0 count @ 0.000s = 0.0/s avg, 0.000s total, 0.0ms avg, 0.0% util
i2s0 : write 76 count @ 1.390s = 54.7/s avg, 1.391s total, 18.3ms avg, 100.0% util
i2s0 : start 2476 count @ 48.338s = 51.2/s avg, 0.192s total, 0.1ms avg, 0.4% util
i2s0 : flush 0 count @ 0.000s = 0.0/s avg, 0.000s total, 0.0ms avg, 0.0% util
i2s1 : open 0 count @ 0.000s = 0.0/s avg, 0.000s total, 0.0ms avg, 0.0% util
i2s1 : write 0 count @ 0.000s = 0.0/s avg, 0.000s total, 0.0ms avg, 0.0% util
i2s1 : start 0 count @ 0.000s = 0.0/s avg, 0.000s total, 0.0ms avg, 0.0% util
i2s1 : flush 0 count @ 0.000s = 0.0/s avg, 0.000s total, 0.0ms avg, 0.0% util
sequence : read 0 count @ 0.000s = 0.0/s avg, 0.000s total, 0.0ms avg, 0.0% util
sequence : skip 0 count @ 0.000s = nan/s avg
leds2:
task : loop 78 count @ 1.428s = 54.6/s avg, 1.445s total, 18.5ms avg, 101.2% util
task : test 79 count @ 1.446s = 54.6/s avg, 0.002s total, 0.0ms avg, 0.1% util
task : artnet 0 count @ 0.000s = 0.0/s avg, 0.000s total, 0.0ms avg, 0.0% util
task : sequence 0 count @ 0.000s = 0.0/s avg, 0.000s total, 0.0ms avg, 0.0% util
task : update 80 count @ 1.465s = 54.6/s avg, 1.480s total, 18.5ms avg, 101.0% util
artnet : timeout 0 count @ 0.000s = nan/s avg
artnet : sync 0 count @ 0.000s = nan/s avg
sync : none 0 count @ 0.000s = nan/s avg
sync : timeout 0 count @ 0.000s = nan/s avg
sync : missed 0 count @ 0.000s = nan/s avg
sync : full 0 count @ 0.000s = nan/s avg
update : timeout 0 count @ 0.000s = nan/s avg
config save -> flash writes have a nasty tendency to block i2s_out_dma_flush(), the EOF / TOTAL_EOF interrupts are somehow getting lost...?
> config save
I (132396) config_save: /config/boot.ini.new
I (132796) config_save: state save
> E (137566) i2s_out_dma_flush: timeout -> bits=00000001
E (137566) i2s_out_wait: i2s_out_dma_flush
E (137566) leds_interface_i2s_tx: i2s_out_start
E (137576) update_leds: leds_tx
W (137576) leds_main: leds1: update_leds
W (137576) user_alert: ERROR_LEDS
W (137576) reset_leds: Reset LEDS interface
E (142576) i2s_out_dma_flush: timeout -> bits=00000001
E (142576) i2s_out_wait: i2s_out_dma_flush
E (142576) leds_interface_i2s_close: i2s_out_close
W (142586) reset_leds: leds_interface_close
This is still recoverable per reset_leds(), with a 2×timeout pause in LED updates.