Files

321 lines
8.1 KiB
C

/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include <string.h>
#include <math.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_err.h"
#include "esp_log.h"
#include "esp_check.h"
#include "app_led.h"
#include "bsp_board.h"
#include "driver/gpio.h"
#include "driver/ledc.h"
#include "led_strip.h"
#include "ui_main.h"
#include "ui_device_ctrl.h"
static const char *TAG = "app_led";
typedef struct {
uint16_t h;
uint8_t s;
uint8_t v;
} hsv_t;
typedef struct {
uint8_t r;
uint8_t g;
uint8_t b;
bool on;
} led_state_t;
static led_state_t g_led_state = {
.on = false,
.r = 180,
.g = 180,
.b = 180,
};
#define LED_CHANNEL_GREEN LEDC_CHANNEL_3
#define LED_CHANNEL_BLUE LEDC_CHANNEL_2
#define LED_CHANNEL_RED LEDC_CHANNEL_0
#define LEDPWM_CNT_TOP 256
static uint8_t g_gamma_table[LEDPWM_CNT_TOP + 1];
static hsv_t g_customize_color = {0};
static gpio_num_t g_last_led_io[3] = {GPIO_NUM_NC};
static bool g_initialized = 0;
/**
* @brief Simple helper function, converting HSV color space to RGB color space
*
* Wiki: https://en.wikipedia.org/wiki/HSL_and_HSV
*
*/
static void led_hsv2rgb(uint32_t h, uint32_t s, uint32_t v, uint8_t *r, uint8_t *g, uint8_t *b)
{
h %= 360; /**< h -> [0,360] */
uint32_t rgb_max = v * 2.55f;
uint32_t rgb_min = rgb_max * (100 - s) / 100.0f;
uint32_t i = h / 60;
uint32_t diff = h % 60;
/**< RGB adjustment amount by hue */
uint32_t rgb_adj = (rgb_max - rgb_min) * diff / 60;
switch (i) {
case 0:
*r = rgb_max;
*g = rgb_min + rgb_adj;
*b = rgb_min;
break;
case 1:
*r = rgb_max - rgb_adj;
*g = rgb_max;
*b = rgb_min;
break;
case 2:
*r = rgb_min;
*g = rgb_max;
*b = rgb_min + rgb_adj;
break;
case 3:
*r = rgb_min;
*g = rgb_max - rgb_adj;
*b = rgb_max;
break;
case 4:
*r = rgb_min + rgb_adj;
*g = rgb_min;
*b = rgb_max;
break;
default:
*r = rgb_max;
*g = rgb_min;
*b = rgb_max - rgb_adj;
break;
}
}
void led_rgb2hsv(uint8_t r, uint8_t g, uint8_t b, uint16_t *h, uint8_t *s, uint8_t *v)
{
int32_t R = r;
int32_t G = g;
int32_t B = b;
int32_t min, max, delta, tmp;
tmp = R > G ? G : R;
min = tmp > B ? B : tmp;
tmp = R > G ? R : G;
max = tmp > B ? tmp : B;
*v = 100 * max / 255; /**< v */
delta = max - min;
if (max != 0) {
*s = 100 * delta / max; /**< s */
} else {
/**< r = g = b = 0 */
*s = 0;
*h = 0;
return;
}
float h_temp = 0;
if (delta == 0) {
*h = 0;
return;
} else if (R == max) {
h_temp = ((float)(G - B) / (float)delta); /**< between yellow & magenta */
} else if (G == max) {
h_temp = 2.0f + ((float)(B - R) / (float)delta); /**< between cyan & yellow */
} else if (B == max) {
h_temp = 4.0f + ((float)(R - G) / (float)delta); /**< between magenta & cyan */
}
h_temp *= 60.0f;
if (h_temp < 0.0f) {
h_temp += 360;
}
*h = (uint32_t)h_temp; /**< degrees */
}
esp_err_t app_pwm_led_init(gpio_num_t gpio_r, gpio_num_t gpio_g, gpio_num_t gpio_b)
{
esp_err_t ret_val = ESP_OK;
ledc_timer_config_t ledc_timer = {
.speed_mode = LEDC_LOW_SPEED_MODE,
.timer_num = LEDC_TIMER_0,
.duty_resolution = LEDC_TIMER_8_BIT,
.freq_hz = 8192,
.clk_cfg = LEDC_AUTO_CLK
};
ret_val |= ledc_timer_config(&ledc_timer);
ledc_channel_config_t ledc_channel_red = {
.speed_mode = LEDC_LOW_SPEED_MODE,
.channel = LED_CHANNEL_RED,
.timer_sel = LEDC_TIMER_0,
.intr_type = LEDC_INTR_DISABLE,
.gpio_num = gpio_r,
.duty = 0,
.hpoint = 0
};
ret_val |= ledc_channel_config(&ledc_channel_red);
ledc_channel_config_t ledc_channel_green = {
.speed_mode = LEDC_LOW_SPEED_MODE,
.channel = LED_CHANNEL_GREEN,
.timer_sel = LEDC_TIMER_0,
.intr_type = LEDC_INTR_DISABLE,
.gpio_num = gpio_g,
.duty = 0,
.hpoint = 0
};
ret_val |= ledc_channel_config(&ledc_channel_green);
ledc_channel_config_t ledc_channel_blue = {
.speed_mode = LEDC_LOW_SPEED_MODE,
.channel = LED_CHANNEL_BLUE,
.timer_sel = LEDC_TIMER_0,
.intr_type = LEDC_INTR_DISABLE,
.gpio_num = gpio_b,
.duty = 0,
.hpoint = 0
};
ret_val |= ledc_channel_config(&ledc_channel_blue);
g_initialized = 1;
// Generate gamma correction table
for (int i = 0; i < (LEDPWM_CNT_TOP + 1); i++) {
g_gamma_table[i] = (int)roundf(powf((float)i / (float)LEDPWM_CNT_TOP, 1.0 / 0.45) * (float)LEDPWM_CNT_TOP);
}
g_customize_color.h = 0;
g_customize_color.s = 100;
g_customize_color.v = 100;
g_last_led_io[0] = gpio_r;
g_last_led_io[1] = gpio_g;
g_last_led_io[2] = gpio_b;
return ESP_OK;
}
esp_err_t app_pwm_led_change_io(gpio_num_t gpio_r, gpio_num_t gpio_g, gpio_num_t gpio_b)
{
ESP_RETURN_ON_FALSE(g_initialized, ESP_ERR_INVALID_STATE, TAG, "pwm led is not running");
if (g_initialized) {
ESP_LOGI(TAG, "io set to %d,%d,%d; before: %d,%d,%d",
gpio_r, gpio_g, gpio_b,
g_last_led_io[0],
g_last_led_io[1],
g_last_led_io[2]);
gpio_set_direction(g_last_led_io[0], GPIO_MODE_INPUT);
gpio_set_direction(g_last_led_io[1], GPIO_MODE_INPUT);
gpio_set_direction(g_last_led_io[2], GPIO_MODE_INPUT);
}
app_pwm_led_init(gpio_r, gpio_g, gpio_b);
return ESP_OK;
}
esp_err_t app_pwm_led_deinit(void)
{
ESP_LOGW(TAG, "app_pwm_led_deinit() ESP_ERR_NOT_SUPPORTED");
return ESP_ERR_NOT_SUPPORTED;
}
static void update_pwm_led(uint8_t r, uint8_t g, uint8_t b)
{
ledc_set_duty(LEDC_LOW_SPEED_MODE, LED_CHANNEL_RED, r);
ledc_set_duty(LEDC_LOW_SPEED_MODE, LED_CHANNEL_GREEN, g);
ledc_set_duty(LEDC_LOW_SPEED_MODE, LED_CHANNEL_BLUE, b);
ledc_update_duty(LEDC_LOW_SPEED_MODE, LED_CHANNEL_RED);
ledc_update_duty(LEDC_LOW_SPEED_MODE, LED_CHANNEL_GREEN);
ledc_update_duty(LEDC_LOW_SPEED_MODE, LED_CHANNEL_BLUE);
}
esp_err_t app_pwm_led_set_all(uint8_t red, uint8_t green, uint8_t blue)
{
esp_err_t ret_val = ESP_OK;
g_led_state.r = red;
g_led_state.g = green;
g_led_state.b = blue;
app_pwm_led_set_power(true);
return ret_val;
}
esp_err_t app_pwm_led_set_all_hsv(uint16_t h, uint8_t s, uint8_t v)
{
esp_err_t ret_val = ESP_OK;
uint8_t red = 0;
uint8_t green = 0;
uint8_t blue = 0;
led_hsv2rgb(h, s, v, &red, &green, &blue);
g_led_state.r = red;
g_led_state.g = green;
g_led_state.b = blue;
app_pwm_led_set_power(true);
return ret_val;
}
esp_err_t app_pwm_led_set_power(bool power)
{
esp_err_t ret_val = ESP_OK;
uint8_t red = 0;
uint8_t green = 0;
uint8_t blue = 0;
if (power) {
g_led_state.on = true;
ui_dev_ctrl_set_state(UI_DEV_LIGHT, 1);
} else {
g_led_state.on = false;
update_pwm_led(0, 0, 0);
ui_dev_ctrl_set_state(UI_DEV_LIGHT, 0);
return ret_val;
}
red = g_gamma_table[g_led_state.r];
green = g_gamma_table[g_led_state.g];
blue = g_gamma_table[g_led_state.b];
update_pwm_led(red, green, blue);
return ret_val;
}
bool app_pwm_led_get_state(void)
{
return g_led_state.on;
}
esp_err_t app_pwm_led_set_customize_color(uint16_t h, uint8_t s, uint8_t v)
{
ESP_LOGI(TAG, "customize_color: hsv=[%d,%d,%d]", h, s, v);
g_customize_color.h = h;
g_customize_color.s = s;
g_customize_color.v = v;
return ESP_OK;
}
esp_err_t app_pwm_led_get_customize_color(uint16_t *h, uint8_t *s, uint8_t *v)
{
*h = g_customize_color.h;
*s = g_customize_color.s;
*v = g_customize_color.v;
return ESP_OK;
}