一、所选任务介绍
任务名称:卷线电机的断电角度恢复控制
用 ADMT4000 模块模拟控制一个卷线电机(可应用于升降吊灯、卷线器等场景),实现正反转多圈运行,断电后无需回零即可获取当前角度。
任务要求
- 用 ADMT4000 控制卷线电机正反转,实现多圈运行
- 断电后上电直接获取当前角度,无需回零
- 将角度映射为卷绕/放出长度,通过串口或屏幕显示当前状态
- 所有运行范围在 ADMT4000 的 46 圈内
二、项目描述
本项目设计了一款基于 ADMT4000 和 ESP32C6 的可伸缩电动升降吊灯系统。系统以普通手动升降吊灯为改造对象,在原有吊灯结构的基础上增加电机驱动、绕线机构、位置检测模块和无线控制模块,使普通吊灯具备自动升降和智能控制能力。
系统的主要功能包括:
- 电动升降控制
通过无刷电机驱动绕线机构,实现吊灯的上升与下降。 - 高度位置检测
利用 ADMT4000 读取电机或绕线机构的多圈绝对角度,并将其转换为吊灯当前高度。 - 断电位置保持
ADMT4000 具备断电计圈能力,系统断电后仍可记录旋转圈数。重新上电后,主控能够直接读取当前绝对位置,无需重新回零。 - 无线控制功能(BLE+手机小程序)
系统采用 SEEED XIAO ESP32C6 作为主控,利用其 WiFi 和 BLE 能力,实现手机端对吊灯升降、开关和色温的无线控制。 - 灯光控制功能
通过固态继电器控制吊灯电源开关,并实现不同色温模式的切换。 - 手动干预兼容
即使吊灯受到手动升降影响,系统也可以通过 ADMT4000 重新读取当前位置,从而保证控制逻辑不依赖单纯的软件累计值,提高系统可靠性。
因此,本项目的核心设计目标是:
利用 ADMT4000 的多圈绝对角度检测能力,构建一套具备高度感知、断电记忆、电动调节和无线控制功能的智能升降吊灯系统。

图1:项目概览(左:电动升降吊灯主体;中:小程序“蓝牙智控助手”控制页面;右:色温切换效果)
三、硬件介绍
3.1 ADMT4000 多圈磁角度传感器

图2:ADMT4000 多圈磁角度传感器
ADMT4000 是一种磁转数传感器,能够在设备断电状态下记录外部磁场的旋转圈数。系统重新上电后,主控可以通过 SPI 接口读取 ADMT4000 的绝对位置信息,从而获得当前机械结构的位置状态。
ADMT4000 最多可计数 46 圈外部磁场旋转,并能够以顺时针方向递增绝对位置计数。该特性非常适合应用于升降机构、旋钮控制、绕线机构和多圈位置检测等场景。
ADMT4000 内部主要包括以下传感单元:
- GMR 转数计数传感器
用于记录系统旋转圈数,即判断当前处于第几圈。 - GMR 象限检测传感器
用于辅助判断旋转方向和角度所在象限。 - AMR 角度传感器
用于检测单圈 360° 范围内的绝对角度位置。
通过将 GMR 转数计数结果与 AMR 单圈角度信息结合,ADMT4000 能够输出高精度的多圈绝对角度信息。对于本项目而言,ADMT4000 的优势主要体现在:
- 不依赖电机编码器的增量累计;
- 断电后仍可保持圈数信息;
- 上电后无需回零即可获得当前位置;
- 适合用于“角度—位移—高度”映射控制。
3.2 SEEED XIAO ESP32C6 主控模块
图3:SEEED XIAO ESP32C6 模块引脚图
本项目采用 SEEED XIAO ESP32C6 作为主控模块。该模块体积小、集成度高,适合嵌入到吊灯控制板中。
ESP32C6 在本项目中承担以下功能:
- 通过 SPI 接口读取 ADMT4000 的角度和圈数数据;
- 根据角度信息计算吊灯当前高度;
- 接收手机端或串口端发送的目标高度指令;
- 输出 PWM 信号控制 FOC 电机驱动模块;
- 控制固态继电器,实现吊灯开关与色温切换;
- 通过 WiFi 或 BLE 实现无线通信和手机端控制。
3.3 FOC 电机驱动与 DRV8313
项目采用无刷电机作为升降执行机构,并使用 FOC 驱动方式实现电机平稳控制。FOC 控制可以提升电机运行的稳定性和低速控制性能,适合用于吊灯升降这类对平稳性和安全性要求较高的场景。
DRV8313 作为三相无刷电机驱动芯片,可配合 ESP32C6 输出的 3-PWM 控制信号,实现对无刷电机的驱动控制。
3.4 固态继电器与灯光控制模块
为了实现吊灯开关和色温切换,本项目设计了继电器控制电路。ESP32C6 通过普通 GPIO 引脚控制固态继电器,实现对吊灯交流供电回路的通断控制。
在实际应用中,部分吊灯可以通过多次通断电切换不同色温模式。因此,本项目可通过继电器控制时序实现:
- 吊灯开灯;
- 吊灯关灯;
- 暖光、自然光、冷光等色温切换。
四、方案框图与设计思路

