AS5600磁编码器IIC读取详解:从选型到滤波标定的完整实践
2026/9/1 5:20:52 网站建设 项目流程

简介:AS5600磁编码器获取角度(IIC方式)的简单应用,是一份面向嵌入式初学者的STM32实战资料。它演示了如何通过IIC通信读取磁编码器的绝对角度,并区分正转与反转,适用于电机转速测量、云台角度反馈、机器人关节位置检测等场景。资源总共包含5个文件,压缩包大小约780KB,里面既有AS5600的数据手册PDF,也有C语言源文件和头文件,还有正转和反转两张运行效果图,方便边看代码边对照波形或现象。目前已有1699人学习浏览,适合正在学习IIC协议、STM32外设驱动,或需要快速集成非接触式磁旋转传感器到项目的开发者。通过学习这份代码,可以掌握AS5600高低字节读取、零点与量程写入、DIR引脚控制旋转方向等具体方法,同时理解非易失性参数配置和智能低功耗模式的应用,并可将例程直接迁移到自己的固件中,为角度闭环控制或位置反馈打下基础。 如果你做过云台、机械臂关节或者任何需要角度反馈的小项目,大概率会碰到同一个问题:想测角度,但不知道选什么传感器最省事。我去年做一台小型三自由度云台时,一开始图便宜用了电位器,结果电机负载一上去,角度读数就在目标位置附近来回飘,噪声大到完全没法做闭环。后来换成了AS5600磁编码器,用IIC方式读取角度数据,整个控制才算从“能读”变成了“能用”。

这篇文章想把我从选型、接线、读寄存器到滤波、标定的完整过程讲清楚,重点放在IIC通信相关的细节上。内容不挑开发板,Arduino、STM32、ESP32都适用,核心思路是一样的。

1. 选型:电位器、磁编码器和AS5600到底差异在哪

1.1 电位器为什么在闭环项目里容易翻车

电位器是最便宜的角度传感器,本质是一个可变电阻分压器,输出模拟电压,ADC采样后换算成角度。问题在于它是接触式测量:电刷在电阻体上滑动,时间一长会磨损,磨损会产生接触电阻噪声,而且这种噪声是随机、突变的,非常难滤波。

我在云台项目里实测,电位器在静态时读数波动约±5个ADC码,换算成角度大约±1度。对于开环手动控制无所谓,但一接PID就完蛋,微分项会被噪声激励,电机在目标位置附近嗡嗡响。后来我明白一个道理:噪声不是滤波能完全解决的,物理层面的非接触测量才是治本方案

1.2 磁编码器的工作原理

AS5600这种磁编码器内部有一组霍尔传感器阵列,芯片正上方放一个径向充磁的圆盘磁铁,磁铁旋转时,芯片内部能检测到磁场方向的变化,通过内部计算还原出0°到360°的角度。

这个方案是纯非接触的,没有机械磨损,磁铁和芯片之间允许一定间隙,安装容错率比电位器高一个量级。AS5600输出的分辨率是12位,也就是一圈能分辨4096份,换算成分辨率约0.088度,对大多数云台、机械臂关节、舵机反馈来说完全够用。

1.3 AS5600与AS5047P的取舍

可能有人会问,AS5047P是14位分辨率,16384份,精度更高,为什么不用它?我也对比过,AS5047P贵不少,而且它主打的ABI增量输出和UVW换相信号对无刷FOC很有用,但我只是要一个绝对角度反馈,IIC读寄存器就够了。

另外AS5600还支持PWM输出和模拟输出,省资源。最后我选它就是因为:12位分辨率够用、IIC接口成熟、成本低、单芯片方案不需要编码器码盘。关键是我手里正好有径向充磁的磁铁,几块钱包邮一大包

下面这张表,基本就是我当时选型时列出来的对比:

对比项电位器AS5600AS5047P
测量方式接触式非接触磁感应非接触磁感应
分辨率取决于ADC位数12bit / 409614bit / 16384
输出接口模拟电压IIC / SPI / PWM / 模拟SPI / ABI / UVW / PWM
是否怕污染怕,电刷易磨损不怕,只测磁场不怕
价格区间几毛钱十几块钱几十块钱
适合场景开环手动指示云台/关节/机器人闭环电机FOC控制

2. IIC接线中的上拉电阻、电源滤波和地址问题

2.1 引脚接线到底怎么接

AS5600 IIC方式下总共只需要四根线:VCC、GND、SCL、SDA。常见模块会把引脚引出来,有的还会带OUT(模拟/PWM输出)和DIR(方向控制)。

