/** * @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 512 /* 接收超时时间 (毫秒) */ #define EMM_V5_RX_TIMEOUT_MS 100 /** * Emm 固件角度 ↔ 脉冲 换算 * 1.8° 步进电机 + 16 微步 → 3200 脉冲/圈 * 每脉冲 = 1.8° / 16 = 0.1125° */ #define EMM_V5_PULSES_PER_REV 3200 #define EMM_V5_DEG_TO_CLK(deg) ((uint32_t)((float)(deg) * \ (float)EMM_V5_PULSES_PER_REV / 360.0f + 0.5f)) #define EMM_V5_CLK_TO_DEG(clk) ((float)(clk) * 0.1125f) /* ============================================================ * 系统参数枚举 (与 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); /** * @brief 将当前位置清零 * @param addr 电机地址 */ void Emm_V5_Reset_CurPos_To_Zero(uint8_t addr); /** * @brief 解除堵转保护 * @param addr 电机地址 */ void Emm_V5_Reset_Clog_Pro(uint8_t addr); /** * @brief 读取系统参数 * @param addr 电机地址 * @param s 系统参数类型 (@see EmmSysParams_t) */ void Emm_V5_Read_Sys_Params(uint8_t addr, EmmSysParams_t s); /** * @brief 读取电机状态标志 (S_FLAG, 功能码 0x3A) 并解析返回 * @param addr 电机地址 * @param timeout_ms 等待返回超时 (ms), 0 使用默认 EMM_V5_RX_TIMEOUT_MS * @return 状态标志字节, 读取/解析失败返回 0xFF * @note 位定义: bit0=Ens_TF 使能, bit1=Prf_TF 到位, bit2=Cgi_TF 堵转, * bit3=Cgp_TF 堵转保护, bit4/bit5=左/右限位, bit7=Oac_TF 掉电 */ uint8_t Emm_V5_Read_Status_Flag(uint8_t addr, uint32_t timeout_ms); /** * @brief 读取电机目标位置 (S_TPOS, 功能码 0x33) 并解析返回 * @param addr 电机地址 * @return 目标位置 (Emm 固件: 0~65535 对应 0~360°), 读取/解析失败返回 -1 */ int32_t Emm_V5_Read_TPOS(uint8_t addr); /** * @brief 读取电机实时位置 (S_CPOS, 功能码 0x36) 并解析返回 * @param addr 电机地址 * @return 实时位置 (Emm 固件: 0~65535 对应 0~360°), 读取/解析失败返回 -1 */ int32_t Emm_V5_Read_CPOS(uint8_t addr); /** * @brief 修改开环/闭环控制模式 * @param addr 电机地址 * @param svF 是否存储, false=不存储, true=存储 * @param ctrl_mode 控制模式: 0=关闭脉冲输入, 1=开环, 2=闭环, 3=En复用为限位/到位输出 */ void Emm_V5_Modify_Ctrl_Mode(uint8_t addr, bool svF, uint8_t ctrl_mode); /** * @brief 使能信号控制 * @param addr 电机地址 * @param state 使能状态, true=使能, false=关闭 * @param snF 多机同步标志, false=不启用, true=启用 */ void Emm_V5_En_Control(uint8_t addr, bool state, bool snF); /** * @brief 速度模式控制 * @param addr 电机地址 * @param dir 方向, 0=CW, 其他=CCW * @param vel 速度 0~5000 RPM * @param acc 加速度 0~255 (0=直接启动) * @param snF 多机同步标志, false=不启用, true=启用 */ void Emm_V5_Vel_Control(uint8_t addr, uint8_t dir, uint16_t vel, uint8_t acc, bool snF); /** * @brief 位置模式控制 * @param addr 电机地址 * @param dir 方向, 0=CW, 其他=CCW * @param vel 速度 0~5000 RPM * @param acc 加速度 0~255 (0=直接启动) * @param clk 脉冲数 0~(2^32-1) * @param raF 相位/绝对标志, false=相对运动, true=绝对运动 * @param snF 多机同步标志, false=不启用, true=启用 */ void Emm_V5_Pos_Control(uint8_t addr, uint8_t dir, uint16_t vel, uint8_t acc, uint32_t clk, bool raF, bool snF); /** * @brief 位置模式控制 (角度输入, °) * @param addr 电机地址 * @param dir 方向, 0=CW, 其他=CCW * @param vel 速度 0~5000 RPM * @param acc 加速度 0~255 (0=直接启动) * @param angle_deg 目标角度 (°), 内部换算为脉冲 (3200=360°) * @param raF 相对/绝对, false=相对, true=绝对 * @param snF 多机同步标志, false=不启用, true=启用 */ void Emm_V5_Pos_Control_Angle(uint8_t addr, uint8_t dir, uint16_t vel, uint8_t acc, float angle_deg, bool raF, bool snF); /** * @brief 立即停止电机 (所有控制模式通用) * @param addr 电机地址 * @param snF 多机同步标志, false=不启用, true=启用 */ void Emm_V5_Stop_Now(uint8_t addr, bool snF); /** * @brief 触发多机同步开始运动 * @param addr 电机地址; 协议约定使用广播地址 0 发送一次, * 所有以 snF=true 入队的电机会同时启动 */ void Emm_V5_Synchronous_motion(uint8_t addr); /** * @brief 设置单圈回零的零点位置 * @param addr 电机地址 * @param svF 是否存储, false=不存储, true=存储 */ void Emm_V5_Origin_Set_O(uint8_t addr, bool svF); /** * @brief 修改回零参数 * @param addr 电机地址 * @param svF 是否存储, false=不存储, true=存储 * @param o_mode 回零模式: 0=单圈就近, 1=单圈方向, 2=多圈无限位碰撞, 3=多圈有限位开关 * @param o_dir 回零方向, 0=CW, 其他=CCW * @param o_vel 回零速度 (RPM) * @param o_tm 回零超时时间 (ms) * @param sl_vel 碰撞检测转速 (RPM) * @param sl_ma 碰撞检测电流 (mA) * @param sl_ms 碰撞检测时间 (ms) * @param potF 上电自动回零, false=不使能, true=使能 */ 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); /** * @brief 触发回零 * @param addr 电机地址 * @param o_mode 回零模式: 0=单圈就近, 1=单圈方向, 2=多圈无限位碰撞, 3=多圈有限位开关 * @param snF 多机同步标志, false=不启用, true=启用 */ void Emm_V5_Origin_Trigger_Return(uint8_t addr, uint8_t o_mode, bool snF); /** * @brief 强制中断并退出回零 * @param addr 电机地址 */ void Emm_V5_Origin_Interrupt(uint8_t addr); /** * @brief 等待指定电机到位 (事件驱动, 不消费到位位) * * 电机驱动 Response 菜单设为 Reached/Both/Other 后, 位置模式 (0xFD) 命令 * 运动到位会主动回传到位帧 (地址 + FD + 9F + 6B)。UART RX 由本组件内部 * 任务中断读取并解析, 此处事件等待全部到位, 不再轮询 S_TPOS/S_CPOS。 * * @note 本函数不清除到位位, 便于调用方分段等待 (如等待期间闪烁 LED); * 一段 motion 等待结束后必须调用 Emm_V5_Arrival_Consume 清除, * 防止残留位污染下一段 motion。 * @param addrs 电机地址数组, 如 {ACTUATOR_ADDR_X} * @param count 数组长度 * @param timeout_ms 本次等待超时 (ms); 0 = 立即返回当前状态 * @return true=全部到位, false=超时/未到齐 */ bool Emm_V5_Wait_Arrival(const uint8_t *addrs, uint8_t count, uint32_t timeout_ms); /** * @brief 消费 (清除) 指定电机的到位位 * @param addrs 电机地址数组 * @param count 数组长度 * @note 一段 motion 的到位等待结束后调用 (成功/超时都要调) */ void Emm_V5_Arrival_Consume(const uint8_t *addrs, uint8_t count); /** * @brief 清除全部电机到位位 (开始新一段 motion 前调用) * @note 防止上一段 motion 超时后迟到的到位帧 (或 0 脉冲命令仍回传的帧) * 污染新一段 motion 的到位等待 */ void Emm_V5_Arrival_Reset(void); /** * @brief 查询当前已置位的到位位 (调试用) * @return 位掩码: bit = (电机地址 - 1), 仅低 8 位有效 */ uint32_t Emm_V5_Arrival_Get_Bits(void); /** * @brief 发送读取系统参数命令并等待响应帧返回 (请求-响应式) * @param addr 电机地址 * @param s 系统参数类型 (@see EmmSysParams_t) * @param rxOut 接收缓冲区 * @param maxLen 缓冲区最大长度 * @param timeout_ms 等待响应超时 (ms) * @return 实际收到的响应字节数 (含 地址/功能码/.../0x6B), 失败返回 0 */ uint8_t Emm_V5_Read_Sys_Params_Resp(uint8_t addr, EmmSysParams_t s, uint8_t *rxOut, uint8_t maxLen, uint32_t timeout_ms); /** * @brief 接收电机返回数据 * @deprecated UART RX 已由内部 RX 任务独占 (中断驱动), 此函数不再读取 UART; * 网页手动读参数请改用 Emm_V5_Read_Sys_Params_Resp * @param rxCmd 接收缓冲区 (保留, 未使用) * @param maxLen 缓冲区最大长度 (保留, 未使用) * @return 恒为 0 */ uint8_t Emm_V5_Receive_Data(uint8_t *rxCmd, uint8_t maxLen); #ifdef __cplusplus } #endif #endif /* EMM_V5_H */