Arduino UNO i proste menu użytkownika wykonane z wykorzystaniem modułu LCD z magistralą szeregową i2c

Stronę tą wyświetlono już: 618 razy

Same Arduino nie wiele znaczy bez dodatkowych elementów. W tym przypadku postanowiłem stworzyć coś na kształt interfejsu użytkownika w postaci menu wyświetlanego na module LCD wyposażonym w magistralę szeregową i2c, która umożliwia podłączenie i komunikowanie się za pomocą zaledwie czterech przewodów:

  • GND - uziemnienie;
  • VCC - 5+;
  • SDA - linia przesyłanych danych;
  • SDL - linia zegara

Połączenie układu pokazane zostało na poniższym rysunku. Ważne jest, aby zdawać sobie sprawę, że w Arduino UNO piny A4 i A5 mają specjalne przeznaczenie i umożliwiają komunikowanie się z urządzeniami poprzez magistralę szeregową i2c. W tymże układzie w zależności od ustawienia rezystancji rezystora wyświetlana będzie pozycja menu.

Arduino UNO R3 - widok podłączenia modułu LCD z magistralą szeregową i2c oraz rezystora z regulowaną rezystancją
Rys. 1
Arduino UNO R3 - widok podłączenia modułu LCD z magistralą szeregową i2c oraz rezystora z regulowaną rezystancją

Poniżej zamieszczam dokładny opis magistrali szeregowej i2c wraz z modułem LCD. Każda magistrala szeregowa i2c ma przypisany adres, po którym urządzenie komunikuje się z Arduino. Takie rozwiązanie umożliwia podłączenie kilku różnych elementów sterowanych za pomocą magistrali szeregowej i2c pod warunkiem, że każda z nich ma przypisany inny adres. W przypadku poniższego modułu piny A1, A2 i A3 umożliwiają zmianę adresu magistrali. W moim przypadku adres mojej magistrali szeregowej i2c to 0x3F (zapis szesnastkowy), ale zdarzają się też moduły z ustawionym adresem na 0x27.

Rysunek poglądowy modułu LED z magistralą szeregową i2c
Rys. 2
Rysunek poglądowy modułu LED z magistralą szeregową i2c:
  1. piny sterujące i zasilające moduł;
  2. zworki umożliwiające zmianę adresu magistrali szeregowej i2c;
  3. rezystor regulujący kontrast wyświetlacza (jeżeli moduł nie wyświetla danych możliwe, że rezystor ten jest ustawiony w złym położeniu);
  4. zworka włączająca podświetlenie wyświetlacza LCD;
  5. dioda sygnalizująca podłączenie modułu do zasilania;

Jeżeli nie znasz adresu swojej magistrali szeregowej i2c poniższy kod umożliwi wykrycie tego adresu pod warunkiem, że do Arduino podpięte zostanie tylko jedno takie urządzenia.

Listing 1
  1. #include <Wire.h>
  2. void setup()
  3. {
  4. Wire.begin();
  5. Serial.begin(9600);
  6. while (!Serial); // Leonardo: wait for serial monitor
  7. Serial.println("nI2C Scanner");
  8. }
  9. void loop()
  10. {
  11. byte error, address;
  12. int nDevices;
  13. Serial.println("Scanning...");
  14. nDevices = 0;
  15. for(address = 1; address < 127; address++ )
  16. {
  17. // The i2c_scanner uses the return value of
  18. // the Write.endTransmisstion to see if
  19. // a device did acknowledge to the address.
  20. Wire.beginTransmission(address);
  21. error = Wire.endTransmission();
  22. if (error == 0)
  23. {
  24. Serial.print("I2C device found at address 0x");
  25. if (address<16)
  26. Serial.print("0");
  27. Serial.print(address,HEX);
  28. Serial.println(" !");
  29. nDevices++;
  30. }
  31. else if (error==4)
  32. {
  33. Serial.print("Unknown error at address 0x");
  34. if (address<16)
  35. Serial.print("0");
  36. Serial.println(address,HEX);
  37. }
  38. }
  39. if (nDevices == 0)
  40. Serial.println("No I2C devices foundn");
  41. else
  42. Serial.println("donen");
  43. delay(5000); // wait 5 seconds for next scan
  44. }

Zasada działania menu jest prosta, zmiana położenia pokrętła rezystora regulowanego powoduje zmianę odczytanego napięcia na pinie analogowym A0 i w zależności od jego wartości zmienia się pozycja wyświetlanego menu (co można zobaczyć na poniżej zamieszczonym filmie).

