2026 ADI机器控制设计竞赛 - 基于ADMT4000模块实现桌面滑台移动控制
该项目使用了ADMT4000模块,实现了桌面滑台移动控制系统的设计,它的主要功能为:通过读取多圈角度值映射为高度,支持串口控制和Web页面控制目标高度,断电重启后无需回零即可恢复当前高度。。
标签
ESP32
WiFi
ADI
串口
步进电机
控制
磁转数传感器
ADMT4000
桌面滑台
TMC2209
鲜de芒果
更新2026-05-11
28

一、项目介绍

本项目基于ADMT4000模块实现桌面滑台移动控制系统,通过读取多圈角度值映射为高度,支持串口控制和Web页面控制目标高度,断电重启后无需回零即可恢复当前高度。系统使用 ESP32Duino WiFi 开发板、TMC2209SILENTSTEPSTICK模块驱动42步进电机,ADMT4000模块读取电机转动圈数,42步进电机直连T8丝杆驱动滑台移动。


1.1 硬件介绍

  • ADI ADMT4000磁传感器模块: 是一款面向 ADMT4000 多圈磁角度(圈数)传感器 的最小系统核心板,集成了磁畴复位电路与完整的信号引出设计,旨在帮助开发者快速学习、验证和使用 ADMT4000 的核心特性,降低硬件搭建与调试门槛。
  • TMC2209步进电机驱动模块:是TRIAMINIC推出的一款步进电机驱动模块。TMC2209 模块采用独家 TRIAMINIC 技术,使驱动模块静音且高精度。该步进电机模块内嵌12.5 MHz的内部振荡器,UART用于串行数据传输,并提供高分辨率步数,全分辨率高达1/256步,以实现平滑。
  • ESP32Duino WiFi 开发板: 是一款完全兼容经典的 Arduino Uno R3 的物理接口与引脚布局,同时以功能更强大的 ESP32-MINI-1 WiFi 模块作为主控制器。ESP32Duino 无缝桥接了易于上手的 Arduino 生态系统与强大的物联网能力,让开发者能够在不改变原有 Uno 项目硬件结构的情况下,轻松为其添加 Wi-Fi、蓝牙等无线连接功能,极大地简化了从传统单片机项目向物联网应用的升级路径。


1.2 功能概览

本系统主要实现以下功能:

  • 多圈角度实时读取:ESP32通过SPI协议读取ADMT4000传感器输出的绝对角度数据(含已旋转圈数),实时获取滑台的旋转角度位置。
  • 角度→位移→高度的线性映射:将ADMT4000测得的多圈角度(单位:度)通过梯形丝杆的导程参数线性映射为滑台的实际位移/高度值。
  • 串口目标高度控制:用户通过串口发送目标高度(单位:mm),系统计算出需要旋转的总角度和步进电机应响应的步数,驱动TMC2209控制的步进电机转动,带动滑台升降到目标位置。
  • 断电高度恢复:ESP32利用NVS(Non-Volatile Storage)闪存存储机制,在断电前记录当前的绝对角度/高度值。系统重启后自动从NVS读取保存的高度值,确保无需手动归零即可正确恢复位置状态。
  • 断电期间被动位移跟踪:ADMT4000传感器内置的多圈磁计数功能可确保系统在完全断电期间即使被人为转动电机轴,传感器仍能跟踪记录旋转变化并累加记住多圈角度偏移值。上电后通过SPI直接读出包含断电期间位移的绝对位置,与NVS存储值结合可实现双重可靠恢复。
  • 实现WiFi连接和Web服务器功能: 通过Web页面控制滑台位置, 支持自定义快捷按钮个数和快捷移动位置, 防止按钮多次重复点击的逻辑, 网页上实时更新当前位置信息
  • 实现修正按钮功能,用于修正位置偏差


1.3 设计思路

1.3.1 整体架构

系统采用分层架构,由物理层(步进电机+丝杆滑台+永久磁体)、驱动层(TMC2209步进驱动器)、传感层(ADMT4000多圈角度传感器)、主控层(ESP32)和应用层(串口交互)组成。主控层通过SPI总线读取ADMT4000的绝对角度值,通过STEP/DIR引脚控制TMC2209驱动步进电机转动,实现位置闭环控制。通过WiFi模块连接到网络方便调试与远程控制。


