fix rmt legacy driver warning
This commit is contained in:
		
							
								
								
									
										94
									
								
								hw/bsp/espressif/components/led_strip/src/led_strip_api.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								hw/bsp/espressif/components/led_strip/src/led_strip_api.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,94 @@ | ||||
| /* | ||||
|  * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD | ||||
|  * | ||||
|  * SPDX-License-Identifier: Apache-2.0 | ||||
|  */ | ||||
| #include "esp_log.h" | ||||
| #include "esp_check.h" | ||||
| #include "led_strip.h" | ||||
| #include "led_strip_interface.h" | ||||
|  | ||||
| static const char *TAG = "led_strip"; | ||||
|  | ||||
| esp_err_t led_strip_set_pixel(led_strip_handle_t strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue) | ||||
| { | ||||
|     ESP_RETURN_ON_FALSE(strip, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); | ||||
|     return strip->set_pixel(strip, index, red, green, blue); | ||||
| } | ||||
|  | ||||
| esp_err_t led_strip_set_pixel_hsv(led_strip_handle_t strip, uint32_t index, uint16_t hue, uint8_t saturation, uint8_t value) | ||||
| { | ||||
|     ESP_RETURN_ON_FALSE(strip, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); | ||||
|  | ||||
|     uint32_t red = 0; | ||||
|     uint32_t green = 0; | ||||
|     uint32_t blue = 0; | ||||
|  | ||||
|     uint32_t rgb_max = value; | ||||
|     uint32_t rgb_min = rgb_max * (255 - saturation) / 255.0f; | ||||
|  | ||||
|     uint32_t i = hue / 60; | ||||
|     uint32_t diff = hue % 60; | ||||
|  | ||||
|     // RGB adjustment amount by hue | ||||
|     uint32_t rgb_adj = (rgb_max - rgb_min) * diff / 60; | ||||
|  | ||||
|     switch (i) { | ||||
|     case 0: | ||||
|         red = rgb_max; | ||||
|         green = rgb_min + rgb_adj; | ||||
|         blue = rgb_min; | ||||
|         break; | ||||
|     case 1: | ||||
|         red = rgb_max - rgb_adj; | ||||
|         green = rgb_max; | ||||
|         blue = rgb_min; | ||||
|         break; | ||||
|     case 2: | ||||
|         red = rgb_min; | ||||
|         green = rgb_max; | ||||
|         blue = rgb_min + rgb_adj; | ||||
|         break; | ||||
|     case 3: | ||||
|         red = rgb_min; | ||||
|         green = rgb_max - rgb_adj; | ||||
|         blue = rgb_max; | ||||
|         break; | ||||
|     case 4: | ||||
|         red = rgb_min + rgb_adj; | ||||
|         green = rgb_min; | ||||
|         blue = rgb_max; | ||||
|         break; | ||||
|     default: | ||||
|         red = rgb_max; | ||||
|         green = rgb_min; | ||||
|         blue = rgb_max - rgb_adj; | ||||
|         break; | ||||
|     } | ||||
|  | ||||
|     return strip->set_pixel(strip, index, red, green, blue); | ||||
| } | ||||
|  | ||||
| esp_err_t led_strip_set_pixel_rgbw(led_strip_handle_t strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue, uint32_t white) | ||||
| { | ||||
|     ESP_RETURN_ON_FALSE(strip, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); | ||||
|     return strip->set_pixel_rgbw(strip, index, red, green, blue, white); | ||||
| } | ||||
|  | ||||
| esp_err_t led_strip_refresh(led_strip_handle_t strip) | ||||
| { | ||||
|     ESP_RETURN_ON_FALSE(strip, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); | ||||
|     return strip->refresh(strip); | ||||
| } | ||||
|  | ||||
| esp_err_t led_strip_clear(led_strip_handle_t strip) | ||||
| { | ||||
|     ESP_RETURN_ON_FALSE(strip, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); | ||||
|     return strip->clear(strip); | ||||
| } | ||||
|  | ||||
| esp_err_t led_strip_del(led_strip_handle_t strip) | ||||
| { | ||||
|     ESP_RETURN_ON_FALSE(strip, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); | ||||
|     return strip->del(strip); | ||||
| } | ||||
							
								
								
									
										164
									
								
								hw/bsp/espressif/components/led_strip/src/led_strip_rmt_dev.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										164
									
								
								hw/bsp/espressif/components/led_strip/src/led_strip_rmt_dev.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,164 @@ | ||||
