as5048a磁编码器STM32数据采集软件,SPI通讯方式

最近在调AS5048A这个磁编码器,发现网上完整可用的STM32 SPI例程还真不好找。今天把调试经验整理下,手把手教你怎么用STM32撸出这个传感器的角度数据。咱们直接从代码实战出发,先搞定SPI通讯,再处理CRC校验这个坑。

先看SPI初始化函数。我用的是STM32H743的SPI1,注意这个传感器需要CPOL=1、CPHA=1的SPI模式:

void SPI1_Init(void) {
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    SPI_HandleTypeDef hspi1 = {0};
    
    __HAL_RCC_SPI1_CLK_ENABLE();
    __HAL_RCC_GPIOA_CLK_ENABLE();
    
    // PA5/6/7复用为SPI
    GPIO_InitStruct.Pin = GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF5_SPI1;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    hspi1.Instance = SPI1;
    hspi1.Init.Mode = SPI_MODE_MASTER;
    hspi1.Init.Direction = SPI_DIRECTION_2LINES;
    hspi1.Init.DataSize = SPI_DATASIZE_16BIT; // 注意16位数据模式
    hspi1.Init.CLKPolarity = SPI_POLARITY_HIGH; // CPOL=1
    hspi1.Init.CLKPhase = SPI_PHASE_2EDGE;     // CPHA=1
    hspi1.Init.NSS = SPI_NSS_SOFT;
    hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_32; // 12.5MHz @200MHz主频
    HAL_SPI_Init(&hspi1);
}

这里最容易翻车的是数据长度设置——AS5048A的SPI帧是16位的,但很多教程写成8位模式,直接导致数据错位。实测CLK相位设置错误会导致读取数据抖动,特别是电机运行时磁场干扰大的情况。

数据读取函数要注意双字节操作和CRC校验。AS5048A的SPI协议有个隐藏机制:发送的命令字节高8位其实是前一次数据的CRC校验值。这里给出经过验证的读取函数:

uint16_t AS5048A_ReadAngle(void) {
    uint16_t tx_data = 0xFFFF; // 读取角度命令
    uint16_t rx_data;
    
    HAL_GPIO_WritePin(SPI1_CS_GPIO_Port, SPI1_CS_Pin, GPIO_PIN_RESET);
    HAL_SPI_TransmitReceive(&hspi1, (uint8_t*)&tx_data, (uint8_t*)&rx_data, 1, 100);
    HAL_GPIO_WritePin(SPI1_CS_GPIO_Port, SPI1_CS_Pin, GPIO_PIN_SET);
    
    // 校验CRC(低5位)
    uint8_t crc_received = rx_data & 0x1F;
    uint8_t crc_calculated = CalculateCRC((rx_data >> 6) & 0x3FF); // 取高10位数据计算
    
    if(crc_received != crc_calculated) {
        return 0xFFFF; // 错误标志
    }
    
    return (rx_data >> 6) & 0x3FF; // 返回10位原始角度
}

// 魔改版CRC5计算(查表法提速)
static uint8_t crc5_table[32] = {0,5,10,15,20,17,30,27,13,8,7,2,25,28,19,22,26,31,16,21,14,11,4,1,23,18,29,24,3,6,9,12};

uint8_t CalculateCRC(uint16_t data) {
    uint8_t index = ((data >> 5) & 0x1F) ^ (data & 0x1F);
    return crc5_table[index];
}

CRC校验这个坑我踩了整整两天——手册里说用CRC5多项式,但实际实现是查表方式。这里用预先计算好的查找表替代实时计算,既保证速度又避免堆栈溢出(实时计算需要移位操作,在中断里用不安全)。

最后是角度换算。注意AS5048A返回的是0-1023的原始值,转角度时要处理成浮点:

float GetAngleDegrees(void) {
    uint16_t raw = AS5048A_ReadAngle();
    if(raw == 0xFFFF) return -1.0f; // 错误处理
    
    // 多次采样平滑
    static float angle_filter = 0;
    float current_angle = (raw * 360.0f) / 1024.0f;
    angle_filter = angle_filter * 0.9f + current_angle * 0.1f;
    
    return angle_filter; // 返回滤波后的角度
}

实测发现电机运行时原始数据会有±2LSB的抖动,这里用一阶滞后滤波做了平滑处理。如果需要更高精度,可以开启AS5048A的DAEC功能(动态角度误差补偿),不过那得改配置寄存器,下回再细说。

遇到最玄学的问题是SPI时钟速率——当主控芯片发热时,过高的SPI速率会导致数据错乱。建议调试时先用较低速率(比如1MHz),稳定后再逐步提升。有个隐藏技巧:在CS下降沿后延时100ns再触发SPI传输,能显著提高抗干扰能力,这个在HAL库中可以通过插入NOP()指令实现。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