1.3.2 核心映射逻辑:角度 → 位移 → 高度

系统的核心在于建立旋转角度与直线位移之间的线性映射关系,整个映射路径如下:


第一步:ADMT4000传感器值 → 旋转角度

ADMT4000传感器测量的是电机输出轴(或丝杆输入端)的绝对旋转角度。传感器支持顺时针方向递增计数,能够以高精度记录从0°到16,560°(对应46整圈)范围内的多圈绝对角度值。传感器内部通过SPI返回的原始寄存器数据经过简单解析后,可以直接得到以“度”(°)为单位的绝对角度值φ(0° ≤ φ < 46×360° = 16,560°)。


第二步:旋转角度 φ → 丝杆直线位移 L

假定系统中的传动结构已做了这样的物理关系设计:选用的梯形丝杆导程为P(单位:mm/圈),即丝杆旋转一整圈(360°),滑台螺母沿丝杆轴向移动的距离为P mm。

因此,当丝杆(或与丝杆输入端固连的电机)实际旋转了φ°(绝对角度)时,滑台的直线位移L(mm)为:

第三步:位移 L → 滑台高度 H

滑台的高度基准采用零位基准点(H₀)的设计:当滑台位于最底部(即行程下限位置,也称机械零位)时,我们设定丝杆的绝对角度φ刚好为0°(或者人为约定的一个零点基准角度)。当滑台从零位向上移动位移L时,当前滑台高度H(mm)的计算公式为:

其中:

  • H₀——滑台在最低点(机械零位)时的高度,可设置为0 mm,或根据实际机械安装时的基准高度设定;
  • φ——ADMT4000测量的当前绝对旋转角度(°);
  • P——丝杆导程(mm/圈)。


通过上述映射关系,用户可以通过Serial输入目标高度值(如:20 mm),系统根据目标高度反向计算出所需旋转的总角度φ_target:

再根据当前ADMT4000实测角度φ_current与实际目标φ_target的差值Δφ,计算出步进电机需要前进的步数,以此驱动滑台移动到指定高度。


1.3.3 断电重启后位置恢复机制

为了保证断电—上电后的位置恢复能力,本系统设计了“双重冗余恢复”机制:

  • 断电恢复(断电期间未被触动) :系统每次位置变化后都会主动使用ESP32的NVS组件在Flash中持久化保存当前的高度(或φ-angle)。断电重启后,系统执行先读NVS的逻辑,直接从Flash中恢复断电前记录的位置值。此机制保证了普通断电情况下的位置恢复的即时性。
  • 断电期间被动位移恢复:ADMT4000本身具备断电被动记忆能力,当系统处于完全断电状态(外部电源被移除),如果用户无意中拧动电机轴旋转,ADMT4000内部的磁性多圈跟踪模块无需外部供电依然能够记录旋转圈数的变化,并在再次上电后通过SPI接口报告准确的绝对旋转角度。在这一场景下,重启后系统可以读取ADMT4000的最新实测角度值与NVS记录的历史值进行融合判断,或者跳过NVS直接以ADMT4000读取值为准——因为后者已计入断电期间的转动。两种模式可根据应用场景自由配置,最大限度确保断电扰动后的位置连续性。


二、功能实现

2.1 软件流程图

2.2 实现过程

项目基于 PlatformIO + Arduino 框架实现,以下是各模块的核心代码示例。


2.2.1 ADMT4000角度读取

