Hallo,
Voor een project heb ik een menu structuur geimplementeerd op een microcontrollertje. Dat was eerst een simpel menutje op basis van wat switchcases. Echter wordt het menu inmiddels aardig ingewikkeld en is de menu structuur rampzalig aan het worden (en groot).
Nu zat ik te denken om het menu te baseren op een lookup table met characters en function pointers. Dit houdt de leesbaarheid hoog en ik kan kwa code af met een rij simpele functies. Alleen heb ik jullie even nodig om te kijken of mijn plan wel kan op deze manier.
Mijn idee was als volgt:

Uitleg:
De menuindex bestaat uit 8 bits, 4 bits index en 4 bits subindex (voor nu iig)
De gebruiker begint gewoon in 0x00 (index en subindex 0) oftewel de eerste regel van de tabel.
Daarbij wordt de firstmenu functie altijd aangeroepen.
Als de gebruiker op de next toets drukt, wordt de index aangepast naar de nieuwe waarde (0x10) en wordt de functie onder fNext aangeroepen (nu leeg).
Hetzelfde geldt voor de enter toets.
De menu functies bestaan dan telkens uit een vaste set aan acties (uit de LUT) en verder is er een groep met functies voor de opbouw van het scherm (firstMenu etc) en uitvoeren van bewerkingen (funcX) om dat te ondersteunen.
Tot zover lijkt het me een mooi plan, lekker overzichtelijk.
Nu de implementatie, daar schort het wat aan mijn kennis.
De menu array is dan multidimentionaal met zowel functiepointers als waardes. Dat lijkt me het makkelijk met een struct. Dat zou dan worden:
De LUT is dan een 2 dimentionaal array van die structs (dimensie 1 is de index, dimensie 2 de subindex):
De subindex diepte is redelijk constant, dus het efficientie verlies door lege structs is imo verwaarloosbaar.
In de verdere code kan ik dan in principe gewoon dit doen:
Dan de vragen:
- Is dit haalbaar in jullie ogen, of gaat het nooit werken?
- Is een struct definieren als menu entry een goed idee, of kan dat beter anders?
- Met de LUT zoals hierboven komen er soms lege functie velden voor. Hoe moet ik deze dan initialiseren? leeg laten? null-achtige entry? of een lege functie definiëren?
- ...
alvast bedankt
timberleek
Voor een project heb ik een menu structuur geimplementeerd op een microcontrollertje. Dat was eerst een simpel menutje op basis van wat switchcases. Echter wordt het menu inmiddels aardig ingewikkeld en is de menu structuur rampzalig aan het worden (en groot).
Nu zat ik te denken om het menu te baseren op een lookup table met characters en function pointers. Dit houdt de leesbaarheid hoog en ik kan kwa code af met een rij simpele functies. Alleen heb ik jullie even nodig om te kijken of mijn plan wel kan op deze manier.
Mijn idee was als volgt:

Uitleg:
De menuindex bestaat uit 8 bits, 4 bits index en 4 bits subindex (voor nu iig)
De gebruiker begint gewoon in 0x00 (index en subindex 0) oftewel de eerste regel van de tabel.
Daarbij wordt de firstmenu functie altijd aangeroepen.
Als de gebruiker op de next toets drukt, wordt de index aangepast naar de nieuwe waarde (0x10) en wordt de functie onder fNext aangeroepen (nu leeg).
Hetzelfde geldt voor de enter toets.
De menu functies bestaan dan telkens uit een vaste set aan acties (uit de LUT) en verder is er een groep met functies voor de opbouw van het scherm (firstMenu etc) en uitvoeren van bewerkingen (funcX) om dat te ondersteunen.
Tot zover lijkt het me een mooi plan, lekker overzichtelijk.
Nu de implementatie, daar schort het wat aan mijn kennis.
De menu array is dan multidimentionaal met zowel functiepointers als waardes. Dat lijkt me het makkelijk met een struct. Dat zou dan worden:
code:
1
2
3
4
5
6
7
8
| typedef struct { void *fAllways; unsigned char nextIndex; unsigned char enterIndex; void *fEnter; void *fNext; } Menu_LUT_struct; |
De LUT is dan een 2 dimentionaal array van die structs (dimensie 1 is de index, dimensie 2 de subindex):
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| const Menu_LUT_struct Menu_LUT[][3] = { { //index 0 // always execute if(Next) if(Enter) if(Enter) if(Next) { &InitMenu(0) , 0x10 , 0x00 , , }, //subindex 0 { , , , , }, //subindex 1 { , , , , } //subindex 2 }, { //index 1 { &BacklightMenu(0) , 0x20 , 0x11 , , }, { &BacklightMenu(1) , 0x20 , 0x11 , &updateIntensity() , }, { &BacklightMenu(2) , 0x20 , 0x11 , &updateTimeout() , } }, { //index 2 { &RadioMenu(0) , 0x20 , 0x11 , , }, { &RadioMenu(1) , 0x20 , 0x11 , &updateIntensity() , }, { &RadioMenu(2) , 0x20 , 0x11 , &updateTimeout() , } } } |
De subindex diepte is redelijk constant, dus het efficientie verlies door lege structs is imo verwaarloosbaar.
In de verdere code kan ik dan in principe gewoon dit doen:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| updateMenu() { Menu_LUT[currentMenu >> 4 ] [currentMenu & 0x0f].fAllways; //execute allways function if(key.Enter) { newMenu = Menu_LUT[currentMenu >> 4 ] [currentMenu & 0x0f].enterIndex; //get next index Menu_LUT[currentMenu >> 4 ] [currentMenu & 0x0f].fEnter; //execute function } if(key.Next) { newMenu = Menu_LUT[currentMenu >> 4 ] [currentMenu & 0x0f].nextIndex; //get next index Menu_LUT[currentMenu >> 4 ] [currentMenu & 0x0f].fNext; // execute function } currentMenu = nextMenu; // update index } |
Dan de vragen:
- Is dit haalbaar in jullie ogen, of gaat het nooit werken?
- Is een struct definieren als menu entry een goed idee, of kan dat beter anders?
- Met de LUT zoals hierboven komen er soms lege functie velden voor. Hoe moet ik deze dan initialiseren? leeg laten? null-achtige entry? of een lege functie definiëren?
- ...
alvast bedankt
timberleek