Código de ejemplo de Arduino para codificadores absolutos SPI

Por Damon Tarry, Design Applications Engineer, Same Sky

Este tutorial de código de ejemplo de Arduino tiene como objetivo ofrecer a los usuarios un punto de partida sólido para configurar y leer datos de los codificadores absolutos AMT22 de Same Sky con comunicación de interfaz periférica serial (SPI). El tutorial proporcionará el hardware y el software necesarios, los requisitos clave de configuración y paquetes de código de muestra e instrucciones para las opciones de salida monovuelta y multivuelta. He aquí una lista de lo que se necesita para empezar:

Visión general del codificador absoluto AMT22

El AMT22 de Same Sky (antes CUI Devices) es un codificador absoluto que se ofrece con una resolución de 12 o 14 bits, lo que significa que proporciona un número preciso de posiciones únicas por revolución. En la variante de 12 bits, esto se traduce en 4,096 posiciones distintas, mientras que el modelo de 14 bits ofrece 16,384 posiciones por revolución. Independientemente de cuántas veces se gire el dispositivo, informa continuamente de su posición absoluta, proporcionando a los usuarios información precisa sobre el ángulo exacto del dispositivo.

Este codificador está disponible en modelos monovuelta y multivuelta. La variante de monovuelta mide la posición dentro de un único giro de 360 grados, mientras que la versión multivuelta no sólo registra la posición dentro de un giro, sino también el número total de giros completos. Además, las variantes monovuelta incorporan un punto cero programable que permite a los usuarios definir una referencia personalizada para el origen del codfificador.

Primeros pasos

Asegúrese de que el dispositivo está en modo RUN ajustando el interruptor situado en la parte posterior del codificador a la posición adecuada (Figura 1). Ahora coloque el codificador AMT22 a un motor o ensamble usando las instrucciones de montaje AMT para asegurar una instalación correcta. La AMT22 admite 9 tamaños de eje diferentes, desde 2 mm hasta 8 mm.

Diagrama del codificador AMT22 de Same Sky conmutado a modo RUNFigura 1: Ponga el interruptor de la parte posterior del codificador AMT22 en modo RUN. (Fuente de la imagen: Same Sky)

Las conexiones descritas en la Figura 2 y la Tabla 1 son específicas para la placa Arduino Uno, pero el código proporcionado debería ser compatible con la mayoría de las placas Arduino. No obstante, tenga en cuenta que la configuración de los pines puede variar según el modelo de Arduino. Para obtener detalles precisos de conexión en otras placas, se recomienda consultar la documentación de Arduino correspondiente.

Esquema de conexiones del cableado de Arduino Uno con el codificador AMT22Figura 2: Conexiones de cableado de Arduino Uno con el codificador AMT22. (Fuente de la imagen: Same Sky)

Función Número de pines del codificador Pin Arduino Uno AMT-DBC-1-036
+5 V 1 5 V Blanco/verde
SCLK 2 13 Azul/blanco
MOSI 3 11 Blanco/azul
GND 4 GND Verde/blanco
MISO 5 12 Naranja/blanco
CS 6 2 Blanco/naranja

Tabla 1: Conexiones de cableado de Arduino Uno más definidas. (Fuente de la imagen: Same Sky)

El codificador AMT22 comienza a transmitir sus datos de posición absoluta inmediatamente cuando se inicia la comunicación SPI, eliminando la necesidad de una estructura tradicional de comando-respuesta. Durante el primer byte de la transferencia SPI, el host envía 0x00 y el AMT22 responde simultáneamente con datos de posición válidos.

Si el host necesita emitir un comando (Tabla 2), como un comando de puesta a cero, se enviará en el segundo byte de la transmisión. Esto se denomina comando ampliado. Para obtener información técnica detallada, consulte la hoja de datos del AMT22.

Comando Byte Notas
Obtener posición 0x00 0x00
Ajustar en cero 0x00 0x70 Sólo monovuelta
Obtener vueltas 0x00 0xA0 Sólo multivuelta

Tabla 2: Comandos AMT22 definidos. (Fuente de la imagen: Same Sky)

Tutorial de código - incluye y define

