Nicht blockierendes Menü

OP #2113811
Lesenswert?

Hallo zusammen,
ich suche eine Möglichkeit ein Menü zu schreiben, das beim warten auf 
eine Eingabe den uC nicht komplett blockiert.

Genauer:
Ich arbeite an einer Funkfernbedienung für einen hausbus. Da die 
Fernbedienung gleichzeitig der Master im single Master- Multi Slave 
System ist, muss ich dafür sorgen das andere Prozesse auch aufgerufen 
werden während sich ein Benutzer durch das Menü bewegt.
Ich bin nicht ganz sicher wie ich das am Besten mache.

Ich dachte mir allerdings schon das es evt. möglich sei, das sobald das 
Menu seine punkte ausgegeben hat, wieder in die main loop zu springen, 
und sobald ein neuer Satz Befehle für das menü eingegeben wurde, sollte 
wieder ins menu gesprungen werden. Was ja schon mal bedeutet das ich 
feste punkte für Unterbrechung setzen kann. Das schien mir theoretisch 
bisher am einfachsten, allerdings weiß ich nicht wie man einen solchen 
Sprung realisieren könnte. Wobei das Menu ja seine Arbeit Transparent 
wieder aufnehmen können muss.

Ich wäre für hinweise Bezüglich der Umsetzung sehr dankbar, und auch 
offen für weitere Vorschläge.


Grüße

Felix
Gast #2113834
Lesenswert?

Einfach zu lösen mit einer State-Machine.
ohne den Overhead und die Einarbeitungszeit in ein RTOS ....

Deine Software, und dein Menu genau durchdesignen und dann in einer 
State-Machine abbilden.

innerhalb der States "NIEMALS!" while() oder do{}while() verwenden, 
sonder immer mit Rückgabewerten arbeiten die den State beeinflussen.

BSP:

switch(state)
{
  case state_1 :
  {
    state = functioncall_state_1();
    break;
  }
  case state_2 :
  {
    state = functioncall_state_2();
    break;
  }
  default :
  {
    state = functioncall_state_init() //falls was schief läuft ...
  }
}

gruss SoundSo
#2113838
Lesenswert?

Am einfachsten machst Du es mit einer Statemachine (switch/case).
Die Menüfunktion macht eine Aktion, setzt bei Erfolg den nächsten State 
und geht wieder zum Main.

Ein RTOS klingt erstmal nett, macht aber dafür nen Haufen zusätzlicher 
Probleme.
Aus Funktionssicht wird nämlich alles gleichzeitig ausgeführt, die 
Funktion weiß nicht, wann sie von welcher anderen unterbrochen wird.
Ressourcen (LCD, UART, I2C) können belegt sein, Variablen können sich 
mitten im Zugriff ändern usw.
Ein RTOS erfordert sehr viel Disziplin beim Programmieren. Es macht 
Fehler leicht und verzeiht sie nicht.
Und beim Debuggen mit einem RTOS rauft man sich eh die Haare.


Peter
OP #2113857
Lesenswert?

Wow, das ging ziemlich schnell!
Also an Flags habe ich natürlich auch schon gedacht, nur bin ich nicht 
sicher wie das realisieren soll, da mein Programm ziemlich komplex ist, 
es gibt Profile programmierbare Knöpfe bei den slaves, am master, 
unzählige Sensoren und kurz tausende von Einstellungs Möglichkeiten. 
Programmierbare Displays... Etc... Kurz es ist ziemlich komplex, und 
irgendwie weiß ich nicht wie ich das mit anhand von flags merken soll wo 
genau ich gerade im menü bin. Bis auf ich würde es bei jeder eingabe 
wieder durchrechnen lassen.
An Multitasking habe ich auch schon gedacht, das ist ja eigentlich auch 
mehr oder weniger was ich vor habe, nur halt in einer sehr simplen 
Form... Es soll halt nur an eine stelle springen können und bei bedarf 
wieder zurück... Wie heißt den so ein Sprung blos?
Ein komplettes OS scheint mir bis auf weiteres ein wenig overkill, da 
ich bisher auch alle anderen Aufgaben anders lösen konnte... Aber ich 
werde es mir trotzdem mal ansehen Tobi, da es mich interessiert.
Bevor ihr euch allzu viel mühe mit den antworten gebt, werde ich noch 
einmal probieren ein normales Menü zu programmieren, habe es noch nie 
gemacht, und vielleicht fällt mir ja auch selber etwas ein wenn ich die 
Praxis erst mal soweit drauf habe...

