Browse Source

将esp32s3_espidf代码从5.5升级到6.0版本

corvin_zhang 1 month ago
parent
commit
cf33d0de3e

+ 3 - 2
guguji_real_robot/docs/real_robot_deployment_guide.md

@@ -2,7 +2,7 @@
 
 ## 1. 技术选择
 
-本项目第一版选择 ESP-IDF,而不是 Arduino。
+本项目选择 ESP-IDF,而不是 Arduino。固件当前已经迁移到 ESP-IDF 6.0.1,并通过 `idf_component.yml` 约束为 `>=6.0.1,<6.1.0`。
 
 原因是 ESP32S3 需要同时处理 WiFi UDP、TWAI/CAN、I2C 传感器、命令超时保护和多任务控制循环。ESP-IDF 对 FreeRTOS、WiFi、TWAI 和 I2C 的控制更直接,后续调试真实机器人会更稳。
 
@@ -76,10 +76,11 @@ guguji_real_robot/firmware/esp32s3_espidf/main/robot_config.h
 
 ## 4. 编译和刷写 ESP32S3
 
-安装 ESP-IDF 后执行
+安装并在 VSCode 中选择 ESP-IDF 6.0.1 后,首次从旧 IDF 切换时先清理旧配置
 
 ```bash
 cd /home/corvin/Project/guguji_simulation/guguji_real_robot/firmware/esp32s3_espidf
+rm -rf build sdkconfig sdkconfig.old managed_components dependencies.lock
 idf.py set-target esp32s3
 idf.py build
 idf.py -p /dev/ttyUSB0 flash monitor

+ 46 - 4
guguji_real_robot/firmware/esp32s3_espidf/README.md

@@ -1,6 +1,13 @@
-# ESP32S3 固件
+# ESP32S3 固件(ESP-IDF 6.0.1)
 
-这是 guguji 真实机器人桥接固件,基于 ESP-IDF。
+这是 guguji 真实机器人桥接固件,当前要求 ESP-IDF `>=6.0.1,<6.1.0`。
+
+底层驱动已经迁移到 ESP-IDF 6 新接口:
+
+- I2C:`driver/i2c_master.h` 句柄式 master bus/device API
+- TWAI:`esp_twai.h` + `esp_twai_onchip.h` node API
+- RMT:`driver/rmt_tx.h` + bytes encoder API
+- LEDC:ESP-IDF 6 LEDC API
 
 默认功能:
 
@@ -13,19 +20,54 @@
 - GPIO45:WS2812 RGB LED 状态提示
 - 20ms 控制周期,200ms 命令超时停止电机
 
-编译:
+## VSCode 配置
+
+在 VSCode 命令面板执行:
+
+```text
+ESP-IDF: Configure ESP-IDF Extension
+```
+
+选择 ESP-IDF `6.0.1`。随后单独打开本目录作为工作区:
+
+```text
+/home/corvin/Project/guguji_simulation/guguji_real_robot/firmware/esp32s3_espidf
+```
+
+通过 `ESP-IDF: Open ESP-IDF Terminal` 打开终端,确认版本:
+
+```bash
+idf.py --version
+```
+
+输出中应包含 `v6.0.1`。
+
+## 首次从旧版本切换
+
+旧 ESP-IDF 生成的 `build/` 和 `sdkconfig` 不能直接复用。首次切换到 6.0.1 时执行:
+
+```bash
+rm -rf build sdkconfig sdkconfig.old managed_components dependencies.lock
+idf.py set-target esp32s3
+```
+
+`main/idf_component.yml` 会阻止使用低于 6.0.1 或高于等于 6.1.0 的 IDF 误编译。
+
+## 编译
 
 ```bash
 idf.py set-target esp32s3
 idf.py build
 ```
 
-刷写:
+## 刷写和监视
 
 ```bash
 idf.py -p /dev/ttyUSB0 flash monitor
 ```
 
+如果开发板枚举为 `/dev/ttyACM0`,把端口名称替换掉。串口监视器使用 `Ctrl+]` 退出。
+
 请先修改 `main/config.h` 中的引脚,再修改 `main/robot_config.h` 中的 CAN ID、方向和零偏。
 
 状态提示:

+ 13 - 0
guguji_real_robot/firmware/esp32s3_espidf/main/CMakeLists.txt

@@ -6,4 +6,17 @@ idf_component_register(
         "sensors.c"
         "status_outputs.c"
     INCLUDE_DIRS "."
+    REQUIRES
+        esp_driver_gpio
+        esp_driver_i2c
+        esp_driver_ledc
+        esp_driver_rmt
+        esp_driver_twai
+        esp_event
+        esp_netif
+        esp_timer
+        esp_wifi
+        freertos
+        lwip
+        nvs_flash
 )

+ 0 - 1
guguji_real_robot/firmware/esp32s3_espidf/main/config.h

@@ -24,7 +24,6 @@
 // 无屏状态提示硬件。
 #define GUGUJI_BUZZER_GPIO GPIO_NUM_42
 #define GUGUJI_WS2812_GPIO GPIO_NUM_45
-#define GUGUJI_WS2812_RMT_CHANNEL RMT_CHANNEL_0
 
 #define GUGUJI_I2C_PORT I2C_NUM_0
 #define GUGUJI_I2C_SDA_GPIO GPIO_NUM_8

+ 3 - 0
guguji_real_robot/firmware/esp32s3_espidf/main/idf_component.yml

@@ -0,0 +1,3 @@
+dependencies:
+  idf:
+    version: ">=6.0.1,<6.1.0"

+ 121 - 27
guguji_real_robot/firmware/esp32s3_espidf/main/rs00_mit.c

@@ -1,10 +1,24 @@
 #include "rs00_mit.h"
 
-#include <math.h>
-#include <string.h>
-
-#include "driver/twai.h"
+#include "esp_check.h"
+#include "esp_log.h"
+#include "esp_twai.h"
+#include "esp_twai_onchip.h"
 #include "esp_timer.h"
+#include "freertos/FreeRTOS.h"
+#include "freertos/queue.h"
+
+static const char *TAG = "rs00_mit";
+
+typedef struct {
+    uint32_t identifier;
+    uint8_t data_length;
+    bool is_extended;
+    uint8_t data[TWAI_FRAME_MAX_LEN];
+} rs00_rx_frame_t;
+
+static twai_node_handle_t g_twai_node = NULL;
+static QueueHandle_t g_rx_queue = NULL;
 
 static float clampf(float value, float min_value, float max_value)
 {
@@ -34,32 +48,112 @@ static float uint_to_float(uint32_t value, float min_value, float max_value, uin
 
 static esp_err_t transmit_standard(uint32_t identifier, const uint8_t data[8])
 {
-    twai_message_t message = {
-        .identifier = identifier,
-        .data_length_code = 8,
-        .flags = 0,
+    twai_frame_t frame = {
+        .header = {
+            .id = identifier,
+            .dlc = 8,
+            .ide = 0,
+            .rtr = 0,
+            .fdf = 0,
+        },
+        .buffer = (uint8_t *)data,
+        .buffer_len = 8,
     };
-    memcpy(message.data, data, 8);
-    return twai_transmit(&message, pdMS_TO_TICKS(3));
+    esp_err_t err = twai_node_transmit(g_twai_node, &frame, 3);
+    if (err != ESP_OK) {
+        return err;
+    }
+    // 每帧等待发送完成,保证调用方栈上的 8 字节缓冲区在驱动使用期间有效。
+    return twai_node_transmit_wait_all_done(g_twai_node, 3);
+}
+
+static bool IRAM_ATTR rs00_rx_done_callback(
+    twai_node_handle_t handle,
+    const twai_rx_done_event_data_t *event_data,
+    void *user_context)
+{
+    (void)event_data;
+    (void)user_context;
+    rs00_rx_frame_t received = {0};
+    twai_frame_t frame = {
+        .buffer = received.data,
+        .buffer_len = sizeof(received.data),
+    };
+    BaseType_t higher_priority_task_woken = pdFALSE;
+
+    if (twai_node_receive_from_isr(handle, &frame) == ESP_OK) {
+        received.identifier = frame.header.id;
+        received.data_length = (uint8_t)frame.header.dlc;
+        received.is_extended = frame.header.ide != 0;
+        xQueueSendFromISR(g_rx_queue, &received, &higher_priority_task_woken);
+    }
+    return higher_priority_task_woken == pdTRUE;
+}
+
+static bool IRAM_ATTR rs00_state_change_callback(
+    twai_node_handle_t handle,
+    const twai_state_change_event_data_t *event_data,
+    void *user_context)
+{
+    (void)handle;
+    (void)user_context;
+    if (event_data->new_sta == TWAI_ERROR_BUS_OFF) {
+        ESP_EARLY_LOGE(TAG, "TWAI 进入 BUS-OFF,请检查 TJA1050T、终端电阻和 CANH/CANL");
+    }
+    return false;
 }
 
 esp_err_t rs00_mit_init(gpio_num_t tx_gpio, gpio_num_t rx_gpio)
 {
-    twai_general_config_t general_config = TWAI_GENERAL_CONFIG_DEFAULT(tx_gpio, rx_gpio, TWAI_MODE_NORMAL);
-    general_config.tx_queue_len = 32;
-    general_config.rx_queue_len = 32;
-    twai_timing_config_t timing_config = TWAI_TIMING_CONFIG_1MBITS();
-    twai_filter_config_t filter_config = TWAI_FILTER_CONFIG_ACCEPT_ALL();
-
-    esp_err_t err = twai_driver_install(&general_config, &timing_config, &filter_config);
-    if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) {
-        return err;
+    if (g_twai_node != NULL) {
+        return ESP_ERR_INVALID_STATE;
     }
-    err = twai_start();
-    if (err == ESP_ERR_INVALID_STATE) {
-        return ESP_OK;
+
+    g_rx_queue = xQueueCreate(32, sizeof(rs00_rx_frame_t));
+    if (g_rx_queue == NULL) {
+        return ESP_ERR_NO_MEM;
     }
-    return err;
+
+    const twai_onchip_node_config_t node_config = {
+        .io_cfg = {
+            .tx = tx_gpio,
+            .rx = rx_gpio,
+            .quanta_clk_out = GPIO_NUM_NC,
+            .bus_off_indicator = GPIO_NUM_NC,
+        },
+        .bit_timing = {
+            .bitrate = 1000000,
+        },
+        // 限制单帧失败重试次数,断线时避免某一帧永久占住发送队列。
+        .fail_retry_cnt = 3,
+        .tx_queue_depth = 32,
+    };
+    ESP_RETURN_ON_ERROR(twai_new_node_onchip(&node_config, &g_twai_node), TAG, "创建 TWAI node 失败");
+
+    const twai_mask_filter_config_t accept_standard_frames = {
+        .id = 0,
+        .mask = 0,
+        .is_ext = false,
+        .no_classic = false,
+        .no_fd = true,
+    };
+    ESP_RETURN_ON_ERROR(
+        twai_node_config_mask_filter(g_twai_node, 0, &accept_standard_frames),
+        TAG,
+        "配置 TWAI 接收过滤器失败");
+
+    const twai_event_callbacks_t callbacks = {
+        .on_rx_done = rs00_rx_done_callback,
+        .on_state_change = rs00_state_change_callback,
+    };
+    ESP_RETURN_ON_ERROR(
+        twai_node_register_event_callbacks(g_twai_node, &callbacks, NULL),
+        TAG,
+        "注册 TWAI 回调失败");
+    ESP_RETURN_ON_ERROR(twai_node_enable(g_twai_node), TAG, "启用 TWAI node 失败");
+
+    ESP_LOGI(TAG, "TWAI 初始化完成: TX=%d RX=%d bitrate=1Mbps", tx_gpio, rx_gpio);
+    return ESP_OK;
 }
 
 esp_err_t rs00_mit_send_enable(uint8_t motor_id)
@@ -115,15 +209,15 @@ esp_err_t rs00_mit_send_control(
 
 bool rs00_mit_poll_feedback(rs00_feedback_t *feedback, uint32_t timeout_ms)
 {
-    twai_message_t message = {0};
-    if (twai_receive(&message, pdMS_TO_TICKS(timeout_ms)) != ESP_OK) {
+    rs00_rx_frame_t frame = {0};
+    if (xQueueReceive(g_rx_queue, &frame, pdMS_TO_TICKS(timeout_ms)) != pdTRUE) {
         return false;
     }
-    if ((message.flags & TWAI_MSG_FLAG_EXTD) != 0 || message.data_length_code != 8) {
+    if (frame.is_extended || frame.data_length != 8) {
         return false;
     }
 
-    const uint8_t *data = message.data;
+    const uint8_t *data = frame.data;
     const uint32_t p = ((uint32_t)data[1] << 8) | data[2];
     const uint32_t v = ((uint32_t)data[3] << 4) | (data[4] >> 4);
     const uint32_t t = ((uint32_t)(data[4] & 0x0F) << 8) | data[5];

+ 47 - 28
guguji_real_robot/firmware/esp32s3_espidf/main/sensors.c

@@ -1,13 +1,11 @@
 #include "sensors.h"
 
 #include <math.h>
-#include <string.h>
 
 #include "config.h"
-#include "driver/i2c.h"
+#include "driver/i2c_master.h"
+#include "esp_check.h"
 #include "esp_log.h"
-#include "freertos/FreeRTOS.h"
-#include "freertos/task.h"
 
 static const char *TAG = "guguji_sensors";
 
@@ -27,16 +25,19 @@ static const char *TAG = "guguji_sensors";
 
 static bool g_qmi_ok = false;
 static bool g_qmc_ok = false;
+static i2c_master_bus_handle_t g_i2c_bus = NULL;
+static i2c_master_dev_handle_t g_qmi_device = NULL;
+static i2c_master_dev_handle_t g_qmc_device = NULL;
 
-static esp_err_t i2c_write_u8(uint8_t addr, uint8_t reg, uint8_t value)
+static esp_err_t i2c_write_u8(i2c_master_dev_handle_t device, uint8_t reg, uint8_t value)
 {
     uint8_t payload[2] = {reg, value};
-    return i2c_master_write_to_device(GUGUJI_I2C_PORT, addr, payload, sizeof(payload), pdMS_TO_TICKS(50));
+    return i2c_master_transmit(device, payload, sizeof(payload), 50);
 }
 
-static esp_err_t i2c_read(uint8_t addr, uint8_t reg, uint8_t *data, size_t length)
+static esp_err_t i2c_read(i2c_master_dev_handle_t device, uint8_t reg, uint8_t *data, size_t length)
 {
-    return i2c_master_write_read_device(GUGUJI_I2C_PORT, addr, &reg, 1, data, length, pdMS_TO_TICKS(50));
+    return i2c_master_transmit_receive(device, &reg, 1, data, length, 50);
 }
 
 static int16_t le_i16(const uint8_t *data)
@@ -46,29 +47,47 @@ static int16_t le_i16(const uint8_t *data)
 
 esp_err_t guguji_sensors_init(void)
 {
-    i2c_config_t config = {
-        .mode = I2C_MODE_MASTER,
+    i2c_master_bus_config_t bus_config = {
+        .i2c_port = GUGUJI_I2C_PORT,
         .sda_io_num = GUGUJI_I2C_SDA_GPIO,
         .scl_io_num = GUGUJI_I2C_SCL_GPIO,
-        .sda_pullup_en = GPIO_PULLUP_ENABLE,
-        .scl_pullup_en = GPIO_PULLUP_ENABLE,
-        .master.clk_speed = GUGUJI_I2C_FREQ_HZ,
-        .clk_flags = 0,
+        .clk_source = I2C_CLK_SRC_DEFAULT,
+        .glitch_ignore_cnt = 7,
+        .flags.enable_internal_pullup = true,
     };
-    ESP_ERROR_CHECK(i2c_param_config(GUGUJI_I2C_PORT, &config));
-    ESP_ERROR_CHECK(i2c_driver_install(GUGUJI_I2C_PORT, config.mode, 0, 0, 0));
+    ESP_RETURN_ON_ERROR(i2c_new_master_bus(&bus_config, &g_i2c_bus), TAG, "创建 I2C master bus 失败");
+
+    i2c_device_config_t qmi_config = {
+        .dev_addr_length = I2C_ADDR_BIT_LEN_7,
+        .device_address = GUGUJI_QMI8658A_ADDR,
+        .scl_speed_hz = GUGUJI_I2C_FREQ_HZ,
+    };
+    ESP_RETURN_ON_ERROR(
+        i2c_master_bus_add_device(g_i2c_bus, &qmi_config, &g_qmi_device),
+        TAG,
+        "添加 QMI8658A I2C 设备失败");
+
+    i2c_device_config_t qmc_config = {
+        .dev_addr_length = I2C_ADDR_BIT_LEN_7,
+        .device_address = GUGUJI_QMC6309_ADDR,
+        .scl_speed_hz = GUGUJI_I2C_FREQ_HZ,
+    };
+    ESP_RETURN_ON_ERROR(
+        i2c_master_bus_add_device(g_i2c_bus, &qmc_config, &g_qmc_device),
+        TAG,
+        "添加 QMC6309 I2C 设备失败");
 
     uint8_t who_am_i = 0;
-    if (i2c_read(GUGUJI_QMI8658A_ADDR, QMI_REG_WHO_AM_I, &who_am_i, 1) == ESP_OK && who_am_i == 0x05) {
+    if (i2c_read(g_qmi_device, QMI_REG_WHO_AM_I, &who_am_i, 1) == ESP_OK && who_am_i == 0x05) {
         // CTRL1: 开启地址自增并使用 little-endian,方便一次读完 AX~GZ。
-        i2c_write_u8(GUGUJI_QMI8658A_ADDR, QMI_REG_CTRL1, 0x40);
+        i2c_write_u8(g_qmi_device, QMI_REG_CTRL1, 0x40);
         // CTRL2: 加速度 ±8g,ODR 约 112Hz;CTRL3: 陀螺仪 ±512dps,ODR 约 112Hz。
-        i2c_write_u8(GUGUJI_QMI8658A_ADDR, QMI_REG_CTRL2, 0x26);
-        i2c_write_u8(GUGUJI_QMI8658A_ADDR, QMI_REG_CTRL3, 0x56);
+        i2c_write_u8(g_qmi_device, QMI_REG_CTRL2, 0x26);
+        i2c_write_u8(g_qmi_device, QMI_REG_CTRL3, 0x56);
         // 打开加速度和陀螺仪低通滤波。
-        i2c_write_u8(GUGUJI_QMI8658A_ADDR, QMI_REG_CTRL5, 0x11);
+        i2c_write_u8(g_qmi_device, QMI_REG_CTRL5, 0x11);
         // 使能 accel + gyro。
-        i2c_write_u8(GUGUJI_QMI8658A_ADDR, QMI_REG_CTRL7, 0x03);
+        i2c_write_u8(g_qmi_device, QMI_REG_CTRL7, 0x03);
         g_qmi_ok = true;
         ESP_LOGI(TAG, "QMI8658A 初始化成功");
     } else {
@@ -76,10 +95,10 @@ esp_err_t guguji_sensors_init(void)
     }
 
     uint8_t chip_id = 0;
-    if (i2c_read(GUGUJI_QMC6309_ADDR, QMC_REG_CHIP_ID, &chip_id, 1) == ESP_OK && chip_id == 0x90) {
+    if (i2c_read(g_qmc_device, QMC_REG_CHIP_ID, &chip_id, 1) == ESP_OK && chip_id == 0x90) {
         // 数据手册推荐的连续测量配置:32Gauss 量程、Set/Reset 开、OSR1/OSR2=8。
-        i2c_write_u8(GUGUJI_QMC6309_ADDR, QMC_REG_CTRL2, 0x00);
-        i2c_write_u8(GUGUJI_QMC6309_ADDR, QMC_REG_CTRL1, 0x63);
+        i2c_write_u8(g_qmc_device, QMC_REG_CTRL2, 0x00);
+        i2c_write_u8(g_qmc_device, QMC_REG_CTRL1, 0x63);
         g_qmc_ok = true;
         ESP_LOGI(TAG, "QMC6309 初始化成功");
     } else {
@@ -93,7 +112,7 @@ static void update_imu(guguji_sensor_state_t *state)
 {
     uint8_t raw[12] = {0};
     state->imu_ok = false;
-    if (!g_qmi_ok || i2c_read(GUGUJI_QMI8658A_ADDR, QMI_REG_DATA_START, raw, sizeof(raw)) != ESP_OK) {
+    if (!g_qmi_ok || i2c_read(g_qmi_device, QMI_REG_DATA_START, raw, sizeof(raw)) != ESP_OK) {
         return;
     }
 
@@ -120,7 +139,7 @@ static void update_mag(guguji_sensor_state_t *state)
 {
     uint8_t status = 0;
     state->mag_ok = false;
-    if (!g_qmc_ok || i2c_read(GUGUJI_QMC6309_ADDR, QMC_REG_STATUS, &status, 1) != ESP_OK) {
+    if (!g_qmc_ok || i2c_read(g_qmc_device, QMC_REG_STATUS, &status, 1) != ESP_OK) {
         return;
     }
     if ((status & 0x01u) == 0) {
@@ -128,7 +147,7 @@ static void update_mag(guguji_sensor_state_t *state)
     }
 
     uint8_t raw[6] = {0};
-    if (i2c_read(GUGUJI_QMC6309_ADDR, QMC_REG_DATA_START, raw, sizeof(raw)) != ESP_OK) {
+    if (i2c_read(g_qmc_device, QMC_REG_DATA_START, raw, sizeof(raw)) != ESP_OK) {
         return;
     }
     // QMC6309 典型分辨率约 1mGs/LSB,即 0.1uT/LSB。

+ 38 - 24
guguji_real_robot/firmware/esp32s3_espidf/main/status_outputs.c

@@ -4,7 +4,8 @@
 
 #include "config.h"
 #include "driver/ledc.h"
-#include "driver/rmt.h"
+#include "driver/rmt_encoder.h"
+#include "driver/rmt_tx.h"
 #include "esp_log.h"
 #include "freertos/FreeRTOS.h"
 #include "freertos/task.h"
@@ -16,10 +17,13 @@ static const char *TAG = "status_outputs";
 #define BUZZER_LEDC_MODE LEDC_LOW_SPEED_MODE
 #define BUZZER_DUTY_RES LEDC_TIMER_10_BIT
 #define BUZZER_DUTY_ON 512
+#define WS2812_RMT_RESOLUTION_HZ 10000000
 
 static guguji_status_mode_t g_last_mode = GUGUJI_STATUS_BOOT;
 static uint32_t g_last_beep_ms = 0;
 static bool g_has_mode = false;
+static rmt_channel_handle_t g_ws2812_channel = NULL;
+static rmt_encoder_handle_t g_ws2812_encoder = NULL;
 
 static const char *status_name(guguji_status_mode_t mode)
 {
@@ -92,24 +96,14 @@ static void buzzer_pattern_for_transition(guguji_status_mode_t mode)
 
 static void ws2812_write_rgb(uint8_t red, uint8_t green, uint8_t blue)
 {
-    rmt_item32_t items[24] = {0};
     const uint8_t grb[3] = {green, red, blue};
-    int item_index = 0;
-
-    for (int byte_index = 0; byte_index < 3; ++byte_index) {
-        for (int bit = 7; bit >= 0; --bit) {
-            const bool one = ((grb[byte_index] >> bit) & 0x01u) != 0;
-            // RMT 时钟 40MHz,1 tick=25ns。下面约为 WS2812 的 0/1 码时序。
-            items[item_index].level0 = 1;
-            items[item_index].duration0 = one ? 34 : 14;
-            items[item_index].level1 = 0;
-            items[item_index].duration1 = one ? 14 : 34;
-            item_index++;
-        }
-    }
-
-    rmt_write_items(GUGUJI_WS2812_RMT_CHANNEL, items, 24, true);
-    rmt_wait_tx_done(GUGUJI_WS2812_RMT_CHANNEL, pdMS_TO_TICKS(20));
+    const rmt_transmit_config_t tx_config = {
+        .loop_count = 0,
+        .flags.eot_level = 0,
+    };
+    ESP_ERROR_CHECK(rmt_transmit(g_ws2812_channel, g_ws2812_encoder, grb, sizeof(grb), &tx_config));
+    ESP_ERROR_CHECK(rmt_tx_wait_all_done(g_ws2812_channel, 20));
+    // WS2812 需要至少 50us 的低电平复位间隔,状态任务 1ms 足够。
     vTaskDelay(pdMS_TO_TICKS(1));
 }
 
@@ -163,18 +157,38 @@ esp_err_t guguji_status_outputs_init(void)
         .gpio_num = GUGUJI_BUZZER_GPIO,
         .speed_mode = BUZZER_LEDC_MODE,
         .channel = BUZZER_LEDC_CHANNEL,
-        .intr_type = LEDC_INTR_DISABLE,
         .timer_sel = BUZZER_LEDC_TIMER,
         .duty = 0,
         .hpoint = 0,
     };
     ESP_ERROR_CHECK(ledc_channel_config(&channel_config));
 
-    rmt_config_t ws2812_rmt_config = RMT_DEFAULT_CONFIG_TX(GUGUJI_WS2812_GPIO, GUGUJI_WS2812_RMT_CHANNEL);
-    ws2812_rmt_config.clk_div = 2;
-    ws2812_rmt_config.mem_block_num = 1;
-    ESP_ERROR_CHECK(rmt_config(&ws2812_rmt_config));
-    ESP_ERROR_CHECK(rmt_driver_install(GUGUJI_WS2812_RMT_CHANNEL, 0, 0));
+    rmt_tx_channel_config_t ws2812_channel_config = {
+        .gpio_num = GUGUJI_WS2812_GPIO,
+        .clk_src = RMT_CLK_SRC_DEFAULT,
+        .resolution_hz = WS2812_RMT_RESOLUTION_HZ,
+        .mem_block_symbols = 64,
+        .trans_queue_depth = 2,
+    };
+    ESP_ERROR_CHECK(rmt_new_tx_channel(&ws2812_channel_config, &g_ws2812_channel));
+
+    rmt_bytes_encoder_config_t ws2812_encoder_config = {
+        .bit0 = {
+            .level0 = 1,
+            .duration0 = 3, // 0.3us
+            .level1 = 0,
+            .duration1 = 9, // 0.9us
+        },
+        .bit1 = {
+            .level0 = 1,
+            .duration0 = 9, // 0.9us
+            .level1 = 0,
+            .duration1 = 3, // 0.3us
+        },
+        .flags.msb_first = 1,
+    };
+    ESP_ERROR_CHECK(rmt_new_bytes_encoder(&ws2812_encoder_config, &g_ws2812_encoder));
+    ESP_ERROR_CHECK(rmt_enable(g_ws2812_channel));
 
     ws2812_write_rgb(0, 0, 0);
     buzzer_beep(1600, 70);