1. 读取原始角度数据

任务目标

通过SPI接口读取AS5048磁编码器的14位原始角度数据(0~16383),并验证通信数据的有效性。本案例使用STM32 HAL库实现主机端读取,适用于任何支持HAL SPI的STM32系列。

硬件连接与依赖

  • AS5048与MCU连接:CS→PA4,SCK→PA5,MOSI→PA7,MISO→PA6(以SPI1为例)
  • AS5048的CSN引脚需由GPIO控制(非硬件SPI NSS)
  • SPI配置:模式1(CPOL=0,CPHA=1),8位数据,主模式,最高速率10MHz
  • AS5048供电3.3V,VIO3P3接3.3V

关键实现

AS5048 SPI协议为16位帧:前8位为命令/寄存器地址(最高位为读写标志,1=读),后8位为数据。读取角度寄存器(0x3FFF)时,发送0xFFFF后接收到的16位数据中,高14位为角度值。数据有效性通过第14位(EF,Error Flag)和第15位(OFS,Offset Flag)判断——正常工作时EF=0,OFS=0。

完整代码

// as5048.h
#ifndef AS5048_H
#define AS5048_H

#include "main.h"

#define AS5048_ANGLE_REG 0x3FFF
#define AS5048_ERR_FLAG  0x4000  // bit14
#define AS5048_OFS_FLAG  0x8000  // bit15

typedef struct {
    SPI_HandleTypeDef *hspi;
    GPIO_TypeDef *cs_port;
    uint16_t cs_pin;
    uint16_t last_raw_angle;
    uint8_t error;
} AS5048_t;

void AS5048_Init(AS5048_t *dev, SPI_HandleTypeDef *hspi,
                 GPIO_TypeDef *cs_port, uint16_t cs_pin);
uint8_t AS5048_ReadAngle(AS5048_t *dev, uint16_t *angle);
uint8_t AS5048_CheckParity(uint16_t data);

#endif
// as5048.c
#include "as5048.h"

void AS5048_Init(AS5048_t *dev, SPI_HandleTypeDef *hspi,
                 GPIO_TypeDef *cs_port, uint16_t cs_pin) {
    dev->hspi = hspi;
    dev->cs_port = cs_port;
    dev->cs_pin = cs_pin;
    dev->last_raw_angle = 0;
    dev->error = 0;
    HAL_GPIO_WritePin(cs_port, cs_pin, GPIO_PIN_SET);
}

uint8_t AS5048_CheckParity(uint16_t data) {
    uint8_t parity = 0;
    for (int i = 0; i < 16; i++) {
        if (data & (1 << i)) parity ^= 1;
    }
    return parity;
}

uint8_t AS5048_ReadAngle(AS5048_t *dev, uint16_t *angle) {
    uint8_t tx_data[2] = {0xFF, 0xFF};
    uint8_t rx_data[2] = {0, 0};
    uint16_t raw = 0;

    HAL_GPIO_WritePin(dev->cs_port, dev->cs_pin, GPIO_PIN_RESET);
    HAL_Delay(1);  // 确保CS建立时间

    if (HAL_SPI_TransmitReceive(dev->hspi, tx_data, rx_data, 2, 100) != HAL_OK) {
        HAL_GPIO_WritePin(dev->cs_port, dev->cs_pin, GPIO_PIN_SET);
        dev->error = 1;
        return 0;
    }

    HAL_GPIO_WritePin(dev->cs_port, dev->cs_pin, GPIO_PIN_SET);

    raw = ((uint16_t)rx_data[0] << 8) | rx_data[1];

    // 校验:EF位和OFS位必须为0,偶校验必须通过
    if (raw & AS5048_ERR_FLAG) {
        dev->error = 2;  // 磁铁未检测到或信号过弱
        return 0;
    }
    if (raw & AS5048_OFS_FLAG) {
        dev->error = 3;  // 坐标偏移未完成
        return 0;
    }
    if (AS5048_CheckParity(raw) != 0) {
        dev->error = 4;  // 奇偶校验失败
        return 0;
    }

    dev->last_raw_angle = raw & 0x3FFF;  // 取低14位
    *angle = dev->last_raw_angle;
    dev->error = 0;
    return 1;
}
// main.c 调用示例
AS5048_t encoder;
uint16_t angle = 0;

void setup(void) {
    AS5048_Init(&encoder, &hspi1, GPIOA, GPIO_PIN_4);
}

void loop(void) {
    if (AS5048_ReadAngle(&encoder, &angle)) {
        printf("角度: %u (0~16383)\r\n", angle);
    } else {
        printf("读取失败, 错误码: %u\r\n", encoder.error);
    }
    HAL_Delay(100);
}

运行与验证

  1. 将代码加入STM32CubeMX生成的工程,SPI1配置为模式1、8位、主模式,速率分频后≤10MHz
  2. 编译下载后,打开串口助手(波特率115200),观察输出
  3. 通过条件:缓慢旋转磁铁,串口输出的角度值在0~16383之间连续变化,无跳变到65535或错误码输出;当磁铁移开时,程序报错码2(EF位置位)
  4. 可用逻辑分析仪抓取SPI时序:CS低电平期间应有16个SCK脉冲,MISO上返回0xFFFF格式数据帧

常见问题

  • 读回全0或全1:检查SPI模式是否为CPOL=0/CPHA=1,CS引脚是否配置为推挽输出
  • 角度跳变:确认磁铁中心与芯片中心对齐,气隙在0.5~3mm内
  • 偶校验失败:检查电源纹波,SPI线长建议<10cm,必要时降低SPI速率