ESP32 Native version of Blinky, featureful controller code for WS2811/WS2812/NeoPixels
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

196 linhas
6.0KB

  1. static const char *TAG = "rmt_leds";
  2. #include <esp_log.h>
  3. #include <freertos/FreeRTOS.h>
  4. #include <freertos/task.h>
  5. #include "rmt_leds.hpp"
  6. #define WS2811_PULSE_WIDTH_NS 825
  7. #define WS2811_RESET_WIDTH_US 300
  8. // Each pulse is divided into thirds - two high / one low for 1, vice versa for 0
  9. #define SHORT_TICKS 1
  10. #define LONG_TICKS 2
  11. #define BIT_TICKS (SHORT_TICKS + LONG_TICKS)
  12. #define RMT_RESOLUTION_HZ ((BIT_TICKS * (uint64_t)1e9) / WS2811_PULSE_WIDTH_NS)
  13. #define RESET_TICKS ((RMT_RESOLUTION_HZ * WS2811_RESET_WIDTH_US) / (uint32_t)1e6)
  14. // Use RMT CHannel 3 for TX + DMA
  15. #define RMT_TX_CHANNEL RMT_CHANNEL_3
  16. static size_t IRAM_ATTR ws2811_rmt_encode(
  17. rmt_encoder_t *base_encoder, rmt_channel_handle_t channel,
  18. const void *data, size_t data_size, rmt_encode_state_t *ret_state
  19. ) {
  20. static const rmt_symbol_word_t reset_symbol = (rmt_symbol_word_t){
  21. .duration0 = RESET_TICKS,
  22. .level0 = 0,
  23. .duration1 = 0,
  24. .level1 = 0,
  25. };
  26. int state = 0;
  27. rmt_encode_state_t substate = (rmt_encode_state_t)0;
  28. size_t n_encoded = 0;
  29. ws2811_rmt_encoder *encoder = (ws2811_rmt_encoder*)base_encoder;
  30. if (!encoder->bytes_encoded) {
  31. // Encode remaining bytes
  32. n_encoded += encoder->bytes_encoder->encode(
  33. encoder->bytes_encoder, channel,
  34. data, data_size,
  35. &substate
  36. );
  37. if (substate & RMT_ENCODING_COMPLETE) {
  38. encoder->bytes_encoded = true;
  39. }
  40. }
  41. if (encoder->bytes_encoded) {
  42. // All bytes encoded; encode reset pulse
  43. n_encoded += encoder->reset_encoder->encode(
  44. encoder->reset_encoder, channel,
  45. &reset_symbol, sizeof(reset_symbol),
  46. &substate
  47. );
  48. if (substate & RMT_ENCODING_COMPLETE) {
  49. state |= RMT_ENCODING_COMPLETE;
  50. }
  51. }
  52. if (substate & RMT_ENCODING_MEM_FULL) {
  53. state |= RMT_ENCODING_MEM_FULL;
  54. }
  55. *ret_state = (rmt_encode_state_t)state;
  56. return n_encoded;
  57. }
  58. static esp_err_t ws2811_rmt_reset(rmt_encoder_t *base_encoder) {
  59. ws2811_rmt_encoder *encoder = (ws2811_rmt_encoder*)base_encoder;
  60. rmt_encoder_reset(encoder->bytes_encoder);
  61. rmt_encoder_reset(encoder->reset_encoder);
  62. encoder->bytes_encoded = false;
  63. return ESP_OK;
  64. }
  65. static esp_err_t ws2811_rmt_del(rmt_encoder_t *base_encoder) {
  66. ws2811_rmt_encoder *encoder = (ws2811_rmt_encoder*)base_encoder;
  67. rmt_del_encoder(encoder->bytes_encoder);
  68. rmt_del_encoder(encoder->reset_encoder);
  69. return ESP_OK;
  70. }
  71. static bool IRAM_ATTR ws2811_tx_done(
  72. rmt_channel_handle_t tx_chan,
  73. const rmt_tx_done_event_data_t *edata,
  74. void *user_ctx
  75. ) {
  76. BaseType_t high_task_wakeup = pdFALSE;
  77. QueueHandle_t queue = (QueueHandle_t)user_ctx;
  78. // send the transmitted RMT symbols to the parser task
  79. xQueueSendFromISR(queue, edata, &high_task_wakeup);
  80. return high_task_wakeup == pdTRUE;
  81. }
  82. RMT_LEDs::RMT_LEDs(int _gpio, int length) :
  83. LEDStrip(length), gpio(_gpio), channel(NULL),
  84. encoder({{
  85. .encode = ws2811_rmt_encode,
  86. .reset = ws2811_rmt_reset,
  87. .del = ws2811_rmt_del,
  88. },
  89. NULL, NULL, false
  90. }),
  91. queue(xQueueCreate(1, sizeof(rmt_tx_done_event_data_t)))
  92. {
  93. rmt_tx_channel_config_t config = {
  94. .gpio_num = gpio, // GPIO number
  95. .clk_src = RMT_CLK_SRC_DEFAULT, // select source clock
  96. .resolution_hz = RMT_RESOLUTION_HZ, // 3.6 MHz tick resolution -> 278 ns/tick
  97. .mem_block_symbols = 64, // memory block size, 64 * 4 = 256Bytes
  98. .trans_queue_depth = 1, // set the number of transactions that can pend in the background
  99. .flags = {
  100. .invert_out = false, // don't invert output signal
  101. .with_dma = true, // use DMA backend
  102. .io_loop_back = 0,
  103. .io_od_mode = 0,
  104. },
  105. };
  106. ESP_ERROR_CHECK(rmt_new_tx_channel(&config, &channel));
  107. rmt_tx_event_callbacks_t cbs = {
  108. .on_trans_done = ws2811_tx_done,
  109. };
  110. ESP_ERROR_CHECK(rmt_tx_register_event_callbacks(channel, &cbs, queue));
  111. ESP_ERROR_CHECK(rmt_enable(channel));
  112. rmt_bytes_encoder_config_t bytes_config = {
  113. .bit0 = {
  114. .duration0 = SHORT_TICKS,
  115. .level0 = 1,
  116. .duration1 = LONG_TICKS,
  117. .level1 = 0,
  118. },
  119. .bit1 = {
  120. .duration0 = LONG_TICKS,
  121. .level0 = 1,
  122. .duration1 = SHORT_TICKS,
  123. .level1 = 0,
  124. },
  125. .flags = {
  126. .msb_first = 1,
  127. },
  128. };
  129. ESP_ERROR_CHECK(rmt_new_bytes_encoder(&bytes_config, &encoder.bytes_encoder));
  130. rmt_copy_encoder_config_t copy_config = {};
  131. ESP_ERROR_CHECK(rmt_new_copy_encoder(&copy_config, &encoder.reset_encoder));
  132. configure(length);
  133. }
  134. RMT_LEDs::~RMT_LEDs() {
  135. ESP_ERROR_CHECK(rmt_del_encoder(&encoder));
  136. ESP_ERROR_CHECK(rmt_disable(channel));
  137. ESP_ERROR_CHECK(rmt_del_channel(channel));
  138. }
  139. void RMT_LEDs::configure(int n_leds) {
  140. // We don't own the RMT buffer, but maybe we need a threadsafe copy of pixels?
  141. }
  142. void RMT_LEDs::show() {
  143. esp_err_t err;
  144. static const rmt_transmit_config_t config = {
  145. .loop_count = 0,
  146. .flags = 0,
  147. };
  148. if (length <= 0) {
  149. ESP_LOGW(TAG, "Invalid length write: %d", length);
  150. return;
  151. }
  152. rmt_encoder_reset(&encoder);
  153. err = rmt_transmit(channel, &encoder, pixels, length * 3, &config);
  154. if (err != ESP_OK) {
  155. ESP_LOGE(TAG, "Failed to write sample to RMT: %d", err);
  156. } else {
  157. // TODO: Do a queue check prior to TX instead
  158. // This was an attempt to avoid clobbering pixel data,
  159. // but that doesn't appear to be why everything gets weird during OTA.
  160. rmt_tx_done_event_data_t tx_data;
  161. if (xQueueReceive(queue, &tx_data, portMAX_DELAY) != pdTRUE) {
  162. ESP_LOGE(TAG, "Failed to wait for last TX");
  163. }
  164. }
  165. }