sâmbătă, 17 martie 2012

Meniu cu LCD si PIC18F4550 Microchip

Articolul in limba romana - mai jos, dupa scurta explicatie in limba engleza  

*******
Short intro English

Here you can find a method to implement a menu on an LCD display driven by a microcontroller (Arduino board, PIC microcontroller etc).
Idea is simple: you have the top menus, under a top menu you have sub-menus (parent menu with children menus), sub-menus can have sub-sub-menus and so on. I advise to put you menu structure on a paper to understand the exact structure of the menus, sub-menus, sub-sub-menus. It will look like an oriented graph.
I put the menu structure on a matrix, here is an example:

**1, 8, 11, 1, 0, 0
-this is element 1
-is a parent menu
-this one has sub-menus starting from element 8 ending at element 11

**14, 100, 100, 2, 0, 0
-this is element 14
-is a sub-menu
-has no sub-menus


On the bottom of this post you can find the source code commented on English, the variable names are still in Romanian.


*******
O parte dintre aplicatiile cu microcontroler au nevoie de o metoda de interactiune cu utilizatorul, aici ma refer la ceea ce vrea sa ne spuna echipamentul: fie simple LEDuri, fie ecrane cu cristale lichide (LCD). Aceste LCD-uri sunt fie LCDuri grafice (o matrice de puncte) ori sunt LCDuri cu rinduri de caractere.

LCDurile cu rinduri cu caractere pot fi de 2 rinduri x 16 caractere (2 x 16), 2 x 20, 4 x 16, 4 x 20 etc. Toate aceste LCDuri functioneaza dupa acelasi principiu: au 8 linii de date (D0..D7), au linii de control (RS, RW, E). Pe aceste linii se dau datele care sunt ulterior interpretate de un chipset (de obicei compatibil Hitachi HD4470).

Ecran cu cristale lichide (Liquid Crystals Display - LCD) 
cu 2 rinduri de cate 16 caractere si lumina de fundal albastra



Datele trimise catre LCD sunt traduse in caractere pe ecran (un caracter este format din puncte: 5 puncte x 8 puncte). Se pot crea si caractere speciale definite de utilizator.

Asadar pentru afisare trebuie sa specificam rindul, numarul caracterului din rind si ce vrem sa afisam. Este o solutie convenabila de afisare, dar fiindca se utilizeaza un microcotroler, uneori un LCD da bataie de cap.

Problema mea a fost sa realizez o aplicatie cu microcontroler, butoane si LCD 2x16 si care sa ofere un meniu de selectie al optiunilor. Am inceput sa sap pe Internet in ideea ca altii inaintea mea au avut aceeasi problema si deci solutia e simpla si rafinata deja, nu are sens sa inventez roata. Totusi nu este chiar asa, caci la microcontrolere solutiile sunt oarecum specifice aplicatiei date. Am gasit o prezentare facuta de un domn care a realizat un meniu foarte complex, avea vreo 70 de elemente, elemente care se activau daca in prealabil activai un meniu sau daca erai in modul de service, avea meniu in mai multe limbi straine - prea complex pentru mine si pentru aplicatia mea. In fine, am ajuns sa creez un mic Frankenstein, dar functioneaza bine :)

Placa de dezvoltare cu LCD si PIC18F4550
Schema electrica - cu mentiunea ca push button-anele sunt 
legateastfel: RB0, RB1, RB4 si RB5


Asadar pentru simplitate sa consideram ca avem 5 meniuri principale: Meniu1 .. Meniu5, iar primele 3 meniuri (Meniu 1 .. Meniu 3) au sub-meniuri:

Structura meniului

Pentru a defini aceste elemente de meniu am ales sa utilizez o matrice, fiecare linie defineste un element de meniu astfel:

Descrierea unui element din meniu

In aceasta matrice, primele 5 linii sunt pentru elementele de meniu, apoi urmatoarele linii reprezinta sub-meniurile. In cazul nostru avem 5 elemente de meniu si 11 elemente de sub-meniu, deci avem 5+11 = 16 linii in matrice ori 16 elemente de meniu.