我自己的接法是:VCC接3.3V,GND共地,SCL和SDA分别接MCU的IIC引脚。DIR引脚不用接,悬空或者根据模块默认状态处理。这里有一个容易踩的坑:AS5600的供电范围是2.7V到5.5V,但IIC上拉电平取决于模块上的设计,如果你用5V给模块供电,模块上的上拉电阻可能已经拉到5V,这时MCU如果是3.3V的IO,可能会存在电平不匹配风险。稳妥做法是先看模块原理图,确定上拉是接到VCC还是3.3V,再用逻辑分析仪确认SCL/SDA高电平是3.3V还是5V。

2.2 上拉电阻:为什么必须有,以及怎么取

IIC总线的通信原理是开漏输出,也就是说芯片只能把SCL/SDA拉低,不能主动拉高。高电平全靠上拉电阻把总线拽到VCC。如果没有上拉电阻,总线会一直卡在低电平,通信直接失败。

所以问题不是“要不要上拉”,而是“上拉多大”。这个和IIC速率、总线电容、总线上挂几个设备都有关系。IIC标准里对信号上升时间有要求,标准模式100kHz要求上升沿不超过1000ns,快速模式400kHz要求不超过300ns。上升时间太大,信号沿太缓,从机可能采样不到正确电平,就会出现偶发通信错误

上拉电阻的估算公式是:上升时间约等于0.8倍的RC,其中C是总线电容,包括芯片引脚电容、走线寄生电容。以400kHz为例,如果总线电容为200pF,取R=1kΩ,时间常数约200ns,还不够;想更稳就取R=1kΩ再配短走线。如果只需要100kHz,总线电容又不高,4.7k甚至10k都能用。

我实际项目里的经验是:3.3V系统、MCU和模块近距离连接,用4.7k很稳;如果总线速度降到100kHz,10k也可以。前提是模块上没有额外的上拉电阻,如果模块出厂已经焊了上拉,你再加一个并联,上拉等效电阻会变小,拉低电平时的灌电流变大,长期跑可能影响器件寿命。所以在用模块之前,先查清楚模块上有没有自带上拉。

一个实际案例:我一次测试中SCL/SDA没有接上拉电阻,代码写了半天,逻辑分析仪一看波形,SCL高电平只有0.8V,一直识别不出来,这就是经典的上拉缺失问题。加了2.2k上拉之后,波形立刻正常。

2.3 地址和总线冲突问题

AS5600在IIC总线上默认7位地址是0x36,读地址是0x6D,写地址是0x6C。IIC标准地址空间有128个地址,如果总线上只有这一台设备还好,但如果你想把两个AS5600挂到同一条总线上,问题就来了——它们默认地址相同,会冲突。

这里有个细节:不同批次的AS5600模块可能已经把地址改过,建议每次拿到模块先用IIC扫描程序把地址扫一遍,不要想当然。我吃过这个亏,焊好板子才发现模块地址不是默认的0x36,而是0x39,最后查原理图才知道该模块的地址配置引脚被拉高了。如果要多路角度采集,最省事的方式是加IIC多路选择器,或者直接换其他带地址引脚的磁编码器型号。

3. 读角度寄存器:写指针、重复起始的完整流程与代码

3.1 要读哪个寄存器、为什么读它

AS5600内部有十几个寄存器,但IIC方式读角度,最常用的就两个:

寄存器地址名称说明
0x0CANGLE 高字节12位角度的高8位
0x0DANGLE 低字节12位角度的低4位,高4位有效
0x0BAGC自动增益控制,可用来判断磁场强度

0x0C和0x0D拼起来是一个16位数据,但只用低12位,数值范围是0到4095,直接对应0°到360°。手册里还有一个0x0E/0x0F的寄存器,是配置过零点偏移之后的角度,我建议如果你做项目,直接用0x0C/0x0D读原始角度,零点偏移用软件自己处理。这样后期调试方向、改零点都不用烧录EEPROM,代码里改个变量就行。

之所以推荐软件处理零点,是因为AS5600的EEPROM写操作不是无限次数的,反复烧录有写坏风险。把零点偏移放在代码里,想改就改,编译烧录完事,方便太多。

3.2 IIC读取的时序细节

IIC读一个指定寄存器,标准流程是:

  1. 主机发送启动信号
  2. 发送从机地址加写位(0x36 << 1 | 0),也就是0x6C
  3. 发送寄存器指针,比如0x0C
  4. 发送重复起始信号(Repeated Start),这一步很关键
  5. 发送从机地址加读位(0x36 << 1 | 1),也就是0x6D
  6. 读取高字节,读取低字节
  7. 主机发送停止信号

