Tajemnice wejść analogowych Arduino Leonardo i Yun

W Arduino Leonardo i Arduino Yun zastosowano inny kontroler niż w tradycyjnych płytkach. Jest nim ATmega32u4. Ma on kilka zalet w porównaniu z poprzednikami – sprzętowe USB, więcej wyjść PWM i więcej wejść analogowych.

Wejścia analogowe oprócz tradycyjnych cech opisanych w poprzednim wpisie mają też nowe możliwości.

long readADC(byte ref, byte input)
{
  ADMUX = (input & 31) | ((ref & 3) << 6);
  bitWrite(ADCSRB, 5, ((input & 32) >> 6));
  delay(2); // Wait for Vref to settle
  ADCSRA |= _BV(ADSC); // Convert
  while (bit_is_set(ADCSRA,ADSC));
  long result = ADCL;
  result |= ADCH<<8;
  
  return result;
}

void setup() 
{
  while (!Serial);
  Serial.begin(9600);
}

void loop() 
{
  Serial.println(readADC(3, 39));
  delay(1000);
}

Użyta w programie funkcja readADC pozwala je odkryć.

word value = readADC(ref, input);

  • value – wartość odczytana z wejścia analogowego (0 – 1023)
  • ref – numer źródła napięcia referencyjnego
  • input – numer wejścia analogowego.

Argument “ref” może przyjąć następujące wartości:

  • 0 – napięcie referencyjne to napięcie zasilania
  • 1 – napięcie referencyjne pochodzi z pinu AREF
  • 3 – napięcie referencyjne pochodzi z dokładnego źródła 2,56 V

Jeśli czytałeś poprzedni wpis to zadanie i możliwości napięcia referencyjnego są ci znane.

Dużo ciekawiej jest z drugim argumentem “input”. Zacznę od standardowych możliwości.

Wprowadzając tam jedną z tych wartości odczytujesz standardowe wejścia analogowe:

  • 0    A5
  • 1    A4
  • 4    A3
  • 5    A2
  • 6    A1
  • 7    A0
  • 32    D4/A6
  • 33    D12/A7
  • 34    D6/A8
  • 35    D8/A9
  • 36    D9/A10
  • 37    D10/A11

Do tej pory nuuuda. Wszystko prawie jak bibliotekach Arduino, tylko bardziej zagmatwane ;-). Jednak mamy do dyspozycji więcej liczb w tym argumencie, które kryją dodatkowe możliwości.

Następna grupa to:

  • 30    1.1 V
  • 31    0 V
  • 39    Temperatura

Wartość 30 argumentu podaje na wejście przetwornika dokładne napięcie 1,1 V. Pozwala to skalibrować wynik podawany przez wejście i napięcie odniesienia. Tak jak w poprzednim wpisie.

Wartość 31 podaje na wejście analogowe 0 V. To taka wartość testowa, czy przetwornik zwróci wynik 0.

Wartośc 39 odczytuje temperaturę panującą w środku kontrolera. Pomiar nie jest zbyt precyzyjny. Ma dokładność +/-10 stopni Celsjusza. Służy głównie do tego by kalibrować wewnętrzne elementy wrażliwe na temperaturę (jak generator RC). Trudno nim zmierzyć temperaturę w pokoju, bo przecież kontroler też się nagrzewa podczas pracy.

Kolejne wartości dotyczą wejść symetrycznych.

Standardowo wejścia analogowe Arduino mierzą napięcie między pinami GND i wybranym wejściem analogowym.

W wejściach symetrycznych dostajesz do dyspozycji 2 wejścia analogowe. Jedno z biegunem dodatnim “+”, a drugie z biegunem ujemnym “-”. Napięcie jest mierzone między tymi biegunami.

Wejścia symetryczne ułatwiają wiele obwodów w których nie chcesz do pomiarów używać GND. Jak np. w pomiarze prądu za pomocą rezystora o małej wartości, albo przy czujnikach wagi w układzie mostka.

Wartości wejść symetrycznych to:

  • 16    +A5    -A4
  • 20    +A3    -A4
  • 21    +A2    -A4
  • 22    +A1    -A4
  • 23    +A0    -A4

Czyli biegun dodatni takiego wejścia jest w wybranym pinie A0, A1, A2, A3, A5, a biegun ujemny jest w pinie A4.

Wejścia symetryczne mają jeszcze jedną opcję. Pozwalają przepuścić wejście przez wzmacniacz i wzmocnić sygnał od 10 do 200 razy.