Explicatii:
**nr element meniu: in cazul nostru nr element meniu este intre 0 si 15 (caci avem 16 elemente de meniu)
**primul sub-element: daca elementul are sub-elemente, indica numarul primului element; daca valoarea este 100, atunci nu are sub-elemente 
**ultimul sub-element: daca elementul are sub-elemente, indica numarul ultimului element; daca valoarea este 100, atunci nu are sub-elemente
**nivel: nivelul din care provine elementul; aici este 1 pt nivelul meniu si 2 pentru nivelul sub-meniu
**folosinta ulterioara: ultimile pozitii sunt pentru aplicatii ulterioara (meniu activ, meniu activ doar in mod service etc)

Exemplu:
**1, 8, 11, 1, 0, 0
este elementul 1 de meniu si are sub-meniuri care incep de la elementul 8 si pana la elementul 11

**14, 100, 100, 2, 0, 0
este elementul 14, este sub-meniu, nu mai are sub-elemente

Acum urmeaza sa vedem cum parcurgem aceste elemente de meniu: fie folosim 3 butoane (sus, jos, intra meniu) si un element de meniu care sa ne intoarca de unde am pornit, fie folosim 4 butoane (sus, jos, intra meniu, iesire meniu). Din generozitate am folosit 4 butoane :)


Partea cu butoanele: 3 pe placa si unul la rotary encoder (se apasa axul)

Acum problema se rezuma la a parcurge matricea din imagine si de a afisa textul corespunzator pe cele 2 linii ale ecranului. Mai e un amanunt: avem si un cursor care poate fi ori pe linia 1 ori pe linia 2 a ecranului LCD.

Din start folosim ceea ce stim: avem meniuri de la elementele 0 la 4 (5 meniuri), avem sub-meniuri de la elementele 5 la 15. Daca alegem sa intram intr-un meniu, trebuie sa salvam undeva contextul:  meniul din care am venit, starea ecranului LCD (ce era pe rindul 1 si 2 si unde era cursorul). Acest lucruri fac metoda scalabila la un numar mare de meniuri si sub-meniuri, inclusiv sub-sub-meniuri. Totul depinde de capacitatile microcontrolerului, memoria este limitata.

Iata ce a iesit (cod mikroC pentru PIC18F4550):

