Forum: Mikrocontroller und Digitale Elektronik Nicht blockierendes Menü


von Felix H. (masterq)


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

von Klaus F. (kfalser)


Lesenswert?

Suche nach Multitasking und z.B. FreeRTOS

von Random .. (thorstendb) Benutzerseite


Lesenswert?

Hi,

(RT)OS / Scheduler:
Das einfachste, da mehrere while(1) { ... }

Flags, pollen / ISR:
Menuanzeige aktualisieren, mit Flags letzten Zustand merken und gleich 
wieder nach main() oder so zurück. Dort die Eingabe mit pollen oder per 
ISR.

von hacker-tobi (Gast)


Lesenswert?

Da kannst du auch mein OS nano OS verwenden, das hier im Forum 
weiterentwickelt wird und ebenfalls multitaskingfähig ist.

Thread dazu

Beitrag "NeuesOS für AVR Mikrocontroller"

Code auf Sourceforge

http://sourceforge.net/projects/nanoos/

gruß

tobi

von SoundSo (Gast)


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

von Peter D. (peda)


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

von Felix H. (masterq)


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

von Falk B. (falk)


Lesenswert?

Siehe Multitasking

von Bernhard M. (boregard)


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

von hacker-tobi (Gast)


Lesenswert?

Hi,

Ladyada z.B. implementiert bei deren IceTubeClock auch ein Menü, welches 
aber z.B. per Interrupt unterbrochen werden kann, und selbbsttätig nach 
einer gewissen Zeit zurückspringt.

Ich habe den code vor ewigkeiten mal etwas modifiziert. Zu finden hier

Beitrag "Alternative Firmware für LadyAda IceCubeClock"

von hacker-tobi (Gast)


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

von Peter D. (peda)


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

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.