From eb1c3497d14ba2fcfb6d0d9d483386a0c59f91db Mon Sep 17 00:00:00 2001 From: jrhoffa Date: Wed, 14 Sep 2022 11:42:22 -0700 Subject: [PATCH] Add SPI backend --- main/CMakeLists.txt | 1 + main/leds.cpp | 4 -- main/leds.hpp | 3 +- main/main.cpp | 39 ++++++++++++-- main/spi_leds.cpp | 127 ++++++++++++++++++++++++++++++++++++++++++++ main/spi_leds.hpp | 23 ++++++++ 6 files changed, 187 insertions(+), 10 deletions(-) create mode 100644 main/spi_leds.cpp create mode 100644 main/spi_leds.hpp diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index c7079c2..952eed0 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -9,6 +9,7 @@ idf_component_register( "utils.cpp" "leds.cpp" "presets.cpp" + "spi_leds.cpp" INCLUDE_DIRS "." ) diff --git a/main/leds.cpp b/main/leds.cpp index 768deae..378bd57 100644 --- a/main/leds.cpp +++ b/main/leds.cpp @@ -81,7 +81,3 @@ void TerminalLEDs::show() const { line += "\x1b[0m"; ESP_LOGI(TAG, "%s", line.c_str()); } - -void TerminalLEDs::length_changing(int new_len) { - ESP_LOGI(TAG, "Length changing: %d->%d", length, new_len); -} diff --git a/main/leds.hpp b/main/leds.hpp index a67bc6b..dc57d7f 100644 --- a/main/leds.hpp +++ b/main/leds.hpp @@ -6,7 +6,7 @@ struct Color { uint8_t r, g, b; -}; +} __attribute__ ((packed)); typedef uint16_t Fixed; constexpr int factor = 65536; @@ -59,5 +59,4 @@ class TerminalLEDs : public LEDStrip { public: TerminalLEDs(int length = 0) : LEDStrip(length) {} void show() const; - void length_changing(int); }; diff --git a/main/main.cpp b/main/main.cpp index 367aa51..68eef8e 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -15,6 +15,7 @@ static const char *TAG = "blinky"; #include "leds.hpp" #include "presets.hpp" #include "utils.hpp" +#include "spi_leds.hpp" // mDNS / NetBIOS @@ -167,7 +168,7 @@ extern "C" void app_main(void) { cJSON_Delete(json); } - LEDStrip *LEDs = new TerminalLEDs(); + LEDStrip *LEDs = new SPI_LEDs(39); //new TerminalLEDs(); while (true) { ESP_LOGI(TAG, "Configuring LEDs"); @@ -209,16 +210,46 @@ extern "C" void app_main(void) { } else { ESP_LOGI(TAG, "Starting animation"); - int period_us = 1000000 / 4; - int64_t target_us = time_us(); + int period_us = 1000000 / 60; + int64_t start = time_us(); + int64_t target_us = start; + 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; while (true) { - int64_t delay_us = target_us - time_us(); + int64_t now = time_us(); + + if (after_sleep_us > 0) { + total_process_us += now - after_sleep_us; + ++n_processes; + } + + if (now - start >= 5000000) { + ESP_LOGI(TAG, "Average delay: %f ms", (double)total_delay_us / (n_delays * 1000)); + total_delay_us = 0; + n_delays = 0; + + ESP_LOGI(TAG, "Average processing: %f us", (double)total_process_us / n_processes); + total_process_us = 0; + n_processes = 0; + + start = now; + } + + 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; } + after_sleep_us = time_us(); + + total_delay_us += (after_sleep_us - now); + ++n_delays; + LEDs->step(); LEDs->show(); diff --git a/main/spi_leds.cpp b/main/spi_leds.cpp new file mode 100644 index 0000000..ff84d5e --- /dev/null +++ b/main/spi_leds.cpp @@ -0,0 +1,127 @@ +static const char *TAG = "spi_leds"; +#include + +#include + +#include +#include + +#include "spi_leds.hpp" + + +#define RET_ON_ERR(func, err) { if (err) { ESP_LOGE(TAG, #func ": %d", err); return; } } + + +// LCD is using bus 2 in quad SPI mode +SPI_LEDs::SPI_LEDs(int _gpio, int length) : + LEDStrip(length), + host(SPI3_HOST), gpio(_gpio), device(NULL), + out_buf(NULL) { + int ret; + + spi_bus_config_t buscfg = { + .mosi_io_num = gpio, + .miso_io_num = -1, + .sclk_io_num = -1, + .data2_io_num = -1, + .data3_io_num = -1, + .data4_io_num = -1, + .data5_io_num = -1, + .data6_io_num = -1, + .data7_io_num = -1, + .max_transfer_sz = 0, // 4092 (?) + .flags = SPICOMMON_BUSFLAG_MASTER, + .intr_flags = 0, + }; + + // Initialize the SPI bus + ret = spi_bus_initialize(host, &buscfg, SPI_DMA_CH_AUTO); + RET_ON_ERR(spi_bus_initialize, ret); + + // Invert the signal! + // TODO: Clean way thru API? + uint32_t *GPIO39_REG = (uint32_t*)(void*)(0x3f404000 + 0x0554 + (4 * 39)); + *GPIO39_REG |= 0x200; + + spi_device_interface_config_t devcfg = { + .command_bits = 0, + .address_bits = 0, + .dummy_bits = 0, + .mode = 0, // moot + .duty_cycle_pos = 0, // moot + .cs_ena_pretrans = 0, // moot + .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, + .input_delay_ns = 0, // moot + .spics_io_num = -1, + .flags = 0, // Keep it MSB first + .queue_size = 1, + .pre_cb = NULL, + .post_cb = NULL, + }; + + ret = spi_bus_add_device(host, &devcfg, &device); + RET_ON_ERR(spi_bus_add_device, ret); + + configure(length); +} + +void SPI_LEDs::configure(int len) { + if (out_buf) { + free(out_buf); + out_buf = NULL; + } + + if (len <= 0) return; + + out_buf = (uint8_t*)heap_caps_malloc(tx_len(len), MALLOC_CAP_DMA); +} + +// Inverted +constexpr uint8_t nibble_0 = 0b0111; +constexpr uint8_t nibble_1 = 0b0011; + +static uint8_t* encode_byte(uint8_t *out, uint8_t in) { + for (int i = 8; i > 0; i -= 2) { + *out++ = (((in & 0x80) ? nibble_1 : nibble_0) << 4) | + (((in & 0x40) ? nibble_1 : nibble_0) << 0); + in <<= 2; + } + return out; +} + +void SPI_LEDs::show() const { + int ret; + const Color *pixel = pixels; + uint8_t *out = out_buf; + + for (int i = length; i > 0; --i) { + out = encode_byte(out, pixel->r); + out = encode_byte(out, pixel->g); + out = encode_byte(out, pixel->b); + ++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, + }; + + ret = spi_device_transmit(device, &transaction_ext.base); + RET_ON_ERR(spi_device_transmit, ret); +} diff --git a/main/spi_leds.hpp b/main/spi_leds.hpp new file mode 100644 index 0000000..786491c --- /dev/null +++ b/main/spi_leds.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include + +#include "leds.hpp" + + +class SPI_LEDs : public LEDStrip { +public: + SPI_LEDs(int gpio, int length = 0); + void show() const; + void length_changing(int len) { configure(len); } + +private: + spi_host_device_t host; + int gpio; + spi_device_handle_t device; + uint8_t *out_buf; + + static size_t tx_len(int len) { return (len * 3) * 4; } + + void configure(int length); +};