Do obsługi wyświetlacza konieczne jest załączenie następujących bibliotek:

Listing 2
  1. #include <Wire.h> // standardowa biblioteka Arduino
  2. #include <LiquidCrystal_I2C.h> // dolaczenie pobranej biblioteki I2C dla LCD

Bibliotekę New LiquidCrystal można pobrać z strony tutaj.

Inicjalizacja obiektu klasy LiquidCrystal_I2C odpowiedzialnego za komunikację z modułem LCD wygląda następująco:

Listing 3
  1. LiquidCrystal_I2C lcd(0x3f, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);

Sama konfiguracja wyświetlacza w funkcji setup wygląda następująco:

Listing 4
  1. lcd.begin(16,2); // liczba znaków w jednej linii, liczba linii tekstu
  2. lcd.setCursor(0,0);
  3. lcd.print("obliczeniowo.com");
  4. lcd.setCursor(0,1);
  5. lcd.print(".pl");
  6. lcd.setCursor(0,0);

Zadeklarowane przeze mnie klasy, które wykorzystuję do obsługi wyświetlania menu to:

  • MenuItem - reprezentująca pojedynczy obiekt pozycji menu;
  • Menu - reprezentująca menu i realizująca zadania związane z przewijaniem pozycji menu;

Klasa MenuItem jest swego rodzaju uproszczoną listą dwukierunkową. Oto deklaracje wyżej wymienionych klas:

Listing 5
  1. class MenuItem{
  2. MenuItem* before;
  3. MenuItem* after;
  4. String text;
  5. public:
  6. MenuItem(String text, MenuItem* before = NULL, MenuItem* after = NULL) : text(text), before(before), after(after){};
  7. ~MenuItem(){
  8. if(after){
  9. delete after;
  10. }
  11. }
  12. void addMenuPos(String text){
  13. if(after){
  14. after->addMenuPos(text);
  15. }else{
  16. after = new MenuItem(text, this);
  17. }
  18. }
  19. byte count(){
  20. if(after){
  21. return 1 + after->count();
  22. }
  23. return 1;
  24. }
  25. String getItemText(byte index){
  26. if(index && after){
  27. return after->getItemText(index - 1);
  28. }
  29. return text;
  30. }
  31. };
  32. class Menu{
  33. MenuItem *items;
  34. byte menuPos;
  35. public:
  36. Menu() : items(NULL), menuPos(0){};
  37. ~Menu() {
  38. delete items;
  39. }
  40. void addMenuPos(String text){
  41. if(items)
  42. items->addMenuPos(text);
  43. else
  44. items = new MenuItem(text);
  45. };
  46. void setMenuPos(byte menuPos, LiquidCrystal_I2C &lcd){
  47. if(this->menuPos != menuPos){
  48. lcd.clear();
  49. lcd.print(items->getItemText(menuPos));
  50. this->menuPos = menuPos;
  51. }
  52. }
  53. void menuLoop(LiquidCrystal_I2C &lcd){
  54. if(items){
  55. byte count = items->count();
  56. int maximum = 240;
  57. int minimum = 0;
  58. int dx = (maximum - minimum) / count;
  59. byte i = 0;
  60. int a0_read = analogRead(A0);
  61. for(int mp = maximum - dx; mp > minimum - dx; mp -= dx){
  62. if(mp <= a0_read){
  63. setMenuPos(i, lcd);
  64. break;
  65. }
  66. i++;
  67. }
  68. }
  69. }
  70. };

Globalnie utworzony obiekt klasy Menu:

Listing 6
  1. Menu menu;

Tworzenie menu w funkcji setup jest banalnie proste:

Listing 7
  1. menu.addMenuPos("Temperatura");
  2. menu.addMenuPos("Cisnienie");
  3. menu.addMenuPos("Naswietlenie");
  4. menu.addMenuPos("Pogoda");
  5. menu.addMenuPos("Czas");
  6. menu.addMenuPos("Napiecie");
  7. menu.addMenuPos("Menu 1");
  8. menu.addMenuPos("Menu 2");
  9. menu.addMenuPos("Menu 3");

Dodatkowo konieczne jest podciągnięcie rezystora pod pin A0:

Listing 8
  1. pinMode(A0, INPUT_PULLUP);

Oraz wywołanie pętli menu w funkcji loop:

Listing 9
  1. menu.menuLoop(lcd);
  2. delay(100);
Strony powiązane
strony powiązane
  1. bitbucket.org/fmalpartida/new-liquidcrystal/downloads/ - biblioteka new-liquidcrystal

Komentarze