1:  /*LCD menu on LCD 2 rows by 16 characters  
2:  buttons are on: RD7, RD4, RD3 si RD2, when press, logical 1 on pin  
3:  rotary encoder on: RD5, RD6  
4:  LCD lines: RB2..RB7  
5:  LED: RC0, RD0, RD1, to turn on a LED, set logical 0   
6:  modified on 2013.02.10   
7:  */  
8:    
9:  int nr_element, temp_int, rindLCD, MsgLCD1, MsgLCD2;  
10:  int start_element, stop_element, element_activ, meniu_parinte;  
11:    
12:  // define LCD lines  
13:  sbit LCD_RS at RB2_bit;  
14:  sbit LCD_EN at RB3_bit;  
15:  sbit LCD_D4 at RB4_bit;  
16:  sbit LCD_D5 at RB5_bit;  
17:  sbit LCD_D6 at RB6_bit;  
18:  sbit LCD_D7 at RB7_bit;  
19:    
20:  sbit LCD_RS_Direction at TRISB2_bit;  
21:  sbit LCD_EN_Direction at TRISB3_bit;  
22:  sbit LCD_D4_Direction at TRISB4_bit;  
23:  sbit LCD_D5_Direction at TRISB5_bit;  
24:  sbit LCD_D6_Direction at TRISB6_bit;  
25:  sbit LCD_D7_Direction at TRISB7_bit;  
26:  // end LCD lines definitions  
27:    
28:  int m_Meniu[16][6] = {{0,5,7,1,0,0},  //menu element Men1, this one has 3 sub-menus (3 children)  
29:  {1,8,11,1,0,0},             //element meniu Men2 are 4 copii sub meniu  
30:  {2,12,15,1,0,0},             //element meniu Men3 are 4 copii sub meniu  
31:  {3,100,100,1,0,0},           //element meniu Men4 nu are copii sub meniu  
32:  {4,100,100,1,0,0},           //element meniu Men5 nu are copii sub meniu  
33:  {5,100,100,2,0,0},           //sub menu 1 from Men1  
34:  {6,100,100,2,0,0},           //sub meniu 2 de la Men1  
35:  {7,100,100,2,0,0},           //sub meniu 3 de la Men1 ***  
36:  {8,100,100,2,0,0},           //sub meniu 1 de la Men2  
37:  {9,100,100,2,0,0},           //sub meniu 2 de la Men2  
38:  {10,100,100,2,0,0},           //sub meniu 3 de la Men2  
39:  {11,100,100,2,0},           //sub meniu 4 de la Men2 ***  
40:  {12,100,100,2,0,0},           //sub meniu 1 de la Men3  
41:  {13,100,100,2,0,0},           //sub meniu 2 de la Men3  
42:  {14,100,100,2,0,0},           //sub meniu 3 de la Men4  
43:  {15,100,100,2,0,0}};          //sub meniu 4 de la Men5  
44:    
45:  // {nr element meniu} {start }  
46:    
47:  //sub-menus have no sub-sub menus  
48:  //100, 100 menas that there are no sub-sub menus  
49:    
50:  int m_Nivel[8] = {0, 0, 0, 0, 0, 0, 0, 0}; //this is to save the context before going down on the menu  
51:    
52:  char *Mesaj_LCD[16] = {"Men1     ",  
53:  "Men2     ",  
54:  "Men3     ",  
55:  "Men4     ",  
56:  "Men5     ", //element 4  
57:  "SubMen1_1  ",  
58:  "SubMen1_2  ",  
59:  "SubMen1_3  ", //el 7  
60:  "SubMen2_1  ",  
61:  "SubMen2_2  ",  
62:  "SubMen2_3  ",  
63:  "SubMen2_4  ", //11  
64:  "SubMen3_1  ",  
65:  "SubMen3_2  ",  
66:  "SubMen3_3  ",  
67:  "SubMen3_4  "}; //el 15  Mesaj_LCD[0][15]  
68:    
69:    
70:  void main() {  
71:    
72:    ADCON1 |= 0x0F;   // Configure AN pins as digital  
73:    CMCON |= 7;     // Disable comparators  
74:    
75:    TRISD0_bit = 0;      // set direction to be output for LED RD0  
76:    RD0_bit = 1;       // Turn OFF LED on PORTD0  
77:      
78:    TRISD2_bit = 1;      // set direction to be input button RD2  
79:    TRISD3_bit = 1;      // set direction to be input button RD3  
80:    TRISD4_bit = 1;      // set direction to be input button RD4  
81:    TRISD7_bit = 1;      // set direction to be input button RD7  
82:    
83:    
84:    nr_element=0; //row index  
85:    element_activ = 0;  
86:  //*******************************************  
87:  //here it was pressed the MENU button  
88:  //I press the button for the menu, I go down on that menu and I have on LCD on the first row menu number 1 (default [0][5])  
89:  //now I wait to press up, down, menu in, menu out  
90:    rindLCD = 1; // this is to know the exact row we are, we can be on row 1 or row 2  
91:            //this will be used to know where to set the LCD display cursor (which menu / message on the LCD display is selected)  
92:            //by default, the LCD cursor is on row 1, on menu 1  
93:    start_element = m_Meniu[nr_element][0];  
94:    stop_element = m_Meniu[4][0];  
95:    
96:    MsgLCD1 = m_Meniu[nr_element][0];   //message on row 1 on LCD, default is menu 1 - this is telling me what to display on row 1 on the LCD display  
97:    MsgLCD2 = m_Meniu[nr_element + 1][0]; //message on row 2 on LCD, default is menu 2 (next under menu is showing me what to display on row 2 on the LCD display  
98:    
99:    Lcd_Init();               // Initialize LCD  
100:    Lcd_Cmd(_LCD_CLEAR);           // Clear display  
101:    Lcd_Cmd(_LCD_CURSOR_OFF);        // Cursor off  
102:    
103:  //here starts the menu sequence; I poll which button was pressed and I took a certain action  
104:    do{  
105:      
106:    ///*************************************  
107:    if(Button(&PORTD, 2, 50, 1)){ //if <down> button was pressed, I have to go to the next menu  
108:      
109:    if(rindLCD<2){    //I check which row is the cursor on the LCD display - can be 1 or 2  
110:    rindLCD++;      //if I am not on row 2, I go on row (got to row 2)  
111:    element_activ++;  
112:    //I check where is the position on the sub-menu / menu   
113:    if(element_activ > stop_element){element_activ = start_element;}  
114:    nr_element++;  
115:    if(nr_element > stop_element){nr_element = start_element;}  
116:    }  
117:    else{  
118:    rindLCD = 2;    //if cursor is on LCD row number 2, then stay there  
119:    element_activ++;  
120:    nr_element++;  
121:      
122:    if(nr_element>stop_element){  
123:    element_activ = m_Meniu[start_element][0];  
124:    nr_element = m_Meniu[start_element][0];  
125:    MsgLCD1 = MsgLCD2;  
126:    MsgLCD2 = nr_element;  
127:    }  
128:    else{  
129:    MsgLCD1 = MsgLCD2;  
130:    MsgLCD2 = nr_element;  
131:    }  
132:    }  
133:    }  
134:      
135:   //******************************************  
136:    if(Button(&PORTD, 4, 50, 1)){ //if button <up> is pressed, then go to the previous menu  
137:    if(rindLCD>1){ //check which row is the cursor on the LCD display   
138:    rindLCD--;  
139:    element_activ--;  
140:    if(element_activ < start_element){element_activ = stop_element;}  
141:    nr_element--;  
142:    if(nr_element < start_element){nr_element = stop_element;}  
143:    }  
144:    
145:    else{  
146:    rindLCD = 1;  
147:    element_activ--;  
148:    nr_element--;  
149:    
150:    if(nr_element<start_element){  
151:    element_activ = m_Meniu[stop_element][0];  
152:    nr_element = m_Meniu[stop_element][0];  
153:    MsgLCD2 = MsgLCD1;  
154:    MsgLCD1 = m_Meniu[nr_element][0];}  
155:    else{  
156:    MsgLCD2 = MsgLCD1;  
157:    MsgLCD1 = m_Meniu[nr_element][0];}  
158:    }  
159:    }  
160:      
161:    
162:  //*****************************************************  
163:    if(Button(&PORTD, 7, 50, 1)){ //if button <enter> RD7 was pressed   
164:    if(m_Meniu[nr_element][1]!=100){ //check if the menu has sub-menus  
165:    //if yes, I have to save the context  
166:    m_Nivel[0] = nr_element; //this is the parent menu for the sub-menu  
167:    m_Nivel[1] = rindLCD; //row on LCD for the parent menu  
168:    
169:    m_Nivel[2] = MsgLCD1; //message on row 1 on LCD display parent menu  
170:    m_Nivel[3] = MsgLCD2; //message on row 2 on LCD display parent menu  
171:      
172:    m_Nivel[4] = m_Meniu[nr_element][3]; //level of parent element from menu element matrix (1 means menu)  
173:    m_Nivel[5] = m_Meniu[0][0]; //here is the start element of parent menu  
174:  //end of context saving  
175:         
176:    rindLCD = 1;  
177:    
178:    //here I load the sub-menu (child menu) of the parent menu  
179:    start_element = m_Meniu[nr_element][1];  
180:    stop_element = m_Meniu[nr_element][2];  
181:    
182:    temp_int = m_Meniu[start_element][3];//here is the current child level (2 means sub-menu)  
183:    m_Nivel[6] = temp_int;  
184:    
185:    nr_element = start_element;  
186:    
187:    MsgLCD1 = m_Meniu[start_element][0];  
188:    MsgLCD2 = m_Meniu[start_element+1][0];}  
189:    }  
190:    
191:   // *****************************  
192:   //here we pressed the exit menu   
193:    if(Button(&PORTD, 3, 50, 1)){ // if button <out> RD3 was pressed  
194:    
195:    if(m_Nivel[4]==0){ //check if parent menu is a menu  
196:    //if it is a menu, then I write on LCD that there are no other upper menus  
197:    Lcd_Out(1, 1, "There are no");  
198:    Lcd_Out(2, 1, "other menus !");  
199:    Delay_ms(1500);  
200:    Lcd_Cmd(_LCD_CLEAR);}  
201:    
202:    else{ //if we are on a sub-menu and need to go up one level,  
203:    //I have to load the context I was before entering that menu  
204:    nr_element = m_Nivel[0];  
205:    m_Nivel[0] = 0;  
206:    
207:    rindLCD = m_Nivel[1];  
208:    m_Nivel[1] = 0;  
209:    
210:    MsgLCD1 = m_Nivel[2];  
211:    m_Nivel[2] = 0;  
212:    
213:    MsgLCD2 = m_Nivel[3];  
214:    m_Nivel[3] = 0;  
215:    
216:    meniu_parinte = m_Nivel[4];  
217:    m_Nivel[4] = 0;  
218:    start_element = m_Nivel[5]; //here is the start level of the parent menu  
219:    stop_element = 4; //here is the stop level of the parent menu  
220:    }  
221:    }  
222:   //*****************************************************************  
223:     
224:   //here is where the elements are displayed on the LCD display and the active element (cursor)  
225:    if(rindLCD == 1){Lcd_Out(2, 1, " ");  
226:    Lcd_Out(1, 1, ">");} //if active row is 1, then display character ">" on row 1  
227:    if(rindLCD == 2){Lcd_Out(1, 1, " ");  
228:    Lcd_Out(2, 1, ">");} //if active row is 2, then display character ">" on row 2  
229:    
230:    Lcd_Out(1, 2, Mesaj_LCD[MsgLCD1]);  //write correct message on row 1  
231:    Delay_ms(50);  
232:    Lcd_Out(2, 2, Mesaj_LCD[MsgLCD2]);  //write correct message on row 2  
233:    Delay_ms(50);  
234:    
235:    
236:    switch(element_activ){  
237:    case 0: {; break;} //perform action X  
238:    case 1: break; //perform action Y  
239:    case 2: break; //perform action Z  
240:    case 3: break; //perform action aX  
241:    case 4: break;  
242:    case 5: break;  
243:    case 6: break;  
244:    case 7: break;  
245:    case 8: break;  
246:    case 9: break;  
247:    case 10: break;  
248:    case 11: break;  
249:    case 12: break;  
250:    case 13: break;  
251:    case 14: break;  
252:    case 15: break;}  
253:    
254:    }while(1);  
255:  }  

Aici este un scurt clip video despre acest meniu in actiune:



PS: in programare primul element este considerat cel cu numarul 0, spre exemplu numarul maxim de locatii pe 8 biti este de 256 de locatii, insa prima locatie este locatia 0, iar ultima locatie este locatia 255, deci nu este de la 1 la 256, ci de la 0 la 255.
Alt exemplu: 8 biti inseamna bitii 0, 1, 2, 3, 4, 5, 6, 7. Asadar mare atentie la acest amanunt caci poate da mari batai de cap, codul sa fie perfect, dar sa nu de-a rezultatul asteptat.

*******************
Source code (mikroC) with comments on English

/*LCD menu on LCD 2 rows by 16 characters
buttons are on: RD7, RD4, RD3 si RD2, when press, logical 1 on pin
rotary encoder on: RD5, RD6
LCD lines: RB2..RB7
LED: RC0, RD0, RD1, to turn on a LED, set logical 0
modified on 2013.02.10
*/

int nr_element, temp_int, rindLCD, MsgLCD1, MsgLCD2;
int start_element, stop_element, element_activ, meniu_parinte;

// define LCD lines
sbit LCD_RS at RB2_bit;
sbit LCD_EN at RB3_bit;
sbit LCD_D4 at RB4_bit;
sbit LCD_D5 at RB5_bit;
sbit LCD_D6 at RB6_bit;
sbit LCD_D7 at RB7_bit;

sbit LCD_RS_Direction at TRISB2_bit;
sbit LCD_EN_Direction at TRISB3_bit;
sbit LCD_D4_Direction at TRISB4_bit;
sbit LCD_D5_Direction at TRISB5_bit;
sbit LCD_D6_Direction at TRISB6_bit;
sbit LCD_D7_Direction at TRISB7_bit;
// end LCD lines definitions

int m_Meniu[16][6] = {{0,5,7,1,0,0},    //menu element Men1, this one has 3 sub-menus (3 children)
{1,8,11,1,0,0},                          //element meniu Men2  are 4 copii sub meniu
{2,12,15,1,0,0},                         //element meniu Men3  are 4 copii sub meniu
{3,100,100,1,0,0},                      //element meniu Men4  nu are copii sub meniu
{4,100,100,1,0,0},                      //element meniu Men5  nu are copii sub meniu
{5,100,100,2,0,0},                      //sub menu 1  from Men1
{6,100,100,2,0,0},                      //sub meniu 2 de la Men1
{7,100,100,2,0,0},                      //sub meniu 3 de la Men1  ***
{8,100,100,2,0,0},                      //sub meniu 1 de la Men2
{9,100,100,2,0,0},                      //sub meniu 2 de la Men2
{10,100,100,2,0,0},                     //sub meniu 3 de la Men2
{11,100,100,2,0},                     //sub meniu 4 de la Men2  ***
{12,100,100,2,0,0},                     //sub meniu 1 de la Men3
{13,100,100,2,0,0},                     //sub meniu 2 de la Men3
{14,100,100,2,0,0},                     //sub meniu 3 de la Men4
{15,100,100,2,0,0}};                    //sub meniu 4 de la Men5

