4. 零点校准
任务目标
在FOC电机控制中,电角度零点与机械零点的对齐精度直接影响力矩输出质量。本案例实现AS5048磁编码器的零点校准功能:将电机当前所处位置记录为机械零点,计算编码器原始角度与该零点的偏移量,并将偏移量写入EEPROM实现掉电保存。校准完成后,系统输出的角度值将以该零点为基准重新计算。
核心实现
#include "as5048a.h"
#include <avr/eeprom.h>
#define AS5048_CS_PIN PB2
#define EEPROM_ADDR_OFFSET 0x00
static uint16_t zero_offset = 0;
static uint16_t raw_angle_cache = 0;
/* SPI读取AS5048原始角度(14位,0~16383) */
static uint16_t as5048_read_raw(void) {
uint8_t buf[2];
uint16_t raw;
PORTB &= ~(1 << AS5048_CS_PIN); // CS拉低
SPDR = 0x00; // 发送任意字节以触发时钟
while(!(SPSR & (1 << SPIF)));
buf[0] = SPDR;
SPDR = 0x00;
while(!(SPSR & (1 << SPIF)));
buf[1] = SPDR;
PORTB |= (1 << AS5048_CS_PIN); // CS拉高
raw = ((uint16_t)(buf[0] & 0x3F) << 8) | buf[1];
return raw;
}
/* 读取当前角度(已减去零点偏移) */
uint16_t as5048_get_angle(void) {
uint16_t raw = as5048_read_raw();
return (uint16_t)(raw - zero_offset + 16384) % 16384;
}
/* 执行零点校准:将当前角度设为0 */
void as5048_calibrate_zero(void) {
zero_offset = as5048_read_raw();
eeprom_update_word((uint16_t*)EEPROM_ADDR_OFFSET, zero_offset);
}
/* 初始化:从EEPROM加载偏移量 */
void as5048_init(void) {
zero_offset = eeprom_read_word((uint16_t*)EEPROM_ADDR_OFFSET);
if (zero_offset > 16383) { // 首次上电EEPROM为0xFFFF
zero_offset = 0;
}
}
运行与验证
硬件连接:AS5048的CS接PB2,SCK接PB5,MOSI接PB3,MISO接PB4,VCC接3.3V,GND共地。
调用流程:
int main(void) {
spi_init();
as5048_init();
// 将电机转到期望的机械零点位置
as5048_calibrate_zero();
// 校准后读取角度,应接近0
uint16_t angle = as5048_get_angle();
while(1) {
// 正常运行,as5048_get_angle()返回以零点为基准的角度
}
}
验证方法:
- 上电后先调用
as5048_init(),再调用as5048_calibrate_zero(),此时读取as5048_get_angle()返回值应接近0(误差±2 LSB以内); - 手动旋转电机轴90°(对应4096 LSB),读取角度值应约为4096;
- 断电重启,不重新校准则直接调用
as5048_get_angle(),若返回值仍以之前校准的零点为基准(即电机在相同位置时读数仍接近0),说明EEPROM存储与加载逻辑正确。
判定标准:校准后角度读数与理论值偏差不超过±3 LSB;断电重启后零点偏移保持有效,无需重新校准即可复现相同零点。
关键说明
AS5048原始角度为14位(0~16383),对应360°机械角度。本实现中as5048_get_angle()返回的也是14位数值,FOC算法中需将其转换为电角度(乘以极对数再取模)。EEPROM写入采用eeprom_update_word避免无谓擦写,延长寿命。若需在运行时动态调整零点,直接再次调用as5048_calibrate_zero()即可覆盖旧偏移量。