项目摘要:基于 ADMT4000 的多圈绝对值磁编码器验证系统
1. 项目背景与目的
在工业自动化和机器人领域,监测电机的旋转角度和圈数至关重要。传统的增量式编码器在断电后会丢失位置信息,而 ADMT4000 是一款基于磁阻效应的单芯片多圈绝对值编码器,能够实现零功耗多圈计数。 本项目的核心目的是:
1.实现 ADMT4000 角度与圈数数据的实时采集。
2.验证器件在无外部供电(断电)状态下,手动旋转磁铁后,重启能否准确找回绝对位置(不丢圈)。
2. 系统架构
传感器层:ADMT4000 编码器芯片 + 径向充磁磁铁。
控制层:GD32F470单片机通过 SPI 接口 与传感器通信。
交互层: P C端串口调试助手,用于实时数据查看。
3. 核心实验流程
1.硬件初始化:配置 SPI 通信协议,读取 ADMT4000 的初始状。
2.实时采集:手动旋转磁铁,微控制器高速循环读取数据,并通过串口输出:当前角度:0度至360度的绝对位置。累计圈数:顺时针或逆时针旋转的完整周数。
3.断电保持测试(关键步骤):记录当前串口显示的数值。在断电状态下手动旋转磁铁数圈。在断电状态下手动旋转磁铁数圈。重新上电,对比串口输出的第一个数值与断电前的数值及手动旋转的估算值。
4.硬件设计
1.原理图设计


图4.1 单片机与传感器连接图
因本次例程比较简单,并未设计原理图PCB,直接使用杜邦线引脚连接;单片机使用SPI0片上外设实现SPI通讯,ADMT4000的SCLK引脚接到单片机PA5引脚;ADMT4000的SDO引脚接到单片机PA6引脚;ADMT4000的SDI引脚接到单片机PA7引脚;ADMT4000的CS引脚接到单片机PA4引脚。因测试过程中并没有出现异常所以未使用ADMT4000板子提供的复位电路;希望下次活动尝试用这部分功能开发功能。
2.开发平台
主控芯片采用GD32F470单片机,基于cortex-M4内核,主频240MHz,Flash容量1024K,SRAM容量512K,内部集成6个SPI,4个USART;足以满足本次实验。

图4.2 主控板实物图
3.传感器结构设计
期间因为磁铁的选型及结构问题,导致获取的数据精度出现异常,最后通过技术交流尝试将磁铁径向放置测试发现读数非常精准稳定。其中耽误了一些时间导致本次活动作品结构有些简陋。

图4.3 传感器结构图
4.实物连接

图4.4 实物连接图
5.软件流程
设备上电后,首先进行硬件SPI初始化。初始化完成后,单片机通过硬件spi读取ADMT4000内部故障寄存器的值,通过读取到的值判断故障原因,通过硬件SPI给传感器写入数据将故障寄存器清0初始化。完成数据写入后,在读取一次故障寄存器进行自检,如果故障寄存器读取正常。证明当前通讯正常,可正常读取多圈信息寄存器(0x03)和绝对角度寄存器(0X05) 。将多圈信息寄存器和绝对角度寄存器的值换算成实际圈数和角度数,通过串口在PC端打印显示出来。

