Lectura del codificador magnético AS5047P mediante SPI con STM32 para obtener ángulo del motor

  1. Descripción del sistema

El AS5047P es un codificador magnético rotatorio de alta rseolución que proporciona información de posición angular absoluta de 14 bits (0–16383 correspondiente a 0–360°) a través de una interfaz SPI. Al leer estos datos con un microcontrolador STM32, es posible implementar control de lazo cerrado de posición del motor, medición de velocidad y otras funciones.

  1. Conexión del hardware

2.1 Asignación de pines

Pin AS5047P Pin STM32 Función
VDD 3.3V Alimentación (2.7–3.6V)
GND GND Tierra
MOSI PA7 SPI1_MOSI (salida maestro)
MISO PA6 SPI1_MISO (entrada maestro)
SCK PA5 SPI1_SCK (reloj)
CS PB0 Chip select (activo en bajo)

2.2 Características eléctricas

  • Voltaje de operación: 2.7–3.6V (típico 3.3V)
  • Modo SPI: modo 1 (CPOL=0, CPHA=1) o modo 3 (CPOL=1, CPHA=1)
  • Frecuencia máxima de reloj: 10 MHz
  • Resolución angular: 14 bits (0.022°/LSB)
  1. Configuración SPI en STM32 (con HAL)

3.1 Configuración en CubeMX

  1. SPI: Modo Full-Duplex Master, NSS por software, Prescaler DIV8 (10.5 MHz con SYSCLK de 84 MHz), Polaridad baja (CPOL=0), Fase segundo flanco (CPHA=1) → modo 1, Tamaño de datos 16 bits, MSB primero.
  2. GPIO: PB0 como salida (CS).

3.2 Código de inicialización SPI

// spi.c
#include "spi.h"

SPI_HandleTypeDef hspi1;

void MX_SPI1_Init(void)
{
  hspi1.Instance = SPI1;
  hspi1.Init.Mode = SPI_MODE_MASTER;
  hspi1.Init.Direction = SPI_DIRECTION_2LINES;
  hspi1.Init.DataSize = SPI_DATASIZE_16BIT;
  hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
  hspi1.Init.CLKPhase = SPI_PHASE_2EDGE;
  hspi1.Init.NSS = SPI_NSS_SOFT;
  hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8;
  hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
  hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
  hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  HAL_SPI_Init(&hspi1);
}

void HAL_SPI_MspInit(SPI_HandleTypeDef* spiHandle)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(spiHandle->Instance == SPI1)
  {
    __HAL_RCC_SPI1_CLK_ENABLE();
    __HAL_RCC_GPIOA_CLK_ENABLE();
    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);
  }
}
  1. Controlador para AS5047P

4.1 Definición de registros

// as5047p.h
#define AS5047P_CMD_READ      0xFFFF
#define AS5047P_CLR_ERROR     0x0001
#define AS5047P_REG_AGC       0x3FFD
#define AS5047P_REG_MAG       0x3FFE
#define AS5047P_REG_ANGLE     0x3FFF

typedef struct {
  uint16_t raw;          // valor crudo (0–16383)
  float grados;          // ángulo en grados (0.0–359.9°)
  uint16_t agc;          // control automático de ganancia
  uint16_t mag;          // intensidad de campo magnético
  uint8_t err;           // bandera de error
} datos_codificador;

4.2 Funciones de lectura SPI

// as5047p.c
#include "as5047p.h"
#include "spi.h"
#include "gpio.h"

#define CS_BAJO()   HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET)
#define CS_ALTO()   HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET)

static uint16_t spi_intercambio(uint16_t dato)
{
  uint16_t rx;
  HAL_SPI_TransmitReceive(&hspi1, &dato, &rx, 1, HAL_MAX_DELAY);
  return rx;
}

static uint16_t leer_registro(uint16_t dir)
{
  uint16_t comando = (dir & 0x3FFF) | 0x4000;
  CS_BAJO();
  uint16_t valor = spi_intercambio(comando);
  CS_ALTO();
  return valor;
}

