Przykładowy kod Arduino dla enkoderów absolutnych SPI

Przez: Damon Tarry, Design Applications Engineer, Same Sky

Niniejszy samouczek pokazujący przygotowanie przykładowego kodu dla Arduino ma na celu zapewnienie użytkownikom solidnego punktu wyjścia do konfigurowania i odczytywania danych z oferowanych przez firmę Same Sky enkoderów absolutnych AMT22, wyposażonych w komunikację w postaci szeregowego interfejsu urządzeń peryferyjnych (SPI). W samouczku opisano potrzebny sprzęt i oprogramowanie, kluczowe wymagania dotyczące konfiguracji, a także przedstawiono przykładowe pakiety kodów oraz instrukcje dla opcji wyjściowych jednoobrotowych i wieloobrotowych. Oto lista elementów wymaganych do rozpoczęcia pracy:

Omówienie enkodera absolutnego AMT22

Enkoder absolutny AMT22 firmy Same Sky (wcześniej CUI Devices) jest oferowany w rozdzielczości 12-bitowej lub 14-bitowej, co oznacza, że zapewnia precyzyjną liczbę unikalnych położeń na jeden obrót. W przypadku wariantu 12-bitowego oznacza to 4096 różnych położeń, podczas gdy model 14-bitowy ma 16384 położenia na obrót. Bez względu na to, ile razy urządzenie zostanie obrócone, stale zgłasza swoje bezwzględne położenie, dostarczając użytkownikom dokładnej informacji zwrotnej na temat dokładnego kąta urządzenia.

Omawiany enkoder jest dostępny jako model jednoobrotowy i wieloobrotowy. Wariant jednoobrotowy mierzy położenia w ramach pojedynczego obrotu o 360 stopni, podczas gdy wersja wieloobrotowa śledzi nie tylko położenie w obrębie jednego obrotu, ale także całkowitą liczbę pełnych obrotów. Dodatkowo warianty jednoobrotowe posiadają programowany punkt zerowy, umożliwiający użytkownikom zdefiniowanie niestandardowego odniesienia dla punktu początkowego enkodera.

Pierwsze kroki

Upewnij się, że urządzenie znajduje się w trybie działania (RUN), ustawiając przełącznik znajdujący się z tyłu enkodera w odpowiednim położeniu (ilustracja 1). Następnie zamontuj enkoder AMT22 na silniku lub zespole, posługując się instrukcją montażu AMT, aby zapewnić poprawność instalacji. Enkoder AMT22 obsługuje 9 różnych rozmiarów wałów od 2mm do 8mm.

Diagram przedstawiający enkoder AMT22 firmy Same Sky przełączony w tryb działania (RUN)Ilustracja 1: przestaw przełącznik z tyłu enkodera AMT22 w położenie trybu działania (RUN). (Źródło ilustracji: Same Sky)

Połączenia przedstawione na ilustracji 2 i w tabeli 1 są przeznaczone specjalnie dla płytki Arduino Uno, tym niemniej dostarczony kod powinien być kompatybilny z większością płytek Arduino. Należy jednak pamiętać, że konfiguracje wtyków mogą się różnić w zależności od modelu Arduino. Aby uzyskać dokładne szczegóły połączeń innych płytek, należy zapoznać się z odpowiednią dokumentacją Arduino.

Diagram przedstawiający połączenia oprzewodowania Arduino Uno z enkoderem AMT22Ilustracja 2: połączenia oprzewodowania Arduino Uno z enkoderem AMT22. (Źródło ilustracji: Same Sky)

Funkcja Numer wtyku enkodera Wtyk Arduino Uno AMT-DBC-1-036
+5V 1 5V Białe/zielone
SCLK 2 13 Niebieskie/białe
MOSI 3 11 Białe/niebieskie
GND 4 GND Zielone/białe
MISO 5 12 Pomarańczowe/białe
CS 6 2 Białe/pomarańczowe

Tabela 1: definicje połączeń oprzewodowania Arduino Uno. (Źródło ilustracji: Same Sky)

Enkoder AMT22 zaczyna przesyłać dane położenia bezwzględnego natychmiast po rozpoczęciu komunikacji SPI, eliminując potrzebę stosowania tradycyjnej struktury polecenie-odpowiedź. W pierwszym bajcie transferu SPI host wysyła sygnał 0x00, a enkoder AMT22 odpowiada jednocześnie, podając prawidłowe dane położenia.

