2026 ADI机器控制设计竞赛 - 基于ADMT4000模块实现多圈角度读取与断电记忆验证
该项目使用了ADMT4000模块,实现了实现多圈角度读取与断电记忆的设计,它的主要功能为:参加活动/培训。
标签
ADI
参加活动/培训
fuwen0202
更新2026-05-09
24

1. 所选任务介绍

任务名称 【新增 - 入门题】多圈角度读取与断电记忆验证

任务要求

0. 手动旋转磁铁,通过串口实时显示 ADMT4000 的圈数和角度,验证断电后数据是否保持。

  1. 通过 SPI 读取 ADMT4000 的多圈圈数和精确角度
  2. 串口实时打印当前圈数、角度值
  3. 手动旋转到任意位置后断电,重新上电,串口显示的圈数和角度与断电前一致
  4. 在 46 圈范围内多次测试,记录并展示断电前后的数据对比



2. 项目描述

使用Arduino通过SPI连接ADMT4000,完成通过SPI接口的寄存器的读取,对读取到的角度寄存器的换算。以实现断电多圈的实现。

由于手上没有合适的径向磁铁,故选用一个D10*H3的N52轴向磁铁作为传感器,通过美纹胶带固定在步进电机出轴上,同时,ADMT4000传感器模块通过铜柱,一同固定在步进电机上,从而保证一定的同心度,以及保留mm级别的误差,以模拟恶劣的现实条件。后期也可以通过控制步进电机,来完成闭环控制。

通过读取单圈角度和多圈角度,相互对照,来判断读取协议是否存在问题。


3.硬件介绍

3.1.硬件清单

  1. Arduino NANO与拓展模块 1枚
  2. ADMT4000 传感器模块 1枚
  3. GH转杜邦5P线 2组
  4. USB-TypeC线 2组
  5. 42步进电机 1个

3.2.硬件连接

// 硬件连接:
//    MOSI - D11
//    MISO - D12
//    SCLK - D13
//    nCS  - D10
//    RST  - 3.3V / NC
//    SHDN - 3.3V / GND
//    COIL - NC
    • 需要注意的是SHDN引脚,如果悬空会造成数据异常抖动,但是接高电平或者低电平皆不影响。
    • Arduino是5V的逻辑电平,而ADMT的逻辑电平是3.3V,所以RST和SHDN引脚都无法接入GPIO。

3.3.硬件框图

image.png

3.4.硬件实物

7e8f4f036cd6f5e5deee1cdb1fbcbf61.jpg

9773238f8863730bec3cce2c5e3ff2d9.jpg

4.软件代码

4.1.Arduino程序-初始化部分

// 本程序为ADMT4000 磁编码器的测试程序
// 硬件连接:
//    MOSI - D11
//    MISO - D12
//    SCLK - D13
//    nCS  - D10
//    RST  - 3.3V / NC
//    SHDN - 3.3V / GND
//    COIL - NC


/************************* 硬件IO连接说明 *************************/
#define PIN_Serial_RX       0
#define PIN_Serial_TX       1
#define PIN_ADMT_SPI_MOSI   11
#define PIN_ADMT_SPI_MISO   12
#define PIN_ADMT_SPI_SCLK   13
#define PIN_ADMT_SPI_nCS    10


/************************* 串口配置 *************************/
#define Serial_BAUD         115200


/************************* 寄存器地址定义(片内无关页)*************************/ /*(用到)*/
#define ADMT_CNVPAGE    0x01  // 转换启动&页选择寄存器- [15:14]:CNV 位(00 = 触发转换,11 = 中止转换);- [4:0]:PAGE 位(选择目标页地址)
#define ADMT_ABSANGLE   0x03  // 绝对多圈角度寄存器(只读)- [15:10]:整圈数(0~46,0b110110 = 无效圈数);- [9:0]:单圈偏移(分辨率 0.351°)
#define ADMT_DIGIO      0x04  // 数字IO寄存器,需配合DIGIOEN寄存器配置端口模式
#define ADMT_ANGLE      0x05  // 单圈角度寄存器(只读)- [15:4]:12 位有效角度(分辨率 0.08789°,范围 0°~360°);- [3:0]:保留位(无意义)
#define ADMT_FAULT      0x06  // 故障寄存器- 故障位触发后保持高电平,需写0x0000清除;- 关键位:D13(圈数校验错误)、D0~D3(电源故障)


