2026 ADI机器控制设计竞赛 - 基于ADMT4000模块实现多圈角度读取与断电记忆验证
本项目基于rp2350核心板,使用ardunio平台开发ADMT4000模块实现多圈角度读取与断电记忆验证的驱动程序。现在已经实现圈速读取功能,由于基于ardunio平台,其他开发板简单修改spi引脚配置即可快速上手,实现更广泛的应用。
标签
嵌入式系统
测试
Forest
更新2026-05-11
11

1.所选任务介绍

本次选择的任务是ADMT4000模块的多圈角度读取与断电记忆验证,需要实现并验证:通过 SPI 读取 ADMT4000 的多圈圈数和精确角度;串口实时打印当前圈数、角度值、手动旋转到任意位置后断电,重新上电,串口显示的圈数和角度与断电前一致;在 46 圈范围内多次测试,记录并展示断电前后的数据对比;

2.项目描述

本项目基于rp2350开发板,通过ardunio平台开发ADMT4000模块的驱动代码,代码简洁易用,可读取所旋转的圈数与精确的角度,性能稳定,可移植性强。

3,硬件介绍

本次项目所用到的硬件有:RP2350核心板、ADMT4000模块、42步进电机(作为支架)

||RP2350核心板:RP2350 是 Raspberry Pi 推出的一款高性能微控制器,搭载双 Cortex-M33 或 Hazard3 处理器,主频为150Mhz,拥有520KB 多组高性能 SRAM。具有30 个 GPIO 引脚,其中 4 个可用作模拟输入,外设丰富。可基于ardunio开发,简单易用。

||ADMT4000模块:ADMT4000 是一种磁转数传感器,即使在设备断电时也能够记录磁系统的旋转次数。通电时可以查询该套件,以报告系统的绝对位置。绝对位置通过串行外设接口 (SPI) 报告。ADMT4000 最多可计数 46 圈外部磁场,以顺时针 (CW) 方向递增绝对位置计

4.方案框图与设计思路

方案框图

image.png

设计思路

验证多圈角度读取与断电记忆,主控采用rp2350开发板,开发板将读取到的ADMT4000模块的信息通过usb传输给电脑,电脑打印相关信息。将得到的模块做了微小的改动,去掉了排针,以方便安装在42步进电机背部。


5.连接原理图

image.png

6.软件流程图 + 调试软件说明 + 关键代码说明

本项目的核心任务是实现ADMT4000传感器的初始化驱动、数据采集与串口打印。软件架构基于ardunio,主要实现了初始化SPI接口、数据的解析以及串口打印。
软件流程图
image.png

调试软件说明

将代码使用ardunio平台打开,修改对应的spi引脚配置,烧入代码,连接好模块,便能通过串口观看打印的数据

关键代码说明

引脚配置与SPI初始化

cpp// SPI Pin Configuration for RP2350
#define ADMT4000_SDO_PIN 20 // SPI MISO (数据输出,从传感器到MCU)
#define ADMT4000_SDI_PIN 19 // SPI MOSI (数据输入,从MCU到传感器)
#define ADMT4000_SCLK_PIN 18 // SPI时钟
#define ADMT4000_CS_PIN 17 // 片选,低电平有效
#define ADMT4000_RESET_PIN 4 // 复位,低电平有效(实际未使用)

// SPI Configuration
#define ADMT4000_SPI_SPEED 8000000 // 8MHz,留有余量(ADMT4000最大支持10MHz)

说明

  • 使用RP2350的GPIO0-4作为自定义SPI接口
  • SDO是ADMT4000的数据输出引脚,连接MCU的MISO
  • SDI是ADMT4000的数据输入引脚,连接MCU的MOSI
  • SPI速度设置为8MHz
    如果要修改spi引脚配置与目标主控相符,修改以上定义即可

CRC5校验计算

