From c7e4d9711c6c5bc8c13d185734b66d41744c543e Mon Sep 17 00:00:00 2001 From: philippe44 Date: Fri, 1 Sep 2023 21:22:42 -0700 Subject: [PATCH] add addressable led on led API including green/red + make rmt properly shared accross services --- components/led_strip/led_vu.c | 14 +- components/services/globdefs.h | 1 + components/services/infrared.c | 16 +-- components/services/led.c | 256 +++++++++++++++++++++------------ components/services/led.h | 10 +- components/services/services.c | 2 + components/targets/muse/muse.c | 11 +- 7 files changed, 197 insertions(+), 113 deletions(-) diff --git a/components/led_strip/led_vu.c b/components/led_strip/led_vu.c index dbb7be2b..a0dd2d38 100644 --- a/components/led_strip/led_vu.c +++ b/components/led_strip/led_vu.c @@ -21,6 +21,7 @@ #include #include "esp_log.h" +#include "globdefs.h" #include "led_strip.h" #include "platform_config.h" #include "led_vu.h" @@ -28,7 +29,6 @@ static const char *TAG = "led_vu"; #define LED_VU_STACK_SIZE (3*1024) -#define LED_VU_RMT_INTR_NUM 20U #define LED_VU_PEAK_HOLD 6U @@ -39,12 +39,6 @@ static const char *TAG = "led_vu"; #define max(a,b) (((a) > (b)) ? (a) : (b)) struct led_strip_t* led_display = NULL; -static EXT_RAM_ATTR struct led_strip_t led_strip_config = { - .rgb_led_type = RGB_LED_TYPE_WS2812, - .rmt_channel = RMT_CHANNEL_1, - .rmt_interrupt_num = LED_VU_RMT_INTR_NUM, - .gpio = -1, -}; static EXT_RAM_ATTR struct { int gpio; @@ -96,24 +90,26 @@ void led_vu_init() strip.vu_odd = strip.length - 1; // create driver configuration + struct led_strip_t led_strip_config = { .rgb_led_type = RGB_LED_TYPE_WS2812 }; led_strip_config.access_semaphore = xSemaphoreCreateBinary(); led_strip_config.led_strip_length = strip.length; led_strip_config.led_strip_working = heap_caps_malloc(strip.length * sizeof(struct led_color_t), MALLOC_CAP_8BIT); led_strip_config.led_strip_showing = heap_caps_malloc(strip.length * sizeof(struct led_color_t), MALLOC_CAP_8BIT); led_strip_config.gpio = strip.gpio; + led_strip_config.rmt_channel = rmt_system_base_channel++; // initialize driver bool led_init_ok = led_strip_init(&led_strip_config); if (led_init_ok) { led_display = &led_strip_config; - ESP_LOGI(TAG, "led_vu using gpio:%d length:%d", strip.gpio, strip.length); + ESP_LOGI(TAG, "led_vu using gpio:%d length:%d on channek:%d", strip.gpio, strip.length, led_strip_config.rmt_channel); } else { ESP_LOGE(TAG, "led_vu init failed"); goto done; } // reserver max memory for remote management systems - rmt_set_mem_block_num(RMT_CHANNEL_1, 7); + rmt_set_mem_block_num(led_strip_config.rmt_channel, 7); led_vu_clear(led_display); diff --git a/components/services/globdefs.h b/components/services/globdefs.h index db66a875..93aafb8f 100644 --- a/components/services/globdefs.h +++ b/components/services/globdefs.h @@ -17,6 +17,7 @@ extern int i2c_system_port; extern int i2c_system_speed; extern int spi_system_host; extern int spi_system_dc_gpio; +extern int rmt_system_base_channel; typedef struct { int timer, base_channel, max; } pwm_system_t; diff --git a/components/services/infrared.c b/components/services/infrared.c index 623e11ae..c999f660 100644 --- a/components/services/infrared.c +++ b/components/services/infrared.c @@ -14,6 +14,7 @@ #include "esp_err.h" #include "esp_log.h" #include "driver/rmt.h" +#include "globdefs.h" #include "infrared.h" static const char* TAG = "IR"; @@ -83,8 +84,6 @@ typedef struct { ir_parser_t *ir_parser = NULL; -#define RMT_RX_CHANNEL 0 /*!< RMT channel for receiver */ - #define RMT_CHECK(a, str, goto_tag, ret_value, ...) \ do \ { \ @@ -481,10 +480,9 @@ void infrared_receive(RingbufHandle_t rb, infrared_handler handler) { /**************************************************************************************** * */ -void infrared_init(RingbufHandle_t *rb, int gpio, infrared_mode_t mode) { - ESP_LOGI(TAG, "Starting Infrared Receiver mode %s on gpio %d", mode == IR_NEC ? "nec" : "rc5", gpio); - - rmt_config_t rmt_rx_config = RMT_DEFAULT_CONFIG_RX(gpio, RMT_RX_CHANNEL); +void infrared_init(RingbufHandle_t *rb, int gpio, infrared_mode_t mode) { + int rmt_channel = rmt_system_base_channel++; + rmt_config_t rmt_rx_config = RMT_DEFAULT_CONFIG_RX(gpio, rmt_channel); rmt_config(&rmt_rx_config); rmt_driver_install(rmt_rx_config.channel, 1000, 0); ir_parser_config_t ir_parser_config = IR_PARSER_DEFAULT_CONFIG((ir_dev_t) rmt_rx_config.channel); @@ -493,6 +491,8 @@ void infrared_init(RingbufHandle_t *rb, int gpio, infrared_mode_t mode) { ir_parser = (mode == IR_NEC) ? ir_parser_rmt_new_nec(&ir_parser_config) : ir_parser_rmt_new_rc5(&ir_parser_config); // get RMT RX ringbuffer - rmt_get_ringbuf_handle(RMT_RX_CHANNEL, rb); - rmt_rx_start(RMT_RX_CHANNEL, 1); + rmt_get_ringbuf_handle(rmt_channel, rb); + rmt_rx_start(rmt_channel, 1); + + ESP_LOGI(TAG, "Starting Infrared Receiver mode %s on gpio %d and channel %d", mode == IR_NEC ? "nec" : "rc5", gpio, rmt_channel); } diff --git a/components/services/led.c b/components/services/led.c index b4200f7d..decf2f8f 100644 --- a/components/services/led.c +++ b/components/services/led.c @@ -18,6 +18,7 @@ #include "esp_log.h" #include "driver/gpio.h" #include "driver/ledc.h" +#include "driver/rmt.h" #include "platform_config.h" #include "gpio_exp.h" #include "led.h" @@ -31,90 +32,130 @@ #define LEDC_SPEED_MODE LEDC_LOW_SPEED_MODE #else #define LEDC_SPEED_MODE LEDC_HIGH_SPEED_MODE -#endif +#endif static const char *TAG = "led"; +#define RMT_CLK (40/2) + +static int8_t led_rmt_channel = -1; +static uint32_t scale24(uint32_t bright, uint8_t); + +static const struct rmt_led_param_s { + led_type_t type; + uint8_t bits; + // number of ticks in nanoseconds converted in RMT_CLK ticks + rmt_item32_t bit_0; + rmt_item32_t bit_1; + uint32_t green, red; + uint32_t (*scale)(uint32_t, uint8_t); +} rmt_led_param[] = { + { LED_WS2812, 24, {{{350 / RMT_CLK, 1, 1000 / RMT_CLK, 0}}}, {{{1000 / RMT_CLK, 1, 350 / RMT_CLK, 0}}}, 0xff0000, 0x00ff00, scale24 }, + { .type = -1 } }; + static EXT_RAM_ATTR struct led_s { gpio_num_t gpio; bool on; - int onstate; + uint32_t color; int ontime, offtime; - int pwm; + int bright; int channel; + const struct rmt_led_param_s *rmt; int pushedon, pushedoff; bool pushed; TimerHandle_t timer; } leds[MAX_LED]; // can't use EXT_RAM_ATTR for initialized structure -static struct { +static struct led_config_s { int gpio; - int active; - int pwm; -} green = { .gpio = CONFIG_LED_GREEN_GPIO, .active = 0, .pwm = -1 }, - red = { .gpio = CONFIG_LED_RED_GPIO, .active = 0, .pwm = -1 }; - + int color; + int bright; + led_type_t type; +} green = { .gpio = CONFIG_LED_GREEN_GPIO, .color = 0, .bright = -1, .type = LED_GPIO }, + red = { .gpio = CONFIG_LED_RED_GPIO, .color = 0, .bright = -1, .type = LED_GPIO }; + static int led_max = 2; /**************************************************************************************** - * + * */ -static void set_level(struct led_s *led, bool on) { - if (led->pwm < 0 || led->gpio >= GPIO_NUM_MAX) gpio_set_level_x(led->gpio, on ? led->onstate : !led->onstate); - else { - ledc_set_duty(LEDC_SPEED_MODE, led->channel, on ? led->pwm : (led->onstate ? 0 : pwm_system.max)); - ledc_update_duty(LEDC_SPEED_MODE, led->channel); - } +static uint32_t scale24(uint32_t color, uint8_t scale) { + uint32_t scaled = (((color & 0xff0000) >> 16) * scale / 100) << 16; + scaled |= (((color & 0xff00) >> 8) * scale / 100) << 8; + scaled |= (color & 0xff) * scale / 100; + return scaled; } /**************************************************************************************** - * + * + */ +static void set_level(struct led_s *led, bool on) { + if (led->rmt) { + uint32_t data = on ? led->rmt->scale(led->color, led->bright) : 0; + uint32_t mask = 1 << (led->rmt->bits - 1); + rmt_item32_t buffer[led->rmt->bits]; + for (uint32_t bit = 0; bit < led->rmt->bits; bit++) { + uint32_t set = data & mask; + buffer[bit] = set ? led->rmt->bit_1 : led->rmt->bit_0; + mask >>= 1; + } + rmt_write_items(led->channel, buffer, led->rmt->bits, false); + } else if (led->bright < 0 || led->gpio >= GPIO_NUM_MAX) { + gpio_set_level_x(led->gpio, on ? led->color : !led->color); + } else { + ledc_set_duty(LEDC_SPEED_MODE, led->channel, on ? led->bright : (led->color ? 0 : pwm_system.max)); + ledc_update_duty(LEDC_SPEED_MODE, led->channel); + } +} + +/**************************************************************************************** + * */ static void vCallbackFunction( TimerHandle_t xTimer ) { struct led_s *led = (struct led_s*) pvTimerGetTimerID (xTimer); - + if (!led->timer) return; - + led->on = !led->on; - ESP_EARLY_LOGD(TAG,"led vCallbackFunction setting gpio %d level %d (pwm:%d)", led->gpio, led->on, led->pwm); + ESP_EARLY_LOGD(TAG,"led vCallbackFunction setting gpio %d level %d (bright:%d)", led->gpio, led->on, led->bright); set_level(led, led->on); - + // was just on for a while if (!led->on && led->offtime == -1) return; - + // regular blinking xTimerChangePeriod(xTimer, (led->on ? led->ontime : led->offtime) / portTICK_RATE_MS, BLOCKTIME); } /**************************************************************************************** - * + * */ bool led_blink_core(int idx, int ontime, int offtime, bool pushed) { if (!leds[idx].gpio || leds[idx].gpio < 0 ) return false; - + ESP_LOGD(TAG,"led_blink_core %d on:%d off:%d, pushed:%u", idx, ontime, offtime, pushed); if (leds[idx].timer) { // normal requests waits if a pop is pending if (!pushed && leds[idx].pushed) { - leds[idx].pushedon = ontime; - leds[idx].pushedoff = offtime; + leds[idx].pushedon = ontime; + leds[idx].pushedoff = offtime; return true; } xTimerStop(leds[idx].timer, BLOCKTIME); } - + // save current state if not already pushed if (!leds[idx].pushed) { leds[idx].pushedon = leds[idx].ontime; - leds[idx].pushedoff = leds[idx].offtime; + leds[idx].pushedoff = leds[idx].offtime; leds[idx].pushed = pushed; - } - + } + // then set new one leds[idx].ontime = ontime; - leds[idx].offtime = offtime; - + leds[idx].offtime = offtime; + if (ontime == 0) { ESP_LOGD(TAG,"led %d, setting reverse level", idx); set_level(leds + idx, false); @@ -132,39 +173,44 @@ bool led_blink_core(int idx, int ontime, int offtime, bool pushed) { ESP_LOGD(TAG,"led %d, Setting gpio %d and starting timer", idx, leds[idx].gpio); if (xTimerStart(leds[idx].timer, BLOCKTIME) == pdFAIL) return false; } - - - return true; -} -/**************************************************************************************** - * - */ -bool led_brightness(int idx, int pwm) { - if (pwm > 100) pwm = 100; - leds[idx].pwm = pwm_system.max * powf(pwm / 100.0, 3); - if (!leds[idx].onstate) leds[idx].pwm = pwm_system.max - leds[idx].pwm; - - ledc_set_duty(LEDC_SPEED_MODE, leds[idx].channel, leds[idx].pwm); - ledc_update_duty(LEDC_SPEED_MODE, leds[idx].channel); - + return true; } /**************************************************************************************** - * + * + */ +bool led_brightness(int idx, int bright) { + if (bright > 100) bright = 100; + + if (leds[idx].rmt) { + leds[idx].bright = bright; + } else { + leds[idx].bright = pwm_system.max * powf(bright / 100.0, 3); + if (!leds[idx].color) leds[idx].bright = pwm_system.max - leds[idx].bright; + + ledc_set_duty(LEDC_SPEED_MODE, leds[idx].channel, leds[idx].bright); + ledc_update_duty(LEDC_SPEED_MODE, leds[idx].channel); + } + + return true; +} + +/**************************************************************************************** + * */ bool led_unpush(int idx) { if (!leds[idx].gpio || leds[idx].gpio<0) return false; - + led_blink_core(idx, leds[idx].pushedon, leds[idx].pushedoff, true); leds[idx].pushed = false; - + return true; -} +} /**************************************************************************************** - * + * */ int led_allocate(void) { if (led_max < MAX_LED) return led_max++; @@ -172,83 +218,115 @@ int led_allocate(void) { } /**************************************************************************************** - * + * */ -bool led_config(int idx, gpio_num_t gpio, int onstate, int pwm) { +bool led_config(int idx, gpio_num_t gpio, int color, int bright, led_type_t type) { if (gpio < 0) { ESP_LOGW(TAG,"LED GPIO -1 ignored"); return false; } - - ESP_LOGD(TAG,"Index %d, GPIO %d, on state %s", idx, gpio, onstate>0?"On":"Off"); - if (idx >= MAX_LED) return false; - - leds[idx].gpio = gpio; - leds[idx].onstate = onstate; - leds[idx].pwm = -1; - if (pwm < 0 || gpio >= GPIO_NUM_MAX) { + if (idx >= MAX_LED) return false; + + if (bright > 100) bright = 100; + + leds[idx].gpio = gpio; + leds[idx].color = color; + leds[idx].rmt = NULL; + leds[idx].bright = -1; + + if (type != LED_GPIO) { + // first make sure we have a known addressable led + 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; + if (!leds[idx].rmt) return false; + + if (led_rmt_channel < 0) led_rmt_channel = rmt_system_base_channel++; + leds[idx].channel = led_rmt_channel; + leds[idx].bright = bright > 0 ? bright : 100; + + // set counter clock to 40MHz + rmt_config_t config = RMT_DEFAULT_CONFIG_TX(gpio, leds[idx].channel); + config.clk_div = 2; + + rmt_config(&config); + rmt_driver_install(config.channel, 0, 0); + } else if (bright < 0 || gpio >= GPIO_NUM_MAX) { gpio_pad_select_gpio_x(gpio); gpio_set_direction_x(gpio, GPIO_MODE_OUTPUT); - } else { + } else { leds[idx].channel = pwm_system.base_channel++; - leds[idx].pwm = pwm_system.max * powf(pwm / 100.0, 3); - if (!onstate) leds[idx].pwm = pwm_system.max - leds[idx].pwm; - + leds[idx].bright = pwm_system.max * powf(bright / 100.0, 3); + if (!color) leds[idx].bright = pwm_system.max - leds[idx].bright; + ledc_channel_config_t ledc_channel = { .channel = leds[idx].channel, - .duty = leds[idx].pwm, + .duty = leds[idx].bright, .gpio_num = gpio, .speed_mode = LEDC_SPEED_MODE, .hpoint = 0, .timer_sel = pwm_system.timer, }; - + ledc_channel_config(&ledc_channel); } - + set_level(leds + idx, false); - ESP_LOGD(TAG,"PWM Index %d, GPIO %d, on state %s, pwm %d%%", idx, gpio, onstate > 0 ? "On" : "Off", pwm); + ESP_LOGD(TAG,"Index %d, GPIO %d, color/onstate %d / RMT %d, bright %d%%", idx, gpio, color, type, bright); return true; } /**************************************************************************************** - * + * */ void set_led_gpio(int gpio, char *value) { - char *p; - - if (strcasestr(value, "green")) { - green.gpio = gpio; - if ((p = strchr(value, ':')) != NULL) green.active = atoi(p + 1); - } else if (strcasestr(value, "red")) { - red.gpio = gpio; - if ((p = strchr(value, ':')) != NULL) red.active = atoi(p + 1); - } + struct led_config_s *config; + + if (strcasestr(value, "green")) config = &green; + else config = &red; + + config->gpio = gpio; + char *p = value; + while ((p = strchr(p, ':')) != NULL) { + p++; + if ((strcasestr(p, "ws2812")) != NULL) config->type = LED_WS2812; + else config->color = atoi(p); + } + + if (config->type != LED_GPIO) { + for (const struct rmt_led_param_s *p = rmt_led_param; p->type >= 0; p++) { + if (p->type == config->type) { + if (config == &green) config->color = p->green; + else config->color = p->red; + break; + } + } + } } void led_svc_init(void) { #ifdef CONFIG_LED_GREEN_GPIO_LEVEL - green.active = CONFIG_LED_GREEN_GPIO_LEVEL; + green.color = CONFIG_LED_GREEN_GPIO_LEVEL; #endif #ifdef CONFIG_LED_RED_GPIO_LEVEL - red.active = CONFIG_LED_RED_GPIO_LEVEL; + red.color = CONFIG_LED_RED_GPIO_LEVEL; #endif #ifndef CONFIG_LED_LOCKED parse_set_GPIO(set_led_gpio); #endif - char *nvs_item = config_alloc_get(NVS_TYPE_STR, "led_brightness"); + char *nvs_item = config_alloc_get(NVS_TYPE_STR, "led_brightness"); if (nvs_item) { - PARSE_PARAM(nvs_item, "green", '=', green.pwm); - PARSE_PARAM(nvs_item, "red", '=', red.pwm); + PARSE_PARAM(nvs_item, "green", '=', green.bright); + PARSE_PARAM(nvs_item, "red", '=', red.bright); free(nvs_item); } - led_config(LED_GREEN, green.gpio, green.active, green.pwm); - led_config(LED_RED, red.gpio, red.active, red.pwm); - - ESP_LOGI(TAG,"Configuring LEDs green:%d (active:%d %d%%), red:%d (active:%d %d%%)", green.gpio, green.active, green.pwm, red.gpio, red.active, red.pwm ); + led_config(LED_GREEN, green.gpio, green.color, green.bright, green.type); + led_config(LED_RED, red.gpio, red.color, red.bright, red.type); + + ESP_LOGI(TAG,"Configuring LEDs green:%d (on:%d rmt:%d %d%% ), red:%d (on:%d rmt:%d %d%% )", + green.gpio, green.color, green.type, green.bright, + red.gpio, red.color, red.type, red.bright); } diff --git a/components/services/led.h b/components/services/led.h index 1f3e92b9..f01af042 100644 --- a/components/services/led.h +++ b/components/services/led.h @@ -1,4 +1,4 @@ -/* +/* * Squeezelite for esp32 * * (c) Sebastien 2019 @@ -8,20 +8,22 @@ * https://opensource.org/licenses/MIT * */ - + #ifndef LED_H #define LED_H #include "driver/gpio.h" enum { LED_GREEN = 0, LED_RED }; +typedef enum { LED_GPIO = -1, LED_WS2812 } led_type_t; #define led_on(idx) led_blink_core(idx, 1, 0, false) #define led_off(idx) led_blink_core(idx, 0, 0, false) #define led_blink(idx, on, off) led_blink_core(idx, on, off, false) #define led_blink_pushed(idx, on, off) led_blink_core(idx, on, off, true) -bool led_config(int idx, gpio_num_t gpio, int onstate, int pwm); -bool led_brightness(int idx, int percent); +// if type is LED_GPIO then color set the GPIO logic value for "on" +bool led_config(int idx, gpio_num_t gpio, int color, int bright, led_type_t type); +bool led_brightness(int idx, int percent); bool led_blink_core(int idx, int ontime, int offtime, bool push); bool led_unpush(int idx); int led_allocate(void); diff --git a/components/services/services.c b/components/services/services.c index e4fa5e6f..b1c1dbc4 100644 --- a/components/services/services.c +++ b/components/services/services.c @@ -11,6 +11,7 @@ #include "driver/gpio.h" #include "driver/ledc.h" #include "driver/i2c.h" +#include "driver/rmt.h" #include "platform_config.h" #include "gpio_exp.h" #include "battery.h" @@ -28,6 +29,7 @@ int i2c_system_port = I2C_SYSTEM_PORT; int i2c_system_speed = 400000; int spi_system_host = SPI_SYSTEM_HOST; int spi_system_dc_gpio = -1; +int rmt_system_base_channel = RMT_CHANNEL_0; pwm_system_t pwm_system = { .timer = LEDC_TIMER_0, .base_channel = LEDC_CHANNEL_0, diff --git a/components/targets/muse/muse.c b/components/targets/muse/muse.c index 4e902a7d..8f757c7a 100644 --- a/components/targets/muse/muse.c +++ b/components/targets/muse/muse.c @@ -8,6 +8,7 @@ #include #include #include "driver/rmt.h" +#include "globdefs.h" #include "monitor.h" #include "targets.h" @@ -15,7 +16,6 @@ //*********************** NeoPixels *************************** //////////////////////////////////////////////////////////////// #define NUM_LEDS 1 -#define LED_RMT_TX_CHANNEL 0 #define LED_RMT_TX_GPIO 22 #define BITS_PER_LED_CMD 24 @@ -39,6 +39,8 @@ struct led_state { uint32_t leds[NUM_LEDS]; }; +static int rmt_channel; + void ws2812_control_init(void); void ws2812_write_leds(struct led_state new_state); @@ -93,9 +95,10 @@ void setup_rmt_data_buffer(struct led_state new_state); void ws2812_control_init(void) { + rmt_channel = rmt_system_base_channel++; rmt_config_t config; config.rmt_mode = RMT_MODE_TX; - config.channel = LED_RMT_TX_CHANNEL; + config.channel = rmt_channel; config.gpio_num = LED_RMT_TX_GPIO; config.mem_block_num = 3; config.tx_config.loop_en = false; @@ -106,11 +109,13 @@ void ws2812_control_init(void) ESP_ERROR_CHECK(rmt_config(&config)); ESP_ERROR_CHECK(rmt_driver_install(config.channel, 0, 0)); + + ESP_LOGI(TAG, "LED wth ws2812 using gpio %d and channel %d", LED_RMT_TX_GPIO, rmt_channel); } void ws2812_write_leds(struct led_state new_state) { setup_rmt_data_buffer(new_state); - rmt_write_items(LED_RMT_TX_CHANNEL, led_data_buffer, LED_BUFFER_ITEMS, false); + rmt_write_items(rmt_channel, led_data_buffer, LED_BUFFER_ITEMS, false); } void setup_rmt_data_buffer(struct led_state new_state)