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 }