2. 读取电机编码器数据
任务目标
通过SPI接口读取AS5047P磁编码器的原始角度数据(14位,0~16383),并验证读取值与手动旋转电机时的物理角度变化一致。
环境与依赖
- 硬件:STM32F405最小系统板 + AS5047P编码器模块(SPI模式,CS接PB12,SCK=PB13,MISO=PB14,MOSI=PB15)
- 软件:STM32CubeIDE(HAL库),SPI1时钟分频后不超过10MHz(AS5047P最高支持10MHz)
- 接线:编码器VCC=3.3V,GND共地,CS/SCK/MISO/MOSI按上述引脚连接
关键实现说明
AS5047P的SPI读取协议为16位帧:前8位为命令(最高位为读标志1,后7位为寄存器地址),后8位为数据。读取角度寄存器(地址0x3FFF)时,发送0xFFFF后,在下一个16位帧的MISO上返回角度值。本实现使用HAL库的HAL_SPI_TransmitReceive,通过片选拉低启动通信,连续收发两个16位帧完成一次角度读取。
完整代码
// main.c 中用户代码区(USER CODE BEGIN Includes / PV / 0 / 4 之间添加)
#include <stdio.h>
#include <string.h>
SPI_HandleTypeDef hspi1;
#define AS5047P_CS_LOW() HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_RESET)
#define AS5047P_CS_HIGH() HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_SET)
// 读取AS5047P寄存器值(16位命令/数据协议)
uint16_t AS5047P_ReadReg(uint16_t regAddr) {
uint16_t cmd = 0x4000 | (regAddr & 0x3FFF); // 读标志位bit14=1
uint16_t txData[2] = {cmd, 0x0000}; // 第二帧发送0,用于获取数据
uint16_t rxData[2] = {0, 0};
AS5047P_CS_LOW();
HAL_SPI_TransmitReceive(&hspi1, (uint8_t*)txData, (uint8_t*)rxData, 2, HAL_MAX_DELAY);
AS5047P_CS_HIGH();
return rxData[1]; // 第二帧返回寄存器内容
}
// 读取14位角度值(0~16383)
uint16_t AS5047P_GetAngle(void) {
return AS5047P_ReadReg(0x3FFF) & 0x3FFF; // 角度寄存器地址0x3FFF
}
// main() 中 USER CODE BEGIN 2 之后添加初始化与主循环
// SPI1 已在 MX_SPI1_Init() 中配置为:MSB先行,CPOL=0,CPHA=1(SPI模式1),预分频32(此时钟约2.625MHz)
// GPIOB Pin12 已在 MX_GPIO_Init() 中配置为推挽输出,初始为高电平
uint16_t angle;
char msg[64];
while (1) {
angle = AS5047P_GetAngle();
sprintf(msg, "Angle: %u\r\n", angle);
HAL_UART_Transmit(&huart2, (uint8_t*)msg, strlen(msg), 100);
HAL_Delay(200);
}
运行与验证
- 编译烧录后,打开串口助手(波特率115200,对应USART2),观察输出。
- 静态验证:保持电机轴不动,串口输出的角度值应稳定在±2 LSB以内(AS5047P内部噪声极小)。
- 动态验证:手动缓慢旋转电机轴一整圈,观察输出值应从0~16383连续递增(或递减,取决于旋转方向),且无跳变、无重复段。
- 通过标准:旋转360°过程中,输出值覆盖整个0~16383范围且单调变化;停止旋转后数值稳定不漂移。
常见问题
- 输出恒为0或0x3FFF:检查SPI模式是否为模式1(CPOL=0,CPHA=1),以及CS引脚是否在通信前后正确拉低/拉高。
- 数值跳变剧烈:检查MISO/MOSI接线是否松动,或SPI时钟是否超过10MHz(降低分频系数)。
- 角度非线性:确认编码器磁铁是否居中安装,偏心会导致角度读数呈正弦误差。