Dado que el bus SPI del Arduino se está utilizando para interactuar con el codificador AMT22, es necesario incluir la biblioteca SPI en el código. Para enviar los datos de posición desde el Arduino a la computadora, se utiliza la conexión serial USB incorporada dentro del entorno completo de desarrollo integrado (IDE) Arduino, configurada a una velocidad de transmisión de 115200.

Además, hay que definir los comandos que utiliza el AMT22. Dado que el codificador no procesa el contenido del primer byte, se asigna un NOP (no-operation) para simplificar el proceso de comunicación (Listado 1).

Copia
/* Include the SPI library for the arduino boards */
#include <SPI.h>
 
/* Serial rates for UART */
#define BAUDRATE      115200
 
/* SPI commands */
#define AMT22_NOP     0x00
#define AMT22_ZERO    0x70
#define AMT22_TURNS   0xA0

Listado 1: Configuración de la interfaz periférica serial (SPI).

Inicialización

En la función setup() (Listado 2), comienza inicializando todos los pines SPI requeridos y configurando las interfaces serie para la comunicación.

El puerto serial debe inicializarse para permitir la transmisión de datos a la computadora host. Esto se hace pasando el BAUDRATE definido a la función Serial.begin().

Antes de habilitar SPI, asegúrese de que la línea de selección de chip (CS) está en el estado adecuado para preparar el codificador para la comunicación.

Seleccione una velocidad de reloj para el bus SPI para comunicarse con el AMT22. Para la creación de prototipos, es adecuada una velocidad de reloj de 500 kHz, aunque el AMT22 admite velocidades de hasta 2 MHz. Para alcanzar los 500 kHz, se puede utilizar el ajuste SPI_CLOCK_DIV32. Dado el reloj de 16 MHz del Arduino Uno, esta división da como resultado una velocidad de reloj SPI de 500 kHz. Para más detalles sobre la configuración del reloj SPI, consulte la documentación de Arduino.

Después de configurar todo, el bus SPI puede ser inicializado usando SPI.begin(), que configurará los tres pines dedicados SPI: MISO, MOSI, y SCLK, preparando el sistema para la comunicación con el codificador.

Copia
void setup()
{
  uint8_t cs_pin = 2;
 
  //Set the modes for the SPI CS
  pinMode(cs_pin, OUTPUT);
  //Get the CS line high which is the default inactive state
  digitalWrite(cs_pin, HIGH);
 
  //Initialize the UART serial connection for debugging
  Serial.begin(BAUDRATE);
 
  //set the clockrate. Uno clock rate is 16Mhz, divider of 32 gives 500 kHz.
  //500 kHz is a good speed for our test environment
  //SPI.setClockDivider(SPI_CLOCK_DIV2);   // 8 MHz
  //SPI.setClockDivider(SPI_CLOCK_DIV4);   // 4 MHz
  //SPI.setClockDivider(SPI_CLOCK_DIV8);   // 2 MHz
  //SPI.setClockDivider(SPI_CLOCK_DIV16);  // 1 MHz
  SPI.setClockDivider(SPI_CLOCK_DIV32);    // 500 kHz
  //SPI.setClockDivider(SPI_CLOCK_DIV64);  // 250 kHz
  //SPI.setClockDivider(SPI_CLOCK_DIV128); // 125 kHz
 
  //start SPI bus
  SPI.begin();
}

Listado 2: La función setup() que inicializa todas las clavijas SPI.

Comunicación SPI

La comunicación SPI con el AMT22 se gestiona a través de la biblioteca SPI de Arduino, mientras que el control de selección de chip (CS) se gestiona a través del código utilizando pines digitales de E/S. La función digitalWrite() se utiliza para activar o desactivar la línea CS (Listado 3).

El AMT22 espera que se envíen dos bytes de 0x00 y devuelve los datos inmediatamente después de recibir estos bytes. Debido a esta rápida respuesta, deben seguirse ciertos requisitos mínimos de temporización, que se describen en la hoja de datos del AMT22.

Independientemente de si el codificador es una versión de 12 o 14 bits, siempre responde con dos bytes (16 bits) de datos. Los dos bits superiores son bits de control, utilizados para verificar la integridad de los datos. Para la versión de 12 bits, los dos bits inferiores son 0 y el valor devuelto debe desplazarse 2 bits a la derecha (o dividirse por 4) para su uso correcto.

