AS5600这款磁性旋转位置编码器在电机控制和机器人领域已经成了明星产品。我去年在一个机械臂项目中第一次接触它,当时就被它12位的高分辨率和非接触式设计惊艳到了。简单来说,你可以把它想象成一个超级精准的"角度尺",能实时告诉你磁铁转到了什么位置。
和传统的光电编码器不同,AS5600不需要物理接触,靠磁场变化就能工作。这意味着它不会因为机械磨损而降低精度,寿命也更长。在实际项目中,我测试过连续工作1000小时,角度读数依然稳定如初。它支持I2C和PWM两种输出方式,但今天我们要重点聊聊怎么用STM32的HAL库通过I2C接口来读取角度数据。
为什么选择I2C?从我的经验来看,I2C接口布线简单(只需要两根线),而且HAL库已经帮我们封装好了底层操作。特别适合那些对PCB空间有限制的项目,比如小型无人机云台或者微型机器人关节。AS5600的I2C地址固定为0x36,这个地址在芯片出厂时就写死了,省去了配置的麻烦。
第一次用AS5600时,我在电源接法上栽过跟头。芯片支持3.3V和5V两种供电,但切记不要同时接!根据我的实测,用STM32开发板时直接接3.3V最稳妥。以下是经过验证的接线方案:
注意:有些开发板的I2C引脚需要上拉电阻,但大多数STM32芯片内部已经集成了可配置的上拉电阻。如果通信不稳定,可以尝试在SDA和SCL线上各加一个4.7kΩ的外部上拉电阻。
用STM32CubeMX配置I2C能省去大量底层代码工作。这里分享几个实测好用的参数设置:
配置完成后生成代码,HAL库会自动初始化I2C外设。我遇到过时钟配置错误导致通信失败的情况,这时候可以用逻辑分析仪抓一下波形,确认实际通信速率是否符合预期。
AS5600的角度数据存储在0x0C和0x0D两个寄存器中。经过多次项目验证,我总结出最稳定的读取流程:
c复制// AS5600.h 头文件关键定义
#define AS5600_ADDRESS (0x36 << 1) // 注意HAL库要求左移一位
#define ANGLE_HIGH_REG 0x0C
#define ANGLE_LOW_REG 0x0D
// 角度读取函数
float AS5600_GetAngle(void)
{
uint8_t buffer[2];
uint16_t raw_angle;
float angle_rad;
// 读取高字节和低字节
HAL_I2C_Mem_Read(&hi2c1, AS5600_ADDRESS, ANGLE_HIGH_REG,
I2C_MEMADD_SIZE_8BIT, buffer, 2, 100);
// 合并12位数据
raw_angle = ((buffer[0] << 8) | buffer[1]) & 0x0FFF;
// 转换为弧度值 (0-2π)
angle_rad = (float)raw_angle * (2.0f * 3.1415926f) / 4096.0f;
return angle_rad;
}
这段代码有几个关键点:
HAL_I2C_Mem_Read函数直接读取指定寄存器在实际项目中,我发现直接调用这个函数有时会返回异常值。后来通过添加重试机制解决了问题:
c复制#define MAX_RETRY 3
float AS5600_GetAngle_WithRetry(void)
{
uint8_t retry = 0;
float angle;
while(retry < MAX_RETRY)
{
angle = AS5600_GetAngle();
// 检查角度值是否合理
if(angle >= 0 && angle <= 6.2831853f)
return angle;
retry++;
HAL_Delay(1);
}
return -1.0f; // 错误代码
}
另外,如果系统对实时性要求高,可以考虑使用DMA方式读取。我在一个电机控制项目中测试过,DMA方式能减少约30%的CPU占用率。
原始角度数据会有微小跳动,特别是电机运转时。我常用的滤波方法是移动平均滤波:
c复制#define FILTER_WINDOW_SIZE 5
float angle_filter_buffer[FILTER_WINDOW_SIZE];
uint8_t filter_index = 0;
float AS5600_GetFilteredAngle(void)
{
float sum = 0;
float raw_angle = AS5600_GetAngle_WithRetry();
// 更新滤波缓冲区
angle_filter_buffer[filter_index] = raw_angle;
filter_index = (filter_index + 1) % FILTER_WINDOW_SIZE;
// 计算平均值
for(uint8_t i=0; i<FILTER_WINDOW_SIZE; i++)
{
sum += angle_filter_buffer[i];
}
return sum / FILTER_WINDOW_SIZE;
}
这个简单的算法能有效平滑数据,窗口大小可以根据实际需求调整。在要求更高的场合,可以尝试卡尔曼滤波。
AS5600的精度很大程度上取决于磁铁的安装位置。根据我的经验,磁铁应该:
校准时可以旋转一周,记录最大值和最小值。理想情况下,读数应该均匀分布在0-4095之间。如果发现死区或者非线性,就需要调整磁铁位置。
在直流电机控制系统中,AS5600可以作为位置反馈元件。下面是一个简单的PID控制示例:
c复制void Motor_PID_Control(float target_angle)
{
static float last_error = 0;
static float integral = 0;
float current_angle = AS5600_GetFilteredAngle();
float error = target_angle - current_angle;
// PID参数
const float Kp = 1.2f;
const float Ki = 0.01f;
const float Kd = 0.05f;
// PID计算
integral += error * 0.01f; // 假设周期是10ms
float derivative = (error - last_error) / 0.01f;
float output = Kp*error + Ki*integral + Kd*derivative;
// 输出限制
output = fmaxf(-1.0f, fminf(1.0f, output));
// 应用输出到电机
Set_Motor_Speed(output);
last_error = error;
}
这个例子展示了如何将AS5600的角度数据用于实时控制。在实际项目中,你可能需要调整PID参数和采样周期。
在六轴机械臂项目中,我使用了6个AS5600分别监测每个关节的角度。关键是要处理好I2C总线上的多设备通信:
通过这种方案,我们实现了0.1°的角度分辨率,完全满足工业级机械臂的精度要求。