| /* | ||||
|  * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD | ||||
|  * | ||||
|  * SPDX-License-Identifier: Apache-2.0 | ||||
|  */ | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include <sys/cdefs.h> | ||||
| #include "esp_log.h" | ||||
| #include "esp_check.h" | ||||
| #include "driver/rmt_tx.h" | ||||
| #include "led_strip.h" | ||||
| #include "led_strip_interface.h" | ||||
| #include "led_strip_rmt_encoder.h" | ||||
|  | ||||
| #define LED_STRIP_RMT_DEFAULT_RESOLUTION 10000000 // 10MHz resolution | ||||
| #define LED_STRIP_RMT_DEFAULT_TRANS_QUEUE_SIZE 4 | ||||
| // the memory size of each RMT channel, in words (4 bytes) | ||||
| #if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 | ||||
| #define LED_STRIP_RMT_DEFAULT_MEM_BLOCK_SYMBOLS 64 | ||||
| #else | ||||
| #define LED_STRIP_RMT_DEFAULT_MEM_BLOCK_SYMBOLS 48 | ||||
| #endif | ||||
|  | ||||
| static const char *TAG = "led_strip_rmt"; | ||||
|  | ||||
| typedef struct { | ||||
|     led_strip_t base; | ||||
|     rmt_channel_handle_t rmt_chan; | ||||
|     rmt_encoder_handle_t strip_encoder; | ||||
|     uint32_t strip_len; | ||||
|     uint8_t bytes_per_pixel; | ||||
|     uint8_t pixel_buf[]; | ||||
| } led_strip_rmt_obj; | ||||
|  | ||||
| static esp_err_t led_strip_rmt_set_pixel(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue) | ||||
| { | ||||
|     led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base); | ||||
|     ESP_RETURN_ON_FALSE(index < rmt_strip->strip_len, ESP_ERR_INVALID_ARG, TAG, "index out of maximum number of LEDs"); | ||||
|     uint32_t start = index * rmt_strip->bytes_per_pixel; | ||||
|     // In thr order of GRB, as LED strip like WS2812 sends out pixels in this order | ||||
|     rmt_strip->pixel_buf[start + 0] = green & 0xFF; | ||||
|     rmt_strip->pixel_buf[start + 1] = red & 0xFF; | ||||
|     rmt_strip->pixel_buf[start + 2] = blue & 0xFF; | ||||
|     if (rmt_strip->bytes_per_pixel > 3) { | ||||
|         rmt_strip->pixel_buf[start + 3] = 0; | ||||
|     } | ||||
|     return ESP_OK; | ||||
| } | ||||
|  | ||||
| static esp_err_t led_strip_rmt_set_pixel_rgbw(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue, uint32_t white) | ||||
| { | ||||
|     led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base); | ||||
|     ESP_RETURN_ON_FALSE(index < rmt_strip->strip_len, ESP_ERR_INVALID_ARG, TAG, "index out of maximum number of LEDs"); | ||||
|     ESP_RETURN_ON_FALSE(rmt_strip->bytes_per_pixel == 4, ESP_ERR_INVALID_ARG, TAG, "wrong LED pixel format, expected 4 bytes per pixel"); | ||||
|     uint8_t *buf_start = rmt_strip->pixel_buf + index * 4; | ||||
|     // SK6812 component order is GRBW | ||||
|     *buf_start = green & 0xFF; | ||||
|     *++buf_start = red & 0xFF; | ||||
|     *++buf_start = blue & 0xFF; | ||||
|     *++buf_start = white & 0xFF; | ||||
|     return ESP_OK; | ||||
| } | ||||
|  | ||||
| static esp_err_t led_strip_rmt_refresh(led_strip_t *strip) | ||||
| { | ||||
|     led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base); | ||||
|     rmt_transmit_config_t tx_conf = { | ||||
|         .loop_count = 0, | ||||
|     }; | ||||
|  | ||||
|     ESP_RETURN_ON_ERROR(rmt_enable(rmt_strip->rmt_chan), TAG, "enable RMT channel failed"); | ||||
|     ESP_RETURN_ON_ERROR(rmt_transmit(rmt_strip->rmt_chan, rmt_strip->strip_encoder, rmt_strip->pixel_buf, | ||||
|                                      rmt_strip->strip_len * rmt_strip->bytes_per_pixel, &tx_conf), TAG, "transmit pixels by RMT failed"); | ||||
|     ESP_RETURN_ON_ERROR(rmt_tx_wait_all_done(rmt_strip->rmt_chan, -1), TAG, "flush RMT channel failed"); | ||||
|     ESP_RETURN_ON_ERROR(rmt_disable(rmt_strip->rmt_chan), TAG, "disable RMT channel failed"); | ||||
|     return ESP_OK; | ||||
| } | ||||
|  | ||||
| static esp_err_t led_strip_rmt_clear(led_strip_t *strip) | ||||
| { | ||||
|     led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base); | ||||
|     // Write zero to turn off all leds | ||||
|     memset(rmt_strip->pixel_buf, 0, rmt_strip->strip_len * rmt_strip->bytes_per_pixel); | ||||
|     return led_strip_rmt_refresh(strip); | ||||
| } | ||||
|  | ||||
| static esp_err_t led_strip_rmt_del(led_strip_t *strip) | ||||
| { | ||||
|     led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base); | ||||
|     ESP_RETURN_ON_ERROR(rmt_del_channel(rmt_strip->rmt_chan), TAG, "delete RMT channel failed"); | ||||
|     ESP_RETURN_ON_ERROR(rmt_del_encoder(rmt_strip->strip_encoder), TAG, "delete strip encoder failed"); | ||||
|     free(rmt_strip); | ||||
|     return ESP_OK; | ||||
| } | ||||
|  | ||||
| esp_err_t led_strip_new_rmt_device(const led_strip_config_t *led_config, const led_strip_rmt_config_t *rmt_config, led_strip_handle_t *ret_strip) | ||||
| { | ||||
|     led_strip_rmt_obj *rmt_strip = NULL; | ||||
|     esp_err_t ret = ESP_OK; | ||||
|     ESP_GOTO_ON_FALSE(led_config && rmt_config && ret_strip, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument"); | ||||
|     ESP_GOTO_ON_FALSE(led_config->led_pixel_format < LED_PIXEL_FORMAT_INVALID, ESP_ERR_INVALID_ARG, err, TAG, "invalid led_pixel_format"); | ||||
|     uint8_t bytes_per_pixel = 3; | ||||
|     if (led_config->led_pixel_format == LED_PIXEL_FORMAT_GRBW) { | ||||
|         bytes_per_pixel = 4; | ||||
|     } else if (led_config->led_pixel_format == LED_PIXEL_FORMAT_GRB) { | ||||
|         bytes_per_pixel = 3; | ||||
|     } else { | ||||
|         assert(false); | ||||
|     } | ||||
|     rmt_strip = calloc(1, sizeof(led_strip_rmt_obj) + led_config->max_leds * bytes_per_pixel); | ||||
|     ESP_GOTO_ON_FALSE(rmt_strip, ESP_ERR_NO_MEM, err, TAG, "no mem for rmt strip"); | ||||
|     uint32_t resolution = rmt_config->resolution_hz ? rmt_config->resolution_hz : LED_STRIP_RMT_DEFAULT_RESOLUTION; | ||||
|  | ||||
|     // for backward compatibility, if the user does not set the clk_src, use the default value | ||||
|     rmt_clock_source_t clk_src = RMT_CLK_SRC_DEFAULT; | ||||
|     if (rmt_config->clk_src) { | ||||
|         clk_src = rmt_config->clk_src; | ||||
|     } | ||||
|     size_t mem_block_symbols = LED_STRIP_RMT_DEFAULT_MEM_BLOCK_SYMBOLS; | ||||
|     // override the default value if the user sets it | ||||
|     if (rmt_config->mem_block_symbols) { | ||||
|         mem_block_symbols = rmt_config->mem_block_symbols; | ||||
|     } | ||||
|     rmt_tx_channel_config_t rmt_chan_config = { | ||||
|         .clk_src = clk_src, | ||||
|         .gpio_num = led_config->strip_gpio_num, | ||||
|         .mem_block_symbols = mem_block_symbols, | ||||
|         .resolution_hz = resolution, | ||||
|         .trans_queue_depth = LED_STRIP_RMT_DEFAULT_TRANS_QUEUE_SIZE, | ||||
|         .flags.with_dma = rmt_config->flags.with_dma, | ||||
|         .flags.invert_out = led_config->flags.invert_out, | ||||
|     }; | ||||
|     ESP_GOTO_ON_ERROR(rmt_new_tx_channel(&rmt_chan_config, &rmt_strip->rmt_chan), err, TAG, "create RMT TX channel failed"); | ||||
|  | ||||
|     led_strip_encoder_config_t strip_encoder_conf = { | ||||
|         .resolution = resolution, | ||||
|         .led_model = led_config->led_model | ||||
|     }; | ||||
|     ESP_GOTO_ON_ERROR(rmt_new_led_strip_encoder(&strip_encoder_conf, &rmt_strip->strip_encoder), err, TAG, "create LED strip encoder failed"); | ||||
|  | ||||
|  | ||||
|     rmt_strip->bytes_per_pixel = bytes_per_pixel; | ||||
|     rmt_strip->strip_len = led_config->max_leds; | ||||
|     rmt_strip->base.set_pixel = led_strip_rmt_set_pixel; | ||||
|     rmt_strip->base.set_pixel_rgbw = led_strip_rmt_set_pixel_rgbw; | ||||
|     rmt_strip->base.refresh = led_strip_rmt_refresh; | ||||
|     rmt_strip->base.clear = led_strip_rmt_clear; | ||||
|     rmt_strip->base.del = led_strip_rmt_del; | ||||
|  | ||||
|     *ret_strip = &rmt_strip->base; | ||||
|     return ESP_OK; | ||||
| err: | ||||
|     if (rmt_strip) { | ||||
|         if (rmt_strip->rmt_chan) { | ||||
|             rmt_del_channel(rmt_strip->rmt_chan); | ||||
|         } | ||||
|         if (rmt_strip->strip_encoder) { | ||||
|             rmt_del_encoder(rmt_strip->strip_encoder); | ||||
|         } | ||||
|         free(rmt_strip); | ||||
|     } | ||||
|     return ret; | ||||
| } | ||||
| @@ -0,0 +1,194 @@ | ||||
| /* | ||||
|  * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD | ||||
|  * | ||||
|  * SPDX-License-Identifier: Apache-2.0 | ||||
|  */ | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include <sys/cdefs.h> | ||||
| #include "esp_log.h" | ||||
| #include "esp_check.h" | ||||
| #include "driver/rmt.h" | ||||
| #include "led_strip.h" | ||||
| #include "led_strip_interface.h" | ||||
|  | ||||
| static const char *TAG = "led_strip_rmt"; | ||||
|  | ||||
| #define WS2812_T0H_NS   (300) | ||||
| #define WS2812_T0L_NS   (900) | ||||
| #define WS2812_T1H_NS   (900) | ||||
| #define WS2812_T1L_NS   (300) | ||||
|  | ||||
| #define SK6812_T0H_NS   (300) | ||||
| #define SK6812_T0L_NS   (900) | ||||
| #define SK6812_T1H_NS   (600) | ||||
| #define SK6812_T1L_NS   (600) | ||||
|  | ||||
| #define LED_STRIP_RESET_MS (10) | ||||
|  | ||||
| // the memory size of each RMT channel, in words (4 bytes) | ||||
| #if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 | ||||
| #define LED_STRIP_RMT_DEFAULT_MEM_BLOCK_SYMBOLS 64 | ||||
| #else | ||||
| #define LED_STRIP_RMT_DEFAULT_MEM_BLOCK_SYMBOLS 48 | ||||
| #endif | ||||
|  | ||||
| static uint32_t led_t0h_ticks = 0; | ||||
| static uint32_t led_t1h_ticks = 0; | ||||
| static uint32_t led_t0l_ticks = 0; | ||||
| static uint32_t led_t1l_ticks = 0; | ||||
|  | ||||
| typedef struct { | ||||
|     led_strip_t base; | ||||
|     rmt_channel_t rmt_channel; | ||||
|     uint32_t strip_len; | ||||
|     uint8_t bytes_per_pixel; | ||||
|     uint8_t buffer[0]; | ||||
| } led_strip_rmt_obj; | ||||
|  | ||||
| static void IRAM_ATTR ws2812_rmt_adapter(const void *src, rmt_item32_t *dest, size_t src_size, | ||||
|         size_t wanted_num, size_t *translated_size, size_t *item_num) | ||||
| { | ||||
|     if (src == NULL || dest == NULL) { | ||||
|         *translated_size = 0; | ||||
|         *item_num = 0; | ||||
|         return; | ||||
|     } | ||||
|     const rmt_item32_t bit0 = {{{ led_t0h_ticks, 1, led_t0l_ticks, 0 }}}; //Logical 0 | ||||
|     const rmt_item32_t bit1 = {{{ led_t1h_ticks, 1, led_t1l_ticks, 0 }}}; //Logical 1 | ||||
|     size_t size = 0; | ||||
|     size_t num = 0; | ||||
|     uint8_t *psrc = (uint8_t *)src; | ||||
|     rmt_item32_t *pdest = dest; | ||||
|     while (size < src_size && num < wanted_num) { | ||||
|         for (int i = 0; i < 8; i++) { | ||||
|             // MSB first | ||||
|             if (*psrc & (1 << (7 - i))) { | ||||
|                 pdest->val =  bit1.val; | ||||
|             } else { | ||||
|                 pdest->val =  bit0.val; | ||||
|             } | ||||
|             num++; | ||||
|             pdest++; | ||||
|         } | ||||
|         size++; | ||||
|         psrc++; | ||||
|     } | ||||
|     *translated_size = size; | ||||
|     *item_num = num; | ||||
| } | ||||
|  | ||||
| static esp_err_t led_strip_rmt_set_pixel(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue) | ||||
| { | ||||
|     led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base); | ||||
|     ESP_RETURN_ON_FALSE(index < rmt_strip->strip_len, ESP_ERR_INVALID_ARG, TAG, "index out of the maximum number of leds"); | ||||
|     uint32_t start = index * rmt_strip->bytes_per_pixel; | ||||
|     // In thr order of GRB | ||||
|     rmt_strip->buffer[start + 0] = green & 0xFF; | ||||
|     rmt_strip->buffer[start + 1] = red & 0xFF; | ||||
|     rmt_strip->buffer[start + 2] = blue & 0xFF; | ||||
|     if (rmt_strip->bytes_per_pixel > 3) { | ||||
|         rmt_strip->buffer[start + 3] = 0; | ||||
|     } | ||||
|     return ESP_OK; | ||||
| } | ||||
|  | ||||
| static esp_err_t led_strip_rmt_refresh(led_strip_t *strip) | ||||
| { | ||||
|     led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base); | ||||
|     ESP_RETURN_ON_ERROR(rmt_write_sample(rmt_strip->rmt_channel, rmt_strip->buffer, rmt_strip->strip_len * rmt_strip->bytes_per_pixel, true), TAG, | ||||
|                         "transmit RMT samples failed"); | ||||
|     vTaskDelay(pdMS_TO_TICKS(LED_STRIP_RESET_MS)); | ||||
|     return ESP_OK; | ||||
| } | ||||
|  | ||||
| static esp_err_t led_strip_rmt_clear(led_strip_t *strip) | ||||
| { | ||||
|     led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base); | ||||
|     // Write zero to turn off all LEDs | ||||
|     memset(rmt_strip->buffer, 0, rmt_strip->strip_len * rmt_strip->bytes_per_pixel); | ||||
|     return led_strip_rmt_refresh(strip); | ||||
| } | ||||
|  | ||||
| static esp_err_t led_strip_rmt_del(led_strip_t *strip) | ||||
| { | ||||
|     led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base); | ||||
|     ESP_RETURN_ON_ERROR(rmt_driver_uninstall(rmt_strip->rmt_channel), TAG, "uninstall RMT driver failed"); | ||||
|     free(rmt_strip); | ||||
|     return ESP_OK; | ||||
| } | ||||
|  | ||||
| esp_err_t led_strip_new_rmt_device(const led_strip_config_t *led_config, const led_strip_rmt_config_t *dev_config, led_strip_handle_t *ret_strip) | ||||
| { | ||||
|     led_strip_rmt_obj *rmt_strip = NULL; | ||||
|     esp_err_t ret = ESP_OK; | ||||
|     ESP_RETURN_ON_FALSE(led_config && dev_config && ret_strip, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); | ||||
|     ESP_RETURN_ON_FALSE(led_config->led_pixel_format < LED_PIXEL_FORMAT_INVALID, ESP_ERR_INVALID_ARG, TAG, "invalid led_pixel_format"); | ||||
|     ESP_RETURN_ON_FALSE(dev_config->flags.with_dma == 0, ESP_ERR_NOT_SUPPORTED, TAG, "DMA is not supported"); | ||||
|  | ||||
|     uint8_t bytes_per_pixel = 3; | ||||
|     if (led_config->led_pixel_format == LED_PIXEL_FORMAT_GRBW) { | ||||
|         bytes_per_pixel = 4; | ||||
|     } else if (led_config->led_pixel_format == LED_PIXEL_FORMAT_GRB) { | ||||
|         bytes_per_pixel = 3; | ||||
|     } else { | ||||
|         assert(false); | ||||
|     } | ||||
|  | ||||
|     // allocate memory for led_strip object | ||||
|     rmt_strip = calloc(1, sizeof(led_strip_rmt_obj) + led_config->max_leds * bytes_per_pixel); | ||||
|     ESP_RETURN_ON_FALSE(rmt_strip, ESP_ERR_NO_MEM, TAG, "request memory for les_strip failed"); | ||||
|  | ||||
|     // install RMT channel driver | ||||
|     rmt_config_t config = RMT_DEFAULT_CONFIG_TX(led_config->strip_gpio_num, dev_config->rmt_channel); | ||||
|     // set the minimal clock division because the LED strip needs a high clock resolution | ||||
|     config.clk_div = 2; | ||||
|  | ||||
|     uint8_t mem_block_num = 2; | ||||
|     // override the default value if the user specify the mem block size | ||||
|     if (dev_config->mem_block_symbols) { | ||||
|         mem_block_num = (dev_config->mem_block_symbols + LED_STRIP_RMT_DEFAULT_MEM_BLOCK_SYMBOLS / 2) / LED_STRIP_RMT_DEFAULT_MEM_BLOCK_SYMBOLS; | ||||
|     } | ||||
|     config.mem_block_num = mem_block_num; | ||||
|  | ||||
|     ESP_GOTO_ON_ERROR(rmt_config(&config), err, TAG, "RMT config failed"); | ||||
|     ESP_GOTO_ON_ERROR(rmt_driver_install(config.channel, 0, 0), err, TAG, "RMT install failed"); | ||||
|  | ||||
|     uint32_t counter_clk_hz = 0; | ||||
|     rmt_get_counter_clock((rmt_channel_t)dev_config->rmt_channel, &counter_clk_hz); | ||||
|     // ns -> ticks | ||||
|     float ratio = (float)counter_clk_hz / 1e9; | ||||
|     if (led_config->led_model == LED_MODEL_WS2812) { | ||||
|         led_t0h_ticks = (uint32_t)(ratio * WS2812_T0H_NS); | ||||
|         led_t0l_ticks = (uint32_t)(ratio * WS2812_T0L_NS); | ||||
|         led_t1h_ticks = (uint32_t)(ratio * WS2812_T1H_NS); | ||||
|         led_t1l_ticks = (uint32_t)(ratio * WS2812_T1L_NS); | ||||
|     } else if (led_config->led_model == LED_MODEL_SK6812) { | ||||
|         led_t0h_ticks = (uint32_t)(ratio * SK6812_T0H_NS); | ||||
|         led_t0l_ticks = (uint32_t)(ratio * SK6812_T0L_NS); | ||||
|         led_t1h_ticks = (uint32_t)(ratio * SK6812_T1H_NS); | ||||
|         led_t1l_ticks = (uint32_t)(ratio * SK6812_T1L_NS); | ||||
|     } else { | ||||
|         assert(false); | ||||
|     } | ||||
|  | ||||
|     // adapter to translates the LES strip date frame into RMT symbols | ||||
|     rmt_translator_init((rmt_channel_t)dev_config->rmt_channel, ws2812_rmt_adapter); | ||||
|  | ||||
|     rmt_strip->bytes_per_pixel = bytes_per_pixel; | ||||
|     rmt_strip->rmt_channel = (rmt_channel_t)dev_config->rmt_channel; | ||||
|     rmt_strip->strip_len = led_config->max_leds; | ||||
|     rmt_strip->base.set_pixel = led_strip_rmt_set_pixel; | ||||
|     rmt_strip->base.refresh = led_strip_rmt_refresh; | ||||
|     rmt_strip->base.clear = led_strip_rmt_clear; | ||||
|     rmt_strip->base.del = led_strip_rmt_del; | ||||
|  | ||||
|     *ret_strip = &rmt_strip->base; | ||||
|     return ESP_OK; | ||||
|  | ||||
| err: | ||||
|     if (rmt_strip) { | ||||
|         free(rmt_strip); | ||||
|     } | ||||
|     return ret; | ||||
| } | ||||
| @@ -0,0 +1,146 @@ | ||||
| /* | ||||
|  * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD | ||||
|  * | ||||
|  * SPDX-License-Identifier: Apache-2.0 | ||||
|  */ | ||||
|  | ||||
| #include "esp_check.h" | ||||
| #include "led_strip_rmt_encoder.h" | ||||
|  | ||||
| static const char *TAG = "led_rmt_encoder"; | ||||
|  | ||||
| typedef struct { | ||||
|     rmt_encoder_t base; | ||||
|     rmt_encoder_t *bytes_encoder; | ||||
|     rmt_encoder_t *copy_encoder; | ||||
|     int state; | ||||
|     rmt_symbol_word_t reset_code; | ||||
| } rmt_led_strip_encoder_t; | ||||
|  | ||||
| static size_t rmt_encode_led_strip(rmt_encoder_t *encoder, rmt_channel_handle_t channel, const void *primary_data, size_t data_size, rmt_encode_state_t *ret_state) | ||||
| { | ||||
|     rmt_led_strip_encoder_t *led_encoder = __containerof(encoder, rmt_led_strip_encoder_t, base); | ||||
|     rmt_encoder_handle_t bytes_encoder = led_encoder->bytes_encoder; | ||||
|     rmt_encoder_handle_t copy_encoder = led_encoder->copy_encoder; | ||||
|     rmt_encode_state_t session_state = 0; | ||||
|     rmt_encode_state_t state = 0; | ||||
|     size_t encoded_symbols = 0; | ||||
|     switch (led_encoder->state) { | ||||
|     case 0: // send RGB data | ||||
|         encoded_symbols += bytes_encoder->encode(bytes_encoder, channel, primary_data, data_size, &session_state); | ||||
|         if (session_state & RMT_ENCODING_COMPLETE) { | ||||
|             led_encoder->state = 1; // switch to next state when current encoding session finished | ||||
|         } | ||||
|         if (session_state & RMT_ENCODING_MEM_FULL) { | ||||
|             state |= RMT_ENCODING_MEM_FULL; | ||||
|             goto out; // yield if there's no free space for encoding artifacts | ||||
|         } | ||||
|     // fall-through | ||||
|     case 1: // send reset code | ||||
|         encoded_symbols += copy_encoder->encode(copy_encoder, channel, &led_encoder->reset_code, | ||||
|                                                 sizeof(led_encoder->reset_code), &session_state); | ||||
|         if (session_state & RMT_ENCODING_COMPLETE) { | ||||
|             led_encoder->state = 0; // back to the initial encoding session | ||||
|             state |= RMT_ENCODING_COMPLETE; | ||||
|         } | ||||
|         if (session_state & RMT_ENCODING_MEM_FULL) { | ||||
|             state |= RMT_ENCODING_MEM_FULL; | ||||
|             goto out; // yield if there's no free space for encoding artifacts | ||||
|         } | ||||
|     } | ||||
| out: | ||||
|     *ret_state = state; | ||||
|     return encoded_symbols; | ||||
| } | ||||
|  | ||||
| static esp_err_t rmt_del_led_strip_encoder(rmt_encoder_t *encoder) | ||||
| { | ||||
|     rmt_led_strip_encoder_t *led_encoder = __containerof(encoder, rmt_led_strip_encoder_t, base); | ||||
|     rmt_del_encoder(led_encoder->bytes_encoder); | ||||
|     rmt_del_encoder(led_encoder->copy_encoder); | ||||
|     free(led_encoder); | ||||
|     return ESP_OK; | ||||
| } | ||||
|  | ||||
| static esp_err_t rmt_led_strip_encoder_reset(rmt_encoder_t *encoder) | ||||
| { | ||||
|     rmt_led_strip_encoder_t *led_encoder = __containerof(encoder, rmt_led_strip_encoder_t, base); | ||||
|     rmt_encoder_reset(led_encoder->bytes_encoder); | ||||
|     rmt_encoder_reset(led_encoder->copy_encoder); | ||||
|     led_encoder->state = 0; | ||||
|     return ESP_OK; | ||||
| } | ||||
|  | ||||
| esp_err_t rmt_new_led_strip_encoder(const led_strip_encoder_config_t *config, rmt_encoder_handle_t *ret_encoder) | ||||
| { | ||||
|     esp_err_t ret = ESP_OK; | ||||
|     rmt_led_strip_encoder_t *led_encoder = NULL; | ||||
|     ESP_GOTO_ON_FALSE(config && ret_encoder, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument"); | ||||
|     ESP_GOTO_ON_FALSE(config->led_model < LED_MODEL_INVALID, ESP_ERR_INVALID_ARG, err, TAG, "invalid led model"); | ||||
|     led_encoder = calloc(1, sizeof(rmt_led_strip_encoder_t)); | ||||
|     ESP_GOTO_ON_FALSE(led_encoder, ESP_ERR_NO_MEM, err, TAG, "no mem for led strip encoder"); | ||||
|     led_encoder->base.encode = rmt_encode_led_strip; | ||||
|     led_encoder->base.del = rmt_del_led_strip_encoder; | ||||
|     led_encoder->base.reset = rmt_led_strip_encoder_reset; | ||||
|     rmt_bytes_encoder_config_t bytes_encoder_config; | ||||
|     if (config->led_model == LED_MODEL_SK6812) { | ||||
|         bytes_encoder_config = (rmt_bytes_encoder_config_t) { | ||||
|             .bit0 = { | ||||
|                 .level0 = 1, | ||||
|                 .duration0 = 0.3 * config->resolution / 1000000, // T0H=0.3us | ||||
|                 .level1 = 0, | ||||
|                 .duration1 = 0.9 * config->resolution / 1000000, // T0L=0.9us | ||||
|             }, | ||||
|             .bit1 = { | ||||
|                 .level0 = 1, | ||||
|                 .duration0 = 0.6 * config->resolution / 1000000, // T1H=0.6us | ||||
|                 .level1 = 0, | ||||
|                 .duration1 = 0.6 * config->resolution / 1000000, // T1L=0.6us | ||||
|             }, | ||||
|             .flags.msb_first = 1 // SK6812 transfer bit order: G7...G0R7...R0B7...B0(W7...W0) | ||||
|         }; | ||||
|     } else if (config->led_model == LED_MODEL_WS2812) { | ||||
|         // different led strip might have its own timing requirements, following parameter is for WS2812 | ||||
|         bytes_encoder_config = (rmt_bytes_encoder_config_t) { | ||||
|             .bit0 = { | ||||
|                 .level0 = 1, | ||||
|                 .duration0 = 0.3 * config->resolution / 1000000, // T0H=0.3us | ||||
|                 .level1 = 0, | ||||
|                 .duration1 = 0.9 * config->resolution / 1000000, // T0L=0.9us | ||||
|             }, | ||||
|             .bit1 = { | ||||
|                 .level0 = 1, | ||||
|                 .duration0 = 0.9 * config->resolution / 1000000, // T1H=0.9us | ||||
|                 .level1 = 0, | ||||
|                 .duration1 = 0.3 * config->resolution / 1000000, // T1L=0.3us | ||||
|             }, | ||||
|             .flags.msb_first = 1 // WS2812 transfer bit order: G7...G0R7...R0B7...B0 | ||||
|         }; | ||||
|     } else { | ||||
|         assert(false); | ||||
|     } | ||||
|     ESP_GOTO_ON_ERROR(rmt_new_bytes_encoder(&bytes_encoder_config, &led_encoder->bytes_encoder), err, TAG, "create bytes encoder failed"); | ||||
|     rmt_copy_encoder_config_t copy_encoder_config = {}; | ||||
|     ESP_GOTO_ON_ERROR(rmt_new_copy_encoder(©_encoder_config, &led_encoder->copy_encoder), err, TAG, "create copy encoder failed"); | ||||
|  | ||||
|     uint32_t reset_ticks = config->resolution / 1000000 * 50 / 2; // reset code duration defaults to 50us | ||||
|     led_encoder->reset_code = (rmt_symbol_word_t) { | ||||
|         .level0 = 0, | ||||
|         .duration0 = reset_ticks, | ||||
|         .level1 = 0, | ||||
|         .duration1 = reset_ticks, | ||||
|     }; | ||||
|     *ret_encoder = &led_encoder->base; | ||||
|     return ESP_OK; | ||||
| err: | ||||
|     if (led_encoder) { | ||||
|         if (led_encoder->bytes_encoder) { | ||||
|             rmt_del_encoder(led_encoder->bytes_encoder); | ||||
|         } | ||||
|         if (led_encoder->copy_encoder) { | ||||
|             rmt_del_encoder(led_encoder->copy_encoder); | ||||
|         } | ||||
|         free(led_encoder); | ||||
|     } | ||||
|     return ret; | ||||
| } | ||||
| @@ -0,0 +1,38 @@ | ||||
| /* | ||||
|  * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD | ||||
|  * | ||||
|  * SPDX-License-Identifier: Apache-2.0 | ||||
|  */ | ||||
| #pragma once | ||||
|  | ||||
| #include <stdint.h> | ||||
| #include "driver/rmt_encoder.h" | ||||
| #include "led_strip_types.h" | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| #endif | ||||
|  | ||||
| /** | ||||
|  * @brief Type of led strip encoder configuration | ||||
|  */ | ||||
| typedef struct { | ||||
|     uint32_t resolution;   /*!< Encoder resolution, in Hz */ | ||||
|     led_model_t led_model; /*!< LED model */ | ||||
| } led_strip_encoder_config_t; | ||||
|  | ||||
| /** | ||||
|  * @brief Create RMT encoder for encoding LED strip pixels into RMT symbols | ||||
|  * | ||||
|  * @param[in] config Encoder configuration | ||||
|  * @param[out] ret_encoder Returned encoder handle | ||||
|  * @return | ||||
|  *      - ESP_ERR_INVALID_ARG for any invalid arguments | ||||
|  *      - ESP_ERR_NO_MEM out of memory when creating led strip encoder | ||||
|  *      - ESP_OK if creating encoder successfully | ||||
|  */ | ||||
| esp_err_t rmt_new_led_strip_encoder(const led_strip_encoder_config_t *config, rmt_encoder_handle_t *ret_encoder); | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
| @@ -1,171 +0,0 @@ | ||||
| // Copyright 2019 Espressif Systems (Shanghai) PTE LTD | ||||
| // | ||||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| // you may not use this file except in compliance with the License. | ||||
| // You may obtain a copy of the License at | ||||
| // | ||||
| //     http://www.apache.org/licenses/LICENSE-2.0 | ||||
| // | ||||
| // Unless required by applicable law or agreed to in writing, software | ||||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| // See the License for the specific language governing permissions and | ||||
| // limitations under the License. | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include <sys/cdefs.h> | ||||
| #include "esp_log.h" | ||||
| #include "esp_attr.h" | ||||
| #include "led_strip.h" | ||||
| #include "driver/rmt.h" | ||||
|  | ||||
| static const char *TAG = "ws2812"; | ||||
| #define STRIP_CHECK(a, str, goto_tag, ret_value, ...)                             \ | ||||
|     do                                                                            \ | ||||
|     {                                                                             \ | ||||
|         if (!(a))                                                                 \ | ||||
|         {                                                                         \ | ||||
|             ESP_LOGE(TAG, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \ | ||||
|             ret = ret_value;                                                      \ | ||||
|             goto goto_tag;                                                        \ | ||||
|         }                                                                         \ | ||||
|     } while (0) | ||||
|  | ||||
| #define WS2812_T0H_NS (350) | ||||
| #define WS2812_T0L_NS (1000) | ||||
| #define WS2812_T1H_NS (1000) | ||||
| #define WS2812_T1L_NS (350) | ||||
| #define WS2812_RESET_US (280) | ||||
|  | ||||
| static uint32_t ws2812_t0h_ticks = 0; | ||||
| static uint32_t ws2812_t1h_ticks = 0; | ||||
| static uint32_t ws2812_t0l_ticks = 0; | ||||
| static uint32_t ws2812_t1l_ticks = 0; | ||||
|  | ||||
| typedef struct { | ||||
|     led_strip_t parent; | ||||
|     rmt_channel_t rmt_channel; | ||||
|     uint32_t strip_len; | ||||
|     uint8_t buffer[0]; | ||||
| } ws2812_t; | ||||
|  | ||||
| /** | ||||
|  * @brief Convert RGB data to RMT format. | ||||
|  * | ||||
|  * @note For WS2812, R,G,B each contains 256 different choices (i.e. uint8_t) | ||||
|  * | ||||
|  * @param[in] src: source data, to converted to RMT format | ||||
|  * @param[in] dest: place where to store the convert result | ||||
|  * @param[in] src_size: size of source data | ||||
|  * @param[in] wanted_num: number of RMT items that want to get | ||||
|  * @param[out] translated_size: number of source data that got converted | ||||
|  * @param[out] item_num: number of RMT items which are converted from source data | ||||
|  */ | ||||
| static void IRAM_ATTR ws2812_rmt_adapter(const void *src, rmt_item32_t *dest, size_t src_size, | ||||
|         size_t wanted_num, size_t *translated_size, size_t *item_num) | ||||
| { | ||||
|     if (src == NULL || dest == NULL) { | ||||
|         *translated_size = 0; | ||||
|         *item_num = 0; | ||||
|         return; | ||||
|     } | ||||
|     const rmt_item32_t bit0 = {{{ ws2812_t0h_ticks, 1, ws2812_t0l_ticks, 0 }}}; //Logical 0 | ||||
|     const rmt_item32_t bit1 = {{{ ws2812_t1h_ticks, 1, ws2812_t1l_ticks, 0 }}}; //Logical 1 | ||||
|     size_t size = 0; | ||||
|     size_t num = 0; | ||||
|     uint8_t *psrc = (uint8_t *)src; | ||||
|     rmt_item32_t *pdest = dest; | ||||
|     while (size < src_size && num < wanted_num) { | ||||
|         for (int i = 0; i < 8; i++) { | ||||
|             // MSB first | ||||
|             if (*psrc & (1 << (7 - i))) { | ||||
|                 pdest->val =  bit1.val; | ||||
|             } else { | ||||
|                 pdest->val =  bit0.val; | ||||
|             } | ||||
|             num++; | ||||
|             pdest++; | ||||
|         } | ||||
|         size++; | ||||
|         psrc++; | ||||
|     } | ||||
|     *translated_size = size; | ||||
|     *item_num = num; | ||||
| } | ||||
|  | ||||
| static esp_err_t ws2812_set_pixel(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue) | ||||
| { | ||||
|     esp_err_t ret = ESP_OK; | ||||
|     ws2812_t *ws2812 = __containerof(strip, ws2812_t, parent); | ||||
|     STRIP_CHECK(index < ws2812->strip_len, "index out of the maximum number of leds", err, ESP_ERR_INVALID_ARG); | ||||
|     uint32_t start = index * 3; | ||||
|     // In thr order of GRB | ||||
|     ws2812->buffer[start + 0] = green & 0xFF; | ||||
|     ws2812->buffer[start + 1] = red & 0xFF; | ||||
|     ws2812->buffer[start + 2] = blue & 0xFF; | ||||
|     return ESP_OK; | ||||
| err: | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| static esp_err_t ws2812_refresh(led_strip_t *strip, uint32_t timeout_ms) | ||||
| { | ||||
|     esp_err_t ret = ESP_OK; | ||||
|     ws2812_t *ws2812 = __containerof(strip, ws2812_t, parent); | ||||
|     STRIP_CHECK(rmt_write_sample(ws2812->rmt_channel, ws2812->buffer, ws2812->strip_len * 3, true) == ESP_OK, | ||||
|                 "transmit RMT samples failed", err, ESP_FAIL); | ||||
|     return rmt_wait_tx_done(ws2812->rmt_channel, pdMS_TO_TICKS(timeout_ms)); | ||||
| err: | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| static esp_err_t ws2812_clear(led_strip_t *strip, uint32_t timeout_ms) | ||||
| { | ||||
|     ws2812_t *ws2812 = __containerof(strip, ws2812_t, parent); | ||||
|     // Write zero to turn off all leds | ||||
|     memset(ws2812->buffer, 0, ws2812->strip_len * 3); | ||||
|     return ws2812_refresh(strip, timeout_ms); | ||||
| } | ||||
|  | ||||
| static esp_err_t ws2812_del(led_strip_t *strip) | ||||
| { | ||||
|     ws2812_t *ws2812 = __containerof(strip, ws2812_t, parent); | ||||
|     free(ws2812); | ||||
|     return ESP_OK; | ||||
| } | ||||
|  | ||||
| led_strip_t *led_strip_new_rmt_ws2812(const led_strip_config_t *config) | ||||
| { | ||||
|     led_strip_t *ret = NULL; | ||||
|     STRIP_CHECK(config, "configuration can't be null", err, NULL); | ||||
|  | ||||
|     // 24 bits per led | ||||
|     uint32_t ws2812_size = sizeof(ws2812_t) + config->max_leds * 3; | ||||
|     ws2812_t *ws2812 = calloc(1, ws2812_size); | ||||
|     STRIP_CHECK(ws2812, "request memory for ws2812 failed", err, NULL); | ||||
|  | ||||
|     uint32_t counter_clk_hz = 0; | ||||
|     STRIP_CHECK(rmt_get_counter_clock((rmt_channel_t)config->dev, &counter_clk_hz) == ESP_OK, | ||||
|                 "get rmt counter clock failed", err, NULL); | ||||
|     // ns -> ticks | ||||
|     float ratio = (float)counter_clk_hz / 1e9; | ||||
|     ws2812_t0h_ticks = (uint32_t)(ratio * WS2812_T0H_NS); | ||||
|     ws2812_t0l_ticks = (uint32_t)(ratio * WS2812_T0L_NS); | ||||
|     ws2812_t1h_ticks = (uint32_t)(ratio * WS2812_T1H_NS); | ||||
|     ws2812_t1l_ticks = (uint32_t)(ratio * WS2812_T1L_NS); | ||||
|  | ||||
|     // set ws2812 to rmt adapter | ||||
|     rmt_translator_init((rmt_channel_t)config->dev, ws2812_rmt_adapter); | ||||
|  | ||||
|     ws2812->rmt_channel = (rmt_channel_t)config->dev; | ||||
|     ws2812->strip_len = config->max_leds; | ||||
|  | ||||
|     ws2812->parent.set_pixel = ws2812_set_pixel; | ||||
|     ws2812->parent.refresh = ws2812_refresh; | ||||
|     ws2812->parent.clear = ws2812_clear; | ||||
|     ws2812->parent.del = ws2812_del; | ||||
|  | ||||
|     return &ws2812->parent; | ||||
| err: | ||||
|     return ret; | ||||
| } | ||||
							
								
								
									
										209
									
								
								hw/bsp/espressif/components/led_strip/src/led_strip_spi_dev.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										209
									
								
								hw/bsp/espressif/components/led_strip/src/led_strip_spi_dev.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,209 @@ | ||||
| /* | ||||
|  * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD | ||||
|  * | ||||
|  * SPDX-License-Identifier: Apache-2.0 | ||||
|  */ | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include <sys/cdefs.h> | ||||
| #include "esp_log.h" | ||||
| #include "esp_check.h" | ||||
| #include "esp_rom_gpio.h" | ||||
| #include "soc/spi_periph.h" | ||||
| #include "led_strip.h" | ||||
| #include "led_strip_interface.h" | ||||
| #include "hal/spi_hal.h" | ||||
|  | ||||
| #define LED_STRIP_SPI_DEFAULT_RESOLUTION (2.5 * 1000 * 1000) // 2.5MHz resolution | ||||
| #define LED_STRIP_SPI_DEFAULT_TRANS_QUEUE_SIZE 4 | ||||
|  | ||||
| #define SPI_BYTES_PER_COLOR_BYTE 3 | ||||
| #define SPI_BITS_PER_COLOR_BYTE (SPI_BYTES_PER_COLOR_BYTE * 8) | ||||
|  | ||||
| static const char *TAG = "led_strip_spi"; | ||||
|  | ||||
| typedef struct { | ||||
|     led_strip_t base; | ||||
|     spi_host_device_t spi_host; | ||||
|     spi_device_handle_t spi_device; | ||||
|     uint32_t strip_len; | ||||
|     uint8_t bytes_per_pixel; | ||||
|     uint8_t pixel_buf[]; | ||||
| } led_strip_spi_obj; | ||||
|  | ||||
| // please make sure to zero-initialize the buf before calling this function | ||||
| static void __led_strip_spi_bit(uint8_t data, uint8_t *buf) | ||||
| { | ||||
|     // Each color of 1 bit is represented by 3 bits of SPI, low_level:100 ,high_level:110 | ||||
|     // So a color byte occupies 3 bytes of SPI. | ||||
|     *(buf + 2) |= data & BIT(0) ? BIT(2) | BIT(1) : BIT(2); | ||||
|     *(buf + 2) |= data & BIT(1) ? BIT(5) | BIT(4) : BIT(5); | ||||
|     *(buf + 2) |= data & BIT(2) ? BIT(7) : 0x00; | ||||
|     *(buf + 1) |= BIT(0); | ||||
|     *(buf + 1) |= data & BIT(3) ? BIT(3) | BIT(2) : BIT(3); | ||||
|     *(buf + 1) |= data & BIT(4) ? BIT(6) | BIT(5) : BIT(6); | ||||
|     *(buf + 0) |= data & BIT(5) ? BIT(1) | BIT(0) : BIT(1); | ||||
|     *(buf + 0) |= data & BIT(6) ? BIT(4) | BIT(3) : BIT(4); | ||||
|     *(buf + 0) |= data & BIT(7) ? BIT(7) | BIT(6) : BIT(7); | ||||
| } | ||||
|  | ||||
| static esp_err_t led_strip_spi_set_pixel(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue) | ||||
| { | ||||
|     led_strip_spi_obj *spi_strip = __containerof(strip, led_strip_spi_obj, base); | ||||
|     ESP_RETURN_ON_FALSE(index < spi_strip->strip_len, ESP_ERR_INVALID_ARG, TAG, "index out of maximum number of LEDs"); | ||||
|     // LED_PIXEL_FORMAT_GRB takes 72bits(9bytes) | ||||
|     uint32_t start = index * spi_strip->bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE; | ||||
|     memset(spi_strip->pixel_buf + start, 0, spi_strip->bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE); | ||||
|     __led_strip_spi_bit(green, &spi_strip->pixel_buf[start]); | ||||
|     __led_strip_spi_bit(red, &spi_strip->pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE]); | ||||
|     __led_strip_spi_bit(blue, &spi_strip->pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * 2]); | ||||
|     if (spi_strip->bytes_per_pixel > 3) { | ||||
|         __led_strip_spi_bit(0, &spi_strip->pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * 3]); | ||||
|     } | ||||
|     return ESP_OK; | ||||
| } | ||||
|  | ||||
| static esp_err_t led_strip_spi_set_pixel_rgbw(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue, uint32_t white) | ||||
| { | ||||
|     led_strip_spi_obj *spi_strip = __containerof(strip, led_strip_spi_obj, base); | ||||
|     ESP_RETURN_ON_FALSE(index < spi_strip->strip_len, ESP_ERR_INVALID_ARG, TAG, "index out of maximum number of LEDs"); | ||||
|     ESP_RETURN_ON_FALSE(spi_strip->bytes_per_pixel == 4, ESP_ERR_INVALID_ARG, TAG, "wrong LED pixel format, expected 4 bytes per pixel"); | ||||
|     // LED_PIXEL_FORMAT_GRBW takes 96bits(12bytes) | ||||
|     uint32_t start = index * spi_strip->bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE; | ||||
|     // SK6812 component order is GRBW | ||||
|     memset(spi_strip->pixel_buf + start, 0, spi_strip->bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE); | ||||
|     __led_strip_spi_bit(green, &spi_strip->pixel_buf[start]); | ||||
|     __led_strip_spi_bit(red, &spi_strip->pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE]); | ||||
|     __led_strip_spi_bit(blue, &spi_strip->pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * 2]); | ||||
|     __led_strip_spi_bit(white, &spi_strip->pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * 3]); | ||||
|  | ||||
|     return ESP_OK; | ||||
| } | ||||
|  | ||||
| static esp_err_t led_strip_spi_refresh(led_strip_t *strip) | ||||
| { | ||||
|     led_strip_spi_obj *spi_strip = __containerof(strip, led_strip_spi_obj, base); | ||||
|     spi_transaction_t tx_conf; | ||||
|     memset(&tx_conf, 0, sizeof(tx_conf)); | ||||
|  | ||||
|     tx_conf.length = spi_strip->strip_len * spi_strip->bytes_per_pixel * SPI_BITS_PER_COLOR_BYTE; | ||||
|     tx_conf.tx_buffer = spi_strip->pixel_buf; | ||||
|     tx_conf.rx_buffer = NULL; | ||||
|     ESP_RETURN_ON_ERROR(spi_device_transmit(spi_strip->spi_device, &tx_conf), TAG, "transmit pixels by SPI failed"); | ||||
|  | ||||
|     return ESP_OK; | ||||
| } | ||||
|  | ||||
| static esp_err_t led_strip_spi_clear(led_strip_t *strip) | ||||
| { | ||||
|     led_strip_spi_obj *spi_strip = __containerof(strip, led_strip_spi_obj, base); | ||||
|     //Write zero to turn off all leds | ||||
|     memset(spi_strip->pixel_buf, 0, spi_strip->strip_len * spi_strip->bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE); | ||||
|     uint8_t *buf = spi_strip->pixel_buf; | ||||
|     for (int index = 0; index < spi_strip->strip_len * spi_strip->bytes_per_pixel; index++) { | ||||
|         __led_strip_spi_bit(0, buf); | ||||
|         buf += SPI_BYTES_PER_COLOR_BYTE; | ||||
|     } | ||||
|  | ||||
|     return led_strip_spi_refresh(strip); | ||||
| } | ||||
|  | ||||
| static esp_err_t led_strip_spi_del(led_strip_t *strip) | ||||
| { | ||||
|     led_strip_spi_obj *spi_strip = __containerof(strip, led_strip_spi_obj, base); | ||||
|  | ||||
|     ESP_RETURN_ON_ERROR(spi_bus_remove_device(spi_strip->spi_device), TAG, "delete spi device failed"); | ||||
|     ESP_RETURN_ON_ERROR(spi_bus_free(spi_strip->spi_host), TAG, "free spi bus failed"); | ||||
|  | ||||
|     free(spi_strip); | ||||
|     return ESP_OK; | ||||
| } | ||||
|  | ||||
| esp_err_t led_strip_new_spi_device(const led_strip_config_t *led_config, const led_strip_spi_config_t *spi_config, led_strip_handle_t *ret_strip) | ||||
| { | ||||
|     led_strip_spi_obj *spi_strip = NULL; | ||||
|     esp_err_t ret = ESP_OK; | ||||
|     ESP_GOTO_ON_FALSE(led_config && spi_config && ret_strip, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument"); | ||||
|     ESP_GOTO_ON_FALSE(led_config->led_pixel_format < LED_PIXEL_FORMAT_INVALID, ESP_ERR_INVALID_ARG, err, TAG, "invalid led_pixel_format"); | ||||
|     uint8_t bytes_per_pixel = 3; | ||||
|     if (led_config->led_pixel_format == LED_PIXEL_FORMAT_GRBW) { | ||||
|         bytes_per_pixel = 4; | ||||
|     } else if (led_config->led_pixel_format == LED_PIXEL_FORMAT_GRB) { | ||||
|         bytes_per_pixel = 3; | ||||
|     } else { | ||||
|         assert(false); | ||||
|     } | ||||
|     uint32_t mem_caps = MALLOC_CAP_DEFAULT; | ||||
|     if (spi_config->flags.with_dma) { | ||||
|         // DMA buffer must be placed in internal SRAM | ||||
|         mem_caps |= MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA; | ||||
|     } | ||||
|     spi_strip = heap_caps_calloc(1, sizeof(led_strip_spi_obj) + led_config->max_leds * bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE, mem_caps); | ||||
|  | ||||
|     ESP_GOTO_ON_FALSE(spi_strip, ESP_ERR_NO_MEM, err, TAG, "no mem for spi strip"); | ||||
|  | ||||
|     spi_strip->spi_host = spi_config->spi_bus; | ||||
|     // for backward compatibility, if the user does not set the clk_src, use the default value | ||||
|     spi_clock_source_t clk_src = SPI_CLK_SRC_DEFAULT; | ||||
|     if (spi_config->clk_src) { | ||||
|         clk_src = spi_config->clk_src; | ||||
|     } | ||||
|  | ||||
|     spi_bus_config_t spi_bus_cfg = { | ||||
|         .mosi_io_num = led_config->strip_gpio_num, | ||||
|         //Only use MOSI to generate the signal, set -1 when other pins are not used. | ||||
|         .miso_io_num = -1, | ||||
|         .sclk_io_num = -1, | ||||
|         .quadwp_io_num = -1, | ||||
|         .quadhd_io_num = -1, | ||||
|         .max_transfer_sz = led_config->max_leds * bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE, | ||||
|     }; | ||||
|     ESP_GOTO_ON_ERROR(spi_bus_initialize(spi_strip->spi_host, &spi_bus_cfg, spi_config->flags.with_dma ? SPI_DMA_CH_AUTO : SPI_DMA_DISABLED), err, TAG, "create SPI bus failed"); | ||||
|  | ||||
|     if (led_config->flags.invert_out == true) { | ||||
|         esp_rom_gpio_connect_out_signal(led_config->strip_gpio_num, spi_periph_signal[spi_strip->spi_host].spid_out, true, false); | ||||
|     } | ||||
|  | ||||
|     spi_device_interface_config_t spi_dev_cfg = { | ||||
|         .clock_source = clk_src, | ||||
|         .command_bits = 0, | ||||
|         .address_bits = 0, | ||||
|         .dummy_bits = 0, | ||||
|         .clock_speed_hz = LED_STRIP_SPI_DEFAULT_RESOLUTION, | ||||
|         .mode = 0, | ||||
|         //set -1 when CS is not used | ||||
|         .spics_io_num = -1, | ||||
|         .queue_size = LED_STRIP_SPI_DEFAULT_TRANS_QUEUE_SIZE, | ||||
|     }; | ||||
|  | ||||
|     ESP_GOTO_ON_ERROR(spi_bus_add_device(spi_strip->spi_host, &spi_dev_cfg, &spi_strip->spi_device), err, TAG, "Failed to add spi device"); | ||||
|  | ||||
|     int clock_resolution_khz = 0; | ||||
|     spi_device_get_actual_freq(spi_strip->spi_device, &clock_resolution_khz); | ||||
|     // TODO: ideally we should decide the SPI_BYTES_PER_COLOR_BYTE by the real clock resolution | ||||
|     // But now, let's fixed the resolution, the downside is, we don't support a clock source whose frequency is not multiple of LED_STRIP_SPI_DEFAULT_RESOLUTION | ||||
|     ESP_GOTO_ON_FALSE(clock_resolution_khz == LED_STRIP_SPI_DEFAULT_RESOLUTION / 1000, ESP_ERR_NOT_SUPPORTED, err, | ||||
|                       TAG, "unsupported clock resolution:%dKHz", clock_resolution_khz); | ||||
|  | ||||
|     spi_strip->bytes_per_pixel = bytes_per_pixel; | ||||
|     spi_strip->strip_len = led_config->max_leds; | ||||
|     spi_strip->base.set_pixel = led_strip_spi_set_pixel; | ||||
|     spi_strip->base.set_pixel_rgbw = led_strip_spi_set_pixel_rgbw; | ||||
|     spi_strip->base.refresh = led_strip_spi_refresh; | ||||
|     spi_strip->base.clear = led_strip_spi_clear; | ||||
|     spi_strip->base.del = led_strip_spi_del; | ||||
|  | ||||
|     *ret_strip = &spi_strip->base; | ||||
|     return ESP_OK; | ||||
| err: | ||||
|     if (spi_strip) { | ||||
|         if (spi_strip->spi_device) { | ||||
|             spi_bus_remove_device(spi_strip->spi_device); | ||||
|         } | ||||
|         if (spi_strip->spi_host) { | ||||
|             spi_bus_free(spi_strip->spi_host); | ||||
|         } | ||||
|         free(spi_strip); | ||||
|     } | ||||
|     return ret; | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	 hathach
					hathach