Kombinacja wejść symetrycznych i siła wzmocnienia przypisana jest do następujących wartości:

  • 9    +A4    -A5    x10
  • 11    +A4    -A5    x200
  • 38    +A4    -A5    x40
  • 40    +A3    -A5    x10
  • 41    +A2    -A5    x10
  • 42    +A1    -A5    x10
  • 43    +A0    -A5    x10
  • 44    +A3    -A4    x10
  • 45    +A2    -A4    x10
  • 46    +A1    -A4    x10
  • 47    +A0    -A4    x10
  • 48    +A3    -A5    x40
  • 49    +A2    -A4    x40
  • 50    +A1    -A5    x40
  • 51    +A0    -A5    x40
  • 52    +A3    -A4    x40
  • 53    +A2    -A4    x40
  • 54    +A1    -A4    x40
  • 55    +A0    -A4    x40
  • 56    +A3    -A5    x200
  • 57    +A2    -A5    x200
  • 58    +A1    -A5    x200
  • 59    +A0    -A5    x200
  • 60    +A3    -A4    x200
  • 61    +A2    -A4    x200
  • 62    +A1    -A4    x200
  • 63    +A0    -A4    x200

Wzmocnienie 200 razy przy napięciu odniesienia 2,56 V pozwoli zmierzyć napięcia nawet do 12,5 mikrowolta.

sprae

Dokładniejsze wejścia analogowe w Arduino Yun i Leonardo

Każde Arduino ma wejścia analogowe w sekcji ANALOG IN (od A0 do A5). Pozwalają one dokładnie mierzyć napięcie.
Arduino Yun i Leonardo mają więcej wejść analogowych. Są one ukryte w pinach sekcji DIGITAL.

  • Pin cyfrowy 4 to wejście analogowe A6
  • Pin cyfrowy 6 to wejście analogowe A8
  • Pin cyfrowy 8 to wejście analogowe A9
  • Pin cyfrowy 9 to wejście analogowe A10
  • Pin cyfrowy 10 to wejście analogowe A11
  • Pin cyfrowy 12 to wejście analogowe A7

Napięcie na wejściach analogowych mierzysz za pomocą funkcji “analogRead”

word value = analogRead(numer_wejścia);

Gdzie:

  • value – zmienna przechowująca wynik pomiaru od 0 do 1023. Gdzie 0 to 0 V, a 1023 to domyślnie 5 V.
  • numer_wejścia – numer wejścia analogowego zależnie od pinu w Yun to wartości do 0 dla pinu A0 do 11 dla pinu A11.

Dokładność pomiaru wejść analogowych zależy od napięcia odniesienia.

Napięcie odniesienia to wzorcowe napięcie, przy którym przetwornik wejścia analogowego wskazuje wartość maksymalną (1023).

Napięcie odniesienia ustawiasz za pomocą funkcji “analogReference”.

analogReference(reference);

reference – rodzaj napięcia odniesienia.
Rodzaj wybieramy za pomocą stałych, gdzie:

  • DEFAULT – napięciem odniesienia jest napięcie zasilania około 5 V
  • INTERNAL – napięciem odniesienia jest dokładne napięcie 2,56 V
  • EXTERNAL – napięciem odniesienia jest napięcie podłączone do pinu AREF

W zależności od tego jaki zakres wyjść ma czujnik podłączony do wejścia analogowego, wartość napięcia odniesienia powinna być większa lub równa z jego zakresem.

Najbardziej dokładny jest rodzaj INTERNAL, ale nadaje się od tylko do urządzeń z wyjściem od 0 do 2,5 V (np pojedyncze ogniwa baterii).

Najmniej dokładny jest rodzaj DEFAULT, bo napięcie zasilania nie ma idealnie 5 V. Zmienia się pod wpływem różnych warunków np. obciążenia, albo zakłóceń.

Można poprawić dokładność pomiaru z referencją DEFAULT. W tym celu przerobiłem mały programik, który zwróci dokładne napięcie odniesienia.

long readVcc()
{
  ADMUX = _BV(REFS0) | _BV(MUX1) | _BV(MUX2) | _BV(MUX3) | _BV(MUX4);
  delay(2); // Wait for Vref to settle
  ADCSRA |= _BV(ADSC); // Convert
  while (bit_is_set(ADCSRA,ADSC));
  long result = ADCL;
  result |= ADCH<<8;
  result = 1126400L / result; // Back-calculate AVcc in mV
  return result;
}