//读取ADMT4000原始数据
uint32_t readADMT4000Raw() {
uint32_t data = 0;
uint8_t tx[4];
uint8_t rx[4];

// 确保CS为高
digitalWrite(ADMT4000_CS, HIGH);
delayMicroseconds(100);

// 拉低CS
digitalWrite(ADMT4000_CS, LOW);
delayMicroseconds(10); // 等待CS稳定

// 构造发送帧:bit7=1, bit6=0(读), bit5-0=地址 (0x03 = ABSANGLE寄存器)
tx[0] = 0x80 | (0x03 & 0x3F);
tx[1] = 0;
tx[2] = 0;
tx[3] = 0;

// 发送4字节数据并读取4字节响应
for (int i = 0; i < 4; i++) {
rx[i] = SPI.transfer(tx[i]);
}

// 释放CS
digitalWrite(ADMT4000_CS, HIGH);
delayMicroseconds(100); // 增加CS释放时间

// 解析接收数据
uint16_t reg_data = ((uint16_t)rx[1] << 8) | rx[2];
bool msb_valid = (rx[3] & 0x80) != 0;

// CRC校验
uint8_t recv_crc = rx[3] & 0x1F;
uint32_t crc_data = ((uint32_t)tx[0] << 24) | ((uint32_t)rx[1] << 16) |
((uint32_t)rx[2] << 8) | rx[3];
uint8_t calc_crc = admt4000_crc5(crc_data);
bool crc_valid = (recv_crc == calc_crc);

// 组合数据
data = reg_data;

// 保存ADMT4000数据
// 注意:这里只保存原始数据和CRC状态,具体的圈数和角度在readADMT4000Angle中计算后保存
preferences.putUInt("admt_raw_data", data);
preferences.putBool("admt_crc_valid", crc_valid && msb_valid);

return data;
}


2.2.2 角度与高度相互转换

//角度转高度
float angleToHeight(float angle) {
float revolutions = angle / 360.0;
return revolutions * LEAD_SCREW_PITCH; // LEAD_SCREW_PITCH = 2.0
}


2.2.3 步进电机运动控制

//移动到目标高度
void moveToHeight(float targetHeight, bool save) {
//检查是否在有效范围内
float maxHeight = MAX_REVOLUTIONS * LEAD_SCREW_PITCH;
if (targetHeight < 0 || targetHeight > maxHeight) {
Serial.print("Invalid height. Range: 0-");
Serial.print(maxHeight);
Serial.println("mm");
return;
}

//确保电机已启用
digitalWrite(TMC2209_EN, LOW);
delay(10);

//获取当前高度
float currentAngle = readADMT4000Angle();
if (currentAngle == 0) {
Serial.println("ADMT4000 communication error, skipping height move");
return;
}
float currentHeight = angleToHeight(currentAngle);

Serial.print("Moving from height: ");
Serial.print(currentHeight);
Serial.print(" to ");
Serial.println(targetHeight);

//计算高度差
float heightDiff = targetHeight - currentHeight;

//设置方向
if (heightDiff > 0) {
digitalWrite(TMC2209_DIR, HIGH); // 向上
Serial.println("Direction: UP");
} else {
digitalWrite(TMC2209_DIR, LOW); // 向下
Serial.println("Direction: DOWN");
heightDiff = -heightDiff; // 取绝对值
}

//计算步数(默认8微步/步)
const int MICROSTEPS = 8; // 8微步/步
const float STEPS_PER_REV = 200.0; // 每圈200步
const float STEPS_PER_MM = (STEPS_PER_REV * MICROSTEPS) / LEAD_SCREW_PITCH;
int steps = (int)(heightDiff * STEPS_PER_MM);

Serial.print("Height difference: ");
Serial.print(heightDiff);
Serial.print("mm, Steps: ");
Serial.println(steps);

//平滑步进参数 - 速度提高2倍
const int STEP_HIGH_US = 250; // 高电平时间(微秒)
const int STEP_LOW_US = 250; // 低电平时间(微秒)
const int BATCH_SIZE = 50; // 每批执行的步数(减少批次大小,增加位置检查频率)

//分批执行步进,避免长时间禁用中断
for (int i = 0; i < steps; i += BATCH_SIZE) {
//计算当前批的步数
int currentBatchSize = min(BATCH_SIZE, steps - i);

//再次确保电机已启用
digitalWrite(TMC2209_EN, LOW);

portDISABLE_INTERRUPTS(); // 禁止中断,保证时序均匀

//执行当前批的步进
for (int j = 0; j < currentBatchSize; j++) {
//执行一步 - 高电平
digitalWrite(TMC2209_STEP, HIGH);
delayMicroseconds(STEP_HIGH_US);

//执行一步 - 低电平
digitalWrite(TMC2209_STEP, LOW);
delayMicroseconds(STEP_LOW_US);
}

portENABLE_INTERRUPTS(); // 允许中断

//检查当前位置是否超出范围
float currentAngle = readADMT4000Angle();
if (currentAngle > 0) {
float currentHeight = angleToHeight(currentAngle);
float maxHeight = MAX_REVOLUTIONS * LEAD_SCREW_PITCH;

if (currentHeight < 0 || currentHeight > maxHeight) {
Serial.println("Error: Position out of range! Stopping movement.");
//禁用步进电机
digitalWrite(TMC2209_EN, HIGH);
return;
}
}

//短暂延迟,让其他任务有机会执行
delay(1);
}

portENABLE_INTERRUPTS(); // 确保中断已开启

//再次读取最终位置
currentAngle = readADMT4000Angle();
if (currentAngle > 0) {
float currentHeight = angleToHeight(currentAngle);

Serial.print("Final height: ");
Serial.println(currentHeight);

//保存当前高度(仅当save为true时)
if (save) {
saveHeight(currentHeight);
}

Serial.print("Moved to height: ");
Serial.println(currentHeight);
}

//保持电机启用状态
digitalWrite(TMC2209_EN, LOW);
}