/************************* 寄存器地址定义(Page0x0)*************************/ /*(用不到)*/
//主要存储原始传感器数据、温度测量值等基础信息,需通过CNVPAGE寄存器切换至Page 0x0(PAGE[4:0]=0x00)访问。
#define ADMT_SINE       0x10  // 存储 AMR 角度传感器原始正弦值(未校正)
#define ADMT_COSINE     0x11  // 存储 AMR 角度传感器原始余弦值(未校正)
#define ADMT_RADIUS     0x18  // 存储 SINE/COSINE 向量的幅值(SQRT(SINE²+COSINE²))
#define ADMT_TMP        0x20  // 存储内部温度传感器数据(用于角度温度补偿)- 转换公式:T(°C)=(TMP_CODE-1168)/15.66;- [15:4]:12 位温度码


/************************* 寄存器地址定义(Page0x2)*************************/ /*(用不到)*/
//主要存储原始传感器数据、温度测量值等基础信息,需通过CNVPAGE寄存器切换至Page 0x2(PAGE[4:0]=0x02)访问。
#define ADMT_GENERAL    0x10  // 设备核心功能配置(采样模式、滤波、谐波校正源)
#define ADMT_DIGIOEN    0x12  // 配置 GPIO 端口功能模式(GPIO / 专用功能)及输出使能
#define ADMT_CNVCNT     0x14  // 存储转换序列完成次数计数器
#define ADMT_H1MAG      0x15  // 1 次谐波误差幅值校准
#define ADMT_H1PH       0x16  // 1 次谐波误差相位校准
#define ADMT_H2MAG      0x17  // 2 次谐波误差幅值校准
#define ADMT_H2PH       0x18  // 2 次谐波误差相位校准
#define ADMT_H3MAG      0x19  // 3 次谐波误差幅值校准
#define ADMT_H3H        0x1A  // 3 次谐波误差相位校准
#define ADMT_H8MAG      0x1B  // 8 次谐波误差幅值校准(默认使用出厂值)
#define ADMT_H8PH       0x1C  // 8 次谐波误差相位校准
#define ADMT_ECCEDC     0x1D  // 存储用户配置寄存器的错误校正码(ECC)
#define ADMT_UNIQID0    0x1E  // 唯一设备标识寄存器 0 - 出厂烧录,用于设备追溯
#define ADMT_UNIQID1    0x1F  // 唯一设备标识寄存器 1 - 与 UNIQID0/UNIQID2 组合构成完整设备 ID
#define ADMT_UNIQID2    0x20  // 唯一设备标识寄存器 2 - 只读,不可修改
#define ADMT_UNIQID3    0x21  // 唯一设备标识寄存器 3 - 存储 3.3V 供电、产品型号等固定信息
#define ADMT_ECCDIS     0x23  // 禁用 / 启用用户配置寄存器的 ECC 功能 - 写0x4D54禁用 ECC(更新配置前需操作);- 写0x0000启用 ECC

#include <SPI.h>


4.2.Arduino程序-SPI通讯接口API

