ESP32 Native version of Blinky, featureful controller code for WS2811/WS2812/NeoPixels
25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

128 lines
3.2KB

  1. static const char *TAG = "spi_leds";
  2. #include <esp_log.h>
  3. #include <string.h>
  4. #include <driver/gpio.h>
  5. #include <soc/io_mux_reg.h>
  6. #include "spi_leds.hpp"
  7. #define RET_ON_ERR(func, err) { if (err) { ESP_LOGE(TAG, #func ": %d", err); return; } }
  8. // LCD is using bus 2 in quad SPI mode
  9. SPI_LEDs::SPI_LEDs(int _gpio, int length) :
  10. LEDStrip(length),
  11. host(SPI3_HOST), gpio(_gpio), device(NULL),
  12. out_buf(NULL) {
  13. int ret;
  14. spi_bus_config_t buscfg = {
  15. .mosi_io_num = gpio,
  16. .miso_io_num = -1,
  17. .sclk_io_num = -1,
  18. .data2_io_num = -1,
  19. .data3_io_num = -1,
  20. .data4_io_num = -1,
  21. .data5_io_num = -1,
  22. .data6_io_num = -1,
  23. .data7_io_num = -1,
  24. .max_transfer_sz = 0, // 4092 (?)
  25. .flags = SPICOMMON_BUSFLAG_MASTER,
  26. .intr_flags = 0,
  27. };
  28. // Initialize the SPI bus
  29. ret = spi_bus_initialize(host, &buscfg, SPI_DMA_CH_AUTO);
  30. RET_ON_ERR(spi_bus_initialize, ret);
  31. // Invert the signal!
  32. // TODO: Clean way thru API?
  33. uint32_t *GPIO39_REG = (uint32_t*)(void*)(0x3f404000 + 0x0554 + (4 * 39));
  34. *GPIO39_REG |= 0x200;
  35. spi_device_interface_config_t devcfg = {
  36. .command_bits = 0,
  37. .address_bits = 0,
  38. .dummy_bits = 0,
  39. .mode = 0, // moot
  40. .duty_cycle_pos = 0, // moot
  41. .cs_ena_pretrans = 0, // moot
  42. .cs_ena_posttrans = 0, // moot
  43. // Datasheet implies 3.2 MHz => 1.25 us / 4
  44. // We can get away with 4.4_ MHz => 900 ns / 4
  45. .clock_speed_hz = 4444444,
  46. .input_delay_ns = 0, // moot
  47. .spics_io_num = -1,
  48. .flags = 0, // Keep it MSB first
  49. .queue_size = 1,
  50. .pre_cb = NULL,
  51. .post_cb = NULL,
  52. };
  53. ret = spi_bus_add_device(host, &devcfg, &device);
  54. RET_ON_ERR(spi_bus_add_device, ret);
  55. configure(length);
  56. }
  57. void SPI_LEDs::configure(int len) {
  58. if (out_buf) {
  59. free(out_buf);
  60. out_buf = NULL;
  61. }
  62. if (len <= 0) return;
  63. out_buf = (uint8_t*)heap_caps_malloc(tx_len(len), MALLOC_CAP_DMA);
  64. }
  65. // Inverted
  66. constexpr uint8_t nibble_0 = 0b0111;
  67. constexpr uint8_t nibble_1 = 0b0011;
  68. static uint8_t* encode_byte(uint8_t *out, uint8_t in) {
  69. for (int i = 8; i > 0; i -= 2) {
  70. *out++ = (((in & 0x80) ? nibble_1 : nibble_0) << 4) |
  71. (((in & 0x40) ? nibble_1 : nibble_0) << 0);
  72. in <<= 2;
  73. }
  74. return out;
  75. }
  76. void SPI_LEDs::show() const {
  77. int ret;
  78. const Color *pixel = pixels;
  79. uint8_t *out = out_buf;
  80. for (int i = length; i > 0; --i) {
  81. out = encode_byte(out, pixel->r);
  82. out = encode_byte(out, pixel->g);
  83. out = encode_byte(out, pixel->b);
  84. ++pixel;
  85. }
  86. // TODO: Reset pad? Or should we check clocks?
  87. spi_transaction_ext_t transaction_ext = {
  88. .base = {
  89. .flags = 0,
  90. .cmd = 0,
  91. .addr = 0,
  92. .length = tx_len(length) * 8,
  93. .rxlength = 0,
  94. .user = NULL,
  95. .tx_buffer = out_buf,
  96. .rx_buffer = NULL,
  97. },
  98. .command_bits = 160,
  99. .address_bits = 0,
  100. .dummy_bits = 160,
  101. };
  102. ret = spi_device_transmit(device, &transaction_ext.base);
  103. RET_ON_ERR(spi_device_transmit, ret);
  104. }