| @@ -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]) + " "; | |||
| @@ -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(); | |||
| }; | |||
| @@ -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; | |||
| } | |||
| } | |||
| } | |||
| @@ -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); | |||
| } | |||
| } | |||
| */ | |||
| } | |||
| @@ -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); | |||
| }; | |||
| @@ -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; | |||
| } | |||