Para obtener los datos de posición, se llama a la función SPI.transfer(), enviando el comando AMT22_NOP. La línea CS permanece baja durante este proceso. El AMT22 envía primero el byte alto, por lo que el byte recibido se desplaza 8 bits a la izquierda para alinearlo en la mitad superior de una variable uint16_t. Este valor se asigna a la variable encoderPosition en una sola operación. Tras un breve retardo para cumplir los requisitos de temporización, se realiza una segunda llamada a SPI.transfer() para enviar otro comando AMT22_NOP. El resultado es OR'ed con el valor actual en encoderPosition, combinando efectivamente los dos bytes recibidos en una única variable uint16_t. Por último, la línea CS se libera, completando la comunicación.

Copia
uint8_t cs_pin = 2;
 
//set the CS signal to low
digitalWrite(cs_pin, LOW);
delayMicroseconds(3);
 
//read the two bytes for position from the encoder, starting with the high byte
uint16_t encoderPosition = SPI.transfer(AMT22_NOP) << 8; //shift up 8 bits because this is the high byte
delayMicroseconds(3);
encoderPosition |= SPI.transfer(AMT22_NOP); //we do not need a specific command to get the encoder position, just no-op
 
//set the CS signal to high
digitalWrite(cs_pin, HIGH);

Listado 3: Configuración de la comunicación Interfaz periférica serial (SPI).

Verificación de la suma de comprobación

Tras completar la transferencia SPI, es esencial validar los datos recibidos mediante una suma de comprobación (Listado 4).

Para llevar a cabo esta validación, se puede crear una función basada en la ecuación proporcionada en la hoja de datos. La suma de comprobación está contenida en los dos bits superiores del valor recibido, y utiliza paridad impar en los bits pares e impares de la respuesta de posición.

La función realizará los siguientes pasos:

  1. Calcular la paridad de los bits impares (bits 1, 3, 5, 7, 9, 11, 13)
  2. Calcular la paridad de los bits pares (bits 0, 2, 4, 6, 8, 10, 12, 14)
  3. Comparar las paridades calculadas con los valores indicados por los bits de la suma de comprobación.

La función devolverá true si la suma de comprobación es válida, indicando que la integridad de los datos está confirmada. Si la suma de comprobación no es válida, la función devolverá false, indicando un posible error en los datos recibidos.

Copia
/*
 * Using the equation on the datasheet we can calculate the checksums and then make sure they match what the encoder sent.
 */
bool verifyChecksumSPI(uint16_t message)
{
  //checksum is invert of XOR of bits, so start with 0b11, so things end up inverted
  uint16_t checksum = 0x3;
  for(int i = 0; i < 14; i += 2)
  {
    checksum ^= (message >> i) & 0x3;
  }
  return checksum == (message >> 14);
}

Listado 4: Validación de la suma de comprobación.

Formato de los datos

Si la validación de la suma de comprobación confirma la integridad de los datos, el siguiente paso es actualizar la variable encoderPosition eliminando los dos bits superiores (Listado 5). Esto puede lograrse aplicando una operación bit a bit AND con 0x3FFF (o 0b001111111111111111), que efectivamente retiene los 14 bits inferiores de los datos de posición.

Además, hay que tener en cuenta la resolución del codificador, si es de 12 o 14 bits. Si la resolución es de 12 bits, el valor de encoderPosition debe desplazarse 2 bits a la derecha para ajustarse a la resolución más baja. Esto garantiza que los datos de posición se representen con precisión en la variable encoderPosition, reflejando la posición real del codificador en función de su resolución especificada.

Copia
if (verifyChecksumSPI(encoderPosition)) //position was good
{
  encoderPosition &= 0x3FFF; //discard upper two checksum bits
  if (RESOLUTION == 12) encoderPosition = encoderPosition >> 2; //on a 12-bit encoder, the lower two bits will always be zero
 
  Serial.print(encoderPosition, DEC); //print the position in decimal format
  Serial.write('\n');
}
else //position is bad
{
  Serial.print("Encoder position error.\n");
}

Listado 5: Actualización del encoderPosition.

Posición cero ajustada (sólo una vuelta)

