Codice di esempio Arduino per gli encoder assoluti SPI
2024-10-08
Questo tutorial sul codice di esempio di Arduino intende fornire agli utenti un solido punto di partenza per la configurazione e la lettura dei dati dagli encoder assoluti AMT22 di Same Sky con comunicazione tramite interfaccia periferica seriale (SPI). Il tutorial illustra l'hardware e il software necessari, i principali requisiti di configurazione e i pacchetti di codice e le istruzioni di esempio per le opzioni di uscita monogiro e multigiro. Ecco un elenco dell'occorrente per iniziare:
- Scheda Arduino
- Encoder AMT22
- Cavo AMT-06C-1-036 o cavo simile con connettore adeguato
- IDE Arduino
- Scarica il codice di esempio AMT22 monogiro
- Scarica il codice di esempio AMT22 multigiro
Panoramica dell'encoder assoluto AMT22
L'encoder AMT22 di Same Sky (ex CUI Devices) è un encoder assoluto con risoluzione a 12 o 14 bit, che fornisce un numero preciso di posizioni uniche per rivoluzione. Per la variante a 12 bit, ciò si traduce in 4.096 posizioni distinte, mentre il modello a 14 bit offre 16.384 posizioni per rivoluzione. Indipendentemente dal numero di rotazioni, il dispositivo segnala continuamente la sua posizione assoluta, fornendo agli utenti un riscontro preciso sull'angolo esatto del dispositivo.
Questo encoder è disponibile nei modelli monogiro e multigiro. La variante monogiro misura la posizione all'interno di una singola rotazione di 360 gradi, mentre la versione multigiro traccia non solo la posizione all'interno di una rotazione, ma anche il numero totale di rotazioni complete. Inoltre, le varianti monogiro sono dotate di un punto zero programmabile, che consente agli utenti di definire un riferimento personalizzato per l'origine dell'encoder.
Primi passi
Assicurarsi che il dispositivo sia in modalità RUN regolando l'interruttore posto sul retro dell'encoder nella posizione appropriata (Figura 1). Montare l'encoder AMT22 su un motore o su un gruppo utilizzando le istruzioni di montaggio AMT per garantire un'installazione corretta. AMT22 supporta 9 dimensioni dell'albero, da 2 mm a 8 mm.
Figura 1: Portare l'interruttore sul retro dell'encoder AMT22 in modalità RUN. (Immagine per gentile concessione di Same Sky)
I collegamenti indicati nella Figura 2 e nella Tabella 1 sono specifici per la scheda Arduino Uno, ma il codice fornito dovrebbe essere compatibile con la maggior parte delle schede Arduino. Tuttavia, si tenga presente che le configurazioni dei pin possono differire tra i vari modelli Arduino. Per i dettagli precisi sui collegamenti di altre schede, si consiglia di consultare la documentazione Arduino corrispondente.
Figura 2: Collegamenti del cablaggio di Arduino Uno con l'encoder AMT22. (Immagine per gentile concessione di Same Sky)
|
Tabella 1: Collegamenti del cablaggio di Arduino Uno ulteriormente definiti. (Immagine per gentile concessione di Same Sky)
L'encoder AMT22 inizia a trasmettere i dati di posizione assoluta subito dopo l'inizio della comunicazione SPI, eliminando la necessità di una struttura tradizionale di comando-risposta. Durante il primo byte del trasferimento SPI, l'host invia 0x00 e l'encoder AMT22 risponde contemporaneamente con dati di posizione validi.
Se l'host deve inviare un comando (Tabella 2), ad esempio un comando di azzeramento, lo farà nel secondo byte della trasmissione. Questo viene definito un comando esteso. Per le specifiche tecniche dettagliate, consultare la scheda tecnica AMT22.
|
Tabella 2: Comandi AMT22 definiti. (Immagine per gentile concessione di Same Sky)
Tutorial del codice - inclusioni e definizioni
Poiché il bus SPI di Arduino viene utilizzato per interfacciarsi con l'encoder AMT22, è necessario includere nel codice la libreria SPI. Per inviare i dati di posizione da Arduino al computer viene utilizzata la connessione USB-seriale integrata nell'IDE Arduino, configurata con una velocità di trasmissione di 115200.
Inoltre, è necessario definire i comandi utilizzati dall'encoder AMT22. Poiché l'encoder non elabora il contenuto del primo byte, viene assegnato un NOP (non operazione) per semplificare il processo di comunicazione (listato 1).
Copy
/* 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
Listato 1: Impostazione dell'interfaccia SPI.
Inizializzazione
Nella funzione setup() (listato 2), si comincia inizializzando tutti i pin SPI richiesti e configurando le interfacce seriali per la comunicazione.
La porta seriale deve essere inizializzata per consentire la trasmissione dei dati al computer host. Ciò avviene passando il BAUDRATE definito nella funzione Serial.begin().
Prima di abilitare SPI, assicurarsi che la riga chip-select (CS) sia impostata sullo stato appropriato per preparare l'encoder alla comunicazione.
Selezionare una frequenza di clock per il bus SPI per comunicare con AMT22. A fini di prototipazione, è adatta una frequenza di clock di 500 kHz, anche se AMT22 supporta frequenze fino a 2 MHz. Per ottenere 500 kHz è possibile utilizzare l'impostazione SPI_CLOCK_DIV32. Considerando il clock a 16 MHz di Arduino Uno, questa divisione risulta in una frequenza di clock SPI di 500 kHz. Per ulteriori dettagli sulla configurazione del clock SPI, consultare la documentazione di Arduino.
Dopo aver configurato il tutto, il bus SPI può essere inizializzato utilizzando SPI.begin(), che imposterà i tre pin SPI dedicati: MISO, MOSI e SCLK, preparando il sistema alla comunicazione con l'encoder.
Copy
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();
}
Listato 2: La funzione setup() che inizializza tutti i pin SPI.
Comunicazione SPI
La comunicazione SPI con AMT22 è gestita dalla libreria SPI di Arduino, mentre il controllo chip-select (CS) è gestito dal codice tramite i pin I/O digitali. La funzione digitalWrite() viene utilizzata per asserire o deasserire la riga CS (listato 3).
AMT22 si aspetta l'invio di due byte 0x00 e restituisce i dati subito dopo averli ricevuti. A causa della rapidità della risposta, è necessario rispettare alcuni requisiti minimi di temporizzazione, descritti nella scheda tecnica AMT22.
Indipendentemente dalla versione a 12 o 14 bit, l'encoder risponde sempre con due byte (16 bit) di dati. I due bit superiori sono bit di controllo, utilizzati per verificare l'integrità dei dati. Per la versione a 12 bit, i due bit inferiori sono entrambi 0 e il valore restituito deve essere spostato a destra di 2 bit (o diviso per 4) per essere utilizzato correttamente.
Per ottenere i dati di posizione, viene richiamata la funzione SPI.transfer() inviando il comando AMT22_NOP. Durante questo processo, la riga CS rimane bassa. AMT22 invia per primo il byte alto, quindi il byte ricevuto viene spostato a sinistra di 8 bit per allinearlo nella metà superiore di una variabile uint16_t. Questo valore viene assegnato alla variabile encoderPosition in un'unica operazione. Dopo un breve ritardo per soddisfare i requisiti di temporizzazione, viene eseguita una seconda chiamata SPI.transfer() per inviare un altro comando AMT22_NOP. Il risultato viene sottoposto a OR con il valore corrente in encoderPosition, combinando di fatto i due byte ricevuti in una singola variabile uint16_t. Infine, la riga CS viene rilasciata, completando la comunicazione.
Copy
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);
Listato 3: Impostazione della comunicazione SPI.
Verifica del checksum
Dopo aver completato il trasferimento SPI, è essenziale convalidare i dati ricevuti utilizzando un checksum (Listato 4).
Per implementare questa validazione, è possibile creare una funzione basata sull'equazione fornita nella scheda tecnica. Il checksum è contenuto nei due bit superiori del valore ricevuto e utilizza la parità dispari tra i bit pari e dispari della risposta di posizione.
La funzione esegue le seguenti operazioni:
- Calcolo della parità per i bit dispari (bit 1, 3, 5, 7, 9, 11, 13)
- Calcolo della parità per i bit pari (bit 0, 2, 4, 6, 8, 10, 12, 14)
- Confronta le parità calcolate con i valori indicati dai bit di checksum
La funzione restituisce True se il checksum è valido, indicando che l'integrità dei dati è confermata. Se il checksum non è valido, la funzione restituisce False, segnalando un potenziale errore nei dati ricevuti.
Copy
/*
* 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);
}
Listato 4: Convalida del checksum.
Formattazione dei dati
Se la convalida del checksum conferma l'integrità dei dati, il passo successivo consiste nell'aggiornare la variabile encoderPosition rimuovendo i due bit superiori (Listato 5). A tale fine si può applicare un'operazione bitwise AND con 0x3FFF (o 0b001111111111111111), che conserva di fatto tutti i 14 bit inferiori dei dati di posizione.
Inoltre, è necessario tenere conto della risoluzione dell'encoder, che può essere a 12 o 14 bit. Se la risoluzione è di 12 bit, il valore di encoderPosition deve essere spostato di 2 bit a destra per adattarsi alla risoluzione inferiore. Ciò garantisce che i dati di posizione siano rappresentati con precisione nella variabile encoderPosition, che riflette la posizione effettiva dell'encoder in base alla risoluzione specificata.
Copy
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");
}
Listato 5: Aggiornamento di encoderPosition.
Impostazione della posizione zero (solo monogiro)
Alcune varianti dell'encoder AMT22 offrono una funzione di posizione zero programmabile. Per impostare questa posizione zero, è necessario inviare una specifica sequenza di comandi a due byte. La procedura prevede l'invio del comando AMT22_NOP, seguito da una breve attesa per soddisfare i requisiti minimi di temporizzazione specificati dall'encoder AMT22. Trascorso questo tempo di attesa, viene inviato il comando AMT22_ZERO assicurando il rilascio della riga chip-select (CS). Una volta ricevuto questo comando, l'encoder esegue un'operazione di reset (Listato 6).
Per evitare la comunicazione con l'encoder durante questo periodo di reset, viene implementato un ritardo di 250 ms, che assicura che non vengano inviati comandi all'encoder durante il tempo di accensione.
Sebbene sia possibile che il codice imposti la posizione zero dell'encoder all'inizio del funzionamento, nelle applicazioni tipiche è più comune impostarla solo una volta durante la configurazione iniziale del dispositivo per l'uso all'interno del sistema. Questa pratica contribuisce a mantenere l'integrità della retroazione di posizione dell'encoder per tutta la sua durata prevista.
Copy
/*
* 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
}
Listato 6: Impostazione della posizione zero di un encoder AMT22 monogiro.
Lettura del contagiri (solo multigiro)
Alcune varianti dell'encoder AMT22 supportano un contatore multigiro, che consente agli utenti di leggere sia la posizione che il conteggio dei giri in un'unica sequenza di recupero dei dati.
Se i dati di posizione ricevuti non sono validi, il sistema deve notificare all'utente l'errore. Invece, se la posizione è valida, il programma deve riportare la posizione in formato decimale (Listato 7). Questa capacità migliora la funzionalità dell'encoder fornendo una retroazione completa sia sulla posizione assoluta sia sul numero di giri completi, facilitando un monitoraggio e un controllo più accurati nelle applicazioni che richiedono dati rotazionali precisi.
Copy
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);
Listato 7: Lettura di encoderPosition e del contagiri in un encoder AMT22 multigiro.
Esecuzione del codice
Una volta creato il codice, è il momento di caricarlo su Arduino e stabilire la comunicazione con l'encoder AMT22.
Per monitorare l'uscita, aprire il monitor seriale nell'IDE Arduino e assicurarsi che la velocità dei dati sia impostata su 115200 baud. In questo modo gli utenti potranno osservare il funzionamento dell'encoder e visualizzare i dati di posizione riportati in tempo reale. Una volta che il monitor seriale è attivo, l'encoder dovrebbe iniziare a trasmettere le informazioni sulla posizione, dimostrando la sua funzionalità all'interno del sistema (Figura 3).
Figura 3: La posizione riportata dall'encoder, ricevuta da Arduino (Immagine per gentile concessione di Same Sky)
Encoder multipli
Un vantaggio significativo dell'uso di un dispositivo SPI è la possibilità di comunicare con più encoder sullo stesso bus. A tal fine, è necessario allocare un pin I/O digitale aggiuntivo per ciascun encoder, in modo da consentire il controllo individuale di chip-select (CS).
Nel codice di esempio (Listato 8), viene utilizzato un array di pin CS per supportare un numero arbitrario di encoder. Ciò consente una comunicazione scalabile, permettendo all'utente di aggiungere facilmente altri encoder in base alle necessità. Modificando le funzioni per accettare il numero di pin corrispondente al dispositivo desiderato, il codice può controllare dinamicamente quale encoder sia attivo sul bus SPI, garantendo l'accesso e il funzionamento indipendente di ciascun dispositivo.
Copy
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
Listato 8: Impostazione di un array per la lettura di più encoder.
Il passo successivo consiste nell'eseguire un ciclo su ciascun pin CS dell'array e leggere la posizione da ciascun encoder collegato. In questo modo, il sistema può attivare ciascun encoder asserendo la sua riga chip-select, eseguendo il trasferimento SPI e recuperando i dati di posizione. Il codice selezionerà in sequenza ogni encoder, eseguirà la comunicazione SPI e rilascerà la riga CS, assicurando che tutti i dispositivi collegati vengano interrogati per ricavarne le rispettive informazioni di posizione (Listato 9).
Copy
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);
}
Listato 9: Lettura della variabile encoderPosition da più encoder.
Ultimato il trasferimento dei dati, è necessario un tempo di attesa minimo prima di rilasciare la riga chip-select. Secondo la scheda tecnica, questo tempo minimo è di 3 microsecondi. Sebbene questo ritardo sia tipicamente osservato in modo naturale alle velocità di trasmissione dei dati più basse, è buona norma implementarlo esplicitamente nel codice per garantire il corretto funzionamento e l'aderenza alle specifiche di temporizzazione. Ciò garantisce una comunicazione affidabile con l'encoder AMT22.
Conclusione
Gli utenti dovrebbero ora avere una conoscenza di base della configurazione e della lettura dei dati degli encoder assoluti AMT22 di Same Sky. Questo articolo si è concentrato sugli encoder assoluti AMT22. Same Sky offre anche una linea di encoder modulari AMT con una serie di versioni incrementali, assolute e a commutazione.
Esonero della responsabilità: le opinioni, le convinzioni e i punti di vista espressi dai vari autori e/o dai partecipanti al forum su questo sito Web non riflettono necessariamente le opinioni, le convinzioni e i punti di vista di DigiKey o le sue politiche.