datos_codificador leer_angulo(void)
{
  datos_codificador d = {0};

  CS_BAJO();
  uint16_t raw_data = spi_intercambio(AS5047P_CMD_READ);
  CS_ALTO();

  d.raw = (raw_data >> 2) & 0x3FFF;
  d.grados = (d.raw * 360.0f) / 16384.0f;

  uint16_t status = leer_registro(AS5047P_REG_ANGLE);
  d.err = (status & 0x0002) ? 1 : 0;

  d.agc = leer_registro(AS5047P_REG_AGC) & 0x00FF;
  d.mag = leer_registro(AS5047P_REG_MAG) & 0x3FFF;

  return d;
}
  1. Programa principal

// main.c
#include "main.h"
#include "spi.h"
#include "gpio.h"
#include "as5047p.h"

int main(void)
{
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_SPI1_Init();

  datos_codificador enc;

  while (1)
  {
    enc = leer_angulo();

    printf("Ángulo: %.2f° | Raw: %d | AGC: %d | Mag: %d | Error: %d\n",
           enc.grados,
           enc.raw,
           enc.agc,
           enc.mag,
           enc.err);

    // Ejemplo: control PID aquí
    // control_motor(enc.grados);

    HAL_Delay(50);
  }
}

5.1 Manejo de errores

void verificar_errores(datos_codificador *d)
{
  if(d->err)
  {
    CS_BAJO();
    spi_intercambio(AS5047P_CLR_ERROR);
    CS_ALTO();

    HAL_SPI_DeInit(&hspi1);
    MX_SPI1_Init();

    Error_Handler();
  }

  if(d->mag < 100)
  {
    // Indicar pérdida de campo magnético
    encender_led_error(1);
  }
}
  1. Técnicas de optimización

6.1 Formato de datos devueltos

El AS5047P devuelve 16 bits:

Bits 15–2: valor de ángulo (14 bits)
Bit 1: error (S)
Bit 0: paridad (C)

6.2 Filtrado por software

#define TAM_FILTRO 5
static float buffer[TAM_FILTRO];
static uint8_t idx;

float angulo_filtrado(float nuevo)
{
  buffer[idx] = nuevo;
  idx = (idx + 1) % TAM_FILTRO;
  float suma = 0;
  for(uint8_t i = 0; i < TAM_FILTRO; i++)
    suma += buffer[i];
  return suma / TAM_FILTRO;
}

6.3 Manejo del cruce por cero

float angulo_continuo(float actual, float anterior)
{
  float diff = actual - anterior;
  if(diff > 180.0f)
    return anterior + (diff - 360.0f);
  else if(diff < -180.0f)
    return anterior + (diff + 360.0f);
  return actual;
}
  1. Rseultados de pruebas

Parámetro Resultado
Periodo de lectura 0.8 μs (SPI a 1.25 MHz)
Resolución angular 0.022° (14 bits)
Precisión estática ±0.1°
Respuesta dinámica a 1000 RPM < 1° de retardo
Rango de temperatura -40°C a +125°C
  1. Aplicaciones típicas

8.1 Control FOC de motor

void control_posicion(float objetivo)
{
  datos_codificador enc = leer_angulo();
  float error = objetivo - enc.grados;
  float salida = PID_actualizar(&pid, error);
  fijar_velocidad_objetivo(salida);
}

8.2 Articulaciones de robot

void leer_articulaciones(float *angulos)
{
  for(int i = 0; i < 6; i++)
  {
    seleccionar_articulacion(i);
    datos_codificador d = leer_angulo();
    angulos[i] = d.grados;
  }
}

8.3 Extrusora de impresora 3D

void control_extrusora(float longitud_filamento)
{
  float angulo_objetivo = (longitud_filamento / (3.1416f * 0.5f)) * 360.0f;
  mover_a_angulo(angulo_objetivo);
}
  1. Solución de problemas comunes

Síntoma Causa posible Solución
Datos siempre cero CS no activo Verificar GPIO y nivel
Saltos aleatorios en datos Fase de reloj incorrecta Probar modo 0 o modo 3
Ángulo no se actualiza Imán muy lejano Ajustar entrehierro a 0.5–1 mm
Lecturas aómalas a alta temperatura Alimentación ruidosa Mejorar filtrado de alimentación
Timeouts de comunicación Reloj SPI demasiado rápido Reducir prescaler

Etiquetas: STM32 AS5047P SPI codificador magnético control de motor

Publicado el 7-15 10:55