diff --git a/components/i2s_out/esp32/dma.c b/components/i2s_out/esp32/dma.c index f3e262be..23d9ca80 100644 --- a/components/i2s_out/esp32/dma.c +++ b/components/i2s_out/esp32/dma.c @@ -69,7 +69,11 @@ struct dma_desc *commit_dma_desc(struct dma_desc *desc) { desc->owner = 1; - return desc->next; + if (desc->next) { + return desc->next; + } else { + return desc; + } } void init_dma_eof_desc(struct dma_desc *eof_desc, uint32_t value, unsigned count) @@ -107,7 +111,7 @@ void reinit_dma_desc(struct dma_desc *head, unsigned count, struct dma_desc *nex } } -int i2s_out_dma_init(struct i2s_out *i2s_out, size_t size, size_t align) +int i2s_out_dma_init(struct i2s_out *i2s_out, size_t size, size_t align, unsigned repeat) { size_t buf_size = 0; unsigned desc_count = 0; @@ -129,7 +133,7 @@ int i2s_out_dma_init(struct i2s_out *i2s_out, size_t size, size_t align) } } - LOG_DEBUG("size=%u align=%u -> desc_count=%u buf_size=%u", size, align, desc_count, buf_size); + LOG_DEBUG("size=%u align=%u repeat=%u -> desc_count=%u buf_size=%u", size, align, repeat, desc_count, buf_size); // allocate single word-aligned buffer if (!(i2s_out->dma_rx_buf = dma_malloc(buf_size))) { @@ -150,16 +154,24 @@ int i2s_out_dma_init(struct i2s_out *i2s_out, size_t size, size_t align) LOG_ERROR("dma_calloc(dma_rx_desc)"); return -1; } + if (repeat && !(i2s_out->dma_repeat_desc = dma_calloc(desc_count * repeat, sizeof(*i2s_out->dma_rx_desc)))) { + LOG_ERROR("dma_calloc(dma_repeat_desc)"); + return -1; + } if (!(i2s_out->dma_eof_desc = dma_calloc(1, sizeof(*i2s_out->dma_eof_desc)))) { LOG_ERROR("dma_calloc(dma_eof_desc)"); return -1; } // initialize linked list of DMA descriptors - init_dma_desc(i2s_out->dma_rx_desc, desc_count, i2s_out->dma_rx_buf, buf_size, align, i2s_out->dma_eof_desc); - init_dma_desc(i2s_out->dma_eof_desc, 1, i2s_out->dma_eof_buf, DMA_EOF_BUF_SIZE, sizeof(uint32_t), i2s_out->dma_eof_desc); + init_dma_desc(i2s_out->dma_rx_desc, desc_count, i2s_out->dma_rx_buf, buf_size, align, NULL); + for (unsigned i = 0; i < repeat; i++) { + init_dma_desc(i2s_out->dma_repeat_desc + i * desc_count, desc_count, i2s_out->dma_rx_buf, buf_size, align, NULL); + } + init_dma_desc(i2s_out->dma_eof_desc, 1, i2s_out->dma_eof_buf, DMA_EOF_BUF_SIZE, sizeof(uint32_t), NULL); i2s_out->dma_rx_count = desc_count; + i2s_out->dma_rx_repeat = repeat; return 0; } @@ -177,7 +189,7 @@ int i2s_out_dma_setup(struct i2s_out *i2s_out, const struct i2s_out_options *opt init_dma_eof_desc(i2s_out->dma_eof_desc, options->eof_value, options->eof_count); // init RX desc - reinit_dma_desc(i2s_out->dma_rx_desc, i2s_out->dma_rx_count, i2s_out->dma_eof_desc); + reinit_dma_desc(i2s_out->dma_rx_desc, i2s_out->dma_rx_count, NULL); taskENTER_CRITICAL(&i2s_out->mux); @@ -255,7 +267,7 @@ size_t i2s_out_dma_buffer(struct i2s_out *i2s_out, void **ptr, unsigned count, s if (desc->len + size > desc->size) { LOG_DEBUG("commit desc=%p (owner=%u eof=%u buf=%p len=%u size=%u) < size=%u -> next=%p", desc, desc->owner, desc->eof, desc->buf, desc->len, desc->size, size, desc->next); - // commit, try with the next buffer + // commit, try with the next desc, if available i2s_out->dma_write_desc = commit_dma_desc(desc); continue; @@ -304,6 +316,39 @@ int i2s_out_dma_write(struct i2s_out *i2s_out, const void *data, size_t size) return len; } +void i2s_out_dma_repeat(struct i2s_out *i2s_out, unsigned count) +{ + struct dma_desc **nextp = &i2s_out->dma_write_desc->next; + + // commit + i2s_out->dma_write_desc->owner = 1; + + for (unsigned i = 0; i < count; i++) { + for (unsigned j = 0; j < i2s_out->dma_rx_count; j++) { + struct dma_desc *s = &i2s_out->dma_rx_desc[j]; + struct dma_desc *d = &i2s_out->dma_repeat_desc[i * i2s_out->dma_rx_count + j]; + + if (!s->owner) { + break; + } + + if (nextp) { + *nextp = d; + } + + d->len = s->len; + d->owner = s->owner; + + nextp = &d->next; + } + } + + if (nextp) { + // eof + *nextp = i2s_out->dma_eof_desc; + } +} + int i2s_out_dma_pending(struct i2s_out *i2s_out) { if (i2s_out->dma_start) { @@ -321,21 +366,43 @@ int i2s_out_dma_pending(struct i2s_out *i2s_out) void i2s_out_dma_start(struct i2s_out *i2s_out) { - i2s_out->dma_write_desc->owner = 1; - i2s_out->dma_write_desc->next = i2s_out->dma_eof_desc; + // commit if not repeat() + if (!i2s_out->dma_write_desc->owner) { + i2s_out->dma_write_desc->owner = 1; + } + + if (!i2s_out->dma_write_desc->next) { + i2s_out->dma_write_desc->next = i2s_out->dma_eof_desc; + } i2s_out->dma_eof_desc->owner = 1; i2s_out->dma_eof_desc->next = i2s_out->dma_eof_desc; - LOG_DEBUG("dma_write_desc=%p: owner=%d eof=%d len=%u size=%u -> buf=%p next=%p", - i2s_out->dma_write_desc, - i2s_out->dma_write_desc->owner, - i2s_out->dma_write_desc->eof, - i2s_out->dma_write_desc->len, - i2s_out->dma_write_desc->size, - i2s_out->dma_write_desc->buf, - i2s_out->dma_write_desc->next - ); + for (unsigned i = 0; i < i2s_out->dma_rx_count; i++) { + LOG_DEBUG("dma_rx_desc[%u]=%p: owner=%d eof=%d len=%u size=%u buf=%p next=%p", i, + &i2s_out->dma_rx_desc[i], + i2s_out->dma_rx_desc[i].owner, + i2s_out->dma_rx_desc[i].eof, + i2s_out->dma_rx_desc[i].len, + i2s_out->dma_rx_desc[i].size, + i2s_out->dma_rx_desc[i].buf, + i2s_out->dma_rx_desc[i].next + ); + } + + for (unsigned i = 0; i < i2s_out->dma_rx_repeat; i++) { + for (unsigned j = 0; j < i2s_out->dma_rx_count; j++) { + LOG_DEBUG("dma_repeat_desc[%u][%u]=%p: owner=%d eof=%d len=%u size=%u buf=%p next=%p", i, j, + &i2s_out->dma_repeat_desc[i * i2s_out->dma_rx_count + j], + i2s_out->dma_repeat_desc[i * i2s_out->dma_rx_count + j].owner, + i2s_out->dma_repeat_desc[i * i2s_out->dma_rx_count + j].eof, + i2s_out->dma_repeat_desc[i * i2s_out->dma_rx_count + j].len, + i2s_out->dma_repeat_desc[i * i2s_out->dma_rx_count + j].size, + i2s_out->dma_repeat_desc[i * i2s_out->dma_rx_count + j].buf, + i2s_out->dma_repeat_desc[i * i2s_out->dma_rx_count + j].next + ); + } + } LOG_DEBUG("dma_eof_desc=%p: owner=%d eof=%d len=%u size=%u -> buf=%p next=%p", i2s_out->dma_eof_desc, diff --git a/components/i2s_out/esp8266/dma.c b/components/i2s_out/esp8266/dma.c index 5563a9b6..89587ca0 100644 --- a/components/i2s_out/esp8266/dma.c +++ b/components/i2s_out/esp8266/dma.c @@ -70,7 +70,11 @@ struct dma_desc *commit_dma_desc(struct dma_desc *desc) { desc->owner = 1; - return desc->next; + if (desc->next) { + return desc->next; + } else { + return desc; + } } void init_dma_eof_desc(struct dma_desc *eof_desc, uint32_t value, unsigned count) @@ -108,7 +112,7 @@ void reinit_dma_desc(struct dma_desc *head, unsigned count, struct dma_desc *nex } } -int i2s_out_dma_init(struct i2s_out *i2s_out, size_t size, size_t align) +int i2s_out_dma_init(struct i2s_out *i2s_out, size_t size, size_t align, unsigned repeat) { size_t buf_size = 0; unsigned desc_count = 0; @@ -130,7 +134,7 @@ int i2s_out_dma_init(struct i2s_out *i2s_out, size_t size, size_t align) } } - LOG_DEBUG("size=%u align=%u -> desc_count=%u buf_size=%u", size, align, desc_count, buf_size); + LOG_DEBUG("size=%u align=%u repeat=%u -> desc_count=%u buf_size=%u", size, align, repeat, desc_count, buf_size); // allocate single word-aligned buffer if (!(i2s_out->dma_rx_buf = dma_malloc(buf_size))) { @@ -151,16 +155,24 @@ int i2s_out_dma_init(struct i2s_out *i2s_out, size_t size, size_t align) LOG_ERROR("dma_calloc(dma_rx_desc)"); return -1; } + if (repeat && !(i2s_out->dma_repeat_desc = dma_calloc(desc_count * repeat, sizeof(*i2s_out->dma_rx_desc)))) { + LOG_ERROR("dma_calloc(dma_repeat_desc)"); + return -1; + } if (!(i2s_out->dma_eof_desc = dma_calloc(1, sizeof(*i2s_out->dma_eof_desc)))) { LOG_ERROR("dma_calloc(dma_eof_desc)"); return -1; } // initialize linked list of DMA descriptors - init_dma_desc(i2s_out->dma_rx_desc, desc_count, i2s_out->dma_rx_buf, buf_size, align, i2s_out->dma_eof_desc); - init_dma_desc(i2s_out->dma_eof_desc, 1, i2s_out->dma_eof_buf, DMA_EOF_BUF_SIZE, sizeof(uint32_t), i2s_out->dma_eof_desc); + init_dma_desc(i2s_out->dma_rx_desc, desc_count, i2s_out->dma_rx_buf, buf_size, align, NULL); + for (unsigned i = 0; i < repeat; i++) { + init_dma_desc(i2s_out->dma_repeat_desc + i * desc_count, desc_count, i2s_out->dma_rx_buf, buf_size, align, NULL); + } + init_dma_desc(i2s_out->dma_eof_desc, 1, i2s_out->dma_eof_buf, DMA_EOF_BUF_SIZE, sizeof(uint32_t), NULL); i2s_out->dma_rx_count = desc_count; + i2s_out->dma_rx_repeat = repeat; // setup isr slc_isr_mask(); @@ -218,7 +230,7 @@ int i2s_out_dma_setup(struct i2s_out *i2s_out, const struct i2s_out_options *opt init_dma_eof_desc(i2s_out->dma_eof_desc, options->eof_value, options->eof_count); // init RX desc - reinit_dma_desc(i2s_out->dma_rx_desc, i2s_out->dma_rx_count, i2s_out->dma_eof_desc); + reinit_dma_desc(i2s_out->dma_rx_desc, i2s_out->dma_rx_count, NULL); taskENTER_CRITICAL(); @@ -295,7 +307,7 @@ size_t i2s_out_dma_buffer(struct i2s_out *i2s_out, void **ptr, unsigned count, s if (desc->len + size > desc->size) { LOG_DEBUG("commit desc=%p (owner=%u eof=%u buf=%p len=%u size=%u) < size=%u -> next=%p", desc, desc->owner, desc->eof, desc->buf, desc->len, desc->size, size, desc->next); - // commit, try with the next buffer + // commit, try with the next desc, if available i2s_out->dma_write_desc = commit_dma_desc(desc); continue; @@ -343,6 +355,39 @@ int i2s_out_dma_write(struct i2s_out *i2s_out, const void *data, size_t size) return len; } +void i2s_out_dma_repeat(struct i2s_out *i2s_out, unsigned count) +{ + struct dma_desc **nextp = &i2s_out->dma_write_desc->next; + + // commit + i2s_out->dma_write_desc->owner = 1; + + for (unsigned i = 0; i < count; i++) { + for (unsigned j = 0; j < i2s_out->dma_rx_count; j++) { + struct dma_desc *s = &i2s_out->dma_rx_desc[j]; + struct dma_desc *d = &i2s_out->dma_repeat_desc[i * i2s_out->dma_rx_count + j]; + + if (!s->owner) { + break; + } + + if (nextp) { + *nextp = d; + } + + d->len = s->len; + d->owner = s->owner; + + nextp = &d->next; + } + } + + if (nextp) { + // eof + *nextp = i2s_out->dma_eof_desc; + } +} + int i2s_out_dma_pending(struct i2s_out *i2s_out) { if (i2s_out->dma_start) { @@ -360,21 +405,43 @@ int i2s_out_dma_pending(struct i2s_out *i2s_out) void i2s_out_dma_start(struct i2s_out *i2s_out) { - i2s_out->dma_write_desc->owner = 1; - i2s_out->dma_write_desc->next = i2s_out->dma_eof_desc; + // commit if not repeat() + if (!i2s_out->dma_write_desc->owner) { + i2s_out->dma_write_desc->owner = 1; + } + + if (!i2s_out->dma_write_desc->next) { + i2s_out->dma_write_desc->next = i2s_out->dma_eof_desc; + } i2s_out->dma_eof_desc->owner = 1; i2s_out->dma_eof_desc->next = i2s_out->dma_eof_desc; - LOG_DEBUG("dma_write_desc=%p: owner=%d eof=%d len=%u size=%u -> buf=%p next=%p", - i2s_out->dma_write_desc, - i2s_out->dma_write_desc->owner, - i2s_out->dma_write_desc->eof, - i2s_out->dma_write_desc->len, - i2s_out->dma_write_desc->size, - i2s_out->dma_write_desc->buf, - i2s_out->dma_write_desc->next - ); + for (unsigned i = 0; i < i2s_out->dma_rx_count; i++) { + LOG_DEBUG("dma_rx_desc[%u]=%p: owner=%d eof=%d len=%u size=%u buf=%p next=%p", i, + &i2s_out->dma_rx_desc[i], + i2s_out->dma_rx_desc[i].owner, + i2s_out->dma_rx_desc[i].eof, + i2s_out->dma_rx_desc[i].len, + i2s_out->dma_rx_desc[i].size, + i2s_out->dma_rx_desc[i].buf, + i2s_out->dma_rx_desc[i].next + ); + } + + for (unsigned i = 0; i < i2s_out->dma_rx_repeat; i++) { + for (unsigned j = 0; j < i2s_out->dma_rx_count; j++) { + LOG_DEBUG("dma_repeat_desc[%u][%u]=%p: owner=%d eof=%d len=%u size=%u buf=%p next=%p", i, j, + &i2s_out->dma_repeat_desc[i * i2s_out->dma_rx_count + j], + i2s_out->dma_repeat_desc[i * i2s_out->dma_rx_count + j].owner, + i2s_out->dma_repeat_desc[i * i2s_out->dma_rx_count + j].eof, + i2s_out->dma_repeat_desc[i * i2s_out->dma_rx_count + j].len, + i2s_out->dma_repeat_desc[i * i2s_out->dma_rx_count + j].size, + i2s_out->dma_repeat_desc[i * i2s_out->dma_rx_count + j].buf, + i2s_out->dma_repeat_desc[i * i2s_out->dma_rx_count + j].next + ); + } + } LOG_DEBUG("dma_eof_desc=%p: owner=%d eof=%d len=%u size=%u -> buf=%p next=%p", i2s_out->dma_eof_desc, diff --git a/components/i2s_out/i2s_out.c b/components/i2s_out/i2s_out.c index e5e364f8..4db3ee1e 100644 --- a/components/i2s_out/i2s_out.c +++ b/components/i2s_out/i2s_out.c @@ -33,7 +33,7 @@ int i2s_out_init(struct i2s_out *i2s_out, i2s_port_t port) return 0; } -int i2s_out_new(struct i2s_out **i2s_outp, i2s_port_t port, size_t buffer_size, size_t buffer_align) +int i2s_out_new(struct i2s_out **i2s_outp, i2s_port_t port, size_t buffer_size, size_t buffer_align, unsigned repeat_data_count) { struct i2s_out *i2s_out = NULL; int err; @@ -53,7 +53,7 @@ int i2s_out_new(struct i2s_out **i2s_outp, i2s_port_t port, size_t buffer_size, goto error; } - if ((err = i2s_out_dma_init(i2s_out, buffer_size, buffer_align))) { + if ((err = i2s_out_dma_init(i2s_out, buffer_size, buffer_align, repeat_data_count))) { LOG_ERROR("i2s_out_dma_init"); goto error; } @@ -329,6 +329,24 @@ int i2s_out_write_serial32(struct i2s_out *i2s_out, const uint32_t *data, size_t } #endif +int i2s_out_repeat(struct i2s_out *i2s_out, unsigned count) +{ + int err = 0; + + if (!xSemaphoreTakeRecursive(i2s_out->mutex, portMAX_DELAY)) { + LOG_ERROR("xSemaphoreTakeRecursive"); + return -1; + } + + i2s_out_dma_repeat(i2s_out, count); + + if (!xSemaphoreGiveRecursive(i2s_out->mutex)) { + LOG_WARN("xSemaphoreGiveRecursive"); + } + + return err; +} + int i2s_out_flush(struct i2s_out *i2s_out) { int err = 0; diff --git a/components/i2s_out/i2s_out.h b/components/i2s_out/i2s_out.h index 1b7ff5e4..f7901750 100644 --- a/components/i2s_out/i2s_out.h +++ b/components/i2s_out/i2s_out.h @@ -42,9 +42,10 @@ struct i2s_out { /* dma */ uint8_t *dma_rx_buf, *dma_eof_buf; struct dma_desc *dma_rx_desc; + struct dma_desc *dma_repeat_desc; struct dma_desc *dma_eof_desc; - unsigned dma_rx_count; + unsigned dma_rx_count, dma_rx_repeat; // pointer to software-owned dma_rx_desc used for write() struct dma_desc *dma_write_desc; @@ -53,11 +54,12 @@ struct i2s_out { }; /* dma.c */ -int i2s_out_dma_init(struct i2s_out *i2s_out, size_t size, size_t align); +int i2s_out_dma_init(struct i2s_out *i2s_out, size_t size, size_t align, unsigned repeat); int i2s_out_dma_setup(struct i2s_out *i2s_out, const struct i2s_out_options *options); size_t i2s_out_dma_buffer(struct i2s_out *i2s_out, void **ptr, unsigned count, size_t size); void i2s_out_dma_commit(struct i2s_out *i2s_out, unsigned count, size_t size); int i2s_out_dma_write(struct i2s_out *i2s_out, const void *data, size_t size); +void i2s_out_dma_repeat(struct i2s_out *i2s_out, unsigned count); int i2s_out_dma_pending(struct i2s_out *i2s_out); void i2s_out_dma_start(struct i2s_out *i2s_out); int i2s_out_dma_flush(struct i2s_out *i2s_out); diff --git a/components/i2s_out/include/i2s_out.h b/components/i2s_out/include/i2s_out.h index 2bf93ae8..b0cc0518 100644 --- a/components/i2s_out/include/i2s_out.h +++ b/components/i2s_out/include/i2s_out.h @@ -136,7 +136,7 @@ struct i2s_out_options { * The `buffer_align` MUST be a power of two. * The 32-bit hardware FIFO dictates a minimum 4-byte alignment, and `buffer_align` will be adjusted if necessary. */ -int i2s_out_new(struct i2s_out **i2s_outp, i2s_port_t port, size_t buffer_size, size_t buffer_align); +int i2s_out_new(struct i2s_out **i2s_outp, i2s_port_t port, size_t buffer_size, size_t buffer_align, unsigned repeat_data_count); /** * Setup the I2S output. @@ -219,6 +219,13 @@ int i2s_out_write_serial32(struct i2s_out *i2s_out, const uint32_t *data, size_t int i2s_out_write_parallel8x32(struct i2s_out *i2s_out, uint32_t *data, unsigned width); #endif +/** + * Setup DMA to repeat all written buffers given count of times. Completes any writes. + * + * Returns <0 on error, 0 on success. + */ +int i2s_out_repeat(struct i2s_out *i2s_out, unsigned count); + /** * Start I2S output, and wait for the complete TX buffer and EOF frame to be written. * diff --git a/components/leds/include/leds.h b/components/leds/include/leds.h index 3de7bec5..316cde24 100644 --- a/components/leds/include/leds.h +++ b/components/leds/include/leds.h @@ -12,6 +12,7 @@ # define LEDS_I2S_GPIO_PINS_SIZE I2S_OUT_GPIO_PINS_MAX # define LEDS_I2S_PARALLEL_ENABLED I2S_OUT_PARALLEL_SUPPORTED # define LEDS_I2S_PARALLEL_MAX I2S_OUT_PARALLEL_DATA_BITS_MAX +# define LEDS_I2S_REPEAT_MAX 64 #endif #if CONFIG_LEDS_SPI_ENABLED && CONFIG_IDF_TARGET_ESP8266 @@ -238,6 +239,9 @@ enum leds_interface leds_interface_for_protocol(enum leds_protocol protocol); // default 0 -> serial output with a single data signal unsigned parallel; #endif + + // repeat data on each output + unsigned repeat; // LEDS_I2S_REPEAT_MAX }; /* diff --git a/components/leds/interfaces/i2s.h b/components/leds/interfaces/i2s.h index 9f64d9ec..ff9678d1 100644 --- a/components/leds/interfaces/i2s.h +++ b/components/leds/interfaces/i2s.h @@ -56,6 +56,7 @@ struct leds_interface_i2s { union leds_interface_i2s_buf *buf; unsigned parallel; + unsigned repeat; struct i2s_out *i2s_out; struct i2s_out_options i2s_out_options; diff --git a/components/leds/interfaces/i2s/tx.c b/components/leds/interfaces/i2s/tx.c index 88a66c29..eaf0850a 100644 --- a/components/leds/interfaces/i2s/tx.c +++ b/components/leds/interfaces/i2s/tx.c @@ -187,6 +187,7 @@ int leds_interface_i2s_init(struct leds_interface_i2s *interface, const struct l #else interface->parallel = 0; #endif + interface->repeat = options->repeat; if (!(interface->buf = calloc(1, leds_interface_i2s_buf_size(interface->mode, interface->parallel)))) { LOG_ERROR("calloc"); @@ -333,6 +334,13 @@ int leds_interface_i2s_tx(struct leds_interface_i2s *interface, const struct led } } + if (interface->repeat) { + if ((err = i2s_out_repeat(interface->i2s_out, interface->repeat))) { + LOG_ERROR("i2s_out_repeat"); + goto error; + } + } + WITH_STATS_TIMER(&stats->flush) { if ((err = i2s_out_flush(interface->i2s_out))) { LOG_ERROR("i2s_out_flush"); diff --git a/components/logging/include/logging.h b/components/logging/include/logging.h index 5bb248c3..9dc941ad 100644 --- a/components/logging/include/logging.h +++ b/components/logging/include/logging.h @@ -46,7 +46,7 @@ // XXX: CONFIG_LOG_DEFAULT_LEVEL defaults to skip ESP_LOG_DEBUG, and raising will include ALL debug output by default - not possible to override this per-call #if DEBUG - #define LOG_DEBUG_BUFFER(buf, len) IF_DEBUG({ ESP_LOG_BUFFER_HEX_LEVEL(__func__, buf, len, ESP_LOG_INFO); }) + #define LOG_DEBUG_BUFFER(buf, len) do { if (DEBUG) ESP_LOG_BUFFER_HEX_LEVEL(__func__, buf, len, ESP_LOG_INFO); } while(0) #else #define LOG_DEBUG_BUFFER(buf, len) #endif diff --git a/main/leds_config.h b/main/leds_config.h index 7a1e1069..bedda6f6 100644 --- a/main/leds_config.h +++ b/main/leds_config.h @@ -137,6 +137,7 @@ struct leds_config { # if LEDS_I2S_PARALLEL_ENABLED uint16_t i2s_data_width; # endif + uint16_t i2s_data_copies; # if LEDS_I2S_GPIO_PINS_ENABLED unsigned i2s_data_pin_count, i2s_data_inv_pin_count, i2s_clock_pin_count; uint16_t i2s_clock_pins[LEDS_I2S_GPIO_PINS_SIZE]; diff --git a/main/leds_configtab.i b/main/leds_configtab.i index d722e2d8..f24ef35e 100644 --- a/main/leds_configtab.i +++ b/main/leds_configtab.i @@ -66,6 +66,15 @@ const struct configtab LEDS_CONFIGTAB[] = { .description = "Output I2S bit rate. Only used for protocols with a separate clock/data.", .enum_type = { .value = &LEDS_CONFIG.i2s_clock, .values = leds_i2s_clock_enum, .default_value = I2S_CLOCK_DEFAULT }, }, + { CONFIG_TYPE_UINT16, "i2s_data_copies", + .description = ( + "Output multiple copies of the data on each I2S output, to control a series of identical LEDs.\n" + "\tFor example, use count = 600, i2s_data_width = 4, i2s_data_copies = 4 to control 4 sets of 150 LEDs on each of four outputs." + "Each output will be independently controllable, but each set of 150 LEDs per output will behave identically.\n" + "\t0 -> disabled, 1 -> no effect, N -> repeat data for a total of N copies per output." + ), + .uint16_type = { .value = &LEDS_CONFIG.i2s_data_copies, .max = LEDS_I2S_REPEAT_MAX }, + }, # if LEDS_I2S_PARALLEL_ENABLED { CONFIG_TYPE_UINT16, "i2s_data_width", .description = ( diff --git a/main/leds_i2s.c b/main/leds_i2s.c index c70f687a..23dc6448 100644 --- a/main/leds_i2s.c +++ b/main/leds_i2s.c @@ -70,6 +70,7 @@ { const struct leds_i2s_config *i2s_config = &leds_i2s_config; size_t buffer_size = 0, buffer_align = 0; + unsigned data_repeat = 0; bool enabled = false; int err; @@ -127,6 +128,9 @@ if (align > buffer_align) { buffer_align = align; } + if (config->i2s_data_copies > data_repeat + 1) { + data_repeat = config->i2s_data_copies - 1; + } } if (!enabled) { @@ -134,11 +138,11 @@ return 0; } - LOG_INFO("leds: i2s port=%d -> buffer_size=%u buffer_align=%u", i2s_config->port, - buffer_size, buffer_align + LOG_INFO("leds: i2s port=%d -> buffer_size=%u buffer_align=%u repeat_data_count=%u", i2s_config->port, + buffer_size, buffer_align, data_repeat ); - if ((err = i2s_out_new(&leds_i2s_out, i2s_config->port, buffer_size, buffer_align))) { + if ((err = i2s_out_new(&leds_i2s_out, i2s_config->port, buffer_size, buffer_align, data_repeat))) { LOG_ERROR("i2s_out_new(port=%d)", i2s_config->port); return err; } @@ -200,7 +204,14 @@ options->parallel = 0; } #endif - LOG_INFO("leds%d: i2s port=%d: pin_mutex=%p clock_rate=%d gpio_pins_count=%u parallel=%u", state->index + 1, + + if (config->i2s_data_copies > 1) { + LOG_INFO("leds%d: repeat copies=%u", state->index + 1, config->i2s_data_copies); + + options->repeat = config->i2s_data_copies - 1; + } + + LOG_INFO("leds%d: i2s port=%d: pin_mutex=%p clock_rate=%d gpio_pins_count=%u parallel=%u repeat=%u", state->index + 1, i2s_config->port, options->pin_mutex, options->clock_rate, @@ -210,10 +221,11 @@ 0, #endif #if LEDS_I2S_PARALLEL_ENABLED - options->parallel + options->parallel, #else - 0 + 0, #endif + options->repeat ); return 0;