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()返回以零点为基准的角度
    }
}

验证方法:

  1. 上电后先调用as5048_init(),再调用as5048_calibrate_zero(),此时读取as5048_get_angle()返回值应接近0(误差±2 LSB以内);
  2. 手动旋转电机轴90°(对应4096 LSB),读取角度值应约为4096;
  3. 断电重启,不重新校准则直接调用as5048_get_angle(),若返回值仍以之前校准的零点为基准(即电机在相同位置时读数仍接近0),说明EEPROM存储与加载逻辑正确。

判定标准:校准后角度读数与理论值偏差不超过±3 LSB;断电重启后零点偏移保持有效,无需重新校准即可复现相同零点。

关键说明

AS5048原始角度为14位(0~16383),对应360°机械角度。本实现中as5048_get_angle()返回的也是14位数值,FOC算法中需将其转换为电角度(乘以极对数再取模)。EEPROM写入采用eeprom_update_word避免无谓擦写,延长寿命。若需在运行时动态调整零点,直接再次调用as5048_calibrate_zero()即可覆盖旧偏移量。