统一指示灯
This commit is contained in:
1 parent
af4f0edc08
commit
32a3a87ef2
7 files changed
+292
-11
No files matched your search
@@ -42,12 +42,8 @@ void Emm_V5_Init(uart_port_t port, int tx_pin, int rx_pin)
|
||||
UART_PIN_NO_CHANGE,
|
||||
UART_PIN_NO_CHANGE));
|
||||
|
||||
gpio_reset_pin(EMM_V5_LED_GPIO);
|
||||
gpio_set_direction(EMM_V5_LED_GPIO, GPIO_MODE_OUTPUT);
|
||||
gpio_set_level(EMM_V5_LED_GPIO, 0);
|
||||
|
||||
ESP_LOGI(TAG, "初始化完成 (UART%d, TX=%d, RX=%d, LED=GPIO%d)",
|
||||
s_uart_port, tx_pin, rx_pin, EMM_V5_LED_GPIO);
|
||||
ESP_LOGI(TAG, "初始化完成 (UART%d, TX=%d, RX=%d)",
|
||||
s_uart_port, tx_pin, rx_pin);
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
|
||||
@@ -27,9 +27,6 @@ extern "C" {
|
||||
#define EMM_V5_UART_BAUDRATE 115200
|
||||
#define EMM_V5_UART_BUF_SIZE 256
|
||||
|
||||
/* LED 指示 (GPIO2 为多数 ESP32 板载 LED) */
|
||||
#define EMM_V5_LED_GPIO GPIO_NUM_2
|
||||
|
||||
/* 接收超时时间 (毫秒) */
|
||||
#define EMM_V5_RX_TIMEOUT_MS 100
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
idf_component_register(SRCS "rgb.c"
|
||||
INCLUDE_DIRS "."
|
||||
REQUIRES esp_driver_rmt driver)
|
||||
@@ -0,0 +1,223 @@
|
||||
/**
|
||||
* @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 <stdlib.h>
|
||||
|
||||
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;
|
||||
|
||||
/* ── 内部: 发送 GRB 数据 ── */
|
||||
|
||||
static void rgb_transmit(uint8_t g, uint8_t r, uint8_t b)
|
||||
{
|
||||
uint8_t grb[3] = { g, r, b };
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
/* ── 公开接口 ── */
|
||||
|
||||
void rgb_init(int gpio)
|
||||
{
|
||||
if (s_led_chan) {
|
||||
ESP_LOGW(TAG, "已初始化, 跳过");
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* @file rgb.h
|
||||
* @brief 单颗 WS2812 RGB LED 控制 (RMT)
|
||||
*
|
||||
* 使用 RMT 外设驱动一颗 WS2812 / SK6812 等可寻址 RGB LED。
|
||||
* 提供 RGB 直接设色与 HSV 设色, 调用即刷新, 无需手动 flush。
|
||||
*/
|
||||
|
||||
#ifndef RGB_H
|
||||
#define RGB_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief 初始化 RGB LED
|
||||
* @param gpio 数据引脚编号
|
||||
*/
|
||||
void rgb_init(int gpio);
|
||||
|
||||
/**
|
||||
* @brief 设置 RGB 颜色 (立即生效)
|
||||
* @param r 红色 0~255
|
||||
* @param g 绿色 0~255
|
||||
* @param b 蓝色 0~255
|
||||
*/
|
||||
void rgb_set(uint8_t r, uint8_t g, uint8_t b);
|
||||
|
||||
/**
|
||||
* @brief 以 HSV 色彩设置颜色 (立即生效)
|
||||
* @param h 色相 0~359
|
||||
* @param s 饱和度 0~100
|
||||
* @param v 明度 0~100
|
||||
*/
|
||||
void rgb_set_hsv(uint16_t h, uint8_t s, uint8_t v);
|
||||
|
||||
/**
|
||||
* @brief 熄灭 LED
|
||||
*/
|
||||
void rgb_off(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* RGB_H */
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
idf_component_register(SRCS "main.c"
|
||||
INCLUDE_DIRS "."
|
||||
PRIV_REQUIRES emm_v5 magnet servo web_ctrl protocol)
|
||||
PRIV_REQUIRES emm_v5 magnet servo web_ctrl protocol rgb)
|
||||
+14
-1
@@ -11,6 +11,7 @@
|
||||
* ESP32 GPIO12 -> 协议 UART RX (← MPU) (UART0)
|
||||
* ESP32 GPIO2 -> I2C SDA (← MPU)
|
||||
* ESP32 GPIO3 -> I2C SCL
|
||||
* ESP32 GPIO27 -> RGB LED (WS2812)
|
||||
* ESP32 GND -> 电机 GND / MPU GND
|
||||
*
|
||||
* 注: 日志控制台已切换到 USB Serial/JTAG (COM 口监视),
|
||||
@@ -24,6 +25,7 @@
|
||||
#include "servo.h"
|
||||
#include "web_ctrl.h"
|
||||
#include "protocol.h"
|
||||
#include "rgb.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_log.h"
|
||||
@@ -32,6 +34,10 @@ static const char *TAG = "MAIN";
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
/* 初始化 RGB LED (GPIO27, 最先启动, 指示系统状态) */
|
||||
rgb_init(GPIO_NUM_27);
|
||||
rgb_set(32, 32, 0); /* 黄色 (低亮), 表示启动中 */
|
||||
|
||||
/* 初始化电机驱动 (UART1, TX=GPIO6, RX=GPIO7, LED=GPIO2) */
|
||||
Emm_V5_Init(UART_NUM_1, GPIO_NUM_6, GPIO_NUM_7);
|
||||
|
||||
@@ -54,9 +60,16 @@ void app_main(void)
|
||||
/* 启动 WiFi + HTTP 网页控制服务器 */
|
||||
web_ctrl_init();
|
||||
|
||||
/* 系统就绪: 绿灯 (低亮) */
|
||||
rgb_set(0, 32, 0);
|
||||
ESP_LOGI(TAG, "系统就绪, 等待网页指令...");
|
||||
|
||||
while (1) {
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
/* 心跳指示: 绿灯慢闪 (低亮) */
|
||||
rgb_set(0, 32, 0);
|
||||
vTaskDelay(pdMS_TO_TICKS(500));
|
||||
rgb_off();
|
||||
vTaskDelay(pdMS_TO_TICKS(500));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user