blinkenstrip

Documentation: http://frombelow.net/projects/blinkenstrip/
Clone: git clone https://git.frombelow.net/blinkenstrip.git
Log | Files | Refs | README | LICENSE

led_strip.c (16346B)


      1 /*  ----------------------------------------------------------------------------
      2     File: led_strip.c
      3     Author(s):  Lucas Bruder <LBruder@me.com>
      4     Date Created: 11/23/2016
      5     Last modified: 11/26/2016
      6 
      7     Description: LED Library for driving various led strips on ESP32.
      8 
      9     This library uses double buffering to display the LEDs.
     10     If the driver is showing buffer 1, any calls to led_strip_set_pixel_color
     11     will write to buffer 2. When it's time to drive the pixels on the strip, it
     12     refers to buffer 1. 
     13     When led_strip_show is called, it will switch to displaying the pixels
     14     from buffer 2 and will clear buffer 1. Any writes will now happen on buffer 1 
     15     and the task will look at buffer 2 for refreshing the LEDs
     16     ------------------------------------------------------------------------- */
     17 
     18 /* Some minor changes by Gerd Beuster (gb) to work with blinkenstrip. */
     19 
     20 #include "led_strip.h"
     21 #include "freertos/task.h"
     22 #include <string.h>
     23 
     24 // gb: Need handle to web server task
     25 #include "webserver.h"
     26 // gb: LED_STRIP_TASK_SIZE was to small. Increased size in order
     27 // to avoid buffer overflows.
     28 /* #define LED_STRIP_TASK_SIZE             (512) */
     29 #define LED_STRIP_TASK_SIZE             (1024)
     30 #define LED_STRIP_TASK_PRIORITY         (configMAX_PRIORITIES - 1)
     31 
     32 #define LED_STRIP_REFRESH_PERIOD_MS     (30U) // TODO: add as parameter to led_strip_init
     33 
     34 #define LED_STRIP_NUM_RMT_ITEMS_PER_LED (24U) // Assumes 24 bit color for each led
     35 
     36 // RMT Clock source is @ 80 MHz. Dividing it by 8 gives us 10 MHz frequency, or 100ns period.
     37 #define LED_STRIP_RMT_CLK_DIV (8)
     38 
     39 /****************************
     40         WS2812 Timing
     41  ****************************/
     42 #define LED_STRIP_RMT_TICKS_BIT_1_HIGH_WS2812 9 // 900ns (900ns +/- 150ns per datasheet)
     43 #define LED_STRIP_RMT_TICKS_BIT_1_LOW_WS2812  3 // 300ns (350ns +/- 150ns per datasheet)
     44 #define LED_STRIP_RMT_TICKS_BIT_0_HIGH_WS2812 3 // 300ns (350ns +/- 150ns per datasheet)
     45 #define LED_STRIP_RMT_TICKS_BIT_0_LOW_WS2812  9 // 900ns (900ns +/- 150ns per datasheet)
     46 
     47 /****************************
     48         SK6812 Timing
     49  ****************************/
     50 #define LED_STRIP_RMT_TICKS_BIT_1_HIGH_SK6812 6
     51 #define LED_STRIP_RMT_TICKS_BIT_1_LOW_SK6812  6
     52 #define LED_STRIP_RMT_TICKS_BIT_0_HIGH_SK6812 3
     53 #define LED_STRIP_RMT_TICKS_BIT_0_LOW_SK6812  9
     54 
     55 /****************************
     56         APA106 Timing
     57  ****************************/
     58 #define LED_STRIP_RMT_TICKS_BIT_1_HIGH_APA106 14 // 1.36us +/- 150ns per datasheet
     59 #define LED_STRIP_RMT_TICKS_BIT_1_LOW_APA106   3 // 350ns +/- 150ns per datasheet
     60 #define LED_STRIP_RMT_TICKS_BIT_0_HIGH_APA106  3 // 350ns +/- 150ns per datasheet
     61 #define LED_STRIP_RMT_TICKS_BIT_0_LOW_APA106  14 // 1.36us +/- 150ns per datasheet
     62 
     63 // Function pointer for generating waveforms based on different LED drivers
     64 typedef void (*led_fill_rmt_items_fn)(struct led_color_t *led_strip_buf, rmt_item32_t *rmt_items, uint32_t led_strip_length);
     65 
     66 static inline void led_strip_fill_item_level(rmt_item32_t* item, int high_ticks, int low_ticks)
     67 {
     68     item->level0 = 1;
     69     item->duration0 = high_ticks;
     70     item->level1 = 0;
     71     item->duration1 = low_ticks;
     72 }
     73 
     74 static inline void led_strip_rmt_bit_1_sk6812(rmt_item32_t* item)
     75 {
     76     led_strip_fill_item_level(item, LED_STRIP_RMT_TICKS_BIT_1_HIGH_SK6812, LED_STRIP_RMT_TICKS_BIT_1_LOW_SK6812);
     77 }
     78 
     79 static inline void led_strip_rmt_bit_0_sk6812(rmt_item32_t* item)
     80 {
     81     led_strip_fill_item_level(item, LED_STRIP_RMT_TICKS_BIT_0_HIGH_SK6812, LED_STRIP_RMT_TICKS_BIT_0_LOW_SK6812);
     82 }
     83 
     84 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)
     85 {
     86     uint32_t rmt_items_index = 0;
     87     for (uint32_t led_index = 0; led_index < led_strip_length; led_index++) {
     88         struct led_color_t led_color = led_strip_buf[led_index];
     89 
     90         for (uint8_t bit = 8; bit != 0; bit--) {
     91             uint8_t bit_set = (led_color.green >> (bit - 1)) & 1;
     92             if(bit_set) {
     93                 led_strip_rmt_bit_1_sk6812(&(rmt_items[rmt_items_index]));
     94             } else {
     95                 led_strip_rmt_bit_0_sk6812(&(rmt_items[rmt_items_index]));
     96             }
     97             rmt_items_index++;
     98         }
     99         for (uint8_t bit = 8; bit != 0; bit--) {
    100             uint8_t bit_set = (led_color.red >> (bit - 1)) & 1;
    101             if(bit_set) {
    102                 led_strip_rmt_bit_1_sk6812(&(rmt_items[rmt_items_index]));
    103             } else {
    104                 led_strip_rmt_bit_0_sk6812(&(rmt_items[rmt_items_index]));
    105             }
    106             rmt_items_index++;
    107         }
    108         for (uint8_t bit = 8; bit != 0; bit--) {
    109             uint8_t bit_set = (led_color.blue >> (bit - 1)) & 1;
    110             if(bit_set) {
    111                 led_strip_rmt_bit_1_sk6812(&(rmt_items[rmt_items_index]));
    112             } else {
    113                 led_strip_rmt_bit_0_sk6812(&(rmt_items[rmt_items_index]));
    114             }
    115             rmt_items_index++;
    116         }
    117     }
    118 }
    119 
    120 static inline void led_strip_rmt_bit_1_ws2812(rmt_item32_t* item)
    121 {
    122     led_strip_fill_item_level(item, LED_STRIP_RMT_TICKS_BIT_1_HIGH_WS2812, LED_STRIP_RMT_TICKS_BIT_1_LOW_WS2812);
    123 }
    124 
    125 static inline void led_strip_rmt_bit_0_ws2812(rmt_item32_t* item)
    126 {
    127     led_strip_fill_item_level(item, LED_STRIP_RMT_TICKS_BIT_0_HIGH_WS2812, LED_STRIP_RMT_TICKS_BIT_0_LOW_WS2812);
    128 }
    129 
    130 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)
    131 {
    132     uint32_t rmt_items_index = 0;
    133     for (uint32_t led_index = 0; led_index < led_strip_length; led_index++) {
    134         struct led_color_t led_color = led_strip_buf[led_index];
    135 
    136         for (uint8_t bit = 8; bit != 0; bit--) {
    137             uint8_t bit_set = (led_color.green >> (bit - 1)) & 1;
    138             if(bit_set) {
    139                 led_strip_rmt_bit_1_ws2812(&(rmt_items[rmt_items_index]));
    140             } else {
    141                 led_strip_rmt_bit_0_ws2812(&(rmt_items[rmt_items_index]));
    142             }
    143             rmt_items_index++;
    144         }
    145         for (uint8_t bit = 8; bit != 0; bit--) {
    146             uint8_t bit_set = (led_color.red >> (bit - 1)) & 1;
    147             if(bit_set) {
    148                 led_strip_rmt_bit_1_ws2812(&(rmt_items[rmt_items_index]));
    149             } else {
    150                 led_strip_rmt_bit_0_ws2812(&(rmt_items[rmt_items_index]));
    151             }
    152             rmt_items_index++;
    153         }
    154         for (uint8_t bit = 8; bit != 0; bit--) {
    155             uint8_t bit_set = (led_color.blue >> (bit - 1)) & 1;
    156             if(bit_set) {
    157                 led_strip_rmt_bit_1_ws2812(&(rmt_items[rmt_items_index]));
    158             } else {
    159                 led_strip_rmt_bit_0_ws2812(&(rmt_items[rmt_items_index]));
    160             }
    161             rmt_items_index++;
    162         }
    163     }
    164 }
    165 
    166 static inline void led_strip_rmt_bit_1_apa106(rmt_item32_t* item)
    167 {
    168     led_strip_fill_item_level(item, LED_STRIP_RMT_TICKS_BIT_1_HIGH_APA106, LED_STRIP_RMT_TICKS_BIT_1_LOW_APA106);
    169 }
    170 
    171 static inline void led_strip_rmt_bit_0_apa106(rmt_item32_t* item)
    172 {
    173     led_strip_fill_item_level(item, LED_STRIP_RMT_TICKS_BIT_0_HIGH_APA106, LED_STRIP_RMT_TICKS_BIT_0_LOW_APA106);
    174 }
    175 
    176 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)
    177 {
    178     uint32_t rmt_items_index = 0;
    179     for (uint32_t led_index = 0; led_index < led_strip_length; led_index++) {
    180         struct led_color_t led_color = led_strip_buf[led_index];
    181 
    182         for (uint8_t bit = 8; bit != 0; bit--) {
    183             uint8_t bit_set = (led_color.red >> (bit - 1)) & 1;
    184             if(bit_set) {
    185                 led_strip_rmt_bit_1_apa106(&(rmt_items[rmt_items_index]));
    186             } else {
    187                 led_strip_rmt_bit_0_apa106(&(rmt_items[rmt_items_index]));
    188             }
    189             rmt_items_index++;
    190         }
    191         for (uint8_t bit = 8; bit != 0; bit--) {
    192             uint8_t bit_set = (led_color.green >> (bit - 1)) & 1;
    193             if(bit_set) {
    194                 led_strip_rmt_bit_1_apa106(&(rmt_items[rmt_items_index]));
    195             } else {
    196                 led_strip_rmt_bit_0_apa106(&(rmt_items[rmt_items_index]));
    197             }
    198             rmt_items_index++;
    199         }
    200         for (uint8_t bit = 8; bit != 0; bit--) {
    201             uint8_t bit_set = (led_color.blue >> (bit - 1)) & 1;
    202             if(bit_set) {
    203                 led_strip_rmt_bit_1_apa106(&(rmt_items[rmt_items_index]));
    204             } else {
    205                 led_strip_rmt_bit_0_apa106(&(rmt_items[rmt_items_index]));
    206             }
    207             rmt_items_index++;
    208         }
    209     }
    210 }
    211 
    212 static void led_strip_task(void *arg)
    213 {
    214     struct led_strip_t *led_strip = (struct led_strip_t *)arg;
    215     led_fill_rmt_items_fn led_make_waveform = NULL;
    216     bool make_new_rmt_items = true;
    217     bool prev_showing_buf_1 = !led_strip->showing_buf_1;
    218 
    219     size_t num_items_malloc = (LED_STRIP_NUM_RMT_ITEMS_PER_LED * led_strip->led_strip_length);
    220     rmt_item32_t *rmt_items = (rmt_item32_t*) malloc(sizeof(rmt_item32_t) * num_items_malloc);
    221     if (!rmt_items) {
    222         vTaskDelete(NULL);
    223     }
    224 
    225     switch (led_strip->rgb_led_type) {
    226         case RGB_LED_TYPE_WS2812:
    227             led_make_waveform = led_strip_fill_rmt_items_ws2812;
    228             break;
    229 
    230         case RGB_LED_TYPE_SK6812:
    231             led_make_waveform = led_strip_fill_rmt_items_sk6812;
    232             break;
    233 
    234         case RGB_LED_TYPE_APA106:
    235             led_make_waveform = led_strip_fill_rmt_items_apa106;
    236             break;
    237 
    238         default:
    239             // Will avoid keeping it point to NULL
    240             led_make_waveform = led_strip_fill_rmt_items_ws2812;
    241             break;
    242     };
    243 
    244     for(;;) {
    245         // Next line added by gb: Suspend web server task to avoid flicker.
    246         vTaskSuspend(http_server_task);
    247         // Changed by gb
    248         // rmt_wait_tx_done(led_strip->rmt_channel);
    249         rmt_wait_tx_done(led_strip->rmt_channel, portMAX_DELAY);
    250         xSemaphoreTake(led_strip->access_semaphore, portMAX_DELAY);
    251 
    252         /*
    253          * If buf 1 was previously being shown and now buf 2 is being shown,
    254          * it should update the new rmt items array. If buf 2 was previous being shown
    255          * and now buf 1 is being shown, it should update the new rmt items array.
    256          * Otherwise, no need to update the array
    257          */
    258         if ((prev_showing_buf_1 == true) && (led_strip->showing_buf_1 == false)) {
    259             make_new_rmt_items = true;
    260         } else if ((prev_showing_buf_1 == false) && (led_strip->showing_buf_1 == true)) {
    261             make_new_rmt_items = true;
    262         } else {
    263             make_new_rmt_items = false;
    264         }
    265 
    266         if (make_new_rmt_items) {
    267             if (led_strip->showing_buf_1) {
    268                 led_make_waveform(led_strip->led_strip_buf_1, rmt_items, led_strip->led_strip_length);
    269             } else {
    270                 led_make_waveform(led_strip->led_strip_buf_2, rmt_items, led_strip->led_strip_length);
    271             }
    272         }
    273 
    274         rmt_write_items(led_strip->rmt_channel, rmt_items, num_items_malloc, false);
    275         prev_showing_buf_1 = led_strip->showing_buf_1;
    276         xSemaphoreGive(led_strip->access_semaphore);
    277         // Next line added by gb
    278         vTaskResume(http_server_task);
    279         vTaskDelay(LED_STRIP_REFRESH_PERIOD_MS / portTICK_PERIOD_MS);
    280     }
    281 
    282     if (rmt_items) {
    283         free(rmt_items);
    284     }
    285     vTaskDelete(NULL);
    286 }
    287 
    288 static bool led_strip_init_rmt(struct led_strip_t *led_strip)
    289 {
    290     rmt_config_t rmt_cfg = {
    291         .rmt_mode = RMT_MODE_TX,
    292         .channel = led_strip->rmt_channel,
    293         .clk_div = LED_STRIP_RMT_CLK_DIV,
    294         .gpio_num = led_strip->gpio,
    295         .mem_block_num = 1,
    296         .tx_config = {
    297             .loop_en = false,
    298             .carrier_freq_hz = 100, // Not used, but has to be set to avoid divide by 0 err
    299             .carrier_duty_percent = 50,
    300             .carrier_level = RMT_CARRIER_LEVEL_LOW,
    301             .carrier_en = false,
    302             .idle_level = RMT_IDLE_LEVEL_LOW,
    303             .idle_output_en = true,
    304         }
    305     };
    306 
    307     esp_err_t cfg_ok = rmt_config(&rmt_cfg);
    308     if (cfg_ok != ESP_OK) {
    309         return false;
    310     }
    311     esp_err_t install_ok = rmt_driver_install(rmt_cfg.channel, 0, 0);
    312     if (install_ok != ESP_OK) {
    313         return false;
    314     }
    315 
    316     return true;
    317 }
    318 
    319 bool led_strip_init(struct led_strip_t *led_strip)
    320 {
    321     TaskHandle_t led_strip_task_handle;
    322 
    323     if ((led_strip == NULL) ||
    324         (led_strip->rmt_channel == RMT_CHANNEL_MAX) ||
    325         (led_strip->gpio > GPIO_NUM_33) ||  // only inputs above 33
    326         (led_strip->led_strip_buf_1 == NULL) ||
    327         (led_strip->led_strip_buf_2 == NULL) ||
    328         (led_strip->led_strip_length == 0) ||
    329         (led_strip->access_semaphore == NULL)) {
    330         return false;
    331     }
    332 
    333     if(led_strip->led_strip_buf_1 == led_strip->led_strip_buf_2) {
    334         return false;
    335     }
    336 
    337     memset(led_strip->led_strip_buf_1, 0, sizeof(struct led_color_t) * led_strip->led_strip_length);
    338     memset(led_strip->led_strip_buf_2, 0, sizeof(struct led_color_t) * led_strip->led_strip_length);
    339 
    340     bool init_rmt = led_strip_init_rmt(led_strip);
    341     if (!init_rmt) {
    342         return false;
    343     }
    344 
    345     xSemaphoreGive(led_strip->access_semaphore);
    346     BaseType_t task_created = xTaskCreate(led_strip_task,
    347                                             "led_strip_task",
    348                                             LED_STRIP_TASK_SIZE,
    349                                             led_strip,
    350                                             LED_STRIP_TASK_PRIORITY,
    351                                             &led_strip_task_handle
    352                                          );
    353 
    354     if (!task_created) {
    355         return false;
    356     }
    357 
    358     return true;
    359 }
    360 
    361 bool led_strip_set_pixel_color(struct led_strip_t *led_strip, uint32_t pixel_num, struct led_color_t *color)
    362 {
    363     bool set_led_success = true;
    364 
    365     if ((!led_strip) || (!color) || (pixel_num > led_strip->led_strip_length)) {
    366         return false;
    367     }
    368 
    369     if (led_strip->showing_buf_1) {
    370         led_strip->led_strip_buf_2[pixel_num] = *color;
    371     } else {
    372         led_strip->led_strip_buf_1[pixel_num] = *color;
    373     }
    374 
    375     return set_led_success;
    376 }
    377 
    378 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)
    379 {
    380     bool set_led_success = true;
    381 
    382     if ((!led_strip) || (pixel_num > led_strip->led_strip_length)) {
    383         return false;
    384     }
    385 
    386     if (led_strip->showing_buf_1) {
    387         led_strip->led_strip_buf_2[pixel_num].red = red;
    388         led_strip->led_strip_buf_2[pixel_num].green = green;
    389         led_strip->led_strip_buf_2[pixel_num].blue = blue;
    390     } else {
    391         led_strip->led_strip_buf_1[pixel_num].red = red;
    392         led_strip->led_strip_buf_1[pixel_num].green = green;
    393         led_strip->led_strip_buf_1[pixel_num].blue = blue;
    394     }
    395 
    396     return set_led_success;
    397 }
    398 
    399 bool led_strip_get_pixel_color(struct led_strip_t *led_strip, uint32_t pixel_num, struct led_color_t *color)
    400 {
    401     bool get_success = true;
    402 
    403     if ((!led_strip) ||
    404         (pixel_num > led_strip->led_strip_length) ||
    405         (!color)) {
    406         color = NULL;
    407         return false;
    408     }
    409 
    410     if (led_strip->showing_buf_1) {
    411         *color = led_strip->led_strip_buf_1[pixel_num];
    412     } else {
    413         *color = led_strip->led_strip_buf_2[pixel_num];
    414     }
    415 
    416     return get_success;
    417 }
    418 
    419 /**
    420  * Updates the led buffer to be shown
    421  */
    422 bool led_strip_show(struct led_strip_t *led_strip)
    423 {
    424     bool success = true;
    425 
    426     if (!led_strip) {
    427         return false;
    428     }
    429 
    430     xSemaphoreTake(led_strip->access_semaphore, portMAX_DELAY);
    431     if (led_strip->showing_buf_1) {
    432         led_strip->showing_buf_1 = false;
    433         memset(led_strip->led_strip_buf_1, 0, sizeof(struct led_color_t) * led_strip->led_strip_length);
    434     } else {
    435         led_strip->showing_buf_1 = true;
    436         memset(led_strip->led_strip_buf_2, 0, sizeof(struct led_color_t) * led_strip->led_strip_length);
    437     }
    438     xSemaphoreGive(led_strip->access_semaphore);
    439 
    440     return success;
    441 }
    442 
    443 /**
    444  * Clears the LED strip
    445  */
    446 bool led_strip_clear(struct led_strip_t *led_strip)
    447 {
    448     bool success = true;
    449 
    450     if (!led_strip) {
    451         return false;
    452     }
    453 
    454     if (led_strip->showing_buf_1) {
    455         memset(led_strip->led_strip_buf_2, 0, sizeof(struct led_color_t) * led_strip->led_strip_length);
    456     } else {
    457         memset(led_strip->led_strip_buf_1, 0, sizeof(struct led_color_t) * led_strip->led_strip_length);
    458     }
    459 
    460     return success;
    461 }