Browse Source

Add SPI backend

rmt-backend
jrhoffa 3 years ago
parent
commit
eb1c3497d1
6 changed files with 187 additions and 10 deletions
  1. +1
    -0
      main/CMakeLists.txt
  2. +0
    -4
      main/leds.cpp
  3. +1
    -2
      main/leds.hpp
  4. +35
    -4
      main/main.cpp
  5. +127
    -0
      main/spi_leds.cpp
  6. +23
    -0
      main/spi_leds.hpp

+ 1
- 0
main/CMakeLists.txt View File

@@ -9,6 +9,7 @@ idf_component_register(
"utils.cpp"
"leds.cpp"
"presets.cpp"
"spi_leds.cpp"

INCLUDE_DIRS "."
)


+ 0
- 4
main/leds.cpp View File

@@ -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);
}

+ 1
- 2
main/leds.hpp View File

@@ -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);
};

+ 35
- 4
main/main.cpp View File

@@ -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();



+ 127
- 0
main/spi_leds.cpp View File

@@ -0,0 +1,127 @@
static const char *TAG = "spi_leds";
#include <esp_log.h>

#include <string.h>

#include <driver/gpio.h>
#include <soc/io_mux_reg.h>

#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);
}

+ 23
- 0
main/spi_leds.hpp View File

@@ -0,0 +1,23 @@
#pragma once

#include <driver/spi_master.h>

#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);
};

Loading…
Cancel
Save