From 07609764004c0aa23ec5a4548f027c4ad4e849c3 Mon Sep 17 00:00:00 2001 From: jrhoffa Date: Wed, 14 Sep 2022 20:56:06 -0700 Subject: [PATCH] Add asynchronous DMA support and fix timing issues --- main/leds.cpp | 15 ++++++++++-- main/leds.hpp | 4 ++-- main/main.cpp | 17 +++++++------- main/spi_leds.cpp | 59 +++++++++++++++++++++++++++++------------------ main/spi_leds.hpp | 6 +++-- main/utils.cpp | 7 ++++-- 6 files changed, 70 insertions(+), 38 deletions(-) diff --git a/main/leds.cpp b/main/leds.cpp index 378bd57..121ee29 100644 --- a/main/leds.cpp +++ b/main/leds.cpp @@ -37,8 +37,19 @@ void Pattern::step(Color pixels[], int len, int64_t &last_us, Fixed &offset) con last_us = now_us; //ESP_LOGI(TAG, "duration %d", duration_us); + /* + int period_us = 1000000 / 60; + int n_skipped = (duration_us - period_us / 2) / period_us; + if (n_skipped) { + ESP_LOGW(TAG, "Skipped %d frames", n_skipped); + } + */ + // Don't make a major animation jump if it's been terribly long + if (duration_us > 100000) duration_us = 100000; + + int offset_delta = (duration_us << shift) / (cycle_time_ms * 1000); - if (reverse) offset -= offset_delta; else offset += offset_delta; + if (reverse) offset += offset_delta; else offset -= offset_delta; Fixed off = march ? (((int)offset * cycle_length) & ~((1 << shift) - 1)) / cycle_length : offset; @@ -73,7 +84,7 @@ static std::string termcolor(Color c) { std::to_string((int)c.b) + "m"; } -void TerminalLEDs::show() const { +void TerminalLEDs::show() { std::string line; for (int i = 0; i < length; i++) { line += termcolor(pixels[i]) + " "; diff --git a/main/leds.hpp b/main/leds.hpp index dc57d7f..82589f0 100644 --- a/main/leds.hpp +++ b/main/leds.hpp @@ -36,7 +36,7 @@ public: void step(); - virtual void show() const = 0; + virtual void show() = 0; virtual void length_changing(int) {}; void setPattern(const Pattern *_pattern) { pattern = _pattern; } @@ -58,5 +58,5 @@ private: class TerminalLEDs : public LEDStrip { public: TerminalLEDs(int length = 0) : LEDStrip(length) {} - void show() const; + void show(); }; diff --git a/main/main.cpp b/main/main.cpp index dfe8d83..924987b 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -131,6 +131,7 @@ std::string gen_config(bool is_on, const std::string &effect) { constexpr char config_path[] = "/spiffs/blinky.json"; +//#define PROFILE // Entry Point @@ -214,16 +215,16 @@ extern "C" void app_main(void) { int period_us = 1000000 / 60; int64_t start = time_us(); int64_t target_us = start; -/* +#ifdef PROFILE int64_t total_delay_us = 0; int n_delays = 0; int64_t after_sleep_us = 0; int n_processes = 0; int64_t total_process_us = 0; -*/ +#endif // PROFILE while (true) { int64_t now = time_us(); -/* +#ifdef PROFILE if (after_sleep_us > 0) { total_process_us += now - after_sleep_us; ++n_processes; @@ -240,23 +241,23 @@ extern "C" void app_main(void) { start = now; } -*/ +#endif // PROFILE int64_t delay_us = target_us - now; if (delay_us < 0) delay_us = 0; if (device.wait(delay_us / 1000)) { // If the semaphore is set, go back to top of outer loop break; } -/* + + target_us = now + delay_us + period_us; +#ifdef PROFILE after_sleep_us = time_us(); total_delay_us += (after_sleep_us - now); ++n_delays; -*/ +#endif // PROFILE LEDs->step(); LEDs->show(); - - target_us += period_us; } } } diff --git a/main/spi_leds.cpp b/main/spi_leds.cpp index ff84d5e..647444c 100644 --- a/main/spi_leds.cpp +++ b/main/spi_leds.cpp @@ -16,7 +16,8 @@ static const char *TAG = "spi_leds"; SPI_LEDs::SPI_LEDs(int _gpio, int length) : LEDStrip(length), host(SPI3_HOST), gpio(_gpio), device(NULL), - out_buf(NULL) { + out_buf(NULL) + { int ret; spi_bus_config_t buscfg = { @@ -53,7 +54,7 @@ SPI_LEDs::SPI_LEDs(int _gpio, int length) : .cs_ena_posttrans = 0, // moot // Datasheet implies 3.2 MHz => 1.25 us / 4 // We can get away with 4.4_ MHz => 900 ns / 4 - .clock_speed_hz = 4444444, + .clock_speed_hz = 3200000, .input_delay_ns = 0, // moot .spics_io_num = -1, .flags = 0, // Keep it MSB first @@ -77,6 +78,19 @@ void SPI_LEDs::configure(int len) { if (len <= 0) return; out_buf = (uint8_t*)heap_caps_malloc(tx_len(len), MALLOC_CAP_DMA); + + memset(out_buf + tx_len(len) - reset_bytes, 0, reset_bytes); + + transaction = { + .flags = 0, + .cmd = 0, + .addr = 0, + .length = tx_len(len) * 8, + .rxlength = 0, + .user = NULL, + .tx_buffer = out_buf, + .rx_buffer = NULL, + }; } // Inverted @@ -92,7 +106,8 @@ static uint8_t* encode_byte(uint8_t *out, uint8_t in) { return out; } -void SPI_LEDs::show() const { +void SPI_LEDs::show() { + static int index = 0; int ret; const Color *pixel = pixels; uint8_t *out = out_buf; @@ -104,24 +119,24 @@ void SPI_LEDs::show() const { ++pixel; } - // TODO: Reset pad? Or should we check clocks? - - spi_transaction_ext_t transaction_ext = { - .base = { - .flags = 0, - .cmd = 0, - .addr = 0, - .length = tx_len(length) * 8, - .rxlength = 0, - .user = NULL, - .tx_buffer = out_buf, - .rx_buffer = NULL, - }, - .command_bits = 160, - .address_bits = 0, - .dummy_bits = 160, - }; + ++index; - ret = spi_device_transmit(device, &transaction_ext.base); - RET_ON_ERR(spi_device_transmit, ret); + ret = spi_device_queue_trans(device, &transaction, portMAX_DELAY); + RET_ON_ERR(spi_device_queue_trans, ret); + +/* + while ((ret = spi_device_queue_trans(device, &transaction, 0)) != ESP_OK) { + if (ret == ESP_ERR_TIMEOUT) { + // Wait for the previous transaction to finish + ESP_LOGI(TAG, "SPI transaction collision (# %d)", index); + spi_transaction_t *ret_trans; + ret = spi_device_get_trans_result(device, &ret_trans, portMAX_DELAY); + RET_ON_ERR(spi_device_get_trans_result, ret); + ESP_LOGI(TAG, "Transaction complete (# %d)", index); + + } else { + RET_ON_ERR(spi_device_queue_trans, ret); + } + } +*/ } diff --git a/main/spi_leds.hpp b/main/spi_leds.hpp index 786491c..216dc0a 100644 --- a/main/spi_leds.hpp +++ b/main/spi_leds.hpp @@ -8,7 +8,7 @@ class SPI_LEDs : public LEDStrip { public: SPI_LEDs(int gpio, int length = 0); - void show() const; + void show(); void length_changing(int len) { configure(len); } private: @@ -16,8 +16,10 @@ private: int gpio; spi_device_handle_t device; uint8_t *out_buf; + spi_transaction_t transaction; - static size_t tx_len(int len) { return (len * 3) * 4; } + static constexpr int reset_bytes = 30; + static size_t tx_len(int len) { return (len * 3) * 4 + reset_bytes; } void configure(int length); }; diff --git a/main/utils.cpp b/main/utils.cpp index d164c0c..2f6f399 100644 --- a/main/utils.cpp +++ b/main/utils.cpp @@ -4,7 +4,10 @@ int64_t time_us() { - struct timeval tv_now; + /*struct timeval tv_now; gettimeofday(&tv_now, NULL); - return (int64_t)tv_now.tv_sec * 1000000L + (int64_t)tv_now.tv_usec; + return (int64_t)tv_now.tv_sec * 1000000L + (int64_t)tv_now.tv_usec;*/ + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return (int64_t)ts.tv_sec * 1000000L + (int64_t)ts.tv_nsec / 1000L; }