• Witaj na Forum Arduino Polska! Zapraszamy do rejestracji!
  • Znajdziesz tutaj wiele informacji na temat hardware / software.
Witaj! Logowanie Rejestracja


Ocena wątku:
  • 0 głosów - średnia: 0
  • 1
  • 2
  • 3
  • 4
  • 5
Problem z wyświetlaczem (chyba)
#1
Witam. Jestem tutaj nowy, z Arduino też zacząłem niedawno i mam pewien problem z kodem. Od rana próbuję się nauczyć robić proste menu na wyświetlaczu LCD 2x16 (LCD-03336) obsługiwane za pomocą trzech przycisków (DOWN, UP, OK), które włącza (albo chciałbym aby włączało  Big Grin ) jedną z 3 diod na określony czas. Posiadam Arduino UNO R3, używam Arduino IDE 1.8.14.0. Kod zapożyczony od użytkownika wojtekizk i trochę przerobiony:


Kod:
#include <LiquidCrystal.h> //Dołączenie bilbioteki
LiquidCrystal lcd(2, 3, 4, 5, 6, 7); //Informacja o podłączeniu nowego wyświetlacza
/*  Proste MENU dla Arduino ...by wojtekizk@warcaby.lh.pl
-------------------------------------------------------------------------------------------
Opis działania:
Za pomocą 3 przycisków: UP, DOWN i OK budujemy dośc spore menu.
W tym przykładzie nasze głowne MENU ma 5 opcji, a każda z opcji ma swoje
SUBMENU. W sumie mamy 31 opcji do wyboru. Naturalnie potem w programie każdy
taki wybór trzeba obsłuzyć np. w konstrukcji switch - case, ale to juz drugorzędna sprawa.
Masz tu przykład jak budować sobie takie nawet spore menu.
Poniższy przykład jest opatrzony dość szczegółowym komentarzem. Dla prezentacji samego
MENU wykorzystujemy tu komunikację po Serialu, ale bez problemu można ten kod
łatwo zaadoptować na wyświetlacz LCD.
Klawiszami UP - DOWN poruszamy się po poszczególnych opcjach, a klawiszem OK
zatwierdzamy wybór. Symbol "<<<-" oznacza powrót do głównego menu.
--------------------------------------------------------------------------------------------
Instrukcja:
1) 3 MIKROSWITCHE podepnij do pinów 2,3,4 Twojego Arduino, drugi biegun do masy.
2) Wgraj ten szkic na swoje Arduino
3) Odpal Serial i patrz co się dzieje jak będziesz bawił się przyciskami :-)
*/
// --- definicje przycisków ------------------------------------
#define UP 17       // przycisk  UP na Pin3
#define DOWN 19     // przycisk  DOWN na pinie 2
#define OK 18       // przycisk  OK na pinie 4
#define LED_1 11
#define LED_2 12
#define LED_3 13
// --- zmienne: ------------------------------------------------
String s[35]={     // tablica wszystkich opcji (tutaj przesadziłem z tym 50, bo jest ich 31)
"LED 1","LED 2","LED 3","RESET","POMOC"                  // poziom 0 MENU GŁÓWNE
"<<<-","1s","2s","3s","4s","5s","6s","7s","8s","9s",    // poziom 1 Submenu dla PLIK
"<<<-","1s","2s","3s","4s","5s","6s","7s","8s","9s",           // poziom 2 Submenu dla EDYCJA
"<<<-","1s","2s","3s","4s","5s","6s","7s","8s","9s",  };                // poziom 3 Submenu dla SZKIC
/*  "<<<-","Archiwum","Port","Plytka","Programator","Wypal",       // poziom 4 Submenu dla NARZEDZIA
"<<<-","Samouczek","Pytanie","O nas"};                         // poziom 5 Submenu dla POMOC              */
int Min[4]={0,5,15,25};   // tablica położeń dla pierwszej opcji dla danego poziomu
int Max[4]={4,14,24,34};   // tablica położeń dla ostatniej opcji dla danego poziomu

boolean czyWlaczony1 = false;
boolean czyWlaczony2 = false;
boolean czyWlaczony3 = false;
int sekundy = 0;
int sek = 0;