图4:方案框图
本项目在普通手动升降吊灯的基础上增加电动绕线机构。吊灯绳索绕过绕线耦合圈,由无刷电机带动绕线机构旋转,从而实现吊灯高度的升降调节。ADMT4000 安装在电机或绕线机构附近,通过检测磁体旋转角度和圈数,获取当前绕线状态。
系统整体以 ESP32C6 为控制核心,ADMT4000 作为位置检测单元,FOC 驱动模块作为电机执行单元,固态继电器作为灯光控制单元,手机小程序作为无线交互终端。
系统整体工作逻辑如下:
- ADMT4000 实时检测绕线机构的多圈角度;
- ESP32C6 读取角度数据,并计算当前吊灯高度;
- 用户通过手机小程序、串口或按键输入目标高度;
- 主控计算当前高度与目标高度之间的误差;
- 根据误差方向控制电机正转或反转;
- 当吊灯到达目标高度附近时,电机停止;
- 系统实时保存并显示当前位置;
- 断电重启后,系统重新读取 ADMT4000 的绝对角度,恢复当前高度状态。
五、原理图、PCB设计
本项目设计了一块集成控制 PCB,用于连接电源模块、ESP32C6 主控、ADMT4000 位置检测模块、FOC 电机驱动模块以及固态继电器模块。通过 PCB 集成化设计,可以减少外部飞线数量,使系统结构更加紧凑,同时提升整体连接可靠性和调试便利性。

图5:原理图

图6:PCB

图7:PCB 3D模型

图8:PCB焊接完成图
5.1 电源设计
系统外部输入为 220V 交流电。输入电源首先经过保险丝、压敏保护器件、Y 电容和滤波电路,用于提高电源输入的安全性和抗干扰能力。随后,220V 交流电接入 AC-DC 电源模块,转换得到 12V 直流电源。随后,系统通过 LDO 或 DC-DC 降压模块获得 5V 电源。
5.2 强弱电隔离设计
由于本项目涉及 220V 强电输入和低压控制电路,因此 PCB 设计中重点考虑了安全隔离问题:强弱电区域分离、PCB 开槽隔离、合理走线宽度设计、继电器隔离控制。额外的,对焊接完成后的强电裸露引脚进行绝缘处理,防止误触和短路。
5.3 主控ESP32C6 引脚连接表
ESP32C6 模块引脚 | GPIO / 功能标注 | 网络名 | 连接对象 | 功能说明 |
|---|---|---|---|---|
1 | D0 / GPIO0 | IN1 | FOC 驱动模块 | 电机驱动控制信号 1 |
2 | D1 / GPIO1 | IN2 | FOC 驱动模块 | 电机驱动控制信号 2 |
3 | D2 / GPIO2 | IN3 | FOC 驱动模块 | 电机驱动控制信号 3 |
4 | D3 / GPIO21 | EN | FOC 驱动模块 | 电机驱动使能信号 |
5 | D4 / GPIO22 | LED | 固态继电器控制电路 | 控制吊灯开关与色温切换 |
8 | D7 / GPIO17 | SCLK | ADMT4000 模块 | SPI 时钟信号 |
9 | D8 / GPIO19 | SDO | ADMT4000 模块 | ADMT4000 数据输出,主控读取数据 |
10 | D9 / GPIO20 | SDI | ADMT4000 模块 | ADMT4000 数据输入,主控发送指令 |
11 | D10 / GPIO18 | CS | ADMT4000 模块 | SPI 片选信号 |
13 | GND | GND | 系统地 | 主控接地 |
14 | 5V | +5V | 5V 电源 | 主控供电 |
六、软件流程图与关键代码
本项目软件系统以 ESP32C6 为控制核心,围绕 ADMT4000 角度读取、高度换算、BLE 通信、指令解析、电机升降控制、灯光开关控制和参数保存展开。软件流程按照“上电初始化—高度恢复—无线连接—指令执行—状态保存”的顺序运行。
6.1 软件整体流程