Algunas variantes del encóder AMT22 ofrecen una función de posición cero programable. Para fijar esta posición cero, debe enviarse una secuencia de comandos específica de dos bytes. El proceso implica enviar primero el comando AMT22_NOP, seguido de una breve espera para cumplir los requisitos mínimos de temporización especificados por el AMT22. Después de esta espera, se envía el comando AMT22_ZERO mientras se asegura que se libera la línea de selección de chip (CS). Una vez que el codificador recibe este comando, realizará una operación de reinicio (Listado 6).

Para evitar la comunicación con el codificador durante este período de reinicialización, se implementa un retardo de 250 ms, que garantiza que no se envíen comandos al codificador durante su tiempo de encendido.

Aunque es posible que el código establezca la posición cero del codificador al inicio de la operación, es más común en aplicaciones típicas establecer la posición cero sólo una vez durante la configuración inicial del dispositivo para su uso dentro del sistema. Esta práctica ayuda a mantener la integridad de la información de posición del codificador durante toda su vida útil.

Copia
/*
 * The AMT22 bus allows for extended commands. The first byte is 0x00 like a normal position transfer,
 * but the second byte is the command.
 * This function takes the pin number of the desired device as an input
 */
void setZeroSPI(uint8_t cs_pin)
{
  //set CS to low
  digitalWrite(cs_pin, LOW);
  delayMicroseconds(3);
 
  //send the first byte of the command
  SPI.transfer(AMT22_NOP);
  delayMicroseconds(3);
 
  //send the second byte of the command
  SPI.transfer(AMT22_ZERO);
  delayMicroseconds(3);
 
  //set CS to high
  digitalWrite(cs_pin, HIGH);
 
  delay(250); //250 millisecond delay to allow the encoder to reset
}

Listado 6: Ajuste de la posición cero de un codificador AMT22 de una vuelta.

Lectura del contador de vueltas (sólo multivueltas)

Ciertas variantes del encóder AMT22 admiten un contador multivuelta, lo que permite a los usuarios leer tanto la posición como el recuento de vueltas en una única secuencia de recuperación de datos.

Si los datos de posición recibidos no son válidos, el sistema debe notificar el error al usuario. Por el contrario, si la posición es válida, el programa debe informar de la posición en formato decimal (Listado 7). Esta capacidad mejora la funcionalidad del codificador al proporcionar información exhaustiva tanto de la posición absoluta como del número de vueltas completas, lo que facilita una supervisión y un control más precisos en aplicaciones que requieren datos de rotación exactos.

Copia
uint8_t cs_pin = 2;
 
//set the CS signal to low
digitalWrite(cs_pin, LOW);
delayMicroseconds(3);
 
//read the two bytes for position from the encoder, starting with the high byte
uint16_t encoderPosition = SPI.transfer(AMT22_NOP) << 8; //shift up 8 bits because this is the high byte
delayMicroseconds(3);
encoderPosition |= SPI.transfer(AMT22_TURNS); //we send the turns command (0xA0) here, to tell the encoder to send us the turns count after the position
 
//wait 40us before reading the turns counter
delayMicroseconds(40);
 
//read the two bytes for turns from the encoder, starting with the high byte
uint16_t encoderTurns = SPI.transfer(AMT22_NOP) << 8; //shift up 8 bits because this is the high byte
delayMicroseconds(3);
encoderTurns |= SPI.transfer(AMT22_NOP);
delayMicroseconds(3);
 
//set the CS signal to high
digitalWrite(cs_pin, HIGH);

Listado 7: Lectura del encoderPosition y del contador de vueltas en un encoder AMT22 multivuelta.

Ejecutar el código

Con el código creado correctamente, es hora de subirlo al Arduino y establecer comunicación con el codificador AMT22.

Para monitorizar la salida, abra el monitor serie en el entorno completo de desarrollo integrado (IDE) de Arduino y asegúrese de que la velocidad de transmisión de datos está ajustada a 115200 baudios. Esto permitirá a los usuarios observar el funcionamiento del codificador y ver los datos de posición notificados en tiempo real. Una vez que el monitor serie está activo, el encóder debería empezar a transmitir su información de posición, demostrando su funcionalidad dentro del sistema (Figura 3).

Imagen de la posición comunicada por el codificadorFigura 3: Posición notificada por el codificador, recibida por el Arduino (Imagen: Same Sky)

Codificadores múltiples

