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 // HOWEVER, better implementations divide 1.25 us into thirds! // Thus, 2.4 MHz has the same H times and slightly longer L times. .clock_speed_hz = 2400000, .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); memset(out_buf + tx_len(len) - reset_bytes, 0, reset_bytes); transaction = (spi_transaction_t){ .flags = 0, .cmd = 0, .addr = 0, .length = tx_len(len) * 8, .rxlength = 0, .user = NULL, .tx_buffer = out_buf, .rx_buffer = NULL, }; } // 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() { // We generate a spurious SPI error with a 0-length transaction. if (length <= 0) return; static int index = 0; 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; } ++index; 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); } } */ }