Código de amostra do Arduino para codificadores absolutos SPI
2024-10-08
Este tutorial de código de amostra do Arduino visa oferecer aos usuários um ponto de partida sólido para configurar e ler dados dos codificadores absolutos AMT22 da Same Sky com comunicação SPI (interface periférica serial). O tutorial fornecerá o hardware e o software necessários, os principais requisitos de configuração, além de pacotes de códigos de amostra e instruções para as opções de saída de uma volta e de várias voltas. Aqui está uma lista do que é necessário para começar:
- Placa Arduino
- Codificador AMT22
- Cabo AMT-06C-1-036 ou cabo similar com conector adequado
- IDE do Arduino
- Baixe o código de amostra do AMT22 de uma volta
- Baixe o código de amostra do AMT22 de várias voltas
Visão geral do codificador absoluto AMT22
O AMT22 da Same Sky (antiga CUI Devices) é um codificador absoluto oferecido com resolução de 12 ou 14 bits, o que significa que ele fornece um número preciso de posições exclusivas por rotação. Para a variante de 12 bits, isso se traduz em 4.096 posições distintas, enquanto o modelo de 14 bits apresenta 16.384 posições por rotação. Independentemente de quantas vezes o dispositivo for rotacionado, ele informa continuamente sua posição absoluta, fornecendo aos usuários uma resposta precisa sobre o ângulo exato do dispositivo.
Esse codificador está disponível nos modelos de uma volta e de várias voltas. A variante de uma volta mede a posição em uma única rotação de 360 graus, enquanto a versão de várias voltas rastreia não apenas a posição em uma rotação, mas também no número total de rotações completas. Além disso, as variantes de uma volta apresentam um ponto zero programável, permitindo que os usuários definam uma referência personalizada para a origem do codificador.
Primeiros passos
Certifique-se de que o dispositivo esteja no modo RUN ajustando a chave localizada na parte traseira do codificador para a posição apropriada (Figura 1). Agora, monte o codificador AMT22 em um motor ou conjunto, usando as instruções de montagem da AMT para garantir a instalação adequada. O AMT22 suporta 9 tamanhos diferentes de eixo, variando de 2 mm a 8 mm.
Figura 1: Coloque o interruptor na parte traseira do codificador AMT22 no modo RUN. (Fonte da imagem: Same Sky)
As conexões descritas na Figura 2 e na Tabela 1 são específicas para a placa Arduino Uno, mas o código fornecido deve ser compatível com a maioria das placas Arduino. No entanto, lembre-se de que as configurações dos pinos podem ser diferentes em vários modelos de Arduino. Para obter detalhes precisos sobre a conexão em outras placas, é recomendável consultar a documentação correspondente do Arduino.
Figura 2: Conexões de fiação do Arduino Uno com o codificador AMT22. (Fonte da imagem: Same Sky)
|
Tabela 1: Conexões de fiação do Arduino Uno mais detalhadas. (Fonte da imagem: Same Sky)
O codificador AMT22 começa a transmitir seus dados de posição absoluta imediatamente, quando a comunicação SPI é iniciada, eliminando a necessidade de uma estrutura tradicional de comando-resposta. Durante o primeiro byte da transferência SPI, o host envia 0x00, e o AMT22 responde simultaneamente com dados de posição válidos.
Se o host precisar emitir um comando (Tabela 2), como um comando de ajuste de zero, ele será enviado no segundo byte da transmissão. Isso é chamado de comando estendido. Para obter detalhes técnicos específicos, consulte a Ficha técnica do AMT22.
|
Tabela 2: Comandos AMT22 definidos. (Fonte da imagem: Same Sky)
Tutorial de código - inclui e define
Como o barramento SPI do Arduino está sendo usado para fazer a interface com o codificador AMT22, a biblioteca SPI precisa ser incluída no código. Para enviar os dados de posição do Arduino para o computador, é utilizada a conexão USB-serial integrada no IDE do Arduino, configurada em uma taxa de transmissão de 115200.
Além disso, os comandos usados pelo AMT22 precisam ser definidos. Como o codificador não processa o conteúdo do primeiro byte, uma função NOP (sem operação) é atribuída para simplificar o processo de comunicação (Listagem 1).
Copiar
/* 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
Listagem 1: Configuração da interface SPI.
Inicialização
Na função setup() (Listagem 2), comece inicializando todos os pinos SPI necessários e configurando as interfaces seriais para comunicação.
A porta serial deve ser inicializada para permitir a transmissão de dados para o computador host. Isso é feito passando a BAUDRATE definida para a função Serial.begin().
Antes de ativar a SPI, verifique se a linha de seleção do chip (CS) está definida no estado apropriado para preparar o codificador para a comunicação.
Selecione uma taxa de clock para que o barramento SPI se comunique com o AMT22. Para fins de prototipagem, uma taxa de clock de 500 kHz é adequada, embora o AMT22 suporte taxas de até 2 MHz. A obtenção de 500 kHz pode ser feita usando a configuração SPI_CLOCK_DIV32. Considerando o clock de 16 MHz do Arduino Uno, essa divisão resulta em uma taxa de clock SPI de 500 kHz. Para obter mais detalhes sobre a configuração do clock SPI, consulte a documentação do Arduino.
Depois de configurar tudo, o barramento SPI pode ser inicializado usando SPI.begin(), que configurará os três pinos SPI dedicados: MISO, MOSI e SCLK, preparando o sistema para a comunicação com o codificador.
Copiar
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();
}
Listagem 2: A função setup() que inicializa todos os pinos SPI.
Comunicação SPI
A comunicação SPI com o AMT22 é feita por meio da biblioteca SPI do Arduino, enquanto o controle de seleção do chip (CS) é gerenciado pelo código, usando pinos de E/S digitais. A função digitalWrite() é usada para afirmar ou retirar a afirmação da linha CS (Listagem 3).
O AMT22 espera que dois bytes de 0x00 sejam enviados e retorna os dados imediatamente, após receber esses bytes. Devido a essa resposta rápida, certos requisitos mínimos de temporização devem ser seguidos, os quais estão descritos na ficha técnica do AMT22.
Independentemente de o codificador ser uma versão de 12 ou 14 bits, ele sempre responde com dois bytes (16 bits) de dados. Os dois bits superiores são bits de verificação, usados para verificar a integridade dos dados. Para a versão de 12 bits, os dois bits inferiores são ambos 0, e o valor retornado deve ser deslocado para a direita em 2 bits (ou dividido por 4) para uso adequado.
Para obter dados de posição, a função SPI.transfer() é chamada, enviando o comando AMT22_NOP. A linha CS permanece em nível baixo durante esse processo. O AMT22 envia primeiro o byte mais significativo, de modo que o byte recebido é deslocado para a esquerda em 8 bits para alinhá-lo na metade superior de uma variável uint16_t. Esse valor é atribuído à variável encoderPosition em uma única operação. Após um breve atraso para atender aos requisitos de temporização, uma segunda chamada SPI.transfer() é feita para enviar outro comando AMT22_NOP. A função OR é utilizada no resultado com o valor atual em encoderPosition, combinando efetivamente os dois bytes recebidos em uma única variável uint16_t. Por fim, a linha CS é liberada, concluindo a comunicação.
Copiar
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);
Listagem 3: Configuração da comunicação SPI.
Verificação da variável checksum
Depois de concluir a transferência SPI, é essencial validar os dados recebidos, usando uma soma de verificação (Listagem 4).
Para implementar essa validação, uma função pode ser criada com base na equação fornecida na ficha técnica. A soma de verificação está contida nos dois bits superiores do valor recebido e utiliza paridade ímpar entre os bits pares e ímpares na resposta de posição.
A função executará os seguintes passos:
- Calcule a paridade para os bits ímpares (bits 1, 3, 5, 7, 9, 11, 13)
- Calcule a paridade para os bits pares (bits 0, 2, 4, 6, 8, 10, 12, 14)
- Compare as paridades calculadas com os valores indicados pelos bits da soma de verificação
A função retornará verdadeiro se a soma de verificação for válida, indicando que a integridade dos dados foi confirmada. Se a soma de verificação for inválida, a função retornará falso, sinalizando um possível erro nos dados recebidos.
Copiar
/*
* 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);
}
Listagem 4: Validação da soma de verificação.
Formatação de dados
Se a validação da soma de verificação confirmar a integridade dos dados, o próximo passo é atualizar a variável encoderPosition, removendo os dois bits superiores (Listagem 5). Isso pode ser feito aplicando uma operação AND de bit a bit com 0x3FFF (ou 0b00111111111111111111), que efetivamente retém todos os 14 bits inferiores dos dados de posição.
Além disso, é necessário levar em conta a resolução do codificador, seja ela de 12 ou 14 bits. Se a resolução for de 12 bits, o valor de encoderPosition deverá ser deslocado 2 bits para a direita para se ajustar à resolução mais baixa. Isso garante que os dados de posição sejam representados com precisão na variável encoderPosition, refletindo a posição real do codificador com base em sua resolução especificada.
Copiar
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");
}
Listagem 5: Atualizando o encoderPosition.
Definir a posição zero (somente uma volta)
Algumas variantes do codificador AMT22 oferecem um recurso de posição zero programável. Para definir essa posição zero, é necessário enviar uma sequência de comandos específica de dois bytes. O processo envolve primeiro o envio do comando AMT22_NOP, seguido de uma breve espera para atender aos requisitos mínimos de temporização, especificados pelo AMT22. Após essa espera, o comando AMT22_ZERO é enviado, garantindo que a linha de seleção do chip (CS) seja liberada. Quando o codificador receber esse comando, ele executará uma operação de reinicialização (Listagem 6).
Para evitar a comunicação com o codificador durante esse período de reinicialização, um atraso de 250 ms é implementado, garantindo que nenhum comando seja enviado ao codificador durante o tempo de ativação.
Embora seja possível que o código defina a posição zero do codificador no início da operação, é mais comum em aplicações típicas definir a posição zero apenas uma vez, durante a configuração inicial do dispositivo para uso no sistema. Essa prática ajuda a manter a integridade da resposta de posição do codificador durante toda a sua vida útil operacional.
Copiar
/*
* 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
}
Listagem 6: Configuração da posição zero de um codificador AMT22 de uma volta.
Ler o contador de voltas (somente várias voltas)
Algumas variantes do codificador AMT22 suportam um contador de várias voltas, permitindo que os usuários leiam a posição e a contagem de voltas em uma única sequência de recuperação de dados.
Se os dados de posição recebidos forem inválidos, o sistema deverá notificar o usuário sobre o erro. Por outro lado, se a posição for válida, o programa deverá informar a posição em formato decimal (Listagem 7). Esse recurso aprimora a funcionalidade do codificador ao fornecer uma resposta abrangente sobre a posição absoluta e o número de voltas completas, facilitando o monitoramento e o controle mais precisos em aplicações que exigem dados rotacionais precisos.
Copiar
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);
Listagem 7: Leitura do encoderPosition e do contador de voltas em um codificador AMT22 de várias voltas.
Execução do código
Com o código criado com sucesso, é hora de carregá-lo no Arduino e estabelecer a comunicação com o codificador AMT22.
Para monitorar a saída, abra o monitor serial no IDE do Arduino e verifique se a taxa de dados está definida como 115200 baud. Isso permitirá que os usuários observem a operação do codificador e visualizem os dados de posição relatados em tempo real. Quando o monitor serial estiver ativo, o codificador deverá começar a transmitir suas informações de posição, demonstrando sua funcionalidade no sistema (Figura 3).
Figura 3: A posição informada pelo codificador, recebida pelo Arduino (Fonte da imagem: Same Sky)
Vários codificadores
Uma vantagem significativa do uso de um dispositivo SPI é a capacidade de se comunicar com vários codificadores no mesmo barramento. Para facilitar isso, um pino de E/S digital adicional precisa ser alocado para cada codificador, permitindo o controle individual de seleção do chip (CS).
No código de exemplo (Listagem 8), uma matriz de pinos CS é utilizada para suportar um número arbitrário de codificadores. Esse design permite a comunicação escalonável, possibilitando que o usuário adicione facilmente mais codificadores, conforme necessário. Ao modificar as funções para aceitar o número do pino correspondente ao dispositivo desejado, o código pode controlar dinamicamente qual codificador está ativo no barramento SPI, garantindo que cada dispositivo possa ser acessado e operado de forma independente.
Copiar
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
Listagem 8: Configuração de uma matriz para leitura de vários codificadores.
A próxima etapa é fazer um laço de repetição em cada pino CS da matriz e ler a posição de cada codificador conectado. Isso permite que o sistema ative cada codificador afirmando sua linha de seleção do chip, realizando a transferência SPI e recuperando os dados de posição. O código selecionará sequencialmente cada codificador, executará a comunicação SPI e liberará a linha CS, garantindo que todos os dispositivos conectados sejam consultados quanto às suas informações de posição (Listagem 9).
Copiar
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);
}
Listagem 9: Leitura da variável encoderPosition de vários codificadores.
Após a transferência de dados, é necessário um tempo mínimo de espera, antes de liberar a linha de seleção do chip. De acordo com a ficha técnica, esse tempo mínimo é de 3 microssegundos. Embora esse atraso seja normalmente observado de forma natural em taxas de dados mais lentas, é uma boa prática implementá-lo explicitamente no código para garantir a operação adequada e a adesão às especificações de temporização. Isso garante uma comunicação confiável com o codificador AMT22.
Conclusão
Os usuários agora devem ter uma compreensão básica da configuração e leitura de dados dos codificadores absolutos AMT22 da Same Sky. Este artigo se concentrou nos codificadores absolutos AMT22. A Same Sky também tem uma linha de codificadores modulares AMT que oferece uma variedade de versões incrementais, absolutas e de comutação.
Disclaimer: The opinions, beliefs, and viewpoints expressed by the various authors and/or forum participants on this website do not necessarily reflect the opinions, beliefs, and viewpoints of DigiKey or official policies of DigiKey.

