de:start
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
de:start [2019/09/01 12:33] – [Problemlos Quellcode aus UML Zustands- und Aktivitätsdiagrammen erzeugen!] pmueller | de:start [2023/09/28 21:56] (current) – [Code aus UML Zustandsdiagrammen und Aktivitätsdiagrammen erzeugen!] webmin | ||
---|---|---|---|
Line 1: | Line 1: | ||
- | {{description> | + | ~~NOTOC~~ |
+ | ====== Code aus UML Zustandsdiagrammen und Aktivitätsdiagrammen erzeugen! ====== | ||
- | ====== Problemlos Quellcode aus UML Zustands- und Aktivitätsdiagrammen erzeugen! ====== | + | ===== Sinelabore ermöglicht es Entwicklern von eingebetteten Systemen, event-basierte Architekturen, |
- | Sinelabore// | + | |
+ | Sinelabore// | ||
==== Wie funktioniert der Generator? ==== | ==== Wie funktioniert der Generator? ==== | ||
Line 12: | Line 14: | ||
Sie werden optimal in den verschiedenen Entwicklungsphasen, | Sie werden optimal in den verschiedenen Entwicklungsphasen, | ||
- | {{:sinelabore_firstpage.png?|Wie funktioniert der Generator}} | + | {{:: |
Funktionen im Überblick: | Funktionen im Überblick: | ||
* Automatisch generierter Code höchster Güte | * Automatisch generierter Code höchster Güte | ||
- | * Erzeugt C, C++, Java, Swift oder C# Code | + | * Erzeugt C, CPP, Java, Swift oder C# Code |
* Automatisierte Robustheitstests auf UML Modellebene | * Automatisierte Robustheitstests auf UML Modellebene | ||
* Keine Laufzeitumgebung notwendig | * Keine Laufzeitumgebung notwendig | ||
Line 34: | Line 36: | ||
+ | ==== Schnelleinstieg ==== | ||
+ | |||
+ | |||
+ | Um einen Eindruck von den Möglichkeiten des Codegenerators zu bekommen, laden sie sich [[wiki: | ||
+ | |||
+ | Sie haben nun folgende Möglichkeiten, | ||
+ | |||
+ | 1) Führen sie die Beispiele auf Ihrem PC aus. Die Beispiele modellieren einen Mikrowellenherd und können ausgeführt und getestet werden. Spielen sie mit dem Modell und verbessern Sie es. Generieren Sie den Code neu und lernen sie aus den Warn- und Fehlermeldungen. | ||
+ | |||
+ | 2) Führen sie die Beispiele auf einem Mikrocontroller aus, z.B. auf einem MSP430-Evaluierungsboard oder unter Arduino. Ein MSP430-Beispiel, | ||
+ | |||
+ | |||
+ | ==== Verwendung von State-Machines in (Low-Power) Embedded Systems ==== | ||
+ | Es gibt verschiedene Möglichkeiten, | ||
+ | |||
+ | === Verwendung von Zustandsautomaten in einer Hauptschleife === | ||
+ | In diesem Design ruft eine Endlosschleife - typischerweise die Hauptfunktion - einen oder mehrere Zustandsautomaten nacheinander auf. | ||
+ | {{: | ||
+ | |||
+ | Die Folgen sind: | ||
+ | * Der gesamte Rahmen-Code muss vom Entwickler bereitgestellt werden. | ||
+ | * Die Hauptschleife muss schnell genug sein, um die geforderte Gesamtreaktionszeit zu erreichen. | ||
+ | * Im Falle von Erweiterungen muss das Timing nochmals sorgfältig überprüft werden. | ||
+ | |||
+ | <hidden Beispiel:> | ||
+ | <code c> | ||
+ | void main(void){ | ||
+ | … | ||
+ | sm_A(); | ||
+ | sm_B(); | ||
+ | … | ||
+ | } | ||
+ | </ | ||
+ | </ | ||
+ | |||
+ | \\ | ||
+ | ==== Verwendung von Zustandsautomaten in einer Hauptschleife mit Event-Queue ==== | ||
+ | Dieser Entwurf ist ähnlich wie der oben vorgestellte. Allerdings erhält der Zustandsautomat seine Ereignisse aus einer Event-Queue. Die Queue wird durch Timer-Ereignisse, | ||
+ | {{ : | ||
+ | Vorteile: | ||
+ | * Ereignisse gehen nicht verloren (Warteschlange) | ||
+ | * Ereignisreihenfolge bleibt erhalten | ||
+ | * Entkopplung der Ereignisverarbeitung von der Ereignisgenerierung. | ||
+ | |||
+ | Konsequenzen: | ||
+ | * Ein minimaler Laufzeitrahmen ist erforderlich: | ||
+ | * Die Hauptschleife muss schnell genug sein, um die erforderliche Gesamtreaktionszeit zu erreichen. | ||
+ | |||
+ | Ein minimales Laufzeit-Framework für C ist hier verfügbar: https:// | ||
+ | |||
+ | Es bietet Zeitgeber und Warteschlangen. Der Verwendungszweck ist wie folgt: | ||
+ | |||
+ | * Jeder Zustandsautomat hat eine eigene Queue | ||
+ | * Schließlich benötigt ein Zustandsautomat einen oder mehrere Timer (einmalig/ | ||
+ | * Ein Zustandsautomat kann so viele Software-Timer wie nötig erstellen. Bei der Erstellung eines Timers muss die Queue des Zustandsautomaten und das Timeout-Ereignis angegeben werden. Für verschiedene Timer ist es sinnvoll, verschiedene Timeout-Ereignisse bereitzustellen. | ||
+ | * Damit der Timer funktioniert, | ||
+ | * Eine tick()-Funktion muss in der Hauptschleife aufgerufen werden, um zu prüfen, ob ein Timer abgelaufen ist. Falls eine Zeitüberschreitung eingetreten ist, wird das entsprechende Ereignis in der Queue des Zustandsautomaten gespeichert. | ||
+ | * Die Hauptschleife muss prüfen, ob für einen Zustandsautomaten Ereignisse in seiner Queue gespeichert sind. Wenn es neue Ereignisse gibt, werden sie aus der Queue geholt und der Zustandsautomat wird mit dem Ereignis aufgerufen. | ||
+ | |||
+ | |||
+ | <hidden Der Beispielcode mit zwei Zustandsautomaten zeigt das allgemeine Prinzip:> | ||
+ | |||
+ | <code c> | ||
+ | // tick irq | ||
+ | void tick(void){ | ||
+ | pulseCnt++; | ||
+ | } | ||
+ | |||
+ | |||
+ | void main(void){ | ||
+ | …. | ||
+ | |||
+ | // create two queues for two state machines and init the timer subsystem | ||
+ | fifoInit(& | ||
+ | fifoInit(& | ||
+ | timerInit(); | ||
+ | | ||
+ | … | ||
+ | | ||
+ | while (1) { | ||
+ | uint8_t evt; | ||
+ | // | ||
+ | // Check if there are new events for the state machine. If yes, | ||
+ | // call state machine with event. | ||
+ | // | ||
+ | bool fifoEmpty = fifoIsEmpty(& | ||
+ | if (!fifoEmpty) { | ||
+ | fifoGet(& | ||
+ | vending_machine(& | ||
+ | } | ||
+ | |||
+ | fifoEmpty = fifoIsEmpty(& | ||
+ | if (!fifoEmpty) { | ||
+ | fifoGet(& | ||
+ | product_store_sm(& | ||
+ | } | ||
+ | |||
+ | // any new timeouts? | ||
+ | | ||
+ | } | ||
+ | </ | ||
+ | </ | ||
+ | |||
+ | \\ | ||
+ | Wie in der Abbildung oben dargestellt, | ||
+ | \\ | ||
+ | <hidden Beispiel:> | ||
+ | <code c> | ||
+ | // add event evErr to a state machine queue. | ||
+ | void ISR_Btn1() { | ||
+ | fifoPut(& | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | \\ | ||
+ | ==== Verwendung von Zustandsautomaten in einer Hauptschleife mit Queue, optimiert für geringen Stromverbrauch ==== | ||
+ | Bei der Entwicklung von Systemen mit geringstem Stromverbrauch besteht ein wichtiges Ziel darin, den Prozessor so lange wie möglich im Energiesparmodus (deep sleep) zu halten und ihn nur aufzuwecken, | ||
+ | \\ | ||
+ | <hidden Ein Skelett für den MSP430 sieht wie folgt aus:> | ||
+ | |||
+ | <code c> | ||
+ | void main(void){ | ||
+ | |||
+ | // init system | ||
+ | |||
+ | while(1) { | ||
+ | |||
+ | // check event queues and run the state machine as shown above | ||
+ | |||
+ | … | ||
+ | | ||
+ | | ||
+ | } | ||
+ | } | ||
+ | |||
+ | |||
+ | // Timer A0 interrupt service routine. If the timer | ||
+ | // function tick() returns true there | ||
+ | // is a timeout and we wakeup the main loop. | ||
+ | #pragma vector=TIMER0_A0_VECTOR | ||
+ | __interrupt void Timer_A0(void) | ||
+ | { | ||
+ | bool retVal=false; | ||
+ | |||
+ | P1OUT |= BIT0; // toggle for debugging | ||
+ | |||
+ | retVal = tick(); | ||
+ | |||
+ | if(retVal){ | ||
+ | // at least one timeout timer fired. | ||
+ | // wake up main loop | ||
+ | bic_SR_register_on_exit(LPM3_bits); | ||
+ | } | ||
+ | P1OUT &= ~BIT0; // toggle for debugging | ||
+ | // no more events must be processed | ||
+ | } | ||
+ | |||
+ | </ | ||
+ | </ | ||
+ | \\ | ||
+ | |||
+ | ==== Verwendung von Zustandsautomaten in Interrupts ==== | ||
+ | Manchmal ist eine zustandsabhängige Interruptbehandlung erforderlich. Dann ist es sinnvoll, den Zustandsautomaten direkt in den Interrupt-Handler einzubetten, | ||
+ | {{ : | ||
+ | Die Verwendung von Zustandsautomaten in einem Interrupt-Handler kann in jedem Systemdesign nützlich sein. | ||
+ | |||
+ | Für die Codegenerierung sind einige Überlegungen notwendig. In der Regel ist es notwendig, Interrupt-Handler mit compilerspezifischen Schlüsselwörtern oder Vektorinformationen usw. auszustatten. Außerdem haben Interrupt-Service-Handler keine Parameter und keinen Rückgabewert. Um diese Anforderungen zu erfüllen, bietet der Sinelabore Codegenerator die Parameter '' | ||
+ | |||
+ | < | ||
+ | |||
+ | <code c> | ||
+ | // generated state machine code for an irq | ||
+ | |||
+ | interrupt (INTERRUPT_VECTOR) IntServiceRoutine(void) | ||
+ | { | ||
+ | /* generated statemachine code goes here */ | ||
+ | } | ||
+ | </ | ||
+ | </ | ||
+ | |||
+ | |||
+ | Um diesen Code zu erzeugen, setzen Sie die Schlüssel/ | ||
+ | |||
+ | < | ||
+ | StateMachineFunctionPrefixCFile=interrupt (INTERRUPT_VECTOR) | ||
+ | HsmFunctionWithInstanceParameters=no | ||
+ | </ | ||
+ | |||
+ | Wenn sich das Präfix der Interruptroutine über mehr als eine Zeile erstrecken muss, kann das Zeichen für den Zeilenumbruch " | ||
+ | |||
+ | < | ||
+ | StateMachineFunctionPrefixCFile=# | ||
+ | </ | ||
+ | |||
+ | Präfixe für den Header und die C-Datei können separat angegeben werden. | ||
+ | |||
+ | ==== Verwendung von Zustandsautomaten mit einem Echtzeitbetriebssystem ==== | ||
+ | Bei diesem Entwurf läuft jeder Zustandsautomat normalerweise im Kontext einer eigenen Task. Der prinzipielle Aufbau ist in der folgenden Abbildung dargestellt. | ||
+ | |||
+ | {{ : | ||
+ | |||
+ | Jede Task führt einen Zustandsautomaten (oft als ' | ||
+ | Dieses Design kann mit jedem Echtzeitbetriebssystem realisiert werden. Lediglich die Mechanismen für den Ereignistransport können sich unterscheiden. | ||
+ | |||
+ | Vorteile: | ||
+ | * Effiziente und gut getestete Laufzeitumgebung, | ||
+ | * Priorisierung von Aufgaben und Scheduling möglich | ||
+ | * Zustandsmaschinen-Verarbeitungszeiten sind voneinander entkoppelt. | ||
+ | |||
+ | Die Folgen: | ||
+ | * Notwendigkeit eines Echtzeitbetriebssystems (Komplexität, | ||
+ | |||
+ | Im How-to-Abschnitt wird ein Beispiel für dieses [[wiki: | ||
+ | |||
+ | <hidden Beispiele für RTEMS> | ||
+ | <code c> | ||
+ | // rtems specific task body | ||
+ | rtems_task oven_task(rtems_task_argument unused) | ||
+ | { | ||
+ | OVEN_INSTANCEDATA_T inst = OVEN_INSTANCEDATA_INIT; | ||
+ | |||
+ | for ( ; ; ) { | ||
+ | // returns if one event was processed | ||
+ | oven(& | ||
+ | } | ||
+ | } | ||
+ | |||
+ | |||
+ | // generated state machine code | ||
+ | extern rtems_id Queue_id; | ||
+ | uint8_t msg=NO_MSG; | ||
+ | size_t received; | ||
+ | rtems_status_code status; | ||
+ | |||
+ | void | ||
+ | |||
+ | OVEN_EV_CONSUMED_FLAG_T evConsumed = 0U; | ||
+ | |||
+ | |||
+ | /*execute entry code of default state once to init machine */ | ||
+ | if (instanceVar-> | ||
+ | ovenOff(); | ||
+ | |||
+ | instanceVar-> | ||
+ | } | ||
+ | |||
+ | /* action code */ | ||
+ | /* wait for message */ | ||
+ | status = rtems_message_queue_receive( | ||
+ | | ||
+ | (void *) &msg, | ||
+ | & | ||
+ | | ||
+ | | ||
+ | ); | ||
+ | if ( status != RTEMS_SUCCESSFUL ) | ||
+ | error_handler(); | ||
+ | }else{ | ||
+ | switch (instanceVar-> | ||
+ | // generated state handling code | ||
+ | … | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | </ | ||
+ | |||
+ | \\ | ||
+ | <hidden Hier ist ein ähnliches Beispiel für das embOS RTOS von Segger.> | ||
+ | |||
+ | |||
+ | <code c> | ||
+ | // state machine instance | ||
+ | SM_INSTANCEDATA_T instanceVar = SM_INSTANCEDATA_INIT; | ||
+ | |||
+ | // Task and queue objects. | ||
+ | static OS_STACKPTR int Stack_TASK_1[128]; | ||
+ | static OS_TASK | ||
+ | static OS_Q MyQueue; | ||
+ | static char MyQBuffer[100]; | ||
+ | |||
+ | OS_TIMER MyTimer; | ||
+ | |||
+ | char txbuf[32] ; | ||
+ | |||
+ | // Routine called from the embOS RTOS to signal | ||
+ | // a timeout. A timeout event is sent to the state | ||
+ | // machine. Multiple timer callback functions might be created if | ||
+ | // several timers are needed at the same time. Each one then fires an own | ||
+ | // event. E.g. ev50ms or ev100ms | ||
+ | static void MyTimerCallback(void) { | ||
+ | |||
+ | uint8_t msg=evtTimeout; | ||
+ | |||
+ | OS_Q_Put(& | ||
+ | } | ||
+ | |||
+ | // Task blocked until a new event is present. The new event is | ||
+ | // then sent to the state machine. | ||
+ | static void TaskRunningStateMachine(void) { | ||
+ | char* pData; | ||
+ | |||
+ | while (1) { | ||
+ | // waiting for new event | ||
+ | volatile int Len = OS_Q_GetPtr(& | ||
+ | volatile char msg = *pData; | ||
+ | |||
+ | sm(& | ||
+ | |||
+ | OS_Q_Purge(& | ||
+ | |||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | </ | ||
+ | |||
+ | \\ | ||
+ | ~~DISCUSSION: | ||
- | :!: Weitere Details finden Sie auf unseren englischen Seiten. |
de/start.1567334039.txt.gz · Last modified: 2019/09/01 12:33 by pmueller