2.2.4 断电保存与恢复

采用ESP32的 Preferences 库实现掉电不丢失的键值存储。在每次位置变化后主动调用 saveHeight()  saveADMT4000Data() 将当前角度值保存到Flash中,上电开机时通过 loadHeight() 读取之前保存的位置实现重启高度恢复。

//保存高度
void saveHeight(float height) {
//使用ESP32的Preferences存储
preferences.putFloat("height", height);
}

//保存ADMT4000数据
void saveADMT4000Data(float turns, float angle, uint32_t rawData, bool crcValid) {
//使用ESP32的Preferences存储
preferences.putFloat("admt_turns", turns);
preferences.putFloat("admt_angle", angle);
preferences.putUInt("admt_raw_data", rawData);
preferences.putBool("admt_crc_valid", crcValid);
}

//加载高度
float loadHeight() {
//使用ESP32的Preferences存储
float height = 0.0;
if (preferences.isKey("height")) {
height = preferences.getFloat("height", 0.0);
} else {
// 如果height键不存在,保存默认值
preferences.putFloat("height", height);
}
return height;
}


2.2.5 断电期间被动位移恢复

当断电期间被动位移发生后,上电重启后系统可以读取ADMT4000的最新实测角度值与NVS记录的历史值进行融合判断。当结果不一致时会以红灯警示,按下开发板上的 BOOT 按键或通过WEB页面按下 Correct Position 按钮可触发修正位移。

//读取当前角度和高度
float angle = readADMT4000Angle();
float height = angleToHeight(angle);

//获取保存的高度并验证
float savedHeight = loadHeight();
if (savedHeight > 0 && height > 0) {
if (!verifyPosition(height, savedHeight)) {
Serial.println("Warning: Position drift detected!");
}
}

//更新位置状态
void updatePositionStatus() {
//获取当前高度和保存的高度
float currentAngle = readADMT4000Angle();
float currentHeight = angleToHeight(currentAngle);
float savedHeight = loadHeight();

//验证位置
bool positionValid = verifyPosition(currentHeight, savedHeight);

//根据位置状态设置颜色
if (positionValid) {
//位置一致,显示绿色
setWS2812BColor(0, 255, 0);
} else {
//位置有偏差,显示红色
setWS2812BColor(255, 0, 0);
}
}


2.2.6 串口指令解析

这里我简单地使用 H:[高度值] 的格式作为串口控制指令的格式,通过串口发送移动指令(例如 H:60.0 )来控制滑台移动到指定高度。其中高度值的单位为毫米(mm)。

//检查串口指令
if (Serial.available() > 0) {
String command = Serial.readStringUntil('\n');
if (command.startsWith("H:")) {
float targetHeight = command.substring(2).toFloat();
moveToHeight(targetHeight, true);
}
}


2.2.7 WEB控制