Jeśli host musi wydać polecenie (tabela 2), na przykład polecenie zerowania, zostanie ono wysłane w drugim bajcie transmisji. Nazywa się to poleceniem rozszerzonym. Szczegółowe dane techniczne można znaleźć w arkuszu danych AMT22.

Polecenie Bajt Uwagi
Pobierz położenie 0x00 0x00
Ustaw zero 0x00 0x70 Tylko jednoobrotowe
Pobierz obroty 0x00 0xA0 Tylko wieloobrotowe

Tabela 2: definicje poleceń urządzenia AMT22. (Źródło ilustracji: Same Sky)

Samouczek kodowania - zawartość i definicje

Ponieważ do współpracy z enkoderem AMT22 używana jest magistrala SPI Arduino, kod musi zawierać bibliotekę SPI. Do wysłania danych położenia z Arduino do komputera wykorzystuje się wbudowane połączenie szeregowe USB w zintegrowanym środowisku deweloperskim Arduino, skonfigurowane na szybkość transmisji 115200 bodów.

Ponadto należy zdefiniować polecenia używane przez enkoder AMT22. Ponieważ enkoder nie przetwarza zawartości pierwszego bajtu, w celu uproszczenia procesu komunikacji przypisywany jest stan NOP (brak operacji) (listing 1).

