led.c 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. /*
  2. This example code is in the Public Domain (or CC0 licensed, at your option.)
  3. Unless required by applicable law or agreed to in writing, this
  4. software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
  5. CONDITIONS OF ANY KIND, either express or implied.
  6. */
  7. #include <stdio.h>
  8. #include <stdlib.h>
  9. #include <unistd.h>
  10. #include <string.h>
  11. #include <math.h>
  12. #include "freertos/FreeRTOS.h"
  13. #include "freertos/task.h"
  14. #include "freertos/timers.h"
  15. #include "esp_system.h"
  16. #include "esp_log.h"
  17. #include "driver/gpio.h"
  18. #include "driver/ledc.h"
  19. #include "driver/rmt.h"
  20. #include "platform_config.h"
  21. #include "gpio_exp.h"
  22. #include "led.h"
  23. #include "globdefs.h"
  24. #include "accessors.h"
  25. #define MAX_LED 8
  26. #define BLOCKTIME 10 // up to portMAX_DELAY
  27. #ifdef CONFIG_IDF_TARGET_ESP32S3
  28. #define LEDC_SPEED_MODE LEDC_LOW_SPEED_MODE
  29. #else
  30. #define LEDC_SPEED_MODE LEDC_HIGH_SPEED_MODE
  31. #endif
  32. static const char *TAG = "led";
  33. #define RMT_CLK (40/2)
  34. static int8_t led_rmt_channel = -1;
  35. static uint32_t scale24(uint32_t bright, uint8_t);
  36. static const struct rmt_led_param_s {
  37. led_type_t type;
  38. uint8_t bits;
  39. // number of ticks in nanoseconds converted in RMT_CLK ticks
  40. rmt_item32_t bit_0;
  41. rmt_item32_t bit_1;
  42. uint32_t green, red;
  43. uint32_t (*scale)(uint32_t, uint8_t);
  44. } rmt_led_param[] = {
  45. { LED_WS2812, 24, {{{350 / RMT_CLK, 1, 1000 / RMT_CLK, 0}}}, {{{1000 / RMT_CLK, 1, 350 / RMT_CLK, 0}}}, 0xff0000, 0x00ff00, scale24 },
  46. { .type = -1 } };
  47. static EXT_RAM_ATTR struct led_s {
  48. gpio_num_t gpio;
  49. bool on;
  50. uint32_t color;
  51. int ontime, offtime;
  52. int bright;
  53. int channel;
  54. const struct rmt_led_param_s *rmt;
  55. int pushedon, pushedoff;
  56. bool pushed;
  57. TimerHandle_t timer;
  58. } leds[MAX_LED];
  59. // can't use EXT_RAM_ATTR for initialized structure
  60. static struct led_config_s {
  61. int gpio;
  62. int color;
  63. int bright;
  64. led_type_t type;
  65. } green = { .gpio = CONFIG_LED_GREEN_GPIO, .color = 0, .bright = -1, .type = LED_GPIO },
  66. red = { .gpio = CONFIG_LED_RED_GPIO, .color = 0, .bright = -1, .type = LED_GPIO };
  67. static int led_max = 2;
  68. /****************************************************************************************
  69. *
  70. */
  71. static uint32_t scale24(uint32_t color, uint8_t scale) {
  72. uint32_t scaled = (((color & 0xff0000) >> 16) * scale / 100) << 16;
  73. scaled |= (((color & 0xff00) >> 8) * scale / 100) << 8;
  74. scaled |= (color & 0xff) * scale / 100;
  75. return scaled;
  76. }
  77. /****************************************************************************************
  78. *
  79. */
  80. static void set_level(struct led_s *led, bool on) {
  81. if (led->rmt) {
  82. uint32_t data = on ? led->rmt->scale(led->color, led->bright) : 0;
  83. uint32_t mask = 1 << (led->rmt->bits - 1);
  84. rmt_item32_t buffer[led->rmt->bits];
  85. for (uint32_t bit = 0; bit < led->rmt->bits; bit++) {
  86. uint32_t set = data & mask;
  87. buffer[bit] = set ? led->rmt->bit_1 : led->rmt->bit_0;
  88. mask >>= 1;
  89. }
  90. rmt_write_items(led->channel, buffer, led->rmt->bits, false);
  91. } else if (led->bright < 0 || led->gpio >= GPIO_NUM_MAX) {
  92. gpio_set_level_x(led->gpio, on ? led->color : !led->color);
  93. } else {
  94. ledc_set_duty(LEDC_SPEED_MODE, led->channel, on ? led->bright : (led->color ? 0 : pwm_system.max));
  95. ledc_update_duty(LEDC_SPEED_MODE, led->channel);
  96. }
  97. }
  98. /****************************************************************************************
  99. *
  100. */
  101. static void vCallbackFunction( TimerHandle_t xTimer ) {
  102. struct led_s *led = (struct led_s*) pvTimerGetTimerID (xTimer);
  103. if (!led->timer) return;
  104. led->on = !led->on;
  105. ESP_EARLY_LOGD(TAG,"led vCallbackFunction setting gpio %d level %d (bright:%d)", led->gpio, led->on, led->bright);
  106. set_level(led, led->on);
  107. // was just on for a while
  108. if (!led->on && led->offtime == -1) return;
  109. // regular blinking
  110. xTimerChangePeriod(xTimer, (led->on ? led->ontime : led->offtime) / portTICK_RATE_MS, BLOCKTIME);
  111. }
  112. /****************************************************************************************
  113. *
  114. */
  115. bool led_blink_core(int idx, int ontime, int offtime, bool pushed) {
  116. if (!leds[idx].gpio || leds[idx].gpio < 0 ) return false;
  117. ESP_LOGD(TAG,"led_blink_core %d on:%d off:%d, pushed:%u", idx, ontime, offtime, pushed);
  118. if (leds[idx].timer) {
  119. // normal requests waits if a pop is pending
  120. if (!pushed && leds[idx].pushed) {
  121. leds[idx].pushedon = ontime;
  122. leds[idx].pushedoff = offtime;
  123. return true;
  124. }
  125. xTimerStop(leds[idx].timer, BLOCKTIME);
  126. }
  127. // save current state if not already pushed
  128. if (!leds[idx].pushed) {
  129. leds[idx].pushedon = leds[idx].ontime;
  130. leds[idx].pushedoff = leds[idx].offtime;
  131. leds[idx].pushed = pushed;
  132. }
  133. // then set new one
  134. leds[idx].ontime = ontime;
  135. leds[idx].offtime = offtime;
  136. if (ontime == 0) {
  137. ESP_LOGD(TAG,"led %d, setting reverse level", idx);
  138. set_level(leds + idx, false);
  139. } else if (offtime == 0) {
  140. ESP_LOGD(TAG,"led %d, setting level", idx);
  141. set_level(leds + idx, true);
  142. } else {
  143. if (!leds[idx].timer) {
  144. ESP_LOGD(TAG,"led %d, Creating timer", idx);
  145. leds[idx].timer = xTimerCreate("ledTimer", ontime / portTICK_RATE_MS, pdFALSE, (void *)&leds[idx], vCallbackFunction);
  146. }
  147. leds[idx].on = true;
  148. set_level(leds + idx, true);
  149. ESP_LOGD(TAG,"led %d, Setting gpio %d and starting timer", idx, leds[idx].gpio);
  150. if (xTimerStart(leds[idx].timer, BLOCKTIME) == pdFAIL) return false;
  151. }
  152. return true;
  153. }
  154. /****************************************************************************************
  155. *
  156. */
  157. bool led_brightness(int idx, int bright) {
  158. if (bright > 100) bright = 100;
  159. if (leds[idx].rmt) {
  160. leds[idx].bright = bright;
  161. } else {
  162. leds[idx].bright = pwm_system.max * powf(bright / 100.0, 3);
  163. if (!leds[idx].color) leds[idx].bright = pwm_system.max - leds[idx].bright;
  164. ledc_set_duty(LEDC_SPEED_MODE, leds[idx].channel, leds[idx].bright);
  165. ledc_update_duty(LEDC_SPEED_MODE, leds[idx].channel);
  166. }
  167. return true;
  168. }
  169. /****************************************************************************************
  170. *
  171. */
  172. bool led_unpush(int idx) {
  173. if (!leds[idx].gpio || leds[idx].gpio<0) return false;
  174. led_blink_core(idx, leds[idx].pushedon, leds[idx].pushedoff, true);
  175. leds[idx].pushed = false;
  176. return true;
  177. }
  178. /****************************************************************************************
  179. *
  180. */
  181. int led_allocate(void) {
  182. if (led_max < MAX_LED) return led_max++;
  183. return -1;
  184. }
  185. /****************************************************************************************
  186. *
  187. */
  188. bool led_config(int idx, gpio_num_t gpio, int color, int bright, led_type_t type) {
  189. if (gpio < 0) {
  190. ESP_LOGW(TAG,"LED GPIO -1 ignored");
  191. return false;
  192. }
  193. if (idx >= MAX_LED) return false;
  194. if (bright > 100) bright = 100;
  195. leds[idx].gpio = gpio;
  196. leds[idx].color = color;
  197. leds[idx].rmt = NULL;
  198. leds[idx].bright = -1;
  199. if (type != LED_GPIO) {
  200. // first make sure we have a known addressable led
  201. for (const struct rmt_led_param_s *p = rmt_led_param; !leds[idx].rmt && p->type >= 0; p++) if (p->type == type) leds[idx].rmt = p;
  202. if (!leds[idx].rmt) return false;
  203. if (led_rmt_channel < 0) led_rmt_channel = rmt_system_base_channel++;
  204. leds[idx].channel = led_rmt_channel;
  205. leds[idx].bright = bright > 0 ? bright : 100;
  206. // set counter clock to 40MHz
  207. rmt_config_t config = RMT_DEFAULT_CONFIG_TX(gpio, leds[idx].channel);
  208. config.clk_div = 2;
  209. rmt_config(&config);
  210. rmt_driver_install(config.channel, 0, 0);
  211. } else if (bright < 0 || gpio >= GPIO_NUM_MAX) {
  212. gpio_pad_select_gpio_x(gpio);
  213. gpio_set_direction_x(gpio, GPIO_MODE_OUTPUT);
  214. } else {
  215. leds[idx].channel = pwm_system.base_channel++;
  216. leds[idx].bright = pwm_system.max * powf(bright / 100.0, 3);
  217. if (!color) leds[idx].bright = pwm_system.max - leds[idx].bright;
  218. ledc_channel_config_t ledc_channel = {
  219. .channel = leds[idx].channel,
  220. .duty = leds[idx].bright,
  221. .gpio_num = gpio,
  222. .speed_mode = LEDC_SPEED_MODE,
  223. .hpoint = 0,
  224. .timer_sel = pwm_system.timer,
  225. };
  226. ledc_channel_config(&ledc_channel);
  227. }
  228. set_level(leds + idx, false);
  229. ESP_LOGD(TAG,"Index %d, GPIO %d, color/onstate %d / RMT %d, bright %d%%", idx, gpio, color, type, bright);
  230. return true;
  231. }
  232. /****************************************************************************************
  233. *
  234. */
  235. void set_led_gpio(int gpio, char *value) {
  236. struct led_config_s *config;
  237. if (strcasestr(value, "green")) config = &green;
  238. else config = &red;
  239. config->gpio = gpio;
  240. char *p = value;
  241. while ((p = strchr(p, ':')) != NULL) {
  242. p++;
  243. if ((strcasestr(p, "ws2812")) != NULL) config->type = LED_WS2812;
  244. else config->color = atoi(p);
  245. }
  246. if (config->type != LED_GPIO) {
  247. for (const struct rmt_led_param_s *p = rmt_led_param; p->type >= 0; p++) {
  248. if (p->type == config->type) {
  249. if (config == &green) config->color = p->green;
  250. else config->color = p->red;
  251. break;
  252. }
  253. }
  254. }
  255. }
  256. void led_svc_init(void) {
  257. #ifdef CONFIG_LED_GREEN_GPIO_LEVEL
  258. green.color = CONFIG_LED_GREEN_GPIO_LEVEL;
  259. #endif
  260. #ifdef CONFIG_LED_RED_GPIO_LEVEL
  261. red.color = CONFIG_LED_RED_GPIO_LEVEL;
  262. #endif
  263. #ifndef CONFIG_LED_LOCKED
  264. parse_set_GPIO(set_led_gpio);
  265. #endif
  266. char *nvs_item = config_alloc_get(NVS_TYPE_STR, "led_brightness");
  267. if (nvs_item) {
  268. PARSE_PARAM(nvs_item, "green", '=', green.bright);
  269. PARSE_PARAM(nvs_item, "red", '=', red.bright);
  270. free(nvs_item);
  271. }
  272. led_config(LED_GREEN, green.gpio, green.color, green.bright, green.type);
  273. led_config(LED_RED, red.gpio, red.color, red.bright, red.type);
  274. ESP_LOGI(TAG,"Configuring LEDs green:%d (on:%d rmt:%d %d%% ), red:%d (on:%d rmt:%d %d%% )",
  275. green.gpio, green.color, green.type, green.bright,
  276. red.gpio, red.color, red.type, red.bright);
  277. }