//处理设置高度请求
void handleSetHeight() {
if (server.hasArg("height")) {
float targetHeight = server.arg("height").toFloat();
Serial.print("Web request: Set height to ");
Serial.println(targetHeight);

//移动到目标高度
moveToHeight(targetHeight, true);

//返回成功响应
server.send(200, "text/plain", "Height set successfully");
} else {
server.send(400, "text/plain", "Missing height parameter");
}
}

//处理获取高度请求
void handleGetHeight() {
//读取当前高度
float angle = readADMT4000Angle();
float height = angleToHeight(angle);

//返回当前高度
server.send(200, "text/plain", String(height));
}


三、功能展示

角度→高度映射的正确性验证

启动系统后,通过串口输入目标高度(例如:H:50.0),观察滑台运动位置相对刻度尺的读数,同时在调试窗口观察ADMT4000角度反馈值的实时变化。

通过打印丝杆处于不同角度时的反馈位置并反算应该匹配的线性位移,验证比例系数和丝杆导程进行线性映射,确保其比例一致性。

串口指令控制功能验证

在Windows/Mac下使用串口调试助手(如CoolTerm或Arduino IDE串口监视器)发送位置指令,测试系统对不同目标高度指令的响应情况。测试内容包括:上升指令、下降指令、超出行程范围指令(系统自动约束至46圈允许的范围内)。

断电重启恢复功能验证

普通断电恢复测试:将滑台移动到某个已知高度(如80 mm),然后断开系统电源,等待10秒后再重新上电。系统启动后自动打印出当前恢复的高度值为80 mm(假设断电期间滑台位置未发生任何机械外力转动),表明NVS存储恢复机制运行正常。

断电期间位移产生后的恢复测试:将系统完全断电,利用工具手动旋转丝杆使其滑台位置发生位移(模拟断电期间用户移动),之后重新给系统上电。上电后主控读取ADMT4000当前绝对角度,能够自动读取到断电时的机械位移信息。上电后检测到发生位移开发板会以红灯展示。

这一能力得益于ADMT4000的“真正通电多圈计数器”核心优势:即使在全断电状态下,只要有磁场旋转运动发生,多圈计数器就可以无源方式记忆可检测的圈数变化,确保上电后能够真实反映断电后的最终机械位置。

此外,系统也实现了高度(角度)的NVS持久存储,使系统在外部电源完全丢失并重新上电后无需用户干预即可直接使用;通过NVS闪存与ADMT4000的双重保障,既保证了断电期间的静态记录能力,也实现了无额外硬件成本的最简设计。

有效行程验证

根据丝杆导程计算:最大有效行程最大对应46圈×2 mm/圈=92 mm(本项目中行程上限为92 mm),验证目标位置受限不超过最大角度值46×360°。通过串口发送超出范围指令,系统自动约束并提示上限。


四、总结

遇到的问题

  1. 静置一段时间后,断电之后读取到的圈数始终在 46 左右。经群友提醒将磁铁间隔加大后恢复正常。
  2. 在最后一次拍摄断电恢复演示视频时,由于 Type-C 接口有点松动,插拔时导致高压混入数据引脚,导致开发板连同磁传感器模块一同烧毁,无法正常通信,可谓损失惨重。


心得体会

本项目基于ESP32开发板、TMC2209驱动模块和ADMT4000多圈传感器,成功实现了一款具有断电重启位置记忆功能的桌面升降控制系统。整套方案的核心在于将ADMT4000测量的绝对旋转角度通过线性映射函数换算为滑台的实际高度,实现了 角度↔位移↔高度 之间的透明解耦。


项目成功验证了三个关键的创新设计点:

  • 其一,ADMT4000与丝杆之间的直接映射方案,在省去额外的线性位移传感器的同时,保证了位置测量的完整性和可靠性;
  • 其二,结合NVS闪存存储与ADMT4000固有的多圈跟踪能力,设计了双重冗余的断电恢复策略,保障了断电期间的位移记忆与功耗全无状态下的数据持久化;
  • 其三,将0-46圈的总量和丝杆设计相结合,实现了有效行程的精准数学约束。


五、参考资料

附件下载
ADMT4000.zip
PlatformIO工程源码
团队介绍
业余电子爱好者
团队成员
鲜de芒果
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号