volatile int ak;                  // numer aktualnej opcji
volatile int poziom;              // numer aktualnego poziomu menu
int min,max;                      // min i max dla danego poziomu, czyli pierwsza i ostatnia opcja
int pop=-1;                        // zmienna pomocnicza do sprawdzenia czy nastąpiła zmiana w MENU
// ---- funkcja co wybrano ----------------------------------------
int co()            //funkcja zwraca wartość równą indeksowi tablicy s (lub -1)
{
max=Max[poziom];    // określamy granicę przesuwania menu dla klawisza UP
min=Min[poziom];    // określamy granicę przesuwania menu dla klawisza UP
if(digitalRead(DOWN)==LOW){delay(40);if(digitalRead(DOWN)==LOW) {ak--;if(ak<min)ak=min;}}  // jeśli DOWN
if(digitalRead(UP)==LOW){delay(40);if(digitalRead(UP)==LOW) {ak++;if(ak>max)ak=max;}}      // jeśli UP
if(digitalRead(OK)==LOW){delay(40);if(digitalRead(OK)==LOW)                                // jeśli OK
{

 if(poziom ==0 && ak==Min[0]){poziom=1;ak=Min[1]+1;return -1;}            // takie tam sztuczki :-)        
 if(poziom ==0 && ak>Min[0]){poziom=ak+1;ak=Min[poziom]+1;return -1;}
 if(poziom>0 && ak>Min[poziom]){return ak;}
 if(poziom>0 && ak==Min[poziom]){ak=poziom-1;poziom=0;return -1;}
 
}
}
return -1;                                // jak nic nie naciśnięto to jednak coś trza zwrócić   :-)
}
// --- funkcja do prezentacji MENU na Serialu --------------------------
void pokazSerial()
{
lcd.begin(16, 2); //Deklaracja typu
for(int i=Min[poziom];i<=Max[poziom];i++)    // wyświetlam wszystkie aktualne opcje
  {
    if(i==ak) {Serial.print(" [ ");Serial.print(s[i]);Serial.print(" ] ");lcd.clear();lcd.setCursor(11, 1);lcd.print(s[i]);} // jeśli ta opcja jest aktywna
                                                                 // to wyświetam ją w nawiasie kwadratowym
    else Serial.print(s[i]);                                     // jeśli nie to bez nawiasu
    Serial.print(" ");                                           // odstęp
  }
  Serial.println();
  lcd.setCursor(0, 0);
  lcd.print("MENU");                                              // nowa linia
}
// ---------------------------------------------------------------------
void setup()
{
lcd.begin(16, 2); //Deklaracja typu
lcd.setCursor(0, 0);
lcd.print("MENU");
Serial.begin(9600);            // inicjalizacja Seriala
pinMode(DOWN,INPUT_PULLUP);    // konfiguracja pinów dla przycisków
pinMode(UP,INPUT_PULLUP);      // konfiguracja pinów dla przycisków
pinMode(OK,INPUT_PULLUP);      // konfiguracja pinów dla przycisków

pinMode(LED_1,OUTPUT);
pinMode(LED_2,OUTPUT);
pinMode(LED_3,OUTPUT);

ak=0; poziom=0;                // parametry początkowe MENU
max=Max[poziom];               // index dla ost. pozycji menu
min=Min[poziom];               // index dla pierwszej pozycji menu
}
// === neverending story ===============================================
void loop()
{ /*
//  --- dla testów pokazujemy raport na Serialu : ---------------
if(co()!=-1)
{
  Serial.print("Wybrano klawisz OK --- Wykonuje Akcje dla opcji:  ");
  Serial.println(s[ak]);   // wyświetlam komunikat o podjętej akcji
}
//  --- ale  w programie korzystamy z konstrukcji switch - case ----
//  --- de facto to tu trzeba obsłuzyć wszystkie opcje :-)
 */









 
switch(co())
{
/* case 0:      // funkcja obsługi opcji "LED 1"
 if (czyWlaczony1 == false){
   digitalWrite(LED_1, HIGH);
   Serial.print("Dioda 1 wlaczona!\n");
   czyWlaczony1 = true;
   lcd.clear();
   lcd.setCursor(0, 1);
   lcd.print("LED 1 wlaczony");
 }
 else{
   digitalWrite(LED_1, LOW);
   Serial.print("Dioda 1 wylaczona!\n");
   czyWlaczony1 = false;
   lcd.clear();
   lcd.setCursor(0, 1);
   lcd.print("LED 1 wylaczony");
 }
  break;
case 1:     //  funkcja obsługi opcji "LED 2"
 if (czyWlaczony2 == false){
   digitalWrite(LED_2, HIGH);
   Serial.print("Dioda 2 wlaczona!\n");
   czyWlaczony2 = true;
   lcd.clear();
   lcd.setCursor(0, 1);
   lcd.print("LED 2 wlaczony");
 }
 else{
   digitalWrite(LED_2, LOW);
   Serial.print("Dioda 2 wylaczona!\n");
   czyWlaczony2 = false;
   lcd.clear();
   lcd.setCursor(0, 1);
   lcd.print("LED 2 wylaczony");
 }
  break;
case 2:     //  funkcja obsługi opcji "LED 3"
 if (czyWlaczony3 == false){
   digitalWrite(LED_3, HIGH);
   Serial.print("Dioda 3 wlaczona!\n");
   czyWlaczony3 = true;
   lcd.clear();
   lcd.setCursor(0, 1);
   lcd.print("LED 3 wlaczony");
 }
 else{
   digitalWrite(LED_3, LOW);
   Serial.print("Dioda 3 wylaczona!\n");
   czyWlaczony3 = false;
   lcd.clear();
   lcd.setCursor(0, 1);
   lcd.print("LED 3 wylaczony");
 }
  break;   */
case 3:     //  funkcja obsługi opcji "RESET"
 digitalWrite(LED_1, LOW);
 digitalWrite(LED_2, LOW);
 digitalWrite(LED_3, LOW);
 czyWlaczony1 = false;
 czyWlaczony2 = false;
 czyWlaczony3 = false;
 Serial.print("Wylaczona wszystkie diody!\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
 lcd.clear();
 lcd.setCursor(0, 1);
 lcd.print("Diody wylaczone");
 delay(500);
 for(int abc = 1; abc <= 10; abc++){
   digitalWrite(LED_1, HIGH);
   delay(50);
   digitalWrite(LED_1, LOW);
   digitalWrite(LED_2, HIGH);
   delay(50);
   digitalWrite(LED_2, LOW);
   digitalWrite(LED_3, HIGH);
   delay(50);
   digitalWrite(LED_3, LOW);
 }
 for(int petla = 0; petla <=15; petla++){
   lcd.setCursor(petla, 0);
   lcd.print(".");
   delay(30);
 }
 for(int petla = 0; petla <=15; petla++){
   lcd.setCursor(petla, 1);
   lcd.print(".");
   delay(30);
 }
 for(int petla = 0; petla <=15; petla++){
   lcd.setCursor(petla, 0);
   lcd.print(" ");
   delay(30);
 }
 for(int petla = 0; petla <=15; petla++){
   lcd.setCursor(petla, 1);
   lcd.print(" ");
   delay(30);
 }
 ak = 0;
 lcd.setCursor(0, 0);
 lcd.print("MENU");
  break;
case 4:     //  funkcja obsługi opcji "POMOC"
 Serial.print("Wybierz diode i nacisnij srodkowy przycisk\n");
 lcd.clear();
 lcd.setCursor(0, 1);
 lcd.print("Wybierz diode");
 for(int abc = 1; abc <= 10; abc++){
   digitalWrite(LED_1, HIGH);
   delay(50);
   digitalWrite(LED_1, LOW);
   digitalWrite(LED_2, HIGH);
   delay(50);
   digitalWrite(LED_2, LOW);
   digitalWrite(LED_3, HIGH);
   delay(50);
   digitalWrite(LED_3, LOW);
 }
 if (czyWlaczony1 == true){digitalWrite(LED_1, HIGH);}
 if (czyWlaczony2 == true){digitalWrite(LED_2, HIGH);}
 if (czyWlaczony3 == true){digitalWrite(LED_3, HIGH);}
  break;
case 6:
case 7:
case 8:
case 9:
case 10:
case 11:
case 12:
case 13:
case 14:
 sekundy = (ak - 5) * 1000;
 sek = sekundy / 1000;
 digitalWrite(LED_1, HIGH);
 Serial.print("Dioda 1 wlaczona!\n");
 lcd.clear();
 lcd.setCursor(0, 0);
 lcd.print("LED 1 wlaczony");
 lcd.setCursor(0, 1);lcd.print("na ");lcd.setCursor(3, 1);lcd.print(sek);lcd.setCursor(4, 1);lcd.print(" s");
 delay(sekundy);
 digitalWrite(LED_1, LOW);
break;
case 16:
case 17:
case 18:
case 19:
case 20:
case 21:
case 22:
case 23:
case 24:
 sekundy = (ak - 15) * 1000;
 sek = sekundy / 1000;
 digitalWrite(LED_2, HIGH);
 Serial.print("Dioda 2 wlaczona!\n");
 lcd.clear();
 lcd.setCursor(0, 0);
 lcd.print("LED 2 wlaczony");
 lcd.setCursor(0, 1);lcd.print("na ");lcd.setCursor(3, 1);lcd.print(sek);lcd.setCursor(4, 1);lcd.print(" s");
 delay(sekundy);
 digitalWrite(LED_2, LOW);
break;
case 26:
case 27:
case 28:
case 29:
case 30:
case 31:
case 32:
case 33:
case 34:
 sekundy = (ak - 25) * 1000;
 sek = sekundy / 1000;
 digitalWrite(LED_3, HIGH);
 Serial.print("Dioda 3 wlaczona!\n");
 lcd.clear();
 lcd.setCursor(0, 0);
 lcd.print("LED 3 wlaczony");
 lcd.setCursor(0, 1);lcd.print("na ");lcd.setCursor(3, 1);lcd.print(sek);lcd.setCursor(4, 1);lcd.print(" s");
 delay(sekundy);
 digitalWrite(LED_3, LOW);
break;
 
// itd .... pozostałe funkcje obsługi .........  
}
















if(pop!=ak) {pokazSerial();pop=ak;}  // aby nie śmiecić na serialu, wyświetlam tylko zmiany :-)
delay(120);                          // aby był czas na oderwanie palucha od przycisku :-)
                    // ten delay dobrać doświadczalnie, bo osoby z Parkinsonem mają refleks :-)
}
// === KONIEC ===========================================================


Problem polega na tym, że podczas próby wgrania kodu na płytkę Arduino IDE pokazuje mi błąd:

Niewykorzystane: C:\Program Files\WindowsApps\ArduinoLLC.ArduinoIDE_1.8.14.0_x86__mdqgnx93n4wtt\libraries\LiquidCrystal
exit status 1
Błąd kompilacji dla płytki Arduino/Genuino Uno.

Z góry dzięki za pomoc  Wink

@EDIT:
Dodam jeszcze, że wcześniej miałem tylko jeden poziom menu i wszystko śmigało jak należy. Problem zaczął się pojawiać gdy chciałem dodać kolejne poziomy w celu wyboru czasu, przez który dioda ma świecić.

@EDIT2:
Przy okazji jeśli ktoś połasiłby się odpowiedzieć na kilka pytań to byłbym bardzo wdzięczny.
1. Czy jest jakieś wygodniejsze rozwiązanie od tego przedstawionego w kodzie, używając switch'a, na wykonanie akcji jeśli zmienna przyjmuje jakąś wartość z przedziału (np. x > 5)?
2. Czy to działanie ma prawo działać? Zależy mi na kolejności wykonania działań.
    sekundy = (ak - 15) * 1000;
3. Czy zmienna volatile zachowuje swoją wartość jeśli używam jej w różnych funkcjach?
4. Czy coś takiego https://botland.com.pl/moduly-bluetooth/...33v5v.html wystarczy aby sterować, załóżmy diodami, z poziomu aplikacji w smartfonie?
 
Odpowiedź
#2
Taki komunikat zazwyczaj jest jak nie można użyć UNO do danego szkicu.
 
Odpowiedź
#3
Czyli UNO nie obsługuje czegoś co umieściłem w kodzie? Dobrze rozumiem?
 
Odpowiedź
#4
Podany kod kompiluje się , to jest cała treść błędu ?

"Czy to działanie ma prawo działać? Zależy mi na kolejności wykonania działań.
sekundy = (ak - 15) * 1000; "
Będzie działać tak jak uczyli na matematyce .
 
Odpowiedź
#5
To nie jest cały kod błędu, po prostu nie masz ustawionego pokazywania pełnych komunikatów. W menu FILE -> PREFERENCES ptaszki przy COMPILATION i ewentualnie przy UPLOAD (czy tam odpowiednio w PL). Jeśli wcześniej było OK, a teraz po dodaniu tych samych elementów w większej ilości już nie jest to najprawdopodobniej chodzi o flash/RAM. Dowiesz się po włączeniu komunikatów.
 
Odpowiedź
#6
Niestety pełnego komunikatu już nie wkleję, bo naprawiłem kod (nie mam pojęcia co było nie tak i jak to naprawiłem  Sad tu coś wyciąłem, tam coś wpisałem i działa  Big Grin  ). Mógłby ktoś bardziej doświadczony ode mnie wypowiedzieć się na temat modułu bluetooth, do którego link zamieściłem wyżej? Kończą mi się możliwości zabawy z podstawowym zestawem i chcę sobie kilka rzeczy zamówić ale nie wiem czy sam moduł wystarczy aby to opanować, a jeśli zabraknie jakiejś błahostki za 1-2 zł to zapewne wysyłka przekroczy jej wartość 10-krotnie i chciałbym tego uniknąć. Dziękuję wszystkim za pomoc.

@EDIT:
Chętnie dałbym koledze wyżej okejkę ale nie działa  Sad .
 
Odpowiedź
#7
Lepiej kup sobie ten: https://botland.com.pl/moduly-bluetooth/...hc-05.html , HM-10 sprawi Ci więcej problemów na początku. HC-05 jest wykrywany przez mój laptop, HM-10 też, ale ciągle błąd parowania, a mam dwie wersje z różnymi softami, stary telefon łączy się tylko z HC-05, HM-10 wymaga specjalnego softu nawet z telefonem z BT 4.X - BLETerminal i nie jest wykrywany przez telefon. Soft go wykrywa, paruje i dalej już działa jak zwykły HC-05 (w obrębie tego programu). A z PC działa mi najlepiej gdy drugi moduł HM-10 podłącze przez UART do PC i komunikacja jest między dwoma takimi. Wtedy ma z kolei więcej możliwości. Jest też możliwość wgrania softu alternatywnego. Niby 4.0 jest energooszczędny, ale tego nie stwierdziłem.
HC-05 po prostym triku może też wgrywać bezprzewodowo szkice, opisałem to gdzieś tu na forum. Tu z też soft można zmienić tak by był wykrywany jako HID.
Edit:
Volatile służy do wyłączenia buforowania zmiennej. Normalnie często używana zmienna jest kopiowana z ram, gdzie zwykle sobie zmienne siedzą pod swoimi adresami, do rejestru (taka szczególna komórka pamięci) i tu jest odczytywana/zapisywana, jest pod ręką i dostęp do niej jest szybszy. Po skończeniu zabawy z nią jej wartość jest zwracana z powrotem na jej miejsce, nadpisując poprzednią wartość. Ale jeśli zmienną wykorzystujemy w przerwaniu to wtedy takie ulepszenie zaszkodzi. Jedna funkcja działająca normalnie w pętli zmieni wartość zmiennej w buforze, a druga wywołana w przerwaniu o tym wiedzieć nie będzie i zmieni wartość w RAM, funkcja kończąc pracę nadpisze zmienną inną wartością w RAM. Z volatile każda funkcja sięga do oryginalnej zmiennej, jeśli np. w przerwaniu zwiększysz zmienną o 1 i funkcji w pętli loop zwiększysz o 1 to zmienna zostanie prawidłowo zwiększona o 2. Bez volatile funkcja z loop potraktuje zmienną z RAM, bez sprawdzania czy jej wartość jest taka jak w momencie brania do bufora, jako starą i zapisze "swoją" nową wartością.
Natomiast każda zmienna globalna, czyli taka zdefiniowana przed setup, jest dostępna dla każdej funkcji w programie. Jest cały czas, cały czas zajmuje miejsce w RAM, należy więc z rozwagą je używać.
Przerwań u Ciebie nie widzę, więc i sensu też. Przerwanie może być np. od timera, od przycisku. Program zostaje natychmiast przerwany, wywołana funkcja zdefiniowana dla przerwania, po jej zakończeniu praca w loop jest wznawiana w miejscu przerwania. Wszystko po to by nie przegapić czegoś ważnego dla działania programu.
A jak program działa to się pochwal działającym, ktoś kiedyś zajrzy i skorzysta.
 
Odpowiedź
#8
Nie działa , najpierw wykona się nawias i wynik zostanie pomnożony przez tysiąc i zapisany do sekundy no chyba że https://www.arduino.cc/reference/en/lang.../volatile/ volatile int ak;
 
Odpowiedź
#9
(30-08-2018, 00:20)kaczakat napisał(a): Natomiast każda zmienna globalna, czyli taka zdefiniowana przed setup, jest dostępna dla każdej funkcji w programie. Jest cały czas, cały czas zajmuje miejsce w RAM, należy więc z rozwagą je używać.

Ja proponuję, aby od początku zabawy z programowaniem µC, starać się nie używać zmiennych globalnych.
Przekazywać wartości przez funkcje. Używać zmiennych z modyfikatorem static. Jeśli chcemy pracować na zmiennej z poza funkcji, powinno się przekazać wskaźnik zmiennej jako argument. Wtedy taka funkcja modyfikuje zmienną spoza funkcji bez użycia return, więc może być void , a to jest bardzo istotne w rozbudowanych systemach. A przecież każdy, kto zaczyna przygodę z jakimkolwiek µC, marzy o tym, aby w przyszłości tworzyć skomplikowane systemy sterowania i regulacji. Niech więc uczy się dobrych nawyków.
Edit.
Napisałem o przekazywaniu wartości przez funkcje, a potem żeby przekazywać wskaźniki jako argumenty.
Wiem, że jedno kłóci się z drugim, ale w pierwszym przypadku miałem na myśli obiekty zwracające zmienne publiczne.
W Arduino jest na tyle dziwnie, że mamy taki język pomiędzy Objective C, a C++ i obydwa skundlone, że lepiej pisać w C i omijać te ograniczenia wielkim łukiem.
Myślę, że każdy poważnie myślący o projektowaniu systemów wbudowanych, szybko ucieknie od Arduino w kierunku czegokolwiek innego.
Jeśli masz problem z kodem lub sprzętem, zadaj pytanie na forum. Nie odpowiadam na PW, jeśli nie dotyczą one spraw forum lub innych tematów prywatnych.

[Obrazek: SsIndaG.jpg]
 
Odpowiedź
#10
(30-08-2018, 22:27)Robson Kerman napisał(a):
(30-08-2018, 00:20)kaczakat napisał(a): Natomiast każda zmienna globalna, czyli taka zdefiniowana przed setup, jest dostępna dla każdej funkcji w programie. Jest cały czas, cały czas zajmuje miejsce w RAM, należy więc z rozwagą je używać.

Ja proponuję, aby od początku zabawy z programowaniem µC, starać się nie używać zmiennych globalnych.
Przekazywać wartości przez funkcje. Używać zmiennych z modyfikatorem static.
Rozsądne postępowanie zwłaszcza gdy dysponujemy 2k RAM a w funkcjach będą używane tablice. Deklaracja tablicy z modyfikatorem static pozwoli zapanować na zużyciem RAM. Takie postępowanie jest dobre dla początkujących, bo zaawansowany programista jest świadom zagrożeń jakie niesie używanie zmiennych nie static, które bardziej optymalnie gospodaruą pamięcią RAM, której w AVR jest bardzo mało (pomijając Mega1284 ewentualnie Mega1281. 2561 i podobne).

(30-08-2018, 22:27)Robson Kerman napisał(a): Myślę, że każdy poważnie myślący o projektowaniu systemów wbudowanych, szybko ucieknie od Arduino w kierunku czegokolwiek innego.
Od Arduino tak ale od AVR niekoniecznie. Porównując jednak ceny AVR vs ARM. ARM wygrywają a dodatkowo są bogato wyposażone. AVR przy ARM to Trabant vs Ferrari. ale Ferrari tańsze niz Trabant.
 
Odpowiedź
  


Skocz do:


Przeglądający: 1 gości