123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409 |
- /* ----------------------------------------------------------------------------
- File: led_strip.c
- Author(s): Lucas Bruder <LBruder@me.com>
- Date Created: 11/23/2016
- Last modified: 11/26/2016
- Updated: C. Rohs - The update thread now
- only runs when signalled. The double buffer code was modified to copy on show
- instead of the ping pong buffer that destroyed the buffers contents.
- The current code is not thread safe, but is more performant, and the thread
- safety does not matter the was it is currently used.
- Description: LED Library for driving various led strips on ESP32.
- This library uses double buffering to display the LEDs.
- ------------------------------------------------------------------------- */
- #include "led_strip.h"
- #include "freertos/task.h"
- #include <string.h>
- #define LED_STRIP_TASK_SIZE (1024)
- #define LED_STRIP_TASK_PRIORITY (configMAX_PRIORITIES - 1)
- #define LED_STRIP_REFRESH_PERIOD_MS (30U) // TODO: add as parameter to led_strip_init
- #define LED_STRIP_NUM_RMT_ITEMS_PER_LED (24U) // Assumes 24 bit color for each led
- // RMT Clock source is @ 80 MHz. Dividing it by 8 gives us 10 MHz frequency, or 100ns period.
- #define LED_STRIP_RMT_CLK_DIV (8)
- /****************************
- WS2812 Timing
- ****************************/
- #define LED_STRIP_RMT_TICKS_BIT_1_HIGH_WS2812 9 // 900ns (900ns +/- 150ns per datasheet)
- #define LED_STRIP_RMT_TICKS_BIT_1_LOW_WS2812 3 // 300ns (350ns +/- 150ns per datasheet)
- #define LED_STRIP_RMT_TICKS_BIT_0_HIGH_WS2812 3 // 300ns (350ns +/- 150ns per datasheet)
- #define LED_STRIP_RMT_TICKS_BIT_0_LOW_WS2812 9 // 900ns (900ns +/- 150ns per datasheet)
- /****************************
- SK6812 Timing
- ****************************/
- #define LED_STRIP_RMT_TICKS_BIT_1_HIGH_SK6812 6
- #define LED_STRIP_RMT_TICKS_BIT_1_LOW_SK6812 6
- #define LED_STRIP_RMT_TICKS_BIT_0_HIGH_SK6812 3
- #define LED_STRIP_RMT_TICKS_BIT_0_LOW_SK6812 9
- /****************************
- APA106 Timing
- ****************************/
- #define LED_STRIP_RMT_TICKS_BIT_1_HIGH_APA106 14 // 1.36us +/- 150ns per datasheet
- #define LED_STRIP_RMT_TICKS_BIT_1_LOW_APA106 3 // 350ns +/- 150ns per datasheet
- #define LED_STRIP_RMT_TICKS_BIT_0_HIGH_APA106 3 // 350ns +/- 150ns per datasheet
- #define LED_STRIP_RMT_TICKS_BIT_0_LOW_APA106 14 // 1.36us +/- 150ns per datasheet
- // Function pointer for generating waveforms based on different LED drivers
- typedef void (*led_fill_rmt_items_fn)(struct led_color_t *led_strip_buf, rmt_item32_t *rmt_items, uint32_t led_strip_length);
- static inline void led_strip_fill_item_level(rmt_item32_t* item, int high_ticks, int low_ticks)
- {
- item->level0 = 1;
- item->duration0 = high_ticks;
- item->level1 = 0;
- item->duration1 = low_ticks;
- }
- static inline void led_strip_rmt_bit_1_sk6812(rmt_item32_t* item)
- {
- led_strip_fill_item_level(item, LED_STRIP_RMT_TICKS_BIT_1_HIGH_SK6812, LED_STRIP_RMT_TICKS_BIT_1_LOW_SK6812);
- }
- static inline void led_strip_rmt_bit_0_sk6812(rmt_item32_t* item)
- {
- led_strip_fill_item_level(item, LED_STRIP_RMT_TICKS_BIT_0_HIGH_SK6812, LED_STRIP_RMT_TICKS_BIT_0_LOW_SK6812);
- }
- static void led_strip_fill_rmt_items_sk6812(struct led_color_t *led_strip_buf, rmt_item32_t *rmt_items, uint32_t led_strip_length)
- {
- uint32_t rmt_items_index = 0;
- for (uint32_t led_index = 0; led_index < led_strip_length; led_index++) {
- struct led_color_t led_color = led_strip_buf[led_index];
- for (uint8_t bit = 8; bit != 0; bit--) {
- uint8_t bit_set = (led_color.green >> (bit - 1)) & 1;
- if(bit_set) {
- led_strip_rmt_bit_1_sk6812(&(rmt_items[rmt_items_index]));
- } else {
- led_strip_rmt_bit_0_sk6812(&(rmt_items[rmt_items_index]));
- }
- rmt_items_index++;
- }
- for (uint8_t bit = 8; bit != 0; bit--) {
- uint8_t bit_set = (led_color.red >> (bit - 1)) & 1;
- if(bit_set) {
- led_strip_rmt_bit_1_sk6812(&(rmt_items[rmt_items_index]));
- } else {
- led_strip_rmt_bit_0_sk6812(&(rmt_items[rmt_items_index]));
- }
- rmt_items_index++;
- }
- for (uint8_t bit = 8; bit != 0; bit--) {
- uint8_t bit_set = (led_color.blue >> (bit - 1)) & 1;
- if(bit_set) {
- led_strip_rmt_bit_1_sk6812(&(rmt_items[rmt_items_index]));
- } else {
- led_strip_rmt_bit_0_sk6812(&(rmt_items[rmt_items_index]));
- }
- rmt_items_index++;
- }
- }
- }
- static inline void led_strip_rmt_bit_1_ws2812(rmt_item32_t* item)
- {
- led_strip_fill_item_level(item, LED_STRIP_RMT_TICKS_BIT_1_HIGH_WS2812, LED_STRIP_RMT_TICKS_BIT_1_LOW_WS2812);
- }
- static inline void led_strip_rmt_bit_0_ws2812(rmt_item32_t* item)
- {
- led_strip_fill_item_level(item, LED_STRIP_RMT_TICKS_BIT_0_HIGH_WS2812, LED_STRIP_RMT_TICKS_BIT_0_LOW_WS2812);
- }
- static void led_strip_fill_rmt_items_ws2812(struct led_color_t *led_strip_buf, rmt_item32_t *rmt_items, uint32_t led_strip_length)
- {
- uint32_t rmt_items_index = 0;
- for (uint32_t led_index = 0; led_index < led_strip_length; led_index++) {
- struct led_color_t led_color = led_strip_buf[led_index];
- for (uint8_t bit = 8; bit != 0; bit--) {
- uint8_t bit_set = (led_color.green >> (bit - 1)) & 1;
- if(bit_set) {
- led_strip_rmt_bit_1_ws2812(&(rmt_items[rmt_items_index]));
- } else {
- led_strip_rmt_bit_0_ws2812(&(rmt_items[rmt_items_index]));
- }
- rmt_items_index++;
- }
- for (uint8_t bit = 8; bit != 0; bit--) {
- uint8_t bit_set = (led_color.red >> (bit - 1)) & 1;
- if(bit_set) {
- led_strip_rmt_bit_1_ws2812(&(rmt_items[rmt_items_index]));
- } else {
- led_strip_rmt_bit_0_ws2812(&(rmt_items[rmt_items_index]));
- }
- rmt_items_index++;
- }
- for (uint8_t bit = 8; bit != 0; bit--) {
- uint8_t bit_set = (led_color.blue >> (bit - 1)) & 1;
- if(bit_set) {
- led_strip_rmt_bit_1_ws2812(&(rmt_items[rmt_items_index]));
- } else {
- led_strip_rmt_bit_0_ws2812(&(rmt_items[rmt_items_index]));
- }
- rmt_items_index++;
- }
- }
- }
- static inline void led_strip_rmt_bit_1_apa106(rmt_item32_t* item)
- {
- led_strip_fill_item_level(item, LED_STRIP_RMT_TICKS_BIT_1_HIGH_APA106, LED_STRIP_RMT_TICKS_BIT_1_LOW_APA106);
- }
- static inline void led_strip_rmt_bit_0_apa106(rmt_item32_t* item)
- {
- led_strip_fill_item_level(item, LED_STRIP_RMT_TICKS_BIT_0_HIGH_APA106, LED_STRIP_RMT_TICKS_BIT_0_LOW_APA106);
- }
- static void led_strip_fill_rmt_items_apa106(struct led_color_t *led_strip_buf, rmt_item32_t *rmt_items, uint32_t led_strip_length)
- {
- uint32_t rmt_items_index = 0;
- for (uint32_t led_index = 0; led_index < led_strip_length; led_index++) {
- struct led_color_t led_color = led_strip_buf[led_index];
- for (uint8_t bit = 8; bit != 0; bit--) {
- uint8_t bit_set = (led_color.red >> (bit - 1)) & 1;
- if(bit_set) {
- led_strip_rmt_bit_1_apa106(&(rmt_items[rmt_items_index]));
- } else {
- led_strip_rmt_bit_0_apa106(&(rmt_items[rmt_items_index]));
- }
- rmt_items_index++;
- }
- for (uint8_t bit = 8; bit != 0; bit--) {
- uint8_t bit_set = (led_color.green >> (bit - 1)) & 1;
- if(bit_set) {
- led_strip_rmt_bit_1_apa106(&(rmt_items[rmt_items_index]));
- } else {
- led_strip_rmt_bit_0_apa106(&(rmt_items[rmt_items_index]));
- }
- rmt_items_index++;
- }
- for (uint8_t bit = 8; bit != 0; bit--) {
- uint8_t bit_set = (led_color.blue >> (bit - 1)) & 1;
- if(bit_set) {
- led_strip_rmt_bit_1_apa106(&(rmt_items[rmt_items_index]));
- } else {
- led_strip_rmt_bit_0_apa106(&(rmt_items[rmt_items_index]));
- }
- rmt_items_index++;
- }
- }
- }
- static void led_strip_task(void *arg)
- {
- struct led_strip_t *led_strip = (struct led_strip_t *)arg;
- led_fill_rmt_items_fn led_make_waveform = NULL;
- size_t num_items_malloc = (LED_STRIP_NUM_RMT_ITEMS_PER_LED * led_strip->led_strip_length);
- rmt_item32_t *rmt_items = (rmt_item32_t*) malloc(sizeof(rmt_item32_t) * num_items_malloc);
- if (!rmt_items) {
- vTaskDelete(NULL);
- }
- switch (led_strip->rgb_led_type) {
- case RGB_LED_TYPE_WS2812:
- led_make_waveform = led_strip_fill_rmt_items_ws2812;
- break;
- case RGB_LED_TYPE_SK6812:
- led_make_waveform = led_strip_fill_rmt_items_sk6812;
- break;
- case RGB_LED_TYPE_APA106:
- led_make_waveform = led_strip_fill_rmt_items_apa106;
- break;
- default:
- // Will avoid keeping it point to NULL
- led_make_waveform = led_strip_fill_rmt_items_ws2812;
- break;
- };
- for(;;) {
- rmt_wait_tx_done(led_strip->rmt_channel, portMAX_DELAY);
- vTaskDelay(LED_STRIP_REFRESH_PERIOD_MS / portTICK_PERIOD_MS);
- xSemaphoreTake(led_strip->access_semaphore, portMAX_DELAY);
- led_make_waveform(led_strip->led_strip_working,
- rmt_items,
- led_strip->led_strip_length);
- rmt_write_items(led_strip->rmt_channel,
- rmt_items,
- num_items_malloc,
- false);
- }
- if (rmt_items) {
- free(rmt_items);
- }
- vTaskDelete(NULL);
- }
- static bool led_strip_init_rmt(struct led_strip_t *led_strip)
- {
- rmt_config_t rmt_cfg = {
- .rmt_mode = RMT_MODE_TX,
- .channel = led_strip->rmt_channel,
- .clk_div = LED_STRIP_RMT_CLK_DIV,
- .gpio_num = led_strip->gpio,
- .mem_block_num = 1,
- .tx_config = {
- .loop_en = false,
- .carrier_freq_hz = 100, // Not used, but has to be set to avoid divide by 0 err
- .carrier_duty_percent = 50,
- .carrier_level = RMT_CARRIER_LEVEL_LOW,
- .carrier_en = false,
- .idle_level = RMT_IDLE_LEVEL_LOW,
- .idle_output_en = true,
- }
- };
- esp_err_t cfg_ok = rmt_config(&rmt_cfg);
- if (cfg_ok != ESP_OK) {
- return false;
- }
- esp_err_t install_ok = rmt_driver_install(rmt_cfg.channel, 0, 0);
- if (install_ok != ESP_OK) {
- return false;
- }
- return true;
- }
- bool led_strip_init(struct led_strip_t *led_strip)
- {
- static EXT_RAM_ATTR TaskHandle_t task_created;
- StaticTask_t* xTaskBuffer = (StaticTask_t*) heap_caps_malloc(sizeof(StaticTask_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
- static EXT_RAM_ATTR StackType_t xStack[LED_STRIP_TASK_SIZE] __attribute__ ((aligned (4)));
- if ((led_strip == NULL) ||
- (led_strip->rmt_channel >= RMT_CHANNEL_MAX) ||
- (led_strip->gpio > GPIO_NUM_33) ||
- (led_strip->led_strip_working == NULL) ||
- (led_strip->led_strip_showing == NULL) ||
- (led_strip->led_strip_length == 0) ||
- (led_strip->access_semaphore == NULL)) {
- return false;
- }
- if(led_strip->led_strip_working == led_strip->led_strip_showing) {
- return false;
- }
- memset(led_strip->led_strip_working, 0, sizeof(struct led_color_t) * led_strip->led_strip_length);
- memset(led_strip->led_strip_showing, 0, sizeof(struct led_color_t) * led_strip->led_strip_length);
- bool init_rmt = led_strip_init_rmt(led_strip);
- if (!init_rmt) {
- return false;
- }
- xSemaphoreGive(led_strip->access_semaphore);
- task_created = xTaskCreateStatic(led_strip_task,
- "led_strip_task",
- LED_STRIP_TASK_SIZE,
- led_strip,
- LED_STRIP_TASK_PRIORITY,
- xStack, xTaskBuffer);
- if (!task_created) {
- return false;
- }
- return true;
- }
- bool led_strip_set_pixel_color(struct led_strip_t *led_strip, uint32_t pixel_num, struct led_color_t *color)
- {
- bool set_led_success = true;
- if ((!led_strip) || (!color) || (pixel_num > led_strip->led_strip_length)) {
- return false;
- }
- led_strip->led_strip_working[pixel_num] = *color;
- return set_led_success;
- }
- bool led_strip_set_pixel_rgb(struct led_strip_t *led_strip, uint32_t pixel_num, uint8_t red, uint8_t green, uint8_t blue)
- {
- bool set_led_success = true;
- if ((!led_strip) || (pixel_num > led_strip->led_strip_length)) {
- return false;
- }
- led_strip->led_strip_working[pixel_num].red = red;
- led_strip->led_strip_working[pixel_num].green = green;
- led_strip->led_strip_working[pixel_num].blue = blue;
- return set_led_success;
- }
- bool led_strip_get_pixel_color(struct led_strip_t *led_strip, uint32_t pixel_num, struct led_color_t *color)
- {
- bool get_success = true;
- if ((!led_strip) ||
- (pixel_num > led_strip->led_strip_length) ||
- (!color)) {
- color = NULL;
- return false;
- }
- *color = led_strip->led_strip_working[pixel_num];
- return get_success;
- }
- /**
- * Updates the led buffer to be shown
- */
- bool led_strip_show(struct led_strip_t *led_strip)
- {
- bool success = true;
- if (!led_strip) {
- return false;
- }
- /* copy the current buffer for display */
- memcpy(led_strip->led_strip_showing,led_strip->led_strip_working, sizeof(struct led_color_t) * led_strip->led_strip_length);
- xSemaphoreGive(led_strip->access_semaphore);
- return success;
- }
- /**
- * Clears the LED strip
- */
- bool led_strip_clear(struct led_strip_t *led_strip)
- {
- bool success = true;
- if (!led_strip) {
- return false;
- }
- memset(led_strip->led_strip_working,
- 0,
- sizeof(struct led_color_t) * led_strip->led_strip_length);
- return success;
- }
|