/** * @file rgb.c * @brief 单颗 WS2812 RGB LED 控制实现 (RMT) * * 基于 ESP-IDF RMT 外设, 使用 bytes encoder 编码 WS2812 时序: * T0H=0.3µs T0L=0.9µs * T1H=0.9µs T1L=0.3µs * RESET > 50µs * 数据格式: GRB (MSB first) */ #include "rgb.h" #include "esp_check.h" #include "esp_log.h" #include "driver/rmt_tx.h" #include "freertos/FreeRTOS.h" #include "freertos/semphr.h" #include static const char *TAG = "RGB"; #define RESOLUTION_HZ 10000000 /* 10MHz, 1 tick = 0.1µs */ /* ── LED Strip Encoder (参考 esp-idf 官方 led_strip 示例) ── */ typedef struct { rmt_encoder_t base; rmt_encoder_t *bytes_encoder; rmt_encoder_t *copy_encoder; int state; rmt_symbol_word_t reset_code; } rmt_led_strip_encoder_t; static size_t rmt_encode_led_strip(rmt_encoder_t *encoder, rmt_channel_handle_t channel, const void *primary_data, size_t data_size, rmt_encode_state_t *ret_state) { rmt_led_strip_encoder_t *led_encoder = __containerof(encoder, rmt_led_strip_encoder_t, base); rmt_encoder_handle_t bytes_encoder = led_encoder->bytes_encoder; rmt_encoder_handle_t copy_encoder = led_encoder->copy_encoder; rmt_encode_state_t session_state = RMT_ENCODING_RESET; rmt_encode_state_t state = RMT_ENCODING_RESET; size_t encoded_symbols = 0; switch (led_encoder->state) { case 0: /* 发送 GRB 数据 */ encoded_symbols += bytes_encoder->encode(bytes_encoder, channel, primary_data, data_size, &session_state); if (session_state & RMT_ENCODING_COMPLETE) { led_encoder->state = 1; } if (session_state & RMT_ENCODING_MEM_FULL) { state |= RMT_ENCODING_MEM_FULL; goto out; } /* fall-through */ case 1: /* 发送 RESET 码 */ encoded_symbols += copy_encoder->encode(copy_encoder, channel, &led_encoder->reset_code, sizeof(led_encoder->reset_code), &session_state); if (session_state & RMT_ENCODING_COMPLETE) { led_encoder->state = RMT_ENCODING_RESET; state |= RMT_ENCODING_COMPLETE; } if (session_state & RMT_ENCODING_MEM_FULL) { state |= RMT_ENCODING_MEM_FULL; goto out; } break; } out: *ret_state = state; return encoded_symbols; } static esp_err_t rmt_del_led_strip_encoder(rmt_encoder_t *encoder) { rmt_led_strip_encoder_t *led_encoder = __containerof(encoder, rmt_led_strip_encoder_t, base); rmt_del_encoder(led_encoder->bytes_encoder); rmt_del_encoder(led_encoder->copy_encoder); free(led_encoder); return ESP_OK; } static esp_err_t rmt_led_strip_encoder_reset(rmt_encoder_t *encoder) { rmt_led_strip_encoder_t *led_encoder = __containerof(encoder, rmt_led_strip_encoder_t, base); rmt_encoder_reset(led_encoder->bytes_encoder); rmt_encoder_reset(led_encoder->copy_encoder); led_encoder->state = RMT_ENCODING_RESET; return ESP_OK; } static esp_err_t rmt_new_led_strip_encoder(rmt_encoder_handle_t *ret_encoder) { rmt_led_strip_encoder_t *led_encoder = NULL; ESP_RETURN_ON_FALSE(ret_encoder, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); led_encoder = rmt_alloc_encoder_mem(sizeof(rmt_led_strip_encoder_t)); ESP_RETURN_ON_FALSE(led_encoder, ESP_ERR_NO_MEM, TAG, "no mem for led strip encoder"); led_encoder->base.encode = rmt_encode_led_strip; led_encoder->base.del = rmt_del_led_strip_encoder; led_encoder->base.reset = rmt_led_strip_encoder_reset; /* WS2812 时序 (10MHz 分辨率下 duration 直接等于 0.1µs 单位) */ rmt_bytes_encoder_config_t bytes_encoder_config = { .bit0 = { .level0 = 1, .duration0 = 3, /* T0H = 0.3µs */ .level1 = 0, .duration1 = 9, /* T0L = 0.9µs */ }, .bit1 = { .level0 = 1, .duration0 = 9, /* T1H = 0.9µs */ .level1 = 0, .duration1 = 3, /* T1L = 0.3µs */ }, .flags.msb_first = 1, /* WS2812 高位在前, 顺序: G7..G0 R7..R0 B7..B0 */ }; ESP_RETURN_ON_ERROR(rmt_new_bytes_encoder(&bytes_encoder_config, &led_encoder->bytes_encoder), TAG, "create bytes encoder failed"); rmt_copy_encoder_config_t copy_encoder_config = {}; ESP_RETURN_ON_ERROR(rmt_new_copy_encoder(©_encoder_config, &led_encoder->copy_encoder), TAG, "create copy encoder failed"); /* 复位码: 低电平 >50µs */ uint32_t reset_ticks = RESOLUTION_HZ / 1000000 * 50 / 2; /* 250 ticks */ led_encoder->reset_code = (rmt_symbol_word_t){ .level0 = 0, .duration0 = reset_ticks, .level1 = 0, .duration1 = reset_ticks, }; *ret_encoder = &led_encoder->base; return ESP_OK; } /* ── 模块静态变量 ── */ static rmt_channel_handle_t s_led_chan = NULL; static rmt_encoder_handle_t s_led_encoder = NULL; static SemaphoreHandle_t s_lock = NULL; /* 串行化 RMT 访问 */ /* ── 内部: 发送 GRB 数据 ── */ static void rgb_transmit(uint8_t g, uint8_t r, uint8_t b) { if (!s_led_chan || !s_lock) return; uint8_t grb[3] = { g, r, b }; /* main 循环与 Cycle 执行 (_wait_flash) 会从不同任务调用本函数, 必须互斥, 否则并发操作同一 RMT 通道会导致绿灯丢失甚至复位 */ xSemaphoreTake(s_lock, portMAX_DELAY); rmt_transmit_config_t tx_config = { .loop_count = 0 }; ESP_ERROR_CHECK(rmt_transmit(s_led_chan, s_led_encoder, grb, sizeof(grb), &tx_config)); ESP_ERROR_CHECK(rmt_tx_wait_all_done(s_led_chan, portMAX_DELAY)); xSemaphoreGive(s_lock); } /* ── 公开接口 ── */ void rgb_init(int gpio) { if (s_led_chan) { ESP_LOGW(TAG, "已初始化, 跳过"); return; } s_lock = xSemaphoreCreateMutex(); rmt_tx_channel_config_t tx_chan_config = { .clk_src = RMT_CLK_SRC_DEFAULT, .gpio_num = gpio, .mem_block_symbols = 64, .resolution_hz = RESOLUTION_HZ, .trans_queue_depth = 4, }; ESP_ERROR_CHECK(rmt_new_tx_channel(&tx_chan_config, &s_led_chan)); ESP_ERROR_CHECK(rmt_new_led_strip_encoder(&s_led_encoder)); ESP_ERROR_CHECK(rmt_enable(s_led_chan)); ESP_LOGI(TAG, "初始化完成 (GPIO%d)", gpio); } void rgb_set(uint8_t r, uint8_t g, uint8_t b) { rgb_transmit(g, r, b); } void rgb_set_hsv(uint16_t h, uint8_t s, uint8_t v) { h %= 360; uint32_t rgb_max = (uint32_t)(v * 2.55f); uint32_t rgb_min = rgb_max * (100 - s) / 100; uint32_t i = h / 60; uint32_t diff = h % 60; uint32_t rgb_adj = (rgb_max - rgb_min) * diff / 60; uint32_t r = 0, g = 0, b = 0; 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; } if (r > 255) r = 255; if (g > 255) g = 255; if (b > 255) b = 255; rgb_transmit((uint8_t)g, (uint8_t)r, (uint8_t)b); } void rgb_off(void) { rgb_transmit(0, 0, 0); } /* ── 系统状态 ── */ static rgb_state_t s_rgb_state = RGB_STATE_IDLE; void rgb_set_state(rgb_state_t state) { s_rgb_state = state; } rgb_state_t rgb_get_state(void) { return s_rgb_state; }