图9:软件整体流程图
6.2 指令控制功能
指令类型 | 功能说明 |
|---|---|
目标高度设置 | 控制吊灯自动升降到指定高度 |
上升 / 下降 | 控制吊灯向上或向下移动 |
停止 | 立即停止电机运行,并关闭驱动 |
开灯 / 关灯 | 控制固态继电器通断 |
色温切换 | 通过继电器通断时序切换灯光色温 |
当前位置读取 | 返回当前吊灯高度 |
重置校准 | 将当前位置设置为高度零点 |
6.3 升降控制逻辑

图10:升降控制逻辑图
6.4 关键代码
- 项目工程:项目基于ESP-IDF v5.5.2构建,目标设备为ESP32C6。如图,程序主要在main目录中,其中包括主程序、admt4000驱动、ble收发指令处理、继电器控制等;额外的,simple_foc_src目录包含foc控制的相关参数设置。

图11:软件代码工程
- admt4000关键代码(代码位于admt4000.h):按照数据手册编写相关函数,主要读取绝对角度、单圈角度等,用于后续高度映射和FOC控制。
/**
* @brief ADMT4000采样数据结构
*
* 存储从ADMT4000传感器读取的完整采样信息,包括角度、圈数、
* 故障标志和温度等数据。
*/
typedef struct {
uint16_t angle_raw; /* 单圈角度原始值(16位,有效数据在bits[15:4]) */
uint16_t absangle_raw; /* 绝对角度原始值(包含圈数信息在bits[15:10]) */
int16_t turn_count; /* 圈数值(范围:-9到53,负值表示反向旋转) */
uint16_t fault_flags; /* 故障标志寄存器值(16位,每位代表特定故障类型) */
uint16_t temperature_raw; /* 温度原始值(16位,有效数据在bits[15:4]) */
float temperature_c; /* 转换后的温度值(单位:摄氏度) */
bool turn_valid; /* 圈数有效性标志(true表示圈数有效) */
bool valid; /* 采样数据整体有效性标志 */
} admt4000_sample_t;
/**
* @brief ADMT4000诊断上下文结构
*
* 用于维护诊断过程中的历史状态信息,支持检测异常跳变、
* 故障变化和位置静止等情况。
*/
typedef struct {
admt4000_sample_t prev; /* 前一次采样数据的副本 */
bool has_prev; /* 是否已有前一次采样数据 */
uint16_t prev_fault; /* 前一次的故障标志值 */
bool has_prev_fault; /* 是否已有前一次故障标志 */
uint32_t unchanged_counter; /* 连续未变化计数器(用于检测静止状态) */
} admt4000_diag_ctx_t;
/**
* @brief 初始化ADMT4000传感器和SPI接口
*
* 执行以下初始化步骤:
* 1. 创建SPI互斥锁用于线程保护
* 2. 配置并初始化SPI总线(GPIO17/18/19/20)
* 3. 添加SPI设备,配置时钟1MHz、模式0
* 4. 清除故障锁存器
*
* 注意:该函数可重复调用,如果已初始化则直接返回ESP_OK。
*
* @return esp_err_t ESP_OK表示成功,其他值表示错误
* - ESP_ERR_NO_MEM: 创建互斥锁失败
* - 其他SPI初始化错误码
*/
esp_err_t admt4000_init(void);
/**
* @brief 查询ADMT4000当前是否已完成SPI设备初始化
*
* @return true 表示驱动已就绪,可以继续读取
* @return false 表示初始化失败或尚未初始化
*/
bool admt4000_is_ready(void);
/**
* @brief 读取ADMT4000完整采样数据
*
* 一次性读取传感器的所有关键数据:
* - 绝对角度(含圈数)
* - 单圈角度
* - 故障标志
* - 温度值
*
* 并将原始数据转换为工程单位(角度度数、摄氏度)。
*
* @param sample 输出参数,指向admt4000_sample_t结构体,用于存储完整的采样数据
* 不能为NULL
* @return esp_err_t ESP_OK表示成功,其他值表示错误
* - ESP_ERR_INVALID_ARG: sample参数为NULL
* - 其他SPI读取错误码
*/
esp_err_t admt4000_read_sample(admt4000_sample_t *sample);
/**
* @brief 将原始角度值转换为角度(度)
*
* 从16位原始数据中提取12位角度码(bits[15:4]),
* 并将其转换为0-360度的浮点角度值。
* 分辨率:360/4096 ≈ 0.0879度/LSB
*
* @param angle_raw 16位原始角度数据
* @return float 转换后的角度值(单位:度,范围0-360)
*/
float admt4000_angle_deg_from_raw(uint16_t angle_raw);
/**
* @brief 初始化诊断上下文结构
*
* 将诊断上下文的所有字段清零,准备进行新的诊断会话。
* 应在首次调用admt4000_diag_process之前调用此函数。
*
* @param ctx 指向admt4000_diag_ctx_t结构体的指针,用于存储诊断状态
* 如果为NULL则直接返回
*/
void admt4000_diag_init(admt4000_diag_ctx_t *ctx);
/**
* @brief 处理ADMT4000采样数据并进行诊断分析
*
* 对每次采样的数据执行以下诊断检查:
* 1. 验证圈数有效性及范围(0-46圈)
* 2. 检测故障标志变化并解码新故障
* 3. 检测异常的圈数跳变(超过±2圈)
* 4. 检测角度大幅跳变(超过3000个码值)
* 5. 检测位置静止状态(可选寄存器转储)
*
* 该函数维护内部状态以比较前后两次采样,
* 发现异常时通过ESP_LOG输出警告信息。
*
* @param ctx 指向admt4000_diag_ctx_t结构体的指针,维护历史诊断状态
* 不能为NULL
* @param curr 指向当前采样数据的指针,包含本次读取的传感器数据
* 不能为NULL,且curr->valid必须为true
*/
void admt4000_diag_process(admt4000_diag_ctx_t *ctx, const admt4000_sample_t *curr);
- BLE命令相关代码(代码位于ble_ctrl.h):基于这些指令,手机小程序可以进行灯光控制、高度控制、状态获取和重置等操作。
// BLE协议定义
#define BLE_FRAME_HEADER_1 0xAA
#define BLE_FRAME_HEADER_2 0x55
// 命令类型(手机 -> 设备)
// Payload约定:
// - SET_TARGET_POS: len=2, uint16小端, 单位cm, 范围0~200
// - CALIBRATE/LED_SWITCH/RESET_ZERO/MOVE_UP/MOVE_DOWN/EMERGENCY_STOP: len=0
// - STOP_SOFT/RESUME/GET_STATUS/GET_FAULT/ZERO_AND_SAVE/ENABLE_MOTOR/DISABLE_MOTOR: len=0
// - GET_STATUS: 查询当前位置 + LED状态,返回 BLE_STATUS_BASE(0x81)
// - GET_FAULT: 查询电机使能/暂停/传感器状态 + 故障标志,返回 BLE_STATUS_FAULT(0x82)
// - LED_CONTROL: len=2, [led_type, led_on]
#define BLE_CMD_SET_TARGET_POS 0x01 // 设置目标位置(cm)
#define BLE_CMD_CALIBRATE 0x02 // 启动校准
#define BLE_CMD_LED_CONTROL 0x03 // LED控制
#define BLE_CMD_LED_SWITCH 0x04 // LED切色
#define BLE_CMD_RESET_ZERO 0x05 // 重置0坐标
#define BLE_CMD_MOVE_UP 0x06 // 上升5cm
#define BLE_CMD_MOVE_DOWN 0x07 // 下降5cm
#define BLE_CMD_EMERGENCY_STOP 0x08 // 急停
#define BLE_CMD_STOP_SOFT 0x09 // 软停(减速/保持)
#define BLE_CMD_RESUME 0x0A // 恢复运行
#define BLE_CMD_GET_STATUS 0x0B // 查询当前位置 + LED状态
#define BLE_CMD_GET_FAULT 0x0C // 查询电机运行状态 + 故障状态
#define BLE_CMD_ZERO_AND_SAVE 0x0D // 重置0坐标并保存
#define BLE_CMD_ENABLE_MOTOR 0x0E // 使能电机驱动
#define BLE_CMD_DISABLE_MOTOR 0x0F // 关闭电机驱动
- FOC参数设定(位于foc_config.h):参数主要设定了最大电压速度、极对数、转盘参数(直径根据3D模型或者测量情况设置为3cm)、PID参数等。
/* Scheduler and PWM */
#define FOC_CFG_CONTROL_PERIOD_US 1000U
#define FOC_CFG_PWM_FREQ_HZ 20000U
/* Motor and controller limits */
#define FOC_CFG_VOLTAGE_POWER_SUPPLY 12.0f
#define FOC_CFG_VOLTAGE_LIMIT 12.0f
#define FOC_CFG_VELOCITY_LIMIT 20.0f
#define FOC_CFG_VOLTAGE_SENSOR_ALIGN 8.0f
#define FOC_CFG_POLE_PAIRS 11
/* Position control (linear coordinate mapped to motor angle) */
#define FOC_CFG_POSITION_MAX_CM 200.0f
#define FOC_CFG_POSITION_STEP_CM 5.0f
/* Geometry mapping: s = r * theta, where r = diameter/2. */
#define FOC_CFG_SPOOL_DIAMETER_CM 3.0f // 转盘直径设置
#define FOC_CFG_POSITION_DIR_SIGN 1.0f
#define FOC_CFG_POSITION_REACH_TOL_CM 0.5f
#define FOC_CFG_MOTOR_IDLE_DISABLE_MS 5000U
/* Absolute-angle origin used for linear position conversion. */
#define FOC_CFG_POSITION_ABS_OFFSET_TURNS 30.0f
#define FOC_CFG_POSITION_ABS_OFFSET_DEG 0.0f
/*
* Current command limit for non-voltage torque control modes.
* This limits current_sp when torque_controller != Type_voltage.
*/
#define FOC_CFG_CURRENT_SP_LIMIT 20.0f
/* Default control modes at startup */
#define FOC_CFG_DEFAULT_TORQUE_CONTROLLER Type_voltage
#define FOC_CFG_DEFAULT_MOTION_CONTROLLER Type_angle
/* Velocity loop PID (output unit: voltage command) */
#define FOC_CFG_PID_VEL_P 0.2f
#define FOC_CFG_PID_VEL_I 0.0f
#define FOC_CFG_PID_VEL_D 0.0f
#define FOC_CFG_PID_VEL_OUTPUT_RAMP 10.0f
/* Angle loop PID (output unit: velocity command) */
#define FOC_CFG_PID_ANG_P 30.0f
#define FOC_CFG_PID_ANG_I 0.0f
#define FOC_CFG_PID_ANG_D 0.2f
#define FOC_CFG_PID_ANG_OUTPUT_RAMP 30.0f
- 主程序逻辑(位于admt_monitor.c):上电后,进行外设初始化,随后创建多个任务,实现FOC控制、BLE指令、串口输出等。
/**
* @brief 应用程序主入口
*
* 初始化所有硬件组件(LED、继电器灯、BLE、FOC),
* 加载保存的位置零点,创建监控和控制任务。
* 启动顺序:
* 1. 初始化板载LED和继电器灯
* 2. 启动BLE服务并注册命令处理回调
* 3. 启动SimpleFOC控制系统
* 4. 从NVS加载保存的零点位置(如果存在)
* 5. 初始禁用电机,等待运动命令时自动启用
* 6. 创建FOC监控任务和串口控制任务
*/
void app_main(void)
{
#if CONFIG_FREERTOS_HZ != 1000
ESP_LOGE(TAG,
"CONFIG_FREERTOS_HZ=%d, esp_simplefoc requires 1000. Run reconfigure to apply sdkconfig.defaults.",
CONFIG_FREERTOS_HZ);
#endif
esp_err_t err = board_led_init();
if (err != ESP_OK) {
ESP_LOGE(TAG, "Board LED init failed: %s", esp_err_to_name(err));
return;
}
err = relay_light_init();
if (err != ESP_OK) {
ESP_LOGE(TAG, "External lamp init failed: %s", esp_err_to_name(err));
return;
}
relay_light_set_on(false);
// 启动BLE服务,注册命令处理回调
err = ble_ctrl_start(ble_cmd_handler);
if (err != ESP_OK) {
ESP_LOGE(TAG, "BLE init failed: %s", esp_err_to_name(err));
return;
}
// 启动SimpleFOC
simple_foc_start();
// 创建监控任务(替代原来的测试任务)
if (xTaskCreate(foc_monitor_task,
"foc_monitor_task",
4096,
NULL,
4,
NULL) != pdPASS) {
ESP_LOGE(TAG, "Create foc_monitor_task failed");
return;
}
position_state_ensure_initialized();
motor_disable_and_hold();
ESP_LOGI(TAG, "Motor initially disabled, will auto-enable on motion command");
if (xTaskCreate(serial_control_task,
"serial_control_task",
4096,
NULL,
4,
NULL) != pdPASS) {
ESP_LOGE(TAG, "Create serial_control_task failed");
return;
}
ESP_LOGI(TAG, "ADI Motor app started with BLE protocol");
}
七、实物演示
7.1 组装过程展示:安装无刷电机、ADI ADMT4000板、轴承、吊灯、主控PCB板。最后绕上吊灯线。

