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