cppuint8_t calculateCRC5(uint32_t data, uint8_t bitCount) {
uint8_t shft[5] = {1, 1, 1, 1, 1}; // LFSR移位寄存器初始值

for (int8_t i = bitCount - 1; i >= 0; i--) {
// 计算异或操作
uint8_t xor_0 = ((data >> i) & 0x01) ^ shft[4];
// LFSR移位操作
shft[4] = shft[3];
shft[3] = shft[2];
shft[2] = shft[1] ^ xor_0;
shft[1] = shft[0];
shft[0] = xor_0;
}

// 组合5位CRC结果
return (shft[4] << 4) | (shft[3] << 3) | (shft[2] << 2) | (shft[1] << 1) | shft[0];
}

说明

  • ADMT4000使用CRC5进行数据校验,多项式为 x⁵ + x² + 1
  • 这是一个线性反馈移位寄存器(LFSR)实现
  • 种子值为0x1F(5个1)

寄存器读取函数

uint16_t ADMT4000_ReadRegister(uint8_t regAddr) {
uint32_t txFrame = 0;
uint32_t rxFrame = 0;

// 构建发送帧:Bit30=0表示读操作,Bit29-24为寄存器地址
txFrame = (0UL << 30) | ((uint32_t)regAddr << 24);

// 计算地址的CRC5校验码
uint8_t crc = calculateCRC5(txFrame, 8);
// 组装完整帧:地址 + CRC + 填充位
txFrame |= ((uint32_t)crc << 8) | 0x0E;

// 片选拉低,开始SPI传输
digitalWrite(ADMT4000_CS_PIN, LOW);
delayMicroseconds(1); // 等待tCSS建立时间

// 发送32位数据并接收响应
uint8_t* txPtr = (uint8_t*)&txFrame;
uint8_t* rxPtr = (uint8_t*)&rxFrame;
for (int8_t i = 3; i >= 0; i--) {
rxPtr[i] = SPI.transfer(txPtr[i]); // 逐字节发送和接收
}

delayMicroseconds(1); // 等待tCSH保持时间
digitalWrite(ADMT4000_CS_PIN, HIGH); // 片选拉高

// 从接收帧中提取寄存器数据(Bit[23:8])
return (uint16_t)((rxFrame >> 8) & 0xFFFF);
}

说明

  • ADMT4000的SPI帧为32位
  • Bit[30]为读写标志位:0=读,1=写
  • Bit[29:24]为6位寄存器地址
  • 返回值从响应的Bit[23:8]提取

寄存器写入函数

cppvoid ADMT4000_WriteRegister(uint8_t regAddr, uint16_t data) {
uint32_t txFrame = 0;

// 构建写入帧:Bit30=1,地址+数据
txFrame = (1UL << 30) | ((uint32_t)regAddr << 24) | ((uint32_t)data << 8);

// 对26位(地址+数据)计算CRC5
uint8_t crc = calculateCRC5(txFrame >> 6, 26);
txFrame |= crc; // CRC放在Bit[4:0]

// SPI传输
digitalWrite(ADMT4000_CS_PIN, LOW);
delayMicroseconds(1);

uint8_t* txPtr = (uint8_t*)&txFrame;
uint8_t* rxPtr = (uint8_t*)&rxFrame;
for (int8_t i = 3; i >= 0; i--) {
rxPtr[i] = SPI.transfer(txPtr[i]);
}

delayMicroseconds(1);
digitalWrite(ADMT4000_CS_PIN, HIGH);
}

说明

  • 写操作的Bit[30]=1,表示写入操作
  • 需要对地址和数据(26位)一起计算CRC
  • CRC结果放在帧的Bit[4:0]

角度数据解析