void setup() 
{
  while (!Serial);
  Serial.begin(9600);
}

void loop() 
{
  Serial.println(readVcc());
  delay(1000);
}

Funkcja “readVcc” zwraca dokładną wartość napięcia zasilania w mili voltach. Właśnie dla takiego napięcia przetwornik będzie zwracał wartość 1023.

Ciekawe, że Arduino Yun zwróciło mi wartość zasilania 4200 mV.

sprae

Teensy 3.1 – dzień III – wydajność

Dziś wielkie starcie. W prawym narożniku Arduino Leonardo. Zawodnik o wadze 16 MHz. W lewym Teensy 3.1. Jego zegar to 96 MHz.

Sprawdźmy ile razy bardziej wydajne jest Teensy 3.1 w porównaniu do jednej z najbardziej popularnych płytek Arduino Leonardo.

Dzielenie liczb liczb jest jedną z najbardziej czasochłonnych operacji dla procesora. Procesor ARM Cortex M4 w Teensy 3.1 ma specjalny rozkaz, dzięki któremu dzieli liczby sprzętowo.
Procesor AVR w Arduino Leonardo nie ma dzielenia sprzętowego i musi zastępować je kilkoma innymi operacjami.

To program który porówna wydajności w dzieleniu:

unsigned long sum;

void setup()
{
  while (!Serial);
  Serial.begin(9600);
}

void loop()
{
  Serial.println(F("Start"));
  unsigned long timeBegin = millis();
  for (int a=0; a<10000; a++)
  {
    for (int b=1; b<100; b++)
    {
      sum += a / b;
    }
  }
  
  unsigned long processTime = millis() - timeBegin;
  Serial.println(F("End"));
  Serial.print(F("Czas obliczen: "));
  Serial.println(processTime);
}

Program dzieli przez siebie milion liczb i mierzy czas tej operacji.

  • Arduino Leonardo zajęło to 14,978 s
  • Teensy 3.1 zajęło to 0,127 s

Czyli w dzieleniu Teensy okazało się 118 razy szybsze.

Kolejny test jest dość zabawny. Polega na uruchomieniu na obydwu płytkach tego samego emulatora procesora 6502. Na emulatorze uruchomiłem język EhBASIC. Cały kod pochodzi z wątku na forum Arduino, o którym niedawno pisałem.

Uznałem, że emulator to dobre środowisko by przetestować różnorodne operacje. W końcu to taki duży skomplikowany program, który robi dużo obliczeń, dużo komunikuje się z pamięcią Flash i RAM. Będzie dobrze odzwierciedlał ogólną wydajność procesora.

W języku BASIC napisałem prosty program zliczający od 0 do 10000.

Czas jego działania dla każdej płytki to:

  • Arduino Leonardo: 57,22 s
  • Teensy 3.1: 9,8 s

W tym teście Teensy nie ma już takiej ogromnej przewagi. Ale nadal jest prawie 6 razy szybsze.

Na koniec zostawiłem najmocniejszy test. Wziąłem algorytmy do obliczeń GPS ze starego projektu licznika rowerowego. Jest w nich dużo liczb zmiennoprzecinkowych (float) i masa różnych funkcji trygonometrycznych typu sin, cos, tan, atan2.

W oparciu o to, napisałem program, który oblicza trasę 36km z Płocka na zachód. Co 0.0001 stopień szerokości geograficznej.

float lat = 52.512204;
float lon = 19.675606;
float lonTo = 19.143456;

float oldLat = lat;
float oldLon = lon;

float angle;
float distance;

float getDistance(float latf, float lonf, float latt, float lont)
{
  float distance = acos(cos(latf)*cos(lonf)*cos(latt)*cos(lont)+cos(latf)*sin(lonf)*cos(latt)*sin(lont)+sin(latf)*sin(latt)) * 6378.137;
  return distance;
}

float lonTox(float lon)
{
  float x = (lon + PI) / (2 * PI) * 2.0;
  return x;
}

float latToy(float lat)
{
  float y = (1.0 - log(tan(lat) + (1 / cos(lat))) / PI);
  return y;
}

float getAngle(float latf, float lonf, float latt, float lont)
{
  float xf = lonTox(lonf);
  float yf = latToy(latf);
  float xt = lonTox(lont);
  float yt = latToy(latt);
  
  float x = xt - xf;
  float y = yt - yf;
  float angle = atan2(y, x);
  return angle;
}

