Der große Vorteil der modellbasierten Entwicklung mit MATLAB/Simulink ist die relativ einfache Implementierung von komplexen Modellen. Mit dem Zusatzpaket Stateflow ist es zudem möglich Zustandsautomaten anschaulich darzustellen, zu testen und mit dem RealTimeWorkshop (jetzt Coder) daraus direkt C-Code zu erzeugen, welcher auf einem Embedded Device lauffähig ist.
Am Beispiel des oben abgebildeten einfachen Stateflow Modells soll dies im Folgenden für einen Atmega128 Mikrocontroller erläutert werden.
Datentypen für Variablen definieren
Das große Kunststück beim kompilieren von C-Code für embedded Systems ist die korrekte Zuweisung von Datentypen zu den Variablen, damit es sowohl in der Simulation als auch auf dem Mikrocontroller keinen Überlauf gibt. Die Möglichkeiten sind vielfältig, hier soll nur eine Variante beschrieben werden. Um 2000×3000 rechnen zu können, muss mindestens ein int32 als Datentyp deklariert werden, damit es bei der Multiplikation nicht zu Fehlern kommt.
Wird die Eingangsvariable calc auf 1 gesetzt, so multipliziert das Stateflow Modell 2000 mit 3000 und schreibt das Ergebnis auf die out Variable.
Theoretisch kein Problem. Die Simulation läuft sauber durch, alles in bester Ordnung. Kompiliert man diesen Code und führt ihn auf einem Atmega128 aus, so wird dieser die Berechnung nicht durchführen können.
Embedded Hardware korrekt einstellen
Der entscheidende Fakt ist, dass MATLAB bzw. der RealTimeWorkshop wissen muss, für welche Hardware er den C-Code kompilieren soll. Dazu ist für den Atmega128 die ert.tlc eine gute Wahl. Detailliertere Informationen zum kompilieren kann in [Netland – Developing Embedded Control System Platform using Atmel AVR32 Processor] nachgeschlagen werden.
Standardmäßig ist die Embedded Hardware auf „Unspecified (assume 32-bit Generic)“ eingestellt. Darin ist für den Datentyp int ein int32 vorgesehen, wie in nachfolgender Abbildung zu sehen ist.
Das führt dazu, dass der RealTimeWorkshop denkt, dass eine als int32 deklarierte Variable von der Hardware als int32 angenommen wird. Im kompilierten C-Code ist in der typdef.h somit zu lesen:
[c] /*=======================================================================*
* Target hardware information
* Device type: 32-bit Generic
* Number of bits: char: 8 short: 16 int: 32
* long: 32 native word size: 32
* Byte ordering: Unspecified
* Signed integer division rounds to: Undefined
* Shift right on a signed integer as arithmetic shift: on
*=======================================================================*/
/* This ID is used to detect inclusion of an incompatible rtwtypes.h */
#define RTWTYPES_ID_C08S16I32L32N32F1
/*=======================================================================*
* Fixed width word size data types: *
* int8_T, int16_T, int32_T – signed 8, 16, or 32 bit integers *
* uint8_T, uint16_T, uint32_T – unsigned 8, 16, or 32 bit integers *
* real32_T, real64_T – 32 and 64 bit floating point numbers *
*=======================================================================*/
typedef signed char int8_T;
typedef unsigned char uint8_T;
typedef short int16_T;
typedef unsigned short uint16_T;
typedef int int32_T;
typedef unsigned int uint32_T;
typedef float real32_T;
typedef double real64_T;[/c]
In Zeile 25 wird der RealTimeWorkshop eigene Datentype int32_T mit int definiert. Der Datentyp int ist auf der Zielhardware, nämlich dem Atmega128, aber ein int16.
Teilt man dies dem RealTimeWorkshop mit, so definiert er die Variablen auch korrekt.
Die Zielhardware muss korrekt definiert werden. Hier wird mitgeteilt, dass int auf der Zielhardware nur int16 ist, der RealTimeWorkshop somit eine int32 anders definieren muss.
Der damit kompilierte C-Code ist folgender:
[c] /*=======================================================================*
* Target hardware information
* Device type: Specified
* Number of bits: char: 8 short: 16 int: 16
* long: 32 native word size: 32
* Byte ordering: Unspecified
* Signed integer division rounds to: Zero
* Shift right on a signed integer as arithmetic shift: on
*=======================================================================*/
/* This ID is used to detect inclusion of an incompatible rtwtypes.h */
#define RTWTYPES_ID_C08S16I16L32N32F1
/*=======================================================================*
* Fixed width word size data types: *
* int8_T, int16_T, int32_T – signed 8, 16, or 32 bit integers *
* uint8_T, uint16_T, uint32_T – unsigned 8, 16, or 32 bit integers *
* real32_T, real64_T – 32 and 64 bit floating point numbers *
*=======================================================================*/
typedef signed char int8_T;
typedef unsigned char uint8_T;
typedef int int16_T;
typedef unsigned int uint16_T;
typedef long int32_T;
typedef unsigned long uint32_T;
typedef float real32_T;
typedef double real64_T;
[/c]
Die int32_T Variable wurde auf den Datentyp long deklariert. Damit funktioniert die Berechnung sowohl in der Simulation, als auch auf dem Atmega128.