图12:组装过程图

图13:组装结果图
7.2 开关灯:通过小程序,控制灯光开光和色温切换,色温切换是通过短时关闭来实现。

图14:开关灯与切换色温
7.3 吊灯高度控制:小程序直接设置高度或上升下降5cm。最低为0cm,最高根据吊灯线长可设置200cm。

图15:控制吊灯升降
7.4 断电位置保持:当前配置下电机可转动21圈(线轮直径3cm,直线行程200cm),系统断电后仍可记录旋转圈数,通过使用ADMT4000传感器的绝对角度实现。(具体效果可观看视频)
八、遇到的难点及解决方法
8.1 多圈角度与实际高度的映射问题
问题:
ADMT4000 输出的是角度和圈数信息,而系统需要控制的是吊灯的实际高度。因此,需要建立角度、绕线长度和高度之间的对应关系。
解决方法:
根据绕线轮半径和旋转圈数建立数学模型,并通过实测校准绕线轮有效半径和机械误差,使高度计算更加接近实际值。
8.2 电机升降过程中的平稳性问题
问题:
吊灯升降过程中,如果电机启停过快或速度变化较大,容易造成吊灯晃动,影响使用体验和安全性。
解决方法:
采用更稳定的 FOC 电机控制方式,在软件中设置速度限制、误差阈值和缓启动策略,避免电机突然加速或急停。
8.3 电机卷线轮打滑
问题:
3D打印的电机卷线轮出现打滑现象。
解决方法:
通过在接触面张贴普通的双面胶,以提升摩擦力,可以有效解决打滑问题。
九、复刻注意事项
- 器件包括:ADI ADMT4000开发板、ESP32C6和PCB、4015无刷电机、FOC驱动板、608zz轴承、和一个可升降吊灯。3D打印支架后进行组装。
- 程序上,由于电机安装位置、负载的区别,需要在foc_config.h中配置合适的PID参数、电机极对数。
- 由于接了220V强电,焊接、安装、测试过程中均需要特别注意。
十、心得体会
- 通过本次项目设计,我们将 ADMT4000 多圈磁角度传感器应用于可伸缩电动升降吊灯,实现了从竞赛任务到智能家居场景的拓展,进一步理解了多圈绝对位置检测在断电恢复和高度控制中的应用价值。
- 本项目也让我们认识到,完整的机器控制系统不仅需要实现传感、驱动和控制功能,还需要综合考虑机械结构、电源安全、PCB布局和用户交互等因素。