Allso nochmal ein paar Details die vielleicht helfen helfen, ich habe 
eine Zügig durchlaufende main loop, und die Eingabe von zeichen 
geschieht mittels Interrupt.
Im Moment mache ich noch alles über ein selbst implementiertes 
comandline interface, das ist toll und macht spaß... Aber ist wohl nicht 
praxis taglich für eine Fernbedienung :(

Ich schaue mir auch nochmal das multitasking genauer an, im code 
beispielen müssen ja eigentlich irgendwo beispiele für solche sprünge zu 
finden sein.

Grüße und danke

Felix
#2113904
Lesenswert?

Hi,

ich habe sowas mal für das Aquarium meines Sohnes gemacht, eine 
Steuerung die Temperatur regelt (mit einstellbarer Hysterese und 
Nachtabsenkung), zum Füttern die Pumpe abstellt und enstprechend vieler 
Schaltpunkte der Zeitschaltuhr.
In der main-Schleife wird sich der aktuelle Menupunkt gemerkt, der wird 
auch von der Menu-Funktion zurückgegeben, alle anderen zyklischen 
arbeiten werden normal ausgeführt, das Menu blockiert also nicht.

Hauptsächlich läuft das im main so:
1
    // Endlosschleife
2
    while (1)
3
    {
4
        /*
5
        ** Timer handling, as long as there is a tick...
6
        */
7
        while ((millis() - ms_old) > 10)
8
        {
9
            ms_old += 10;
10

11
            /*
12
            ** now call all the regular functionality...
13
            */
14

15
......
16

17
            // check for user interaction...
18
            key = get_key();
19

20
            if (active_menu == NO_MENU)
21
            {
22
                switch (key)
23
                {
24
                    case KEY_FWD:
25
                        menu_timeout = 45;
26
                        active_menu = START_MENU;
27

28
                        // here we enter the menu, so copy all data to
29
                        // menu-space for editing
30
                        menu_print (active_menu);
31
                        break;
32
......
33
                }
34
            }
35
            else
36
            {
37
                if (key == NO_KEY)
38
                {
39
                    if (menu_timeout == 0)
40
                    {
41
                        menu_exit ();
42
                        lcd_clear ();
43
                        active_menu = NO_MENU;
44
                    }
45
                }
46
                else
47
                {
48
                    menu_timeout = 45;
49

50
                    active_menu = menu_action (active_menu, key);
51

52
                    if (active_menu == NO_MENU)
53
                    {
54
                        // here we left the menu, so save all data
55
                        lcd_clear();
56
                        ee_write ((void *)&eedata, (void *)&setup, sizeof (struct eep_setup));
57
                        ee_write ((void *)&eework, (void *)&work,  sizeof (struct eep_work));
58

59
....
60
                    }
61
                    else
62
                        menu_print (active_menu);
63
                }
64
            }
65
        }
66
        sleep_mode();
67
    }
es wird im Prinzip im main gemerkt, in welchem Menupunkt man ist 
(active_menu) und es ist wichtig, ob man in einem Menu ist, denn dann 
darf das main nicht ins Display schreiben...

Das Menusystem selbst kommt immer mit dem aktuellen Menu als 
Rückgabewert zurück, es wird immer aufgerufen mit:
1
  active_menu = menu_action (active_menu, key);
es macht selbst keine Displayausgabe innerhalb dieser Routinen, das 
passiert in
1
  menu_print (active_menu);

Das Menu-Konzept ist ursprünglich basierend auf einem Artikel hier im 
Wiki (find ich jetzt nicht), allerdings stark angepasst.

Wenn Interesse besteht kann ich heute abend mal das Projekt hier 
hochladen, die aktuellen sourcen habe ich momentan nicht hier.


Gruß,
Bernhard
Gast #2113928
Lesenswert?

@Peter:

"Ein RTOS klingt erstmal nett, macht aber dafür nen Haufen zusätzlicher
Probleme.
Aus Funktionssicht wird nämlich alles gleichzeitig ausgeführt, die
Funktion weiß nicht, wann sie von welcher anderen unterbrochen wird."

Im Prinzip ja, aber dafür gibt es die Möglichkeit, das Multitasking zu 
unterbrechen. In Nano_OS z.B. mit TASK_DISABLE_MULTITASKING() und 
TASK_ENABLE_MULTITASKING().

"Ressourcen (LCD, UART, I2C) können belegt sein, Variablen können sich
mitten im Zugriff ändern usw."

Dafür gibt es Semaphore.

"Ein RTOS erfordert sehr viel Disziplin beim Programmieren. Es macht
Fehler leicht und verzeiht sie nicht.
Und beim Debuggen mit einem RTOS rauft man sich eh die Haare."

Das ist bahr. Allerdings hängt es sehr vom RTOS ab, wie sehr man sich 
disziplinieren muss.

gruß

tobi
#2113932
Lesenswert?

Felix H. schrieb:
> es gibt Profile programmierbare Knöpfe bei den slaves, am master,
> unzählige Sensoren und kurz tausende von Einstellungs Möglichkeiten.

Dann ist es umso wichtiger, sich erstmal alle Menüpunkte und 
Einstellungen aufzuschreiben.
Und dann versuchen, das in möglichst allgemeinen Funktionen zu 
beschreiben.
Und nur die Unterschiede in einer Struktur anzulegen, z.B.:
- Menüname
- Parametername, Anzeigeformat, Korrekturfaktor, Adresse, Max-/Minwert
- Aktionen (nur Rücklesen, einstellbar, Einstelldigit).

Eine durchdachte Menüstruktur macht nicht nur das Programmieren 
leichter, sondern auch die Bedienung.


Peter

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren