How To Integrate Embedded Wizard Applications Into Your Platform

HT-005-Platform Integration
Embedded Wizard How-To Document
How To Integrate Embedded Wizard Applications Into Your
Platform
HowTo-No.:
EmWi Version:
Doc. Version:
Date:
Authors:
005
6.10
6.0
21th November 2011
Steffen Hermann,
Mario Stefanutti,
Manfred Schweyer
Keywords:
Platform Integration, Initialization, Application Loop, Core::KeyEvent,
Core::CursorEvent, Event Dispatcher, Message Handling, Event Handling,
Events, Timers, Signals, Memory Management, Garbage Collection, Mouse,
Touch Panel, Guestures, Error Handling, Fatal Error, EwPanic, Endless Loop
Introduction
Embedded Wizard (EmWi) is a platform independent solution for developing and
prototyping of state of the art graphical user interfaces (GUI) on embedded systems. It
can be used to solve almost any 2D GUI task - moreover, with its lightweight 3D
functionality the GUI appearance can be enriched significantly.
This how-to document describes the principles of the integration of an Embedded
Wizard generated GUI application into a certain hardware platform and the
communication between the GUI application and the main application. Please note, that
this document describes the integration of all Embedded Wizard applications using the
class library Mosaic 2.0 and using a Platform Package based on Graphics Engine 2.0.
Within Embedded Wizard a complete GUI application is created in a platform
independent manner – by using the platform independent programming language
Chora. The platform dependence is realized and covered by so-called Platform
Packages. These packages can be considered as a bridge between the platform
independent GUI application and the particular target system. The following figure
demonstrates this approach. The components of the Platform Package are shown here
as gray blocks:
www.embedded-wizard.de
Copyright TARA Systems GmbH
Page 1
HT-005-Platform Integration
Each Platform Package consists therefore of following main components:
Code Generator
It evaluates the entire GUI application and translates it into the
ANSI C code optimized for the particular target C compiler. This generated code can
be compiled for the target system.
Bitmap Converter
It adapts all bitmap resources (image files used in the project)
to the graphical capabilities of the target system. This conversion can include a color
reduction, a dithering and a compression. Finally the converter generates ANSI C
code, which can be compiled for the target system.
Depending on the type of the converter, the generated C code can contain the pixel
data of the bitmap or only a simple reference to an external image file, which can be
loaded at the runtime by an external image loader.
Font Converter
It adapts all font resources (TrueType fonts used in the project) to
the graphical capabilities of the target system. This conversion can include a quality
reduction and a compression. Finally the converter generates ANSI C code, which
can be compiled for the target system.
Depending on the type of the converter, the generated C code can contain the pixel
data of all affected font glyphs or a simple reference to an external TrueType file,
which can be loaded at the runtime by an external TrueType font engine.
Graphics Engine
It provides the GUI application with the common graphical
functionality, like the drawing of lines, filling of rectangles, copying of bitmaps, 3D
projection and text output.
www.embedded-wizard.de
Copyright TARA Systems GmbH
Page 2
HT-005-Platform Integration
Unlike the converters described above, this functionality is needed at the runtime in
the target system. Thus, the Graphics Engine is delivered in C code, which can be
compiled and linked together with the remaining generated C code files.
Runtime Environment
It provides the GUI application with the common runtime
functionality, like the memory allocation, the time evaluation or mathematical
operations.
Unlike the converters described above, this functionality is needed at the runtime in
the target system. Thus, the Runtime Environment is delivered in C code, which can
be compiled and linked together with the remaining generated C code files.
All Platform Package components described above are developed, maintained and
distributed by TARA Systems. Depending on your target system, its graphical
capabilities, the used color format, CPU architecture or the used C compiler TARA
Systems will provide you with optimal matched combination of these components.
For example, the ready-to-use OpenGL ES 2.0 Platform Package makes it possible to
run the GUI applications on OpenGL ES 2.0 capable target system. In contrast the
DirectFB Platform Package is optimized for the Linux DirectFB API.
Installation and Preparation Work
First of all, we have to ensure that all necessary Embedded Wizard components are
available and up-to-date.
Please follow these steps:
• Download the latest version of Embedded Wizard from our Embedded Wizard
download center and execute the setup (if not already done...).
• Install the Platform Package setup for the desired target platform. The version of
the Platform Package has to correspond to the version of Embedded Wizard.
• Ensure that your Embedded Wizard license is activated for generating code for
your target platform. In case you received an activation file from TARA Systems,
please follow these steps in order to activate your Embedded Wizard dongle:
1.) Save the provided *.rfp on your harddisk.
2.) Start the program activate.exe, which you will find in your Embedded Wizard
installation directory.
3.) Plug-in your dongle on your PC and load the *.rfp file into the activate.exe
program and upgrade the dongle.
Generating Code for Your Platform
The following explanations and intended to describe the principles of generating code of
a sample application for your target platform. The concrete implementation might differ
from target to target, but the principles are the same for every platform.
Please follow these steps:
• Start the Embedded Wizard application.
• Open one of the installed examples from the directory \Examples_Mosaic20 (e.g.
AnimationEffects).
• Create a new profile brick within your project and rename it to the name of your
target e.g. 'PlatformXYZ'
www.embedded-wizard.de
Copyright TARA Systems GmbH
Page 3
HT-005-Platform Integration
•
Select the new profile, set the attribute 'PlatformPackage' to the value of the
installed Platform Package e.g. 'PlatformXYZ.Generic.RGBA8888' (you can
easily select it from the drop-down list within the Inspector window) and change
the attribute 'ScreenSize' to the size which corresponds to the size of the display
used within your target system.
•
Select the new profile 'PlatformXYZ' within the profile selection box (the combobox between Composer window and menu bar).
•
Now you can start the Prototyper (Ctrl+F5) to run the application as simulation.
Please note, that if the ScreenSize of your display does not correspond to the
www.embedded-wizard.de
Copyright TARA Systems GmbH
Page 4
HT-005-Platform Integration
•
•
size of the example, only parts may be visible (in case your display is smaller) or
only parts of your display are covered by the application (in case your display is
larger). But we can ignore this for the first bring-up.
Now you can generate the code for your target: To start the code generator,
select the menu items 'Build' - 'Build this profile'.
Within the subdirectory \Examples_Mosaic20\AnimationEffects\PlatformXYZ you
will find a set of header files and 'C' source files. This is the generated code of
the GUI application.
Integrating the Code into Your Makefile
The following explanations are intended to describe the integration in general rather
than referring to a specific hardware platform. The concrete integration into your build
environment depends on your toolchain and might differ from target to target.
After the code generation, the resulting C files of the GUI application are now compiled
together with the source code of Runtime Environment and Graphics Engine for your
hardware platform by using a target compiler.
Please follow these steps:
• Copy all files from the directory \Platforms\PlatformXYZ\Generic\RGBA8888\GFX
into your build system into the subdirectory /GFX. These are the files of the
Graphics Engine. Add all C-files into your makefile:
# compile all files of the Graphics Engine (GFX)
EMWI_GFX_C =
ewextbmp_RGBA8888.c
ewextfnt.c
ewextgfx.c
ewextpxl_RGBA8888.c
ewgfx.c
ewgfxattrtext.c
ewgfxcore.c
ewgfxdriver.c
ewgfxtasks.c
•
Copy all files from the directory \Platforms\PlatfromXYZ\Generic\RTE into your
build system into the subdirectory /RTE. These are the files of the Runtime
Environment. Add all C-files into your makefile:
# compile all files of the Runtime Environment (RTE)
EMWI_RTE_C =
ewcolor.c
ewdebug.c
ewextrte.c
ewobject.c
ewpoint.c
ewrect.c
ewref.c
ewresource.c
ewscalars.c
ewslot.c
ewstring.c
ewtimer.c
•
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
Copy all the generated code from the directory
\Examples_Mosaic20\AnimationEffects\PlatformXYZ into your build system into
www.embedded-wizard.de
Copyright TARA Systems GmbH
Page 5
HT-005-Platform Integration
the subdirectory /APP. In principle, you can add all C files into your makefile.
However, there is more convenient way: During code generation, Embedded
Wizard generates a list of all C source files within the file ewfiles.inc. You can just
add this file to your makefile. This makes it possible to use the same makefile for
different GUI applications or samples without any modification!
# automatically compile all files generated by Embedded Wizard
include $(APP_EMWI_PATH)/ewfiles.inc
APP_EMWI_C = $(EMWIFILES)
•
•
Add the above subdirectories to your include path within your makefile in order to
find all necessary header files.
Add EMWI_GFX_C, EMWI_RTE_C and APP_EMWI_C to the list of objects.
In theory, the makefile is now prepared so that the application could be compiled and
linked. But there is still something missing…
Integrating the Code into Your Application
This chapter is intended to describe the integration of the Embedded Wizard generated
GUI application into your main application. With other words, we will now try to launch
the GUI application and to provide it with all necessary events.
General remarks: The both subdirectories RTE and GFX contain C files for the Runtime
Environment and the Graphics Engine. All of these files are maintained by TARA
Systems so any modification of these files should be strongly avoided.
Additionally, please avoid the modification of generated code, because it will be
overwritten next time when you start the code generation within Embedded Wizard.
Important note: Please be aware that every Embedded Wizard GUI application
requires the execution in a single GUI task.
Includes
First of all, some header files from the appropriate platform package directory have to
be included into your main.c file.
From the runtime environment (RTE):
#include "ewrte.h"
From the graphics engine (GE):
#include "ewgfx.h"
#include "ewextgfx.h"
#include "ewgfxdefs.h"
After that, some generated C files from Mosaic class library have to be included:
#include "Core.h"
#include "Graphics.h"
www.embedded-wizard.de
Copyright TARA Systems GmbH
Page 6
HT-005-Platform Integration
In order to find all of the above header files during the compilation of your main.c file,
the include path has to be set correctly within your makefile. In case of a GCC compiler,
this is typically done by using the -I compiler flag.
Initialization of the GUI Application
Before the Embedded Wizard generated GUI application can be executed on the target
platform, the frame buffer and the Graphics Engine have to be initialized.
The initialization of the frame buffer depends completely on your target system and the
underlying graphics API. For example in case of DirectFB, a DirectFB surface needs to
be created, in case of OpenGL ES 2.0 the frame buffer is typically created via EGL.
In order to simplify this task, TARA Systems typically delivers a template of a main.c file
including the necessary initialization together with a Platform Package.
Nevertheless, the following code snippet illustrates the necessary steps to initialize the
EmWi generated GUI application:
int main( int argc, char **argv )
{
CoreRoot
rootObject;
XViewport*
viewport;
YourFramebuffer framebuffer;
int
w, h;
/* Startup your subsystem and obtain the frame buffer or display device*/
framebuffer = YourSubsystem_Initialize();
/* Query the size of your frame buffer or display device*/
GetSize( framebuffer, &w, &h );
/* Initialize the Graphics Engine and the Runtime Environment */
EwInitGraphicsEngine( &framebuffer );
/* Create the applications root object ... */
rootObject = (CoreRoot)EwNewObjectIndirect( EwApplicationClass, 0 );
EwLockObject( rootObject );
/* ... and initialize the application object. */
CoreRoot__Initialize( rootObject, EwScreenSize );
/* Create the viewport object to provide access to the framebuffer */
viewport = EwInitViewport( EwScreenSize, EwNewRect( 0, 0, w, h ),
0, 255, framebuffer, 0, 0, 0 );
...
The Parameter EwScreenSize is an automatically generated constant in the module
Core.c of type XPoint. This value contains the size of the screen as it was defined in the
attribute ScreenSize within the EmWi profile for that the code was generated.
The Parameter EwApplicationClass, contains the root object of the GUI application.
This application class is also defined within the attribute ApplicationClass of the profile.
Implementing the Main Loop
Embedded Wizard generated UI applications are running in an (endless) loop, which
drives the UI application. This main loop is responsible to provide all user inputs to the
www.embedded-wizard.de
Copyright TARA Systems GmbH
Page 7
HT-005-Platform Integration
UI application, to start the processing of timers and signals, to take care for the update
of the display and finally to start the garbage collection.
The main loop can be implemented within your main() function or it can be implemented
as a separate OS task. However, it is absolutely important, that the complete
Embedded Wizard generated UI is running within one task! Splitting the different
actions in several tasks (e.g. to start the garbage collection from another task) will
cause unpredictable results…
The following code snipped shows the typical implementation of a main loop:
...
XEnum cmd = CoreKeyCodeNoKey;
/* Enless loop, until the 'power' key is pressed... */
while ( cmd != CoreKeyCodePower )
{
int
events = 0;
int
timers = 0;
int
signals = 0;
int
touch;
XPoint point;
/* Step 1: Receive user inputs (key events)... */
cmd = GetKeyCommand();
/* ...and provide it to the application. */
if ( cmd != CoreKeyCodeNoKey )
{
/* Create Mosaic key event object. */
CoreKeyEvent event = EwNewObject( CoreKeyEvent, 0 );
CoreKeyEvent__Initialize( event, cmd, 1 );
/* Insert the event to the root object. */
CoreGroup__DispatchEvent( rootObject, (CoreEvent)event );
/* Set a flag to indicate that event was processed. */
events = 1;
}
/* Step 2: Receive cursor inputs (mouse/touch events)... */
touch = GetTouchEvent( &point );
/* ...and provide it to the application. */
if ( touch > 0 )
{
/* Begin of touch cycle */
if ( touch == 1 )
CoreRoot__DriveCursorHitting( rootObject, 1, 0, pos );
/* Movement during touch cycle */
else if ( touch == 2 )
CoreRoot__DriveCursorMovement( rootObject, pos );
/* End of touch cycle */
else if ( touch == 3 )
CoreRoot__DriveCursorHitting( rootObject, 0, 0, pos );
/* Set a flag to indicate that event was processed. */
www.embedded-wizard.de
Copyright TARA Systems GmbH
Page 8
HT-005-Platform Integration
events = 1;
}
/* Step 3: Process expired timers */
timers = EwProcessTimers();
/* Step 4: Process the pending signals */
signals = EwProcessSignals();
/* Update screen and memory, only if something has changed */
if ( timers || signals || events )
{
/* Step 5: Refresh the screen and draw its content */
Update( viewport, rootObject );
/* Step 6: Start the garbage collection */
EwReclaimMemory();
}
/* Otherwise suspend for a while */
else
usleep( 1 );
}
In the following, we want to have a closer look into the single steps of this main loop.
Step 1: Processing Key Events
Depending on the operating system or generally speaking the application runtime
environment, the input device drivers (keyboard, remote control, etc.) will generate
messages when the input device state changes (e. g. a remote control button is
pressed).
Typically, the input task will have to write the input data into a message queue and the
applications main loop can then process these events out of this queue independently.
If an event is in the message queue (e.g. a remote control key pressed event) the main
application first has to examine the event. This means, the event information has to be
translated into a Mosaic key code. This has to be done within the function
GetKeyCommand(): all key events which are needed within your UI application, needs to
be translated into a Mosaic key code.
For this purpose, the Mosaic class library contains a set of predefined keys in the
enumeration KeyCode of unit Core. For example, the identifier CoreKeyCodeOk
corresponds to the Ok key on the remote control or the ENTER key on a keyboard.
When the main application has translated the incoming key into an appropriate key
code, a new event object is created. An event object is an object of the Mosaic class
Core::Event or a derived class. This event object has to be filled with the key code and
is then passed to the UI application.
Step 2: Processing Cursor Events
Some devices are cursor operated (e.g. touch panels, joysticks or mouse). In this case
a device driver will generate cursor information and write it into a message queue.
www.embedded-wizard.de
Copyright TARA Systems GmbH
Page 9
HT-005-Platform Integration
Similar to key events, the input task will have to write the mouse or touch events into a
message queue and the applications main loop can then process these events out of
this queue independently.
For convenience, the Mosaic base class for applications provides the methods
DriveCursorHitting() and DriveCursorMovement(). Since cursor events are very
common tasks, the method implementation takes care of creating the corresponding
event object and identifying the appropriate view to handle it. Please also refer to the
methods inline documentation.
Step 3: Processing Timers
Within an EmWi generated UI application all timer objects, that are currently running,
are stored in a timer list. In this step, the timer list is traversed and all expired timers are
handled. The number of processed timers is returned by this function. In order to
activate the timer processing, the following function of the runtime environment has to
be called:
int timers = EwProcessTimers();
Step 4: Processing Signals
In an EmWi generated UI application, signals can be sent in order to trigger slot
methods with certain behavior. Besides the regular signals, which are processed
immediately, special post signals and idle signals are collected and executed right
before, respectively after a screen update is performed. The processing of the deferred
signals is done by calling:
int signals = EwProcessSignals();
Step 5: Updating the Screen
In reaction to any key / cursor event, signal or expired timers the UI application can
change its look and for example open a new dialog box or move a bitmap across the
screen. In that case, all affected objects are marked as invalid. During the update of the
root object, all invalid areas are examined and changes to the visible objects are
processed. Finally, the resulting display is shown within the visible frame buffer.
The following function contains the complete display update cycle:
static void Update( XViewport* aViewport, CoreRoot aApplication )
{
XBitmap*
bitmap
= EwBeginUpdate( aViewport );
GraphicsCanvas canvas
= EwNewObject( GraphicsCanvas, 0 );
XRect
updateRect = {{ 0, 0 }, { 0, 0 }};
/* Let's redraw the dirty area of the screen. Cover the returned bitmap
objects within a canvas, so Mosaic can draw to it. */
if ( bitmap && canvas )
{
GraphicsCanvas__AttachBitmap( canvas, (XUInt32)bitmap );
updateRect = CoreRoot__UpdateGE20( aApplication, canvas );
GraphicsCanvas__DetachBitmap( canvas );
}
www.embedded-wizard.de
Copyright TARA Systems GmbH
Page 10
HT-005-Platform Integration
/* Complete the update */
if ( bitmap )
EwEndUpdate( aViewport, updateRect );
}
Step 6: Triggering the Garbage Collection
In the last step of the main loop the garbage collection is only triggered if any
processing has happened. The following function of the runtime environment is called:
EwReclaimMemory();
De-Initialization of the EmWi Application
When the application is finished, the unused resources have to be released using the
following commands:
/* Finished -> release unused resources... */
EwDoneViewport( viewport );
EwUnlockObject( rootObject );
EwReclaimMemory();
/* ... and deinitialize the Graphics Engine */
EwDoneGraphicsEngine();
/* Finally, shutdown your graphics subsystem */
YourSubsystem_Deinitialize();
return 0;
}
www.embedded-wizard.de
Copyright TARA Systems GmbH
Page 11
HT-005-Platform Integration
Communication between the EmWi Application and the Main
Application
The common way to send commands from the EmWi application to the main application
is to embed native C code within the EmWi application. Native statements can either be
implemented within a special “native method” or within a regular method, marked as
native code.
By using native code, functions of the main application can be accessed. The
communication in the opposite direction – i.e. from the main application to the EmWi
application – is done via events, which are created in the main application and sent to
the EmWi application.
EmWi Application
Main Application
method native void
CallExampleFunction()
{
ExampleFunction();
}
void ExampleFunction()
{
...
}
HandleEvent() Method
Create & Dispatch Event
Native methods
A native method is created in Embedded Wizard in two steps. At first, the “Method” brick
is dragged from the Gallery and dropped into a class shown in the Composer. Then, in
Inspector, the attribute “native” of the new method has to be given the value “true”. Now
the EmWi considers this method as native, i.e. the method can contain native C code,
together with Chora code. During code generation, the C statements of the method will
not be translated, but directly taken over into the generated C source code. Therefore,
native methods can be used to call functions and statements in the main application.
Native code in regular methods
Similar to native methods, functions of the main application can be called directly within
regular methods. To achieve that, the native C code has to be embedded within the
“native” statement. Necessary parameters can be passed in brackets as shown in this
example:.
native( aNumber )
{
//native C statements...
printf( “Number %d”, aNumber );
}
www.embedded-wizard.de
Copyright TARA Systems GmbH
Page 12
HT-005-Platform Integration
Chora and Custom Events
To communicate with the EmWi application, the main application can generate event
objects, i.e. objects of the Mosaic class Core::Event or a derived class, can fill it with
certain values and can insert it into the EmWi application. An event can either be
broadcasted by using the BroadcastEvent() method of an EmWi object or can be sent
along the current focus path with the DispatchEvent() method of an object.
Beside the existing event classes in Mosaic, like Core::KeyEvent or
Core::LanguageEvent, developers can create their own event classes for specific
purposes. The new event class has to be derived from the class Core::Event to inherit
the event’s functionality and can then be equipped with certain variables, used to pass
values from the main application to the EmWi application. In the following example a
look is taken on a Set Top Box equipped with a flash card slot.
If a new flash card is inserted, the main application has to create an event, called - for
example – MyUnit::CardEvent. This class contains a variable Type, which stores the
type of the card and a variable WriteProtection.
The creation and initialization of the event has to be realized as follows:
MyUnitCardEvent event = EwNewObject(MyUnitCardEvent,0);
MyUnitCardEvent__Initialize(event, Type, WriteProtection);
CoreGroup__DispatchEvent(Application, (CoreEvent)event);
Note:
This kind of custom events can’t be prototyped. Therefore the recommended way is to
use a user event (USER0-USER19) or the function keys (key F1-F10) and read the
required parameter in case of such an event via a native call from a corresponding API
function. Otherwise everybody who works or supports this project needs a target
development environment that can send this event.
www.embedded-wizard.de
Copyright TARA Systems GmbH
Page 13
HT-005-Platform Integration
Error Handling
EmWi includes a special error handling to react on serious errors. The function
EwPanic() will be called automatically by the Run Time Environment if a fatal system
failure is detected. By default it prints an error message and stays in an endless loop.
Even if this should never happen in a tested product, the default implementation is
intended for debugging purpose. If EwPanic() is called you can set a breakpoint and
investigate the callstack, parameters or memory content. Since each customer might
react in a different way on fatal errors, the function EwPanic() should be changed by
each customer, up to their requirements.
The default endless loop could be sufficient if such a situation is handled by a watchdog
timer, but it is recommended to do an error logging in a non volatile memory storage
and execute a hardware or software reset afterwards. It is also possible to call a system
error handler that is already available in your Software.
Further Information
For a first start, we recommend to study the Embedded Wizard User Manual including
the “Quick Tour”. The Embedded Wizard User Manual is part of the Embedded Wizard
installation (please see subdirectory \EmbeddedWizard\Doc) and it is available at
http://www.embedded-wizard.de within the section Documentation.
To help you, to get familiar with Embedded Wizard faster, we have created several
tutorials for beginners as well as advanced users including step-by-step example
projects. These tutorials are part of the Embedded Wizard installation (please see
subdirectory \EmbeddedWizard\Tutorial_Mosaic20) and they are also provided at
http://www.embedded-wizard.de as part of the available Documentation.
For specific questions related to your project, please visit the Help & Support page. You
will find a collection of Frequently Asked Questions as well as information to contact our
support team.
www.embedded-wizard.de
Copyright TARA Systems GmbH
Page 14