const array met chars en functiepointers mogelijk als LUT?

Pagina: 1
Acties:

Acties:
  • 0 Henk 'm!

  • timberleek
  • Registratie: Juli 2009
  • Laatst online: 08-09 22:13
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:
Afbeeldingslocatie: https://dl.dropboxusercontent.com/u/3264324/Naamloos.png

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

Acties:
  • 0 Henk 'm!

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 11-10 14:49
Wat ik in een vergelijkbare situatie heb gedaan is een Menu struct definieren, met daarin pointers naar functies voor de draw, keypress etc.

Ergens is er een MenuHandler die bepaald welke functies aangeroepen moeten worden. Er is ook een "Menu* current" die het actieve menu voorstelt. ( Jouw updateMenu dus )

Vanuit de code ( dus ook vanuit een handler in een menu zelf ) kun je SetMenu( id ) aanroepen voor de navigatie.

Alle menu's zijn static geinitialiseerd, waarbij ongebruikte callbacks ( functiepointers dus ) op 0 wordeb gezet. ( De MenuHandler entity checked altijd of een callback valid is )

Wel ff oppassen met SetMenu als je die ook vanuit een interrupt handler gaat aanroepen, dan zal er wat meer locking in moeten.

De onderverdeling in index en subindex is wat mij betreft onnodig, welk probleem probeer je hiermee op te lossen?

[edit]
Oh ja, en je initialisatie van de functiepointers is verkeerd: Je moet niet het adres va de returnvalue van de functie hebben, maar het adres van de functie zelf

[edit]
Wat RoadRunner zegt dus...

[ Voor 10% gewijzigd door farlane op 25-01-2016 14:30 ]

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


Acties:
  • 0 Henk 'm!

Verwijderd

Iets als &RadioMenu(2) zal geïnterpreteerd worden als een aanroep van RadioMenu met parameter 2, en het adres van de returnwaarde wordt gebruikt. Dat is naar mijn vermoeden NIET je bedoeling.

Daarnaast, ik zou twee losse velden van Index en Subindex maken, desnoods via een macro of functie om het aan te passen, zodat het je nog steeds maar 1 byte kost.

Moet er niet een een Exit of Leave referentie zijn, gezien je een submenu in kunt, maar ook uit?

[ Voor 12% gewijzigd door Verwijderd op 25-01-2016 14:28 ]


Acties:
  • 0 Henk 'm!

  • timberleek
  • Registratie: Juli 2009
  • Laatst online: 08-09 22:13
Ik heb de pointers in de struct nu inderdaad aangepast.
Lege velden was hij sowieso niet content mee, daarvoor heb ik nu een lege functie "for the time being"

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
typedef struct 
{
    void *fAllways;
    unsigned char nextIndex;
    unsigned char enterIndex;
    void *fEnter;
    void *fNext;
} Menu_LUT_struct;


const Menu_LUT_struct Menu_LUT[][3] = 
{
    {   //index 0
        //  always execute      if(Next)    if(Enter)   if(Enter)               if(Next)
        {   &InitMenu           ,   0x10    , 0x00      , &emptyFunction        , &emptyFunction    }, //subindex 0
        {   &emptyFunction      ,   0x00    , 0x00      , &emptyFunction        , &emptyFunction    }, //subindex 1
        {   &emptyFunction      ,   0x00    , 0x00      , &emptyFunction        , &emptyFunction    }   //subindex 2
},
...
...
...


Is die de goede manier van definiëren dan?, dit compiled namelijk wel.

Alleen bij het aanroepen van de functies gaat het nog mis.
In mijn UpdateMenu functie heb ik nu het volgende:
code:
1
(Menu_LUT[menuIndex>>4][menuIndex&0x0f].fAllways) ();

Met het idee dat dat de huidige fAllways functie aanroept. Met de bovenstaande LUT en een menuIndex van 0x00 zou ik dan:
code:
1
(Menu_LUT[0][0].fAllways) (); = &InitMenu ();

willen aanroepen.

Maar daar is hij het nog niet mee eens. Met die code krijg ik de error: "found 'pointer to void' expected a function.

Volgens mij zit ik wel in de buurt

Acties:
  • 0 Henk 'm!

Verwijderd

Een pointer naar een functie wordt alsvolgt beschreven
code:
1
void (*fAlways)(void);

Waarbij de eerste void het return type van een functie is en de tweede void de parameterlijst van de aan te roepen functie.
Always is trouwens met een enkele L, tenzij je "alle wegen" in plaats van "altijd" wilt zeggen...
Bij het toekennen van een functiepointer mag je trouwens dde haakjes niet gebruiken, want dan roep je je functie aan!
code:
1
Menu_LUT[0][0].fAlways = &InitMenu;

waarbij de & optioneel is in het geval van het verkrijgen van een functiepointer.

Ik zou trouwens i.p.v. lege waarden of emptyFunction kiezen voor NULL, en bij het aanroepen checken of de waarde NULL is.

[ Voor 9% gewijzigd door Verwijderd op 25-01-2016 15:42 ]


Acties:
  • 0 Henk 'm!

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 11-10 14:49
timberleek schreef op maandag 25 januari 2016 @ 15:21:
Volgens mij zit ik wel in de buurt
Zoek ff op Google naar hoe je functiepointers gebruikt. Ikzelf typedef ze altijd want het wordt al snel onleesbare brei met alle braces en stuff.
Als je ze typedeffed is het eigenlijk best goed te lezen:

C:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
typedef void(*FuncPtr_t)( void );

typedef struct
{
    FuncPtr_t   Draw;
    FuncPtr_t   KeyDown;
}Menu_t;

static void SetMenu( int id );

static void Menu_1_Draw( void ) {}
static void Menu_1_KeyDown( void ) { SetMenu( 1 ); }
static void Menu_2_Draw( void ) {}
static void Menu_2_KeyDown( void ) { SetMenu( 0 ); }
static void Menu_3_Draw( void ) {}

static const
Menu_t Menus[] =
{
    { .Draw = Menu_1_Draw, .KeyDown = Menu_1_KeyDown },
    ...
}

const Menu_t* currentMenu = Menus;

 ... etc ... 

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.

Pagina: 1