// {nr element meniu} {start }

//sub-menus have no sub-sub menus
//100, 100 menas that there are no sub-sub menus

int m_Nivel[8] = {0, 0, 0, 0, 0, 0, 0, 0};  //this is to save the context before going down on the menu

char *Mesaj_LCD[16] = {"Men1         ",
"Men2         ",
"Men3         ",
"Men4         ",
"Men5         ", //element 4
"SubMen1_1    ",
"SubMen1_2    ",
"SubMen1_3    ", //el 7
"SubMen2_1    ",
"SubMen2_2    ",
"SubMen2_3    ",
"SubMen2_4    ", //11
"SubMen3_1    ",
"SubMen3_2    ",
"SubMen3_3    ",
"SubMen3_4    "}; //el 15   Mesaj_LCD[0][15]


void main() {

    ADCON1 |= 0x0F;      // Configure AN pins as digital
    CMCON  |= 7;         // Disable comparators

    TRISD0_bit = 0;           // set direction to be output for LED RD0
    RD0_bit = 1;              // Turn OFF LED on PORTD0
   
    TRISD2_bit = 1;           // set direction to be input button RD2
    TRISD3_bit = 1;           // set direction to be input button RD3
    TRISD4_bit = 1;           // set direction to be input button RD4
    TRISD7_bit = 1;           // set direction to be input button RD7


    nr_element=0; //row index
    element_activ = 0;
//*******************************************
//here it was pressed the MENU button
//I press the button for the menu, I go down on that menu and I have on LCD on the first row menu number 1 (default [0][5])
//now I wait to press up, down, menu in, menu out
    rindLCD = 1; // this is to know the exact row we are, we can be on row 1 or row 2
                   //this will be used to know where to set the LCD display cursor (which menu / message on the LCD display is selected)
                   //by default, the LCD cursor is on row 1, on menu 1
    start_element = m_Meniu[nr_element][0];
    stop_element = m_Meniu[4][0];

    MsgLCD1 = m_Meniu[nr_element][0];      //message on row 1 on LCD, default is menu 1 - this is telling me what to display on row 1 on the LCD display
    MsgLCD2 = m_Meniu[nr_element + 1][0];  //message on row 2 on LCD, default is menu 2 (next under menu is showing me what to display on row 2 on the LCD display

    Lcd_Init();                              // Initialize LCD
    Lcd_Cmd(_LCD_CLEAR);                     // Clear display
    Lcd_Cmd(_LCD_CURSOR_OFF);                // Cursor off

//here starts the menu sequence; I poll which button was pressed and I took a certain action
    do{
   
    ///*************************************
    if(Button(&PORTD, 2, 50, 1)){ //if button was pressed, I have to go to the next menu
   
    if(rindLCD<2 -="" 1="" 2="" be="" br="" can="" check="" cursor="" display="" is="" lcd="" nbsp="" on="" or="" row="" the="" which="">    rindLCD++;           //if I am not on row 2, I go on row (got to row 2)
    element_activ++;
    //I check where is the position on the sub-menu / menu
    if(element_activ > stop_element){element_activ = start_element;}
    nr_element++;
    if(nr_element > stop_element){nr_element = start_element;}
    }
    else{
    rindLCD = 2;        //if cursor is on LCD row number 2, then stay there
    element_activ++;
    nr_element++;
   
    if(nr_element>stop_element){
    element_activ = m_Meniu[start_element][0];
    nr_element = m_Meniu[start_element][0];
    MsgLCD1 = MsgLCD2;
    MsgLCD2 = nr_element;
    }
    else{
    MsgLCD1 = MsgLCD2;
    MsgLCD2 = nr_element;
    }
    }
    }
   
  //******************************************
    if(Button(&PORTD, 4, 50, 1)){ //if button is pressed, then go to the previous menu
    if(rindLCD>1){  //check which row is the cursor on the LCD display
    rindLCD--;
    element_activ--;
    if(element_activ < start_element){element_activ = stop_element;}
    nr_element--;
    if(nr_element < start_element){nr_element = stop_element;}
    }

    else{
    rindLCD = 1;
    element_activ--;
    nr_element--;

    if(nr_element    element_activ = m_Meniu[stop_element][0];
    nr_element = m_Meniu[stop_element][0];
    MsgLCD2 = MsgLCD1;
    MsgLCD1 = m_Meniu[nr_element][0];}
    else{
    MsgLCD2 = MsgLCD1;
    MsgLCD1 = m_Meniu[nr_element][0];}
    }
    }
   

//*****************************************************
    if(Button(&PORTD, 7, 50, 1)){ //if button RD7 was pressed
    if(m_Meniu[nr_element][1]!=100){ //check if the menu has sub-menus
    //if yes, I have to save the context
    m_Nivel[0] = nr_element; //this is the parent menu for the sub-menu
    m_Nivel[1] = rindLCD;  //row on LCD for the parent menu

    m_Nivel[2] = MsgLCD1;  //message on row 1 on LCD display parent menu
    m_Nivel[3] = MsgLCD2;  //message on row 2 on LCD display parent menu
   
    m_Nivel[4] = m_Meniu[nr_element][3];  //level of parent element from menu element matrix (1 means menu)
    m_Nivel[5] = m_Meniu[0][0]; //here is the start element of parent menu
//end of context saving
   
    rindLCD = 1;

    //here I load the sub-menu (child menu) of the parent menu
    start_element = m_Meniu[nr_element][1];
    stop_element = m_Meniu[nr_element][2];

    temp_int = m_Meniu[start_element][3];//here is the current child level (2 means sub-menu)
    m_Nivel[6] = temp_int;

    nr_element = start_element;

    MsgLCD1 = m_Meniu[start_element][0];
    MsgLCD2 = m_Meniu[start_element+1][0];}
    }

 // *****************************
 //here we pressed the exit menu
    if(Button(&PORTD, 3, 50, 1)){  // if button RD3 was pressed

    if(m_Nivel[4]==0){ //check if parent menu is a menu
    //if it is a menu, then I write on LCD that there are no other upper menus
    Lcd_Out(1, 1, "There are no");
    Lcd_Out(2, 1, "other menus !");
    Delay_ms(1500);
    Lcd_Cmd(_LCD_CLEAR);}

    else{ //if we are on a sub-menu and need to go up one level,
    //I have to load the context I was before entering that menu
    nr_element = m_Nivel[0];
    m_Nivel[0] = 0;

    rindLCD = m_Nivel[1];
    m_Nivel[1] = 0;

    MsgLCD1 = m_Nivel[2];
    m_Nivel[2] = 0;

    MsgLCD2 = m_Nivel[3];
    m_Nivel[3] = 0;

    meniu_parinte = m_Nivel[4];
    m_Nivel[4] = 0;
    start_element = m_Nivel[5]; //here is the start level of the parent menu
    stop_element = 4;  //here is the stop level of the parent menu
    }
    }
  //*****************************************************************
 
  //here is where the elements are displayed on the LCD display and the active element (cursor)
    if(rindLCD == 1){Lcd_Out(2, 1, " ");
    Lcd_Out(1, 1, ">");} //if active row is 1, then display character ">" on row 1
    if(rindLCD == 2){Lcd_Out(1, 1, " ");
    Lcd_Out(2, 1, ">");} //if active row is 2, then display character ">" on row 2

    Lcd_Out(1, 2, Mesaj_LCD[MsgLCD1]);    //write correct message on row 1
    Delay_ms(50);
    Lcd_Out(2, 2, Mesaj_LCD[MsgLCD2]);    //write correct message on row 2
    Delay_ms(50);


    switch(element_activ){
    case 0:  {; break;}  //perform action X
    case 1:  break;  //perform action Y
    case 2:  break;  //perform action Z
    case 3:  break;  //perform action aX
    case 4:  break;
    case 5:  break;
    case 6:  break;
    case 7:  break;
    case 8:  break;
    case 9:  break;
    case 10: break;
    case 11: break;
    case 12: break;
    case 13: break;
    case 14: break;
    case 15: break;}

    }while(1);
}