Kopiuj
/* 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

Listing 1: konfiguracja interfejsu SPI.

Inicjowanie

W funkcji setup() (listing 2) zainicjuj wszystkie wymagane wtyki interfejsu SPI i skonfiguruj interfejsy szeregowe do komunikacji.

Aby umożliwić transmisję danych do komputera hosta, należy zainicjować port szeregowy. Odbywa się to poprzez przekazanie zdefiniowanej wartości BAUDRATE do funkcji Serial.begin().

Przed włączeniem interfejsu SPI należy upewnić się, że linia wyboru mikroukładu (CS) jest ustawiona w odpowiedni stan, aby przygotować enkoder do komunikacji.

Wybierz częstotliwość taktowania magistrali SPI na potrzeby komunikacji z enkoderem AMT22. Do celów prototypowania odpowiednia jest częstotliwość taktowania 500kHz, chociaż enkoder AMT22 obsługuje częstotliwości do 2MHz. Wartość 500kHz można osiągnąć za pomocą ustawienia SPI_CLOCK_DIV32. Biorąc pod uwagę, że częstotliwość taktowania Arduino Uno wynosi 16MHz, ten podział da częstotliwość SPI równą 500kHz. Więcej szczegółów na temat konfiguracji taktowania SPI można znaleźć w dokumentacji Arduino.

Po zakończeniu konfiguracji, magistralę SPI można zainicjować za pomocą funkcji SPI.begin(), która konfiguruje trzy dedykowane wtyki SPI: MISO, MOSI oraz SCLK, przygotowując system do komunikacji z enkoderem.

Kopiuj
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();
}

Listing 2: funkcja setup(), która inicjuje wszystkie wtyki interfejsu SPI.

Komunikacja SPI

Komunikacja SPI z urządzeniem AMT22 odbywa się za pośrednictwem biblioteki Arduino SPI, natomiast sterowanie wyborem układu mikroelektronicznego (CS) odbywa się za pośrednictwem kodu z wykorzystaniem cyfrowych wtyków wejścia-wyjścia. Funkcja digitalWrite() służy do potwierdzania lub cofania potwierdzenia linii CS (listing 3).

Urządzenie AMT22 oczekuje wysłania dwóch bajtów 0x00 i zwraca dane natychmiast po ich odebraniu. Ze względu na szybką reakcję, należy przestrzegać pewnych minimalnych wymagań dotyczących czasu, które zostały określone w arkuszu danych urządzenia AMT22.

Niezależnie od tego, czy enkoder jest w wersji 12-bitowej, czy 14-bitowej, zawsze w odpowiedzi przesyła dwa bajty (16 bitów) danych. Dwa górne bity są bitami kontrolnymi służącymi do weryfikacji integralności danych. W przypadku wersji 12-bitowej dwa dolne bity są równe 0, a zwracana wartość musi być przesunięta w prawo o 2 bity (lub podzielona przez 4), aby była prawidłowo użyta.

Aby uzyskać dane położenia, wywoływana jest funkcja SPI.transfer(), która wysyła polecenie AMT22_NOP. Linia CS utrzymuje stan niski podczas tego procesu. Urządzenie AMT22 wysyła górny bajt jako pierwszy, więc odebrany bajt jest przesuwany w lewo o 8 bitów w celu wyrównania go z górną połową zmiennej uint16_t. Wartość ta jest przypisywana do zmiennej encoderPosition w jednej operacji. Po krótkim opóźnieniu koniecznym do spełnienia wymagań czasowych wykonywane jest drugie wywołanie SPI.transfer() w celu wysłania kolejnego polecenia AMT22_NOP. Wynik jest poddawany funkcji OR z bieżącą wartością w polu encoderPosition, co w efekcie łączy dwa odebrane bajty w jedną zmienną uint16_t. Na koniec linia CS zostaje zwolniona, co kończy komunikację.

Kopiuj
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);

Listing 3: konfiguracja komunikacji SPI.

Weryfikacja sumy kontrolnej

Po zakończeniu transferu SPI konieczne jest zweryfikowanie odebranych danych za pomocą sumy kontrolnej (listing 4).

Aby wdrożyć tę walidację, można utworzyć funkcję na podstawie równania podanego w arkuszu danych. Suma kontrolna jest zawarta w dwóch górnych bitach odbieranej wartości i wykorzystuje parzystość nieparzystych i parzystych bitów odpowiedzi położenia.

Funkcja wykona następujące kroki:

  1. Obliczenie parzystości dla bitów nieparzystych (bity 1, 3, 5, 7, 9, 11, 13)
  2. Obliczenie parzystości dla bitów parzystych (bity 0, 2, 4, 6, 8, 10, 12, 14)
  3. Porównanie obliczonych parzystości z wartościami wynikających z bitów sumy kontrolnej

Funkcja zwróci wartość „prawda”, jeśli suma kontrolna będzie prawidłowa, wskazując, że integralność danych jest potwierdzona. Jeśli suma kontrolna będzie nieprawidłowa, funkcja zwróci wartość „fałsz”, sygnalizując potencjalny błąd w odebranych danych.

Kopiuj
/*
 * 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);
}

Listing 4: weryfikacja sumy kontrolnej.

Formatowanie danych

Jeśli weryfikacja sumy kontrolnej potwierdzi integralność danych, następnym krokiem jest aktualizacja zmiennej encoderPosition poprzez usunięcie dwóch górnych bitów (listing 5). Można to osiągnąć poprzez zastosowanie operacji bitowej AND z 0x3FFF (lub 0b0011111111111111), która skutecznie zachowuje wszystkie 14 dolnych bitów danych położenia.

Ponadto konieczne jest uwzględnienie rozdzielczości enkodera - niezależnie od tego, czy jest to rozdzielczość 12-bitowa, czy 14-bitowa. Jeśli rozdzielczość wynosi 12 bitów, wartość encoderPosition musi być przesunięta o 2 bity w prawo, aby uzyskać niższą rozdzielczość. Dzięki temu dane położenia są dokładnie reprezentowane w zmiennej encoderPosition, odzwierciedlając rzeczywiste położenie enkodera w oparciu o jego określoną rozdzielczość.

Kopiuj
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");
}

Listing 5: aktualizacja zmiennej encoderPosition.

Ustawianie położenia zerowego (tylko wersje jednoobrotowe)

Niektóre warianty urządzenia AMT22 oferują funkcję programowanego położenia zerowego. Aby ustawić to położenie zerowe, należy wysłać określoną dwubajtową sekwencję poleceń. Proces ten polega na wysłaniu najpierw polecenia AMT22_NOP, a następnie na krótkim oczekiwaniu na spełnienie minimalnych wymagań taktowania określonych przez urządzenie AMT22. Po tym oczekiwaniu wysyłane jest polecenie AMT22_ZERO, przy jednoczesnym upewnieniu się, że linia wyboru mikroukładu (CS) jest zwolniona. Gdy enkoder otrzyma to polecenie, wykona operację resetowania (listing 6).

Aby uniknąć komunikacji z enkoderem w tym okresie resetowania, zastosowano opóźnienie 250ms, które zapewnia, że żadne polecenia nie będą wysyłane do enkodera w czasie jego włączania.

Chociaż możliwe jest ustawienie położenia zerowego enkodera na początku działania, w typowych zastosowaniach bardziej powszechne jest ustawienie położenia zerowego tylko raz podczas początkowej konfiguracji urządzenia do użycia w systemie. Praktyka ta pomaga utrzymać integralność sygnału zwrotnego położenia enkodera przez cały okres jego eksploatacji.

Kopiuj
/*
 * 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
}

Listing 6: ustawianie położenia zerowego enkodera jednoobrotowego AMT22.

Odczyt licznika obrotów (tylko wersje wieloobrotowe)

Niektóre warianty enkodera AMT22 obsługują liczniki wieloobrotowe, umożliwiając użytkownikom odczyt położenia i liczby obrotów w jednej sekwencji pobierania danych.

Jeżeli odebrane dane położenia są nieprawidłowe, system powinien powiadomić użytkownika o błędzie. Natomiast jeśli położenie jest prawidłowe, program powinien podać je w formacie dziesiętnym (listing 7). Funkcja ta rozszerza funkcjonalność enkodera, zapewniając kompleksowe informacje zwrotne dotyczące zarówno położenia bezwzględnego, jak i liczby pełnych obrotów, ułatwiając dokładniejsze monitorowanie i sterowanie w zastosowaniach wymagających precyzyjnych danych dotyczących obrotów.

Kopiuj
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);

Listing 7: odczyt zmiennej encoderPosition i licznika obrotów w enkoderze wieloobrotowym AMT22.

Uruchamianie kodu

Po pomyślnym utworzeniu kodu nadchodzi czas, aby przesłać go do Arduino i nawiązać komunikację z enkoderem AMT22.

Aby monitorować wyjście, należy otworzyć monitor szeregowy w zintegrowanym środowisku deweloperskim (IDE) Arduino i upewnić się, że szybkość przesyłu danych jest ustawiona na 115200 bodów. Umożliwi to użytkownikom obserwację pracy enkodera i wyświetlanie raportowanych danych położenia w czasie rzeczywistym. Po uaktywnieniu monitora szeregowego enkoder powinien rozpocząć przesyłanie informacji o swoim położeniu, potwierdzając swoje działanie w systemie (ilustracja 3).

Ilustracja przedstawiająca położenie zgłaszane przez enkoderIlustracja 3: położenie zgłaszane przez enkoder, odebrane przez Arduino (źródło ilustracji: Same Sky)

Wiele enkoderów

Jedną z istotnych zalet korzystania z urządzenia z komunikacją SPI jest możliwość współpracy z wieloma enkoderami na tej samej magistrali. W tym celu dla każdego enkodera należy przydzielić dodatkowy wtyk wejścia-wyjścia cyfrowego, umożliwiający sterowanie indywidualnym wyborem mikroukładu (CS).

W przykładowym kodzie (listing 8) do obsługi dowolnej liczby enkoderów wykorzystywany jest układ wtyków CS. Taka konstrukcja pozwala na skalowalną komunikację, umożliwiając użytkownikowi łatwe dodawanie większej liczby enkoderów w razie potrzeby. Dzięki modyfikacji funkcji w celu przyjęcia numeru wtyku odpowiadającego żądanemu urządzeniu, kod może dynamicznie sterować tym, który enkoder jest aktywny na magistrali SPI, zapewniając niezależność dostępu do każdego urządzenia i jego obsługi.

Kopiuj
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

Listing 8: konfiguracja układu do odczytu wielu enkoderów.

Następnym krokiem jest przechodzenie w pętli przez poszczególne wtyki CS w układzie i odczytanie położenia z każdego podłączonego enkodera. Pozwala to systemowi na aktywację każdego enkodera poprzez uaktywnienie jego linii wyboru układu mikroelektronicznego, wykonanie transferu SPI i pobranie danych położenia. Kod wybierze kolejno poszczególne enkodery, przeprowadzi komunikację SPI i zwolni linię CS, zapewniając, że wszystkie podłączone urządzenia zostaną odpytane o informacje o położeniu (listing 9).

Kopiuj
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);
}

Listing 9: odczytywanie zmiennej encoderPosition z wielu enkoderów.

Po przesłaniu danych przed zwolnieniem linii wyboru mikroukładu musi upłynąć minimalny czas oczekiwania. Według arkusza danych ten minimalny czas wynosi 3 mikrosekundy. Chociaż opóźnienie to jest zwykle obserwowane w sposób naturalny przy niższych szybkościach przesyłu danych, dobrą praktyką jest zaimplementowanie go wprost w kodzie, aby zapewnić prawidłowe działanie i zgodność ze specyfikacjami taktowania. Zapewnia to niezawodną komunikację z enkoderem AMT22.

Podsumowanie

Użytkownicy powinni już teraz mieć podstawową wiedzę na temat konfigurowania i odczytywania danych z enkoderów absolutnych AMT22 firmy Same Sky. W niniejszym artykule omówiono enkodery absolutne AMT22. Firma Same Sky oferuje również linię enkoderów modułowych AMT, wśród których znajdują się liczne wersje inkrementalne, absolutne i komutacyjne.

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.

Informacje o autorze

Damon Tarry, Design Applications Engineer, Same Sky