// ADMT 通过SPI读取指定地址的寄存器(6位地址),返回16位数据
unsigned int ADMT_READ_REG(byte ADDR)
{
  // SPI读时序:32位帧,格式为[忽略位(0) + 读写位(0为读指令) + 寄存器地址6位 + 读取返回24位(0x00)]
  ADDR = 0b00111111 & ADDR;                   // 发送的高2位均为0,寄存器地址的6位按位取与
  byte ReadByteBuff[] = {0x00,0x00,0x00};     // 用以存储返回的24位的数据,包括校验位
  unsigned int ReturnData = 0x00;              // 用以存储最后读取到的有效值,以供返回
  bool CheckFlag = false;                     // 用以存储教研状态,教研状态通过则为true


  while(CheckFlag == false)                  // 校验数据状态
  {
    digitalWrite(PIN_ADMT_SPI_nCS,LOW);       // 第一步:SPI片选设为低电平,表示ADMT通信开始
    SPI.transfer(ADDR);                       // 第二步:发送读指令和寄存器地址(高位先行)
    for(int i=0;i<3;i++)
    {
      ReadByteBuff[i] = SPI.transfer(0x00);   // 第三步:读取24byte的返回值
    }
    digitalWrite(PIN_ADMT_SPI_nCS,HIGH);      // 第四步:SPI片选设为高电平,表示ADMT通信结束开始


    ReturnData = (ReadByteBuff[0] << 8) | ReadByteBuff[1];  // 第五步:拼接2个Byte数据成一个int型


    CheckFlag = true;
    /*
    if(((ReadByteBuff[2] & 0b10000000) >> 7) == 0x01)                   // 第六步:校验位最高第一位应为1
    {
      if((true) | (((ReadByteBuff[2] & 0b01000000) >> 6) == 0x00))      // 第七步:校验位最高第二位为C1,表示新数据,(0 = 无新数据,1 = 数据已更新),不做校验
      {
        if((true) | (((ReadByteBuff[2] & 0b00100000) >> 5) == 0x00))     // 第八步:校验位最高第三位为C0,表示转换忙标志,(0 = 空闲,1 = 转换中),不做校验
        {
          byte ReadByte = ReadByteBuff[2] & 0b00011111;                 // 第九步,校验CRC5
          byte CheckByte = ReadByte;                                    // 暂不做CRC5校验
          if(ReadByte == CheckByte)
          {
            CheckFlag = true;                                           // 校验通过,Flag为true,不进入下一个循环
          }
        }
      }
    }*/
  }
  return ReturnData;
}

4.3.Arduino程序-主程序

void  setup()
{
  delay(100);
  Serial.begin(Serial_BAUD);              // 串口初始化


  pinMode(PIN_ADMT_SPI_nCS,OUTPUT);       // SPI片选引脚设为输出
  digitalWrite(PIN_ADMT_SPI_nCS,HIGH);    // SPI片选设为高电平,表示未选中ADMT
  SPI.begin();                            // 初始化硬件SPI
  SPI.setClockDivider(SPI_CLOCK_DIV2);    // SPI时钟分频,Uno/Nano为16MHz/2=8MHz(接近10MHz,兼容)
  SPI.setDataMode(SPI_MODE0);             // SPI设置模式为Mode 0
  SPI.setBitOrder(MSBFIRST);              // SPI设置高位先行


  Serial.println("System Online ...");
}


void loop()
{
  // 读取单圈角度和绝对多圈角度
  unsigned int rawAngle = ADMT_READ_REG(ADMT_ANGLE);        // 读取单圈角度寄存器 - [15:4]:12 位有效角度(分辨率 0.08789°,范围 0°~360°);- [3:0]:保留位(无意义)
  rawAngle = rawAngle >> 4;                                 // 12位有效角度,保留高12位,去除低4位无意义的保留位
  rawAngle = rawAngle * 0.08789;                            // 单圈角度转换为角度值,分辨率为0.08789°


  unsigned int rawAbsAngle = ADMT_READ_REG(ADMT_ABSANGLE);  // 读取绝对多圈角度 - [15:10]:整圈数(0~46,0b110110 = 无效圈数);- [9:0]:单圈偏移(分辨率 0.351°)
  unsigned int raw = rawAbsAngle >> 10;                     // 6位整圈数,保留高6位,去除低10位单圈偏移
  unsigned int absAngle = rawAbsAngle & 0b0000001111111111; // 10位单圈偏移,保留低10位,去除高6位整圈数
  absAngle = absAngle * 0.351;                              // 单圈偏移转换为角度值,分辨率为0.351°


  Serial.print("单圈角度: ");
  Serial.print(rawAngle);
  Serial.print(" | 圈数: ");
  Serial.print(raw);
  Serial.print(" | 绝对圈剩余角度: ");
  Serial.println(absAngle);
}


4.4.串口反馈信息:

image.png

4.5.嵌入式代码说明:

  1. 软件代码均有注释,流程简单明了,就不做流程图叻。
  2. CRC5校验看起来比较复杂,这里保留了校验的流程与框架,但实际代码中已跳过。如果感兴趣可以自行补充。
  3. 程序未使用状态机,后续修改可以使用子函数编辑。



附件下载
STEP_MOTER.ino
团队介绍
个人爱好者,12年Arduino经验,15年ad经验的菜鸡。
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号