void setup()
{
  while (!Serial);
  Serial.begin(9600);
}

void loop()
{
  Serial.println(F("Start"));
  unsigned long timeBegin = millis();
  for (float a=lon; a>lonTo; a-=0.0001)
  {
    distance += getDistance(oldLat, oldLon, lat, a);
    angle = getAngle(oldLat, oldLon, lat, a);
    oldLat = lat;
    oldLon = a;
  }
  
  unsigned long processTime = millis() - timeBegin;
  Serial.println(F("End"));
  Serial.print(F("Czas obliczen: "));
  Serial.println(processTime);
  
  Serial.print(F("Odleglosc: "));
  Serial.println(distance);
  Serial.print(F("Kat: "));
  Serial.println(angle);
  distance = 0.0;
  angle = 0.0;
}

Program pokazuje jak dobrze sprawdza się Teensy w autopilotach i innych podobnych systemach związanych z nawigacją, położeniem i obliczeniami z przecinkiem.

  • Arduino Leonardo policzyło trasę w 10,242 s
  • Teensy 3.1 policzyło trasę w 4,215 s

W takich obliczeniach Teensy okazało się 2,6 raza wydajniejsze od Arduino Leonardo.

BTW. Jeśli interesujecie się nawigacją to w tym teście macie gotowe funkcje do obsługi GPS.

Funkcja “getDistance” oblicza odległość między dwoma punktami geograficznymi. Argumenty to długość i szerokość geograficzna punktu początkowego i długość i szerokość geograficzna punktu końcowego. Argumenty podajemy w radianach, a nie stopniach – co mi się zapomniało ;-).

Funkcja “getAngle” oblicza kąt pod jakim jest punkt geograficzny od nas względem zachodu. Jej argumenty są takie same jak “getDistance”.

Funkcje “latToy” i “lonTox” zamieniają długość i szerokość geograficzną na współrzędne punktu  x, y na płaskiej mapie.

Teensy 3.1 siłą rzeczy jest wydajniejsze od Arduino Leonardo. Dzięki testom dowiedziałeś się jak bardzo i w jakich operacjach radzi sobie najlepiej.

Pozytywnie zaskoczyło mnie dziś to, że programy pod Teensy obsługują pamięć Flash tak jak w Arduino. Można używać F() do tekstów. Autor Teensy przeniósł nawet bibliotekę “/avr/pgmspace.h”, żeby biblioteki Arduino obsługujące Flash działały na Teensy bez modyfikacji.

sprae

Windows blokowane kluczykiem

Windows blokowane kluczykiem

Podłączenie klawiatury od Amigi do PC dzięki Arduino Leonardo

Arduino Leonardo ma sprzętową obsługę portu USB. Dzięki któremu może udawać klawiaturę, pada i inne urządzenia podłączane przez to złącze.

Dziś gratka dla wielbicieli retro komputerów i starych klawiatur mechanicznych. Jeśli nie pasują one do waszych nowoczesnych PC, możecie zrobić do nich przejściówkę na USB wykorzystując Arduino Leonardo.

Forum Arduino to całkiem niezły zasób wiedzy na temat pomysłów i ich realizacji. Jeden z wątków na który natrafiłem opowiada o tym jak podłączyć klawiaturę od Amigi 500, 1000, 2000 do PC. Jest tam kod źródłowy i rozpiska podłączenia złącza klawiatury do Arduino.

AMIGA 500/1000/2000 Keyboard Interface

Na podobnej zasadzie możesz podłączyć starą, wygodną klawiaturę mechaniczną od oryginalnego IBM PC ze złączem DIN i cieszyć się niezapomnianymi klikami klawiszy.

Oczywiście podłączanie do PC to tylko wierzchołek góry lodowej. Ciekawszym pomysłem będzie podłączenie takiego zestawu do Raspberry Pi i schowania wszystkiego w dość przestronnej obudowie Amigi 500.

Klawiatury od innych komputerów podłącza się trochę trudniej. Zwykle mają bezpośrednio wyprowadzone wyjścia matrycy klawiszy. Jeśli znajdziesz schemat takiego komputera i starczy pinów w Arduino to nie powinno być problemów. Możesz mieć np. Rabarbarowe Atari, albo C64.

Podobnie można wykorzystać Arduino Leonardo do podłączania prez USB starych kontrolerów gier do wykorzystania w emulatorach.

AMIGA 500/1000/2000 Keyboard Interface na forum Arduino

Arduino Leonardo w sklepie Nettigo

sprae