void ADMT4000_ReadAngleData(int16_t* turns, float* angle) {
uint16_t absAngle = ADMT4000_ReadRegister(REG_ABSANGLE); // 0x03

// 提取圈数:Bit[15:10],共6位
uint8_t turnRaw = (absAngle >> 10) & 0x3F;

// 判断圈数是否有效
if (turnRaw <= 0x35) {
// 0x00-0x35 = 0-53,有效圈数
*turns = turnRaw;
} else if (turnRaw == 0x36) {
// 0x36表示圈数无效
*turns = -1;
} else {
// 0x37-0x3F为二进制补码表示负数,需要符号扩展
*turns = (int8_t)(turnRaw << 2) >> 2;
}

// 提取角度:Bit[9:0],共10位
uint16_t angleRaw = absAngle & 0x3FF;
// 分辨率:0.351°/LSB,范围0-359.649°
*angle = angleRaw * 0.351f;
}

说明

  • ABSANGLE寄存器同时包含圈数和角度信息
  • 圈数范围:0-53圈(0x00-0x35),0x36表示无效,0x37-0x3F表示负数
  • 角度分辨率:10位精度,0.351°/LSB
  • ADMT4000实际支持0-46圈,此处寄存器支持更宽范围

高精度角度读取

cppfloat ADMT4000_ReadAngle360(void) {
uint16_t angleReg = ADMT4000_ReadRegister(REG_ANGLE); // 0x05

// 提取角度:Bit[15:4],共12位
uint16_t angleRaw = (angleReg >> 4) & 0x0FFF;

// 分辨率:360°/4096 = 0.0879°/LSB
return angleRaw * (360.0f / 4096.0f);
}

说明

  • ANGLE寄存器提供0-360°的高精度单圈角度
  • 12位分辨率,精度为0.0879°
  • 与ABSANGLE的区别:ANGLE不含圈数信息,只有单圈角度
  • 适合只需要0-360°范围内精确定位的应用

温度读取

cppfloat ADMT4000_GetTemperature(void) {
ADMT4000_SetPage(PAGE_SENSORS); // 切换到Page 0x00
uint16_t tmpReg = ADMT4000_ReadRegister(REG_TMP); // 0x20

// 提取温度码:Bit[15:4],共12位
uint16_t tmpCode = (tmpReg >> 4) & 0x0FFF;

// 温度计算公式
return (tmpCode - 1168.0f) / 15.66f;
}

说明

  • 温度传感器位于芯片内部,用于角度补偿
  • 需要先切换到Page 0x00才能访问温度寄存器
  • 公式:TMP(°C) = (TMP_CODE - 1168) / 15.66
  • 温度分辨率约0.07°C


7.实物演示及说明

模块被安装在步进电机背面,步进电机主轴上安装有径向磁铁,通过rp2350开发板读取角度与圈数数据。

image.png

上电,串口顺利打印出数据

image.png

转动主轴,角度精确变化

image.png

断电

image.png

再次上电,角度几乎没有变化(微小的变化是固定不太牢靠所导致)

image.png

断电并转动主轴

image.png

断电后主轴的旋转被记录

image.png


8.遇到的难点及解决方法

由于刚接触磁编码器,故买的圆柱磁铁是轴向磁化的,故烧入代码后,转动步进电机的主轴时,编码器没有明显的数据输出。一开始,以为是代码初始话的问题,对照数据手册修改了几版代码,仍然无法运行。排查了几天后,便尝试思考代码之外的问题,故将磁铁取下以各种方式旋转,在将磁铁中心以圆周方式旋转时,数值居然发生了变化,怀疑是磁铁的问题,后面查阅资料后才发现,是需要用径向磁铁,后面功能正常。其他难点便没有遇到了。

9.对本次活动的心得体会

这次活动体验到了全新前沿的模块,非常感谢电子森林举办的这次活动。通过对模块驱动程序的编写,文件手册的翻阅,对该模块有了更深的认识,移值流程也更加熟练。同时,在驱动开发中难免会遇见一些困难,当该困难一一种思路无法解决时,就去广泛思考所有可能的情况,逐步去排查。


附件下载
ADMT4000.zip
团队介绍
个人实现
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号