图5.1 软件流程图
6.代码实现
1.硬件SPI的配置
使用GD32F470,系统主频为240MHZ,使用SPI0最快可达60MHz,但通过技术手册解读ADMT4000的SPI通讯速度要小于10Mhz。给主控的SPI进行32分频。
/**
* @brief 初始化 ADMT4000 SPI 接口
*/
int32_t SPI_init(const SPI_config *config)
{
if (config == NULL) return -1;
/* 1. 使能时钟 */
rcu_periph_clock_enable(config->rcu_gpio);
rcu_periph_clock_enable(config->rcu_spi);
/* 2. 配置 GPIO 复用功能 (SCK, MISO, MOSI) */
// 注意:GD32F470 需要通过 gpio_af_set 设置具体的 AF5/AF6
uint32_t pins = config->sck_pin | config->miso_pin | config->mosi_pin;
gpio_af_set(config->gpio_port, GPIO_AF_5, pins);
gpio_mode_set(config->gpio_port, GPIO_MODE_AF, GPIO_PUPD_NONE, pins);
gpio_output_options_set(config->gpio_port, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, pins);
/* 3. 配置 CS 引脚为普通输出模式 */
gpio_mode_set(config->gpio_port, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, config->cs_pin);
gpio_output_options_set(config->gpio_port, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, config->cs_pin);
gpio_bit_set(config->gpio_port, config->cs_pin); // 默认拉高
/* 4. SPI 协议配置 */
spi_parameter_struct spi_init_struct;
spi_i2s_deinit(config->spi_periph);
spi_init_struct.trans_mode = SPI_TRANSMODE_FULLDUPLEX;
spi_init_struct.device_mode = SPI_MASTER;
spi_init_struct.frame_size = SPI_FRAMESIZE_8BIT; // 建议用8位分4次发,以匹配32位帧
spi_init_struct.clock_polarity_phase = SPI_CK_PL_LOW_PH_1EDGE; // Mode 0
spi_init_struct.nss = SPI_NSS_SOFT;
spi_init_struct.prescale = config->spi_psc; // 确保频率 <= 10MHz [cite: 504]
spi_init_struct.endian = SPI_ENDIAN_MSB;
spi_init(config->spi_periph, &spi_init_struct);
spi_enable(config->spi_periph);
return 0;
}
2.CRC5的校验函数
uint8_t admt4000_crc5(uint32_t data)
{
uint8_t crc = 0x1F; // 初始值:全1
for (int i = 30; i >= 5; i--) {
uint8_t input_bit = ((data >> i) & 0x01);
uint8_t feedback = ((crc >> 4) & 0x01) ^ input_bit;
crc = (crc << 1) & 0x1E; // 左移1位,保持低5位
if (feedback) {
crc ^= 0x05; // 多项式 x^2 + x^0 (二进制: 00101)
}
}
return crc & 0x1F; // 返回低5位
}
3.SPI读取ADMT4000内部寄存器函数
/**
* @brief 从 ADMT4000 读取 16 位寄存器并进行 CRC 校验
* @param hw: 硬件配置结构体 (包含 SPI 外设和 CS 引脚信息)
* @param reg_addr: 寄存器地址 (0x00 - 0x3F)
* @param data_out: 输出读取到的 16 位数据
* @return 0: 成功; -1: 硬件错误; -2: CRC 校验失败; -3: MSB 有效位错误
*/
int32_t admt4000_read_register(const admt4000_hw_t *hw, uint8_t reg_addr, uint16_t *data_out) {
uint8_t tx[4] = {0x80 | (reg_addr & 0x3F), 0x00, 0x00, 0x00};
uint8_t rx[4];
gpio_bit_reset(hw->gpio_port, hw->cs_pin);
for (int i = 0; i < 4; i++) {
while (RESET == spi_i2s_flag_get(hw->spi_periph, SPI_FLAG_TBE));
spi_i2s_data_transmit(hw->spi_periph, tx[i]);
while (RESET == spi_i2s_flag_get(hw->spi_periph, SPI_FLAG_RBNE));
rx[i] = (uint8_t)spi_i2s_data_receive(hw->spi_periph);
}
while (SET == spi_i2s_flag_get(hw->spi_periph, SPI_FLAG_TRANS));
gpio_bit_set(hw->gpio_port, hw->cs_pin);
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;
// 5. 使用新算法验证传感器回传的 32 位数据
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);
if((msb_valid && crc_valid))
{
if (data_out != NULL)
{
*data_out = reg_data;
}
return 0;
}
else{
if (data_out != NULL)
{
*data_out = 0XFFFF;
}
return 1;
}
}
/**
* @brief 一次性读取 0x03 和 0x05 寄存器
* @param hw: 硬件配置结构体(需包含 spi_periph, gpio_port, cs_pin)
* @param reg03_val: 输出 0x03 的值
* @param reg05_val: 输出 0x05 的值
* @return 0: 成功, -1: 通讯失败
*/
int32_t admt4000_read_multiple(const admt4000_hw_t *hw, uint16_t *reg03_val, uint16_t *reg05_val) {
uint8_t tx[8];
uint8_t rx[8];
// 构造第一个寄存器读取命令
tx[0] = 0x80 | (0X03 & 0x3F);
tx[1] = 0;
tx[2] = 0;
tx[3] = 0;
// 构造第二个寄存器读取命令
tx[4] = 0x80 | (0X05 & 0x3F);
tx[5] = 0;
tx[6] = 0;
tx[7] = 0;
// 1. 拉低片选
gpio_bit_reset(hw->gpio_port, hw->cs_pin);
// 2. 硬件 SPI 传输 4 字节
for (int i = 0; i < 8; i++) {
while (RESET == spi_i2s_flag_get(hw->spi_periph, SPI_FLAG_TBE));
spi_i2s_data_transmit(hw->spi_periph, tx[i]);
while (RESET == spi_i2s_flag_get(hw->spi_periph, SPI_FLAG_RBNE));
rx[i] = (uint8_t)spi_i2s_data_receive(hw->spi_periph);
}
// 3. 【关键】等待 SPI 传输彻底完成再拉高 CS
while (SET == spi_i2s_flag_get(hw->spi_periph, SPI_FLAG_TRANS));
// 极短延时确保传感器释放总线
for(volatile int d=0; d<20; d++);
gpio_bit_set(hw->gpio_port, hw->cs_pin);
// 解析第一个寄存器数据
uint16_t reg1_data = ((uint16_t)rx[1] << 8) | rx[2];
bool reg1_valid = (rx[3] & 0x80) != 0;
uint8_t recv_crc1 = rx[3] & 0x1F;
uint32_t crc_data1 = ((uint32_t)tx[0] << 24) | ((uint32_t)rx[1] << 16) |
((uint32_t)rx[2] << 8) | rx[3];
uint8_t calc_crc1 = admt4000_crc5(crc_data1);
// 解析第二个寄存器数据
uint16_t reg2_data = ((uint16_t)rx[5] << 8) | rx[6];
bool reg2_valid = (rx[7] & 0x80) != 0;
uint8_t recv_crc2 = rx[7] & 0x1F;
uint32_t crc_data2 = ((uint32_t)tx[4] << 24) | ((uint32_t)rx[5] << 16) |
((uint32_t)rx[6] << 8) | rx[7];
uint8_t calc_crc2 = admt4000_crc5(crc_data2);
bool crc1_ok = (recv_crc1 == calc_crc1);
if (reg1_valid &&crc1_ok)
{
if (reg03_val != NULL)
{
*reg03_val = reg1_data;
}
}
bool crc2_ok = (recv_crc2 == calc_crc2);
if (reg2_valid && crc2_ok)
{
if (reg05_val != NULL)
{
*reg05_val = reg2_data;
}
}
if(reg1_valid && reg2_valid && crc1_ok && crc2_ok)
{
return 0;
}else
{
return 1;
}
}
4.SPI写入ADMT4000内部寄存器地址数据
/**
* @brief 从 ADMT4000 读取 16 位寄存器并进行 CRC 校验
* @param hw: 硬件配置结构体 (包含 SPI 外设和 CS 引脚信息)
* @param reg_addr: 寄存器地址 (0x00 - 0x3F)
* @param data: 要写入的 16 位数据
* @return 0: 成功;
*/
int32_t admt4000_write_register(const admt4000_hw_t *hw, uint8_t reg_addr, uint16_t data)
{
uint8_t tx[4];
uint8_t rx[4];
tx[0] = 0xC0|(reg_addr & 0x3F);
tx[1] = (data >> 8 )& 0xFF;
tx[2] = data & 0xFF;
uint32_t crc_data = ((uint32_t)tx[0] << 24) | ((uint32_t)tx[1] << 16) |
((uint32_t)tx[2] << 8);
uint8_t crc = admt4000_crc5(crc_data);
tx[3] = crc; // bit7=0, bit4-0=CRC
// 3. SPI 传输
gpio_bit_reset(hw->gpio_port, hw->cs_pin);
for (int i = 0; i < 4; i++) {
while (RESET == spi_i2s_flag_get(hw->spi_periph, SPI_FLAG_TBE));
spi_i2s_data_transmit(hw->spi_periph, tx[i]);
while (RESET == spi_i2s_flag_get(hw->spi_periph, SPI_FLAG_RBNE));
rx[i] = (uint8_t)spi_i2s_data_receive(hw->spi_periph);
}
// 4. 等待硬件发送完毕再拉高 CS
while (SET == spi_i2s_flag_get(hw->spi_periph, SPI_FLAG_TRANS));
for(volatile int i=0; i<50; i++);
gpio_bit_set(hw->gpio_port, hw->cs_pin);
return 0;
}
//清楚警告寄存器错误值
int32_t admt4000_clear_fault(const admt4000_hw_t *hw)
{
return admt4000_write_register(hw, ADMT4000_REG_FAULT, 0x0000);
}
5.检验异常相关函数
/**
* @brief 检查 ADMT4000 故障并构建错误描述字符串
* @param hw: 硬件配置结构体
* @param fault_str: 用于存储错误描述的缓冲区
* @param fault_str_size: 缓冲区大小
* @return true: 有故障或读取失败; false: 系统正常
*/
bool admt4000_check_fault(const admt4000_hw_t *hw, char *fault_str,
size_t fault_str_size)
{
// 1. 硬件参数检查
if (hw == NULL || hw->spi_periph == 0)
{
if (fault_str != NULL && fault_str_size > 0)
{
snprintf(fault_str, fault_str_size, "硬件接口未初始化");
}
return true;
}
uint16_t fault_reg = 0;
// 2. 读取 FAULT 寄存器 (使用之前生成的硬件读函数)
// 假设 ADMT_REG_FAULT 已在头文件中定义为 0x08 (Page 1)
if (admt4000_read_register(hw, ADMT4000_REG_FAULT, &fault_reg) != 0)
{
if (fault_str != NULL && fault_str_size > 0)
{
snprintf(fault_str, fault_str_size,
"读取 FAULT 寄存器失败 (SPI 通信错误)");
}
return true;
}
// 3. 如果没有故障位被置位,返回 false
if (fault_reg == 0)
{
if (fault_str != NULL && fault_str_size > 0)
{
fault_str[0] = '\0';
}
return false;
}
// 4. 解析故障位并构建字符串
if (fault_str != NULL && fault_str_size > 0)
{
int offset = 0;
// 打印原始错误码
offset += snprintf(fault_str + offset, fault_str_size - offset,
"FAULT 0x%04X:\n", fault_reg);
/* 逐位检查 (宏定义需在头文件定义) */
if (fault_reg & ADMT_FAULT_AMR_RADIUS)
{
offset +=
snprintf(fault_str + offset,
(offset < fault_str_size) ? fault_str_size - offset : 0,
" - AMR半径检测失败\n");
}
if (fault_reg & ADMT_FAULT_TURN_CROSSCHK)
{
offset +=
snprintf(fault_str + offset,
(offset < fault_str_size) ? fault_str_size - offset : 0,
" - 圈数交叉检查失败\n");
}
if (fault_reg & ADMT_FAULT_GMR_STATE)
{
offset +=
snprintf(fault_str + offset,
(offset < fault_str_size) ? fault_str_size - offset : 0,
" - GMR状态异常\n");
}
if (fault_reg & ADMT_FAULT_ECC_2BIT)
{
offset +=
snprintf(fault_str + offset,
(offset < fault_str_size) ? fault_str_size - offset : 0,
" - ECC 2位错误\n");
}
if (fault_reg & ADMT_FAULT_NVM_CRC)
{
offset +=
snprintf(fault_str + offset,
(offset < fault_str_size) ? fault_str_size - offset : 0,
" - NVM CRC错误\n");
}
if (fault_reg & ADMT_FAULT_VDRIVE_OV)
{
offset +=
snprintf(fault_str + offset,
(offset < fault_str_size) ? fault_str_size - offset : 0,
" - VDRIVE过压\n");
}
if (fault_reg & ADMT_FAULT_VDRIVE_UV)
{
offset +=
snprintf(fault_str + offset,
(offset < fault_str_size) ? fault_str_size - offset : 0,
" - VDRIVE欠压\n");
}
if (fault_reg & ADMT_FAULT_VDD_OV)
{
offset +=
snprintf(fault_str + offset,
(offset < fault_str_size) ? fault_str_size - offset : 0,
" - VDD过压\n");
}
if (fault_reg & ADMT_FAULT_VDD_UV)
{
offset +=
snprintf(fault_str + offset,
(offset < fault_str_size) ? fault_str_size - offset : 0,
" - VDD欠压\n");
}
}
return true;
}
/**
* @brief 错误解析辅助函数
*/
static void admt4000_decode_fault(uint16_t fault_reg)
{
if (fault_reg == 0)
{
printf("[ADMT4000] 状态正常: 无故障检测到。\r\n");
return;
}
printf("[ADMT4000] 检测到故障 (原始码: 0x%04X):\r\n", fault_reg);
/* 磁场与传感器相关 */
if (fault_reg & ADMT_FAULT_AMR_RADIUS)
{
printf(
" - [ERR] AMR半径异常: 磁场强度可能过低或过高 (需 16mT~31mT)。\r\n");
}
if (fault_reg & ADMT_FAULT_GMR_STATE)
{
printf(" - [ERR] GMR状态机异常: 内部计数状态逻辑错误。\r\n");
}
if (fault_reg & ADMT_FAULT_TURN_CROSSCHK)
{
printf(" - [ERR] 圈数交叉校验失败: 绝对角度与圈数计数不匹配。\r\n");
}
/* 内存与系统相关 */
if (fault_reg & ADMT_FAULT_ECC_2BIT)
{
printf(" - [ERR] ECC错误: 内存检测到双比特错误,数据不可信。\r\n");
}
if (fault_reg & ADMT_FAULT_NVM_CRC)
{
printf(" - [ERR] NVM CRC错误: 非易失性存储器配置已损坏。\r\n");
}
/* 电压监控相关 */
if (fault_reg & ADMT_FAULT_VDRIVE_UV)
{
printf(" - [WARN] VDRIVE欠压: SPI接口电压不足。\r\n");
}
}
6.数据处理函数
/**
* @brief 将原始数据转换为绝对角度 (12-bit)
* @param raw_value: 从寄存器读回的 16 位原始数据
* @return 角度值 (0.0 - 359.91)
*/
float admt4000_convert_to_angle(uint16_t raw_value)
{
// 提取角度值 [15:4] (根据 ADMT4000 手册,高12位为角度)
uint16_t angle_value = (raw_value >> 4) & 0xFFF;
return (float)angle_value * 360.0f / 4096.0f;
}
/**
* @brief 提取单圈内的精细角度 (10-bit)
* @param raw_value: 从相关寄存器读回的低 10 位数据
*/
float admt4000_extract_single_turn_angle(uint16_t raw_value)
{
// 提取单圈角度值 [9:0]
uint16_t angle_value = raw_value & 0x3FF;
// 360/1024 ≈ 0.3515625
return (float)angle_value * 0.3515625f;
}
/**
* @brief 转换包含小数部分的完整圈数信息 (针对多圈应用)
* @param raw_value: 包含 [15:10] 圈数和 [9:0] 角度的原始值
* @return 带有小数的圈数 (例如 12.5 圈), 负值表示错误代码
*/
float admt4000_convert_to_turns_with_fraction(uint16_t raw_value)
{
// 提取整圈数 [15:10]
uint8_t turn_count = (raw_value >> 10) & 0x3F;
// 提取单圈角度值 [9:0]
uint16_t single_turn_angle = raw_value & 0x3FF;
// 检查是否为invalid turn count (0b110110 = 54)
if (turn_count == 0b110110) {
return -999.0f;
}
// 检查是否为两补码负数 (>= 0b110111 = 55)
if (turn_count >= 0b110111) {
int8_t signed_turn = (int8_t)(turn_count << 2) >> 2;
return (float)signed_turn + (float)single_turn_angle / 1024.0f;
}
// 检查是否超出有效范围 (0-46有效)
if (turn_count > 46) {
printf("圈数超出有效范围: %d (最大46)", turn_count);
return -998.0f; // 表示超出范围
}
// 正常正数圈数 + 小数部分
return (float)turn_count + (float)single_turn_angle / 1024.0f;
}
/**
* @brief 最后转换生成实际信息的函数
* @param *hw:
* @param *angle_value:单圈角度
* @param *eabsolute_angle:磁角度
* @param *number_turn:圈数
*
*/
void read_data(const admt4000_hw_t *hw, float *angle_value,float *eabsolute_angle,float *number_turns)
{
uint16_t data_out1,data_out2;
int32_t ret;
ret = admt4000_read_multiple(hw,&data_out1,&data_out2);
if( ret == 0)
{
*number_turns = admt4000_convert_to_turns_with_fraction(data_out1);
*eabsolute_angle = admt4000_convert_to_angle(data_out2);//磁角度
*angle_value = admt4000_extract_single_turn_angle(data_out1); //单圈角度
printf("圈数为 %0.2f,单圈角度为 %0.2f,磁角度为 %0.2f",*number_turns,*angle_value,*eabsolute_angle);
}else {
printf("get data is fail\r\n");
}
}
7.ADMT4000上电初始化函数
/**
* @brief ADMT4000 完整初始化逻辑(含故障自检与清除)
* @param config: 指向之前定义的 GD32 配置结构体的指针
* @param hw: 指向硬件句柄的指针(用于后续读写)
* @return 0: 成功; -1: SPI底层初始化失败; -2: 寄存器访问异常; -3: 故障无法清除
*/
int32_t admt4000_init_with_fault_check(const SPI_config *config,
admt4000_hw_t *hw)
{
int32_t ret;
uint16_t fault_reg;
// 1. 底层硬件初始化 (时钟、GPIO、SPI参数)
// 假设 hw 已经关联了 config 中的 spi_periph, gpio_port, cs_pin
hw->spi_periph = config->spi_periph;
hw->gpio_port = config->gpio_port;
hw->cs_pin = config->cs_pin;
admt_res_gpio_init();
if (SPI_init(config) != 0)
{
printf("[ADMT4000] SPI 总线底层初始化失败\r\n");
return -1;
}
// 2. 读取 FAULT 寄存器 (地址 0x06),验证是否为上电默认值 0xFFFF
// 注意:ADMT4000 上电后所有故障位默认为1
ret = admt4000_read_register(hw, ADMT4000_REG_FAULT, &fault_reg);
if (ret != 0)
{
printf("[ADMT4000] 读取 FAULT 寄存器失败\r\n");
return -2;
}
printf("[ADMT4000] FAULT 寄存器初始值: 0x%04X\r\n", fault_reg);
if (fault_reg == 0X9D00)
{
printf("[ADMT4000] ? 寄存器读取功能正常\r\n");
}
else
{
printf("[ADMT4000] Warning: 期望 0X9D00,实际读到 0x%04X\r\n", fault_reg);
}
// 3. 向 FAULT 寄存器写入 0x0000,清除所有锁存的故障标志
// ADMT4000 手册规定:写入 0x0000 覆盖该寄存器以清除标志
admt4000_write_register(hw, ADMT4000_REG_FAULT, 0x0000);
printf("[ADMT4000] ? 发送清除 FAULT 指令\r\n");
// 4. 再次读取 FAULT 寄存器,确认已清除(应为 0x0000)
ret = admt4000_read_register(hw, ADMT4000_REG_FAULT, &fault_reg);
if (ret != 0)
{
printf("[ADMT4000] 清除后再次读取 FAULT 失败\r\n");
return -2;
}
printf("[ADMT4000] FAULT 寄存器清除后状态: 0x%04X\r\n", fault_reg);
if (fault_reg == 0x9D00)
{
printf("[ADMT4000] ? 故障标志已清除,传感器就绪\r\n");
}
else
{
printf("[ADMT4000] Error: 故障仍存在 (0x%04X)。请检查磁铁安装或电压!\r\n",
fault_reg);
// 如果需要详细分析原因,调用之前生成的解析函数
admt4000_decode_fault(fault_reg);
return -3;
}
return 0; // 初始化圆满成功
}
8.主函数
void main(void)
{
float angle_value,eabsolute_angle,number_turns;
admt4000_init_with_fault_check(&my_SPI0_config,&my_admt_hw);
while (1)
{
read_data(&my_admt_hw, &angle_value,&eabsolute_angle,&number_turns);
hal_day(1000);
}
}
8.开发过程中出现的问题
因为在以前的学习中,并未接触过这类的磁传感器,在刚开始读取故障寄存器时,一直出现圈数校验位异常。这就导致我最终读出来的圈数总是有问题,起初我以为是我的SPI通讯和CRC校验问题,查阅了相关技术手册资料与AI咨询并未解决,这使我的项目搁浅了很长一段时间,后面在技术群里和大家探讨时发现我的磁场设置有两个很严重问题:1.我选择的两款磁铁的磁性都是很小的,2.我起初设计结构时,想把磁体直接吸附到步进电机的轴承上,这样磁传感器只会检测一侧电极的磁场变化,后面尝试将磁铁位置改为径向后,发现数据稳定性提高了。虽然中间走了一段岔路,但是最终还是成功驱动了本次活动的核心芯片。
9.总结
ADMT4000 利用磁阻效应(AMR/GMR)实现了零功耗多圈计数。与增量式编码器不同,它在断电后仍能通过内部机械磁性状态记录位置,无需电池维持。 双寄存器互补:TURN_ANGLE (0x03):负责输出“圈数 + 单圈粗略角度”,是多圈跟踪的核心。ABS_ANGLE (0x05):负责输出“12位高精度单圈角度”,用于精细定位。通信保障:通过 CRC5 校验 和 Valid 状态位 确保了在工业复杂电磁环境下数据的真实性。
在本次活动中,我在磁铁的选型,对于磁场的处理上耗费了较多的时间以至于报告草草总结,希望还会有下次类似的活动,希望到时可以结合电机的一些自控算法去实现更有意思的作品。
10.实物演示

