commit 230ad1254aa9231efb63c65bf11e040a81cd3fe2 Author: kirltrz <63716982+kirltrz@users.noreply.github.com> Date: Wed Jul 29 13:39:34 2026 +0800 步进电机速度控制验证通过 diff --git a/.clangd b/.clangd new file mode 100644 index 0000000..437f255 --- /dev/null +++ b/.clangd @@ -0,0 +1,2 @@ +CompileFlags: + Remove: [-f*, -m*] diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7805557 --- /dev/null +++ b/.gitignore @@ -0,0 +1,78 @@ +# macOS +.DS_Store +.AppleDouble +.LSOverride + +# Directory metadata +.directory + +# Temporary files +*~ +*.swp +*.swo +*.bak +*.tmp + +# Log files +*.log + +# Build artifacts and directories +**/build/ +build/ +*.o +*.a +*.out +*.exe # For any host-side utilities compiled on Windows + +# ESP-IDF specific build outputs +*.bin +*.elf +*.map +flasher_args.json # Generated in build directory +sdkconfig.old +sdkconfig + +# ESP-IDF dependencies +# For older versions or manual component management +/components/.idf/ +**/components/.idf/ +# For modern ESP-IDF component manager +managed_components/ +# If ESP-IDF tools are installed/referenced locally to the project +.espressif/ + +# CMake generated files +CMakeCache.txt +CMakeFiles/ +cmake_install.cmake +install_manifest.txt +CTestTestfile.cmake + +# Python environment files +*.pyc +*.pyo +*.pyd +__pycache__/ +*.egg-info/ +dist/ + +# Virtual environment folders +venv/ +.venv/ +env/ + +# Language Servers +.clangd/ +.ccls-cache/ +compile_commands.json + +# Windows specific +Thumbs.db +ehthumbs.db +Desktop.ini + +# User-specific configuration files +*.user +*.workspace # General workspace files, can be from various tools +*.suo # Visual Studio Solution User Options +*.sln.docstates # Visual Studio diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..622eb4e --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,20 @@ +{ + "configurations": [ + { + "name": "ESP-IDF", + "compilerPath": "C:\\Espressif\\tools\\xtensa-esp-elf\\esp-15.2.0_20251204\\xtensa-esp-elf\\bin\\xtensa-esp32-elf-gcc.exe", + "compileCommands": "${config:idf.buildPath}/compile_commands.json", + "intelliSenseMode": "gcc-x86", + "includePath": [ + "${workspaceFolder}/**" + ], + "browse": { + "path": [ + "${workspaceFolder}" + ], + "limitSymbolsToIncludedHeaders": true + } + } + ], + "version": 4 +} diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..3694ae4 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,10 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "type": "gdbtarget", + "request": "attach", + "name": "Eclipse CDT GDB Adapter" + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..5cbb78c --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,18 @@ +{ + "C_Cpp.intelliSenseEngine": "default", + "idf.extensionActivationMode": "always", + "idf.openOcdConfigs": [ + "board/esp32c5-builtin.cfg" + ], + "idf.currentSetup": "C:\\esp\\v6.0.2\\esp-idf", + "idf.customExtraVars": { + "IDF_TARGET": "esp32c5" + }, + "clangd.path": "C:\\Espressif\\tools\\esp-clang\\esp-20.1.1_20250829\\esp-clang\\bin\\clangd.exe", + "clangd.arguments": [ + "--background-index", + "--query-driver=**", + "--compile-commands-dir=c:\\Users\\Kirl\\Desktop\\EDC\\edc-mcu\\build" + ], + "idf.portWin": "COM4" +} diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..58846b4 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,6 @@ +# The following five lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.22) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(edc-mcu) diff --git a/components/emm_v5/CMakeLists.txt b/components/emm_v5/CMakeLists.txt new file mode 100644 index 0000000..e246c17 --- /dev/null +++ b/components/emm_v5/CMakeLists.txt @@ -0,0 +1,3 @@ +idf_component_register(SRCS "emm_v5.c" + INCLUDE_DIRS "." + REQUIRES esp_driver_uart esp_driver_gpio driver esp_timer) diff --git a/components/emm_v5/emm_v5.c b/components/emm_v5/emm_v5.c new file mode 100644 index 0000000..3388412 --- /dev/null +++ b/components/emm_v5/emm_v5.c @@ -0,0 +1,214 @@ +/** + * @file emm_v5.c + * @brief Emm_V5.0 步进闭环电机驱动器 - ESP-IDF 实现 + */ + +#include "emm_v5.h" +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_timer.h" + +static const char *TAG = "EMM_V5"; + +/* 运行时配置 (由 Emm_V5_Init 设置) */ +static uart_port_t s_uart_port; + +/* ============================================================ + * 初始化 + * ============================================================ */ + +void Emm_V5_Init(uart_port_t port, int tx_pin, int rx_pin) +{ + s_uart_port = port; + + uart_config_t uart_cfg = { + .baud_rate = EMM_V5_UART_BAUDRATE, + .data_bits = UART_DATA_8_BITS, + .parity = UART_PARITY_DISABLE, + .stop_bits = UART_STOP_BITS_1, + .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, + .source_clk = UART_SCLK_DEFAULT, + }; + + ESP_ERROR_CHECK(uart_driver_install(s_uart_port, + EMM_V5_UART_BUF_SIZE * 2, + EMM_V5_UART_BUF_SIZE * 2, + 0, NULL, 0)); + ESP_ERROR_CHECK(uart_param_config(s_uart_port, &uart_cfg)); + ESP_ERROR_CHECK(uart_set_pin(s_uart_port, + tx_pin, + 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); +} + +/* ============================================================ + * 内部辅助 + * ============================================================ */ + +static inline void Emm_V5_Send(const uint8_t *cmd, uint8_t len) +{ + uart_write_bytes(s_uart_port, (const char *)cmd, len); +} + +/* ============================================================ + * 接收数据 + * ============================================================ */ + +uint8_t Emm_V5_Receive_Data(uint8_t *rxCmd, uint8_t maxLen) +{ + uint8_t count = 0; + int64_t last_time, now; + + last_time = esp_timer_get_time(); + + while (1) { + int len = uart_read_bytes(s_uart_port, &rxCmd[count], 1, + pdMS_TO_TICKS(10)); + if (len > 0) { + count++; + if (count >= maxLen) break; + last_time = esp_timer_get_time(); + } else { + now = esp_timer_get_time(); + if ((now - last_time) > (EMM_V5_RX_TIMEOUT_MS * 1000)) { + break; + } + } + } + return count; +} + +/* ============================================================ + * 控制命令 (协议与 Arduino 版本完全一致) + * ============================================================ */ + +void Emm_V5_Reset_CurPos_To_Zero(uint8_t addr) +{ + uint8_t cmd[4] = {addr, 0x0A, 0x6D, 0x6B}; + Emm_V5_Send(cmd, 4); +} + +void Emm_V5_Reset_Clog_Pro(uint8_t addr) +{ + uint8_t cmd[4] = {addr, 0x0E, 0x52, 0x6B}; + Emm_V5_Send(cmd, 4); +} + +void Emm_V5_Read_Sys_Params(uint8_t addr, EmmSysParams_t s) +{ + uint8_t cmd[8] = {0}; + uint8_t i = 0; + + cmd[i++] = addr; + switch (s) { + case S_VER: cmd[i++] = 0x1F; break; + case S_RL: cmd[i++] = 0x20; break; + case S_PID: cmd[i++] = 0x21; break; + case S_VBUS: cmd[i++] = 0x24; break; + case S_CPHA: cmd[i++] = 0x27; break; + case S_ENCL: cmd[i++] = 0x31; break; + case S_TPOS: cmd[i++] = 0x33; break; + case S_VEL: cmd[i++] = 0x35; break; + case S_CPOS: cmd[i++] = 0x36; break; + case S_PERR: cmd[i++] = 0x37; break; + case S_FLAG: cmd[i++] = 0x3A; break; + case S_ORG: cmd[i++] = 0x3B; break; + case S_Conf: cmd[i++] = 0x42; cmd[i++] = 0x6C; break; + case S_State: cmd[i++] = 0x43; cmd[i++] = 0x7A; break; + default: break; + } + cmd[i++] = 0x6B; + Emm_V5_Send(cmd, i); +} + +void Emm_V5_Modify_Ctrl_Mode(uint8_t addr, bool svF, uint8_t ctrl_mode) +{ + uint8_t cmd[6] = {addr, 0x46, 0x69, (uint8_t)svF, ctrl_mode, 0x6B}; + Emm_V5_Send(cmd, 6); +} + +void Emm_V5_En_Control(uint8_t addr, bool state, bool snF) +{ + uint8_t cmd[6] = {addr, 0xF3, 0xAB, (uint8_t)state, (uint8_t)snF, 0x6B}; + Emm_V5_Send(cmd, 6); +} + +void Emm_V5_Vel_Control(uint8_t addr, uint8_t dir, uint16_t vel, uint8_t acc, bool snF) +{ + uint8_t cmd[8] = { + addr, 0xF6, dir, + (uint8_t)(vel >> 8), (uint8_t)(vel >> 0), + acc, (uint8_t)snF, 0x6B + }; + Emm_V5_Send(cmd, 8); +} + +void Emm_V5_Pos_Control(uint8_t addr, uint8_t dir, uint16_t vel, uint8_t acc, + uint32_t clk, bool raF, bool snF) +{ + uint8_t cmd[13] = { + addr, 0xFD, dir, + (uint8_t)(vel >> 8), (uint8_t)(vel >> 0), acc, + (uint8_t)(clk >> 24), (uint8_t)(clk >> 16), + (uint8_t)(clk >> 8), (uint8_t)(clk >> 0), + (uint8_t)raF, (uint8_t)snF, 0x6B + }; + Emm_V5_Send(cmd, 13); +} + +void Emm_V5_Stop_Now(uint8_t addr, bool snF) +{ + uint8_t cmd[5] = {addr, 0xFE, 0x98, (uint8_t)snF, 0x6B}; + Emm_V5_Send(cmd, 5); +} + +void Emm_V5_Synchronous_motion(uint8_t addr) +{ + uint8_t cmd[4] = {addr, 0xFF, 0x66, 0x6B}; + Emm_V5_Send(cmd, 4); +} + +void Emm_V5_Origin_Set_O(uint8_t addr, bool svF) +{ + uint8_t cmd[5] = {addr, 0x93, 0x88, (uint8_t)svF, 0x6B}; + Emm_V5_Send(cmd, 5); +} + +void Emm_V5_Origin_Modify_Params(uint8_t addr, bool svF, uint8_t o_mode, + uint8_t o_dir, uint16_t o_vel, uint32_t o_tm, + uint16_t sl_vel, uint16_t sl_ma, uint16_t sl_ms, bool potF) +{ + uint8_t cmd[20] = { + addr, 0x4C, 0xAE, (uint8_t)svF, + o_mode, o_dir, + (uint8_t)(o_vel >> 8), (uint8_t)(o_vel >> 0), + (uint8_t)(o_tm >> 24), (uint8_t)(o_tm >> 16), + (uint8_t)(o_tm >> 8), (uint8_t)(o_tm >> 0), + (uint8_t)(sl_vel >> 8), (uint8_t)(sl_vel >> 0), + (uint8_t)(sl_ma >> 8), (uint8_t)(sl_ma >> 0), + (uint8_t)(sl_ms >> 8), (uint8_t)(sl_ms >> 0), + (uint8_t)potF, 0x6B + }; + Emm_V5_Send(cmd, 20); +} + +void Emm_V5_Origin_Trigger_Return(uint8_t addr, uint8_t o_mode, bool snF) +{ + uint8_t cmd[5] = {addr, 0x9A, o_mode, (uint8_t)snF, 0x6B}; + Emm_V5_Send(cmd, 5); +} + +void Emm_V5_Origin_Interrupt(uint8_t addr) +{ + uint8_t cmd[4] = {addr, 0x9C, 0x48, 0x6B}; + Emm_V5_Send(cmd, 4); +} diff --git a/components/emm_v5/emm_v5.h b/components/emm_v5/emm_v5.h new file mode 100644 index 0000000..a9779e7 --- /dev/null +++ b/components/emm_v5/emm_v5.h @@ -0,0 +1,89 @@ +/** + * @file emm_v5.h + * @brief Emm_V5.0 步进闭环电机驱动器 - ESP-IDF 组件 + * @author 移植自 Arduino 版本 (原作者: ZHANGDATOU) + * + * 通信协议: UART, 115200bps, 8N1 + * 使用方式: + * Emm_V5_Init(UART_NUM_1, GPIO_NUM_6, GPIO_NUM_7); + */ + +#ifndef EMM_V5_H +#define EMM_V5_H + +#include +#include +#include "driver/uart.h" +#include "driver/gpio.h" +#include "esp_log.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* ============================================================ + * 可配置常量 + * ============================================================ */ +#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 + +/* ============================================================ + * 系统参数枚举 (与 Arduino 版本一致) + * ============================================================ */ +typedef enum { + S_VER = 0, /* 读取固件版本和对应的硬件版本 */ + S_RL = 1, /* 读取相电阻和相电感 */ + S_PID = 2, /* 读取PID参数 */ + S_VBUS = 3, /* 读取总线电压 */ + S_CPHA = 5, /* 读取相电流 */ + S_ENCL = 7, /* 读取经过线性化校准后的编码器值 */ + S_TPOS = 8, /* 读取电机目标位置角度 */ + S_VEL = 9, /* 读取电机实时转速 */ + S_CPOS = 10, /* 读取电机实时位置角度 */ + S_PERR = 11, /* 读取电机位置误差角度 */ + S_FLAG = 13, /* 读取使能/到位/堵转状态标志位 */ + S_Conf = 14, /* 读取驱动参数 */ + S_State = 15, /* 读取系统状态参数 */ + S_ORG = 16, /* 读取正在回零/回零失败状态标志位 */ +} EmmSysParams_t; + +/* ============================================================ + * API 函数声明 + * ============================================================ */ + +/** + * @brief 初始化 UART 和 LED GPIO + * @param port UART 端口号, 如 UART_NUM_1 + * @param tx_pin TX 引脚, 如 GPIO_NUM_6 + * @param rx_pin RX 引脚, 如 GPIO_NUM_7 + */ +void Emm_V5_Init(uart_port_t port, int tx_pin, int rx_pin); +void Emm_V5_Reset_CurPos_To_Zero(uint8_t addr); +void Emm_V5_Reset_Clog_Pro(uint8_t addr); +void Emm_V5_Read_Sys_Params(uint8_t addr, EmmSysParams_t s); +void Emm_V5_Modify_Ctrl_Mode(uint8_t addr, bool svF, uint8_t ctrl_mode); +void Emm_V5_En_Control(uint8_t addr, bool state, bool snF); +void Emm_V5_Vel_Control(uint8_t addr, uint8_t dir, uint16_t vel, uint8_t acc, bool snF); +void Emm_V5_Pos_Control(uint8_t addr, uint8_t dir, uint16_t vel, uint8_t acc, + uint32_t clk, bool raF, bool snF); +void Emm_V5_Stop_Now(uint8_t addr, bool snF); +void Emm_V5_Synchronous_motion(uint8_t addr); +void Emm_V5_Origin_Set_O(uint8_t addr, bool svF); +void Emm_V5_Origin_Modify_Params(uint8_t addr, bool svF, uint8_t o_mode, + uint8_t o_dir, uint16_t o_vel, uint32_t o_tm, + uint16_t sl_vel, uint16_t sl_ma, uint16_t sl_ms, bool potF); +void Emm_V5_Origin_Trigger_Return(uint8_t addr, uint8_t o_mode, bool snF); +void Emm_V5_Origin_Interrupt(uint8_t addr); +uint8_t Emm_V5_Receive_Data(uint8_t *rxCmd, uint8_t maxLen); + +#ifdef __cplusplus +} +#endif + +#endif /* EMM_V5_H */ diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt new file mode 100644 index 0000000..cdeace2 --- /dev/null +++ b/main/CMakeLists.txt @@ -0,0 +1,3 @@ +idf_component_register(SRCS "main.c" + INCLUDE_DIRS "." + PRIV_REQUIRES emm_v5) diff --git a/main/main.c b/main/main.c new file mode 100644 index 0000000..33f6677 --- /dev/null +++ b/main/main.c @@ -0,0 +1,58 @@ +/** + * @file main.c + * @brief Emm_V5.0 步进闭环电机驱动演示 (ESP-IDF) + * + * 硬件接线: + * ESP32 TX (GPIO6) -> 电机 RX + * ESP32 RX (GPIO7) -> 电机 TX + * ESP32 GND -> 电机 GND + */ + +#include "emm_v5.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +void app_main(void) +{ + uint8_t rx_buf[128] = {0}; + uint8_t rx_len; + + /* 初始化驱动 (UART1, TX=GPIO6, RX=GPIO7, LED=GPIO2) */ + Emm_V5_Init(UART_NUM_1, GPIO_NUM_6, GPIO_NUM_7); + + /* 上电延时 2 秒, 等待电机闭环初始化完毕 */ + ESP_LOGI("MAIN", "等待电机初始化..."); + vTaskDelay(pdMS_TO_TICKS(2000)); + + /* ---- 速度模式演示 ---- */ + ESP_LOGI("MAIN", "发送速度控制命令: 地址1, CW, 1000RPM, 加速度10"); + Emm_V5_Vel_Control(1, 0, 100, 10, false); + + /* 接收电机返回的数据 */ + rx_len = Emm_V5_Receive_Data(rx_buf, sizeof(rx_buf)); + + /* 验证校验字节 (0x6B), 点亮/熄灭 LED */ + if (rx_len > 0 && rx_buf[rx_len - 1] == 0x6B) { + gpio_set_level(EMM_V5_LED_GPIO, 1); + ESP_LOGI("MAIN", "通信正常 (校验通过, LED亮)"); + } else { + gpio_set_level(EMM_V5_LED_GPIO, 0); + ESP_LOGW("MAIN", "通信异常 (校验失败, LED灭)"); + } + + /* 打印返回的原始数据 (调试用, 每个字节+1 避免与电机地址冲突) */ + ESP_LOGI("MAIN", "返回数据长度: %d", rx_len); + if (rx_len > 0) { + printf("返回数据(+1偏移): "); + for (int i = 0; i < rx_len; i++) { + printf("%02X ", rx_buf[i] + 1); + } + printf("\n"); + } + + ESP_LOGI("MAIN", "演示完成, 程序挂起"); + while (1) { + vTaskDelay(pdMS_TO_TICKS(1000)); + } +} +