我特意说一下第4步为什么用重复起始而不是停止再启动:如果在多设备总线上,先发停止信号,总线就释放了,别的设备可能趁这个间隙发起传输,你的读操作指针可能被别人打断。重复起始信号在同一个事务里完成“写指针”和“读数据”,不会被其他设备插队。这个细节在实时性要求高的控制系统里很重要,同时也是排查通信随机错乱时的一个重点检查项

3.3 Arduino示例代码

下面这段代码是我在Arduino上用的核心读取函数,直接读0x0C和0x0D,换算成浮点角度:

#include <Wire.h> #define AS5600_ADDR 0x36 // 7位地址 void setup() { Wire.begin(); Wire.setClock(400000); // 400kHz快速模式 Serial.begin(115200); checkMagnet(); } uint16_t readRawAngle() { Wire.beginTransmission(AS5600_ADDR); Wire.write(0x0C); // 写寄存器指针 Wire.endTransmission(false); // 发重复起始,不释放总线 Wire.requestFrom(AS5600_ADDR, 2); if (Wire.available() >= 2) { uint8_t hi = Wire.read(); uint8_t lo = Wire.read(); return ((uint16_t)hi << 8) | lo; // 低12位有效 } return 0; } void loop() { uint16_t raw = readRawAngle(); float angle = (raw / 4096.0f) * 360.0f; // 换算成角度 Serial.println(angle, 2); delay(10); } void checkMagnet() { Wire.beginTransmission(AS5600_ADDR); Wire.write(0x0B); // AGC寄存器 Wire.endTransmission(false); Wire.requestFrom(AS5600_ADDR, 1); if (Wire.available()) { uint8_t agc = Wire.read(); Serial.print("AGC="); Serial.println(agc); } }

有几个细节我想特别强调:

  • Wire.endTransmission(false)这个参数很多人忽略,如果不传false,库会在发送完寄存器指针后发一个停止信号,然后requestFrom再重新启动总线。虽然大多数情况也能工作,但和重复起始的时序有微妙差别,在400kHz高速模式下建议显式传false
  • Wire.setClock(400000)把IIC频率切到快速模式。如果你的项目对速率要求不高,100kHz也可以,但上拉电阻值需要匹配速率,高速率配上拉过大就会出问题。
  • 读出来的16位数一定要用& 0x0FFF取低12位。因为0x0D寄存器的高4位是无效的,不带掩码直接当16位处理,角度值会多出好几倍,这是新手最容易犯错的地方。

4. 实测数据的抖动、跳变与滤波方案

4.1 症状一:数据随机跳变,怎么定位

AS5600接好之后,我第一版代码读出来的角度大部分时间很正常,但偶尔会跳一下,跳的幅度很大,甚至能跳到几百上千的raw值。这种随机跳变通常不是传感器本身精度问题,而是IIC通信层面的问题。

定位方法就是拿逻辑分析仪抓SCL和SDA波形。我抓到的问题是SCL信号上升沿很慢,高电平持续时间不够稳定,导致从机偶尔误判数据位。原因正是上拉电阻取太大,加上杜邦线比较长,总线电容偏大。解决办法是把上拉电阻从10k换到2.2k,重新量波形,上升沿立刻变陡,跳变现象消失。

还有一个容易忽视的干扰来源:电机。云台电机转动时电流变化大,如果电源没有做好滤波,电源纹波会耦合到IIC信号上。一定要在AS5600模块的VCC和GND之间加一个100nF陶瓷电容和一个10uF电解电容,这属于便宜但效果极好的习惯。

4.2 症状二:低几位抖动,不要算故障

还有一种情况是角度在静态时raw值的最低两位不停跳,比如1350跳到1351再跳回1350。这是很正常的,12位分辨率本来就有量化噪声,加上外界磁场微弱波动,低两位抖动是常态。

这时候不要急着加高大上的滤波算法,先分清噪声的量级:如果抖动只在几个LSB以内,一阶低通滤波就够了;如果抖动范围达到几十个LSB,那要回到通信和电源上下功夫。

4.3 滤波方案对比

针对角度数据,我试过三种滤波:

方案原理优点缺点适用场景
滑动平均值取最近N次平均值平滑度高,简单有滞后,N越大滞后越大静态角度测量
中值滤波取N次排序中间值能剔除突发毛刺样本越大计算越慢偶尔跳变的场景
一阶低通新值=α×原始+(1-α)×旧值实时性好,滞后可调α难调,调不好有拖影动态闭环控制