Una ventaja significativa del uso de un dispositivo SPI es la capacidad de comunicarse con varios codificadores en el mismo bus. Para facilitarlo, es necesario asignar un pin/clavija de E/S digital adicional a cada codificador, lo que permite un control individual de selección de chip (CS).

En el código de ejemplo (Listado 8), se utiliza una matriz de pines/clavijas CS para soportar un número arbitrario de codificadores. Este diseño permite una comunicación escalable, lo que permite al usuario fácilmente añadir más codificadores según sea necesario. Modificando las funciones para que acepten el número de pin correspondiente al dispositivo deseado, el código puede controlar dinámicamente qué codificador está activo en el bus SPI, garantizando que se pueda acceder a cada dispositivo y manejarlo de forma independiente.

Copia
uint8_t cs_pins[] = {2}; //only one encoder connected, using pin 2 on arduino for CS
//uint8_t cs_pins[] = {2, 3}; //two encoders connected, using pins 2 & 3 on arduino for CS

Listado 8: Configuración de una matriz para la lectura de múltiples codificadores.

El siguiente paso es recorrer cada pin/clavija CS de la matriz y leer la posición de cada encoder conectado. Esto permite al sistema activar cada codificador afirmando su línea de selección de chip, realizando la transferencia SPI y recuperando los datos de posición. El código seleccionará secuencialmente cada encoder, ejecutará la comunicación SPI, y liberará la línea CS, asegurando que todos los dispositivos conectados son consultados por su información de posición (Listado 9).

Copia
void loop()
{
  for(int encoder = 0; encoder < sizeof(cs_pins); ++encoder)
  {
    uint8_t cs_pin = cs_pins[encoder];
 
    //set the CS signal to low
    digitalWrite(cs_pin, LOW);
    delayMicroseconds(3);
 
    //read the two bytes for position from the encoder, starting with the high byte
    uint16_t encoderPosition = SPI.transfer(AMT22_NOP) << 8; //shift up 8 bits because this is the high byte
    delayMicroseconds(3);
    encoderPosition |= SPI.transfer(AMT22_NOP); //we do not need a specific command to get the encoder position, just no-op
 
    //set the CS signal to high
    digitalWrite(cs_pin, HIGH);
 
    if (verifyChecksumSPI(encoderPosition)) //position was good, print to serial stream
    {
      encoderPosition &= 0x3FFF; //discard upper two checksum bits
      if (RESOLUTION == 12) encoderPosition = encoderPosition >> 2; //on a 12-bit encoder, the lower two bits will always be zero
 
      Serial.print("Encoder #");
      Serial.print(encoder, DEC);
      Serial.print(" position: ");
      Serial.print(encoderPosition, DEC); //print the position in decimal format
      Serial.write('\n');
    }
    else //position is bad, let the user know how many times we tried
    {
      Serial.print("Encoder #");
      Serial.print(encoder, DEC);
      Serial.print(" position error.\n");
    }
  }
 
  //For the purpose of this demo we don't need the position returned that quickly so let's wait a half second between reads
  //delay() is in milliseconds
  delay(500);
}

Listado 9: Lectura de la variable encoderPosition desde múltiples codificadores.

Tras la transferencia de datos, se requiere un tiempo de espera mínimo antes de liberar la línea de selección de chip. Según la Hoja de datos, este tiempo mínimo es de 3 microsegundos. Aunque este retardo suele observarse de forma natural a velocidades de datos más lentas, es una buena práctica implementarlo explícitamente en el código para garantizar un funcionamiento correcto y el cumplimiento de las especificaciones de temporización. Esto garantiza una comunicación fiable con el codificador AMT22.

Conclusión:

Los usuarios deben tener ahora una comprensión básica de la configuración y lectura de datos de los codificadores absolutos AMT22 de Same Sky. Este artículo se centra en los codificadores absolutos AMT22. Same Sky también tiene una línea de codificadores modulares AMT que ofrecen una gama de versiones incrementales, absolutas y de conmutación.

Descargo de responsabilidad: Las opiniones, creencias y puntos de vista expresados por los autores o participantes del foro de este sitio web no reflejan necesariamente las opiniones, las creencias y los puntos de vista de DigiKey o de las políticas oficiales de DigiKey.

Acerca de este autor

Damon Tarry, Design Applications Engineer, Same Sky