14 comentarii:

  1. Multumesc. Excelenta metoda pentru tratarea meniurilor.

    RăspundețiȘtergere
  2. salut, ai cam facut risipa de "biscuite" :)

    RăspundețiȘtergere
  3. if(nr_element element_activ = m_Meniu[stop_element][0]; <--error?

    RăspundețiȘtergere
    Răspunsuri
    1. Fixed, thanks! (strange that copy paste was not working as expected)

      Ștergere
  4. if(Button(&PORTB, 4, 50, 1)){ //daca am apasat butonul "in sus", trebuie sa //merg la meniul anterior
    ...
    ...
    ..
    if(nr_element>stop_element){ <---------------------aceasta ar trebui să repara eroarea
    element_activ = m_Meniu[start_element][0];



    RăspundețiȘtergere
    Răspunsuri
    1. You can ask in English as well, it will be more helpful to understand your advice, thanks!

      Ștergere
    2. There seems to be an error with the UP button.

      if(Button(&PORTB, 4, 50, 1)){ //daca am apasat butonul "in sus", trebuie sa //merg la meniul anterior
      if(rindLCD>1){ //verificam rindul pe ecran
      rindLCD--;
      element_activ--;
      if(element_activ < start_element){element_activ = stop_element;}
      nr_element--;
      if(nr_element < start_element){nr_element = stop_element;}
      }

      else{
      rindLCD = 1;
      element_activ--;
      nr_element--;

      something wrong here? ----------> if(nr_element element_activ = m_Meniu[stop_element][0]{
      nr_element = m_Meniu[stop_element][0];
      MsgLCD2 = MsgLCD1;
      MsgLCD1 = m_Meniu[nr_element][0];}
      else{
      MsgLCD2 = MsgLCD1;
      MsgLCD1 = m_Meniu[nr_element][0];}
      }
      }

      also:

      {11,100,100,2,0}, //sub meniu 4 de la Men2 *** <---- missing an extra '0' ?

      Ștergere
    3. It can be, I will look for it, thanks for pointing it out! On the other hand I think that I must change the formatting for the source code to something much more readable. This source code was copy paste from microC IDE, but there are missing things and I don;t know why, compilation of the original was giving a good result and the menu was operational.

      Ștergere
    4. Catalin, is there a way to contact you by email without using this comments section?

      Ștergere
    5. Sure, try this one: simion dot catalin at gmail dot com

      Ștergere
  5. odată ce este selectat un sub_menu , cum se spune pentru a rula un program ? ( Am traducerea această întrebare din engleză în română )

    RăspundețiȘtergere
    Răspunsuri
    1. You can ask in English as well, it will be more helpful to understand your question, thanks!

      Ștergere
  6. Could you please provide the code for reading the encoder?
    Did you think at using the encoder instead of push button when jumping between menus?

    RăspundețiȘtergere
    Răspunsuri
    1. I didn't implement yet a rotary encoder, in my application I don't use the encoder, I mean not the rotary part of it. I use only the push button from the rotary encoder. But you can find implementations on the WWW, use Google..

      Ștergere