我的推荐是:做闭环控制首选一阶低通,代码简单,实时性足够好。下面是实际用的代码片段:

float alpha = 0.3; float filteredAngle = 0; bool hasLastAngle = false; void loop() { uint16_t raw = readRawAngle(); float angle = (raw / 4096.0f) * 360.0f; if (!hasLastAngle) { filteredAngle = angle; hasLastAngle = true; } else { // 处理跨零点的角度回绕 float diff = angle - filteredAngle; if (diff > 180.0f) diff -= 360.0f; if (diff < -180.0f) diff += 360.0f; filteredAngle += alpha * diff; } // 如果不需要跨零点处理,直接用下面这行 // filteredAngle = alpha * angle + (1 - alpha) * filteredAngle; Serial.println(filteredAngle, 2); delay(5); }

这里有一个很多教程不会提的坑:角度值是0°到360°循环的,如果上一个滤波值是359°,最新值是1°,直接做平均会算出180°,完全错误。处理循环量必须先算差值,再把差值限制在-180°到180°之间,否则滤波越滤越乱。上面的代码就是对这个问题做的针对性处理。

5. 安装标定和闭环控制中的实战经验

5.1 安装误差从哪来

AS5600虽然是非接触测量,但它的精度高度依赖磁铁安装质量。最典型的问题有三个:磁铁没有贴芯片正中心、磁铁表面和芯片平面不平行、磁铁和芯片之间的距离太远导致磁场强度不足。

这些问题最终表现为角度读数不是均匀的,比如转动90°,读出来的变化可能是85°,转到180°时偏差更大。这不是传感器坏了,而是磁场本身变形了。判断磁场强度是否合适,最直接的方法就是读AGC寄存器,也就是0x0B。

AGC数值的含义是芯片自动增益控制值,正常范围是64到128之间,具体和芯片批次有关。我自己的经验:如果读出来AGC大于某个明显偏大的阈值,说明磁场太弱,要把磁铁移近一点;如果AGC太小,说明磁场太强,可能进入饱和区,反而也会影响线性度。

5.2 软件标定的快速做法

如果是要求不高的项目,可以不做硬件上的精确对准,而是用软件标定。最基础的两步标定法是:

  1. 把轴转到机械0°,读角度值记为offset0,这个值以后就是软件零点偏移
  2. 把轴转到机械90°,读角度值记为angle90,用这个值校准整个量程的比例因子

实际计算时,校准后的角度可以近似为:compensated = (raw - offset0) * 90.0f / (angle_raw_at_90 - offset0)

如果要求更高,可以做多点标定,比如每45°一个采样点,形成一张补偿查找表,运行时对读数做插值。这个方法能有效修正安装偏心带来的非线性误差。

我不太建议上来就烧录ZPOS寄存器做硬件零点,因为EEPROM写次数有限,而且标定数据想改就得重新烧录。项目开发阶段用软件标定最灵活,产品定型后再根据需求决定要不要固化成EEPROM配置。

5.3 闭环控制中使用AS5600的建议

我最终在云台上用IIC方式以大约1kHz的循环频率运行PID控制,角度反馈就是AS5600。实际跑下来,几个关键体会和提示:

  • 磁编码器的最大优点是没有机械回程间隙,换向时角度读数不会出现电位器那种滞回。这在对云台方向反转控制时极其舒服。
  • 读角度不要用Wire.requestFrom加上大delay的方式,尽量在控制循环里每周期都读一次,用中断或定时器固定循环频率。
  • 如果控制频率要求更高,比如电机FOC需要好几kHz的角度反馈,IIC可能成为瓶颈,这时AS5600的SPI模式更有优势。IIC适合100Hz到1kHz量级的控制场合,超过这个范围建议考虑SPI
  • AS5600输出的是单圈绝对角度,不能直接当多圈编码器用。如果你的项目需要记录电机转了几圈,要么自己在代码里做多圈计数,要么换带ABI增量输出的型号。

最后再分享一个排查经验:如果AS5600读出来的角度在某几个位置总是不稳定,最先怀疑的是磁铁安装偏心,而不是芯片坏了。把磁铁的轴心对准芯片中心点,用游标卡尺或治具确认,再重新读AGC,问题往往就消失了。硬件安装偏差是磁编码器项目里最常见的“慢性病”,它的表现不像通信故障那么明显,但会影响整个系统的角度精度。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询