3. The main() Function and the qpn_port.h Header File

This QP-nano Tutorial is adapted from Chapter 1 of Practical UML Statecharts in C/C++, Second Edition
by Miro Samek, the founder and president of Quantum Leaps, LLC.

qp_tutorial.jpg

Prev: 2. Let's Play
Next: 4. Designing an Event-Driven Application

Perhaps the best place to start the explanation of the "Fly 'n' Shoot" application code is the main() function, located in the file main.c. Unless indicated otherwise in this Tutorial, you can browse the code either in the DOS version, or the Cortex-M3 version, because the application source code is identical in both.

3. The main() Function and the qpn_port.h Header File

Listing 3-1 shows the main.c source file for the “Fly ‘n’ Shoot” application, which contains the main() function along with some important data structures required by QP-nano.

Note:
To explain code listings, I place numbers in parentheses at the interesting lines in the left margin of the listing. I then use these labels in the left margin of the explanation section that immediately follows the listing. Occasionally, to unambiguously refer to a line of a particular listing from sections of text other than the explanation section, I use the full reference consisting of the listing number followed by the label. For example, Listing 3-1(21) refers to the label (21) in Listing 3-1
Listing 3-1 The file main.c of the "Fly 'n' Shoot" game application.
 (1) #include "qpn_port.h"                                       /* QP-nano port */
 (2) #include "bsp.h"                             /* Board Support Package (BSP) */
 (3) #include "game.h"                                  /* application interface */

     /*..........................................................................*/
 (4) static QEvent l_tunnelQueue[GAME_MINES_MAX + 4];
 (5) static QEvent l_shipQueue[2];
 (6) static QEvent l_missileQueue[2];

     /* QF_active[] array defines all active object control blocks --------------*/
 (7) QActiveCB const Q_ROM Q_ROM_VAR QF_active[] = {
 (8)     { (QActive *)0,           (QEvent *)0,    0                     },
 (9)     { (QActive *)&AO_tunnel,  l_tunnelQueue,  Q_DIM(l_tunnelQueue)  },
(10)     { (QActive *)&AO_ship,    l_shipQueue,    Q_DIM(l_shipQueue)    },
(11)     { (QActive *)&AO_missile, l_missileQueue, Q_DIM(l_missileQueue) }
     };

     /* make sure that the QF_active[] array matches QF_MAX_ACTIVE in qpn_port.h */
(12) Q_ASSERT_COMPILE(QF_MAX_ACTIVE == Q_DIM(QF_active) - 1);

     /*..........................................................................*/
     void main (void) {
(13)     Tunnel_ctor ();
(14)     Ship_ctor   ();
(15)     Missile_ctor(GAME_MISSILE_SPEED_X);

(16)     BSP_init();                                     /* initialize the board */

(17)     QF_run();                                /* transfer control to QF-nano */
     }

In QP-nano, I use every opportunity to place data in ROM rather than in the precious RAM. The QActiveCB structure contains data elements known at compile time, so that these elements can be placed in ROMas opposed to placing them in the active object structure (RAM). That way, I save anywhere from 10 to 80 bytes of RAM, depending on the number of active objects and the pointer size of the target CPU. The Q_ROM macro is necessary on some CPU architecture to enforce placement of constant objects, such as the QF_active[] array, in ROM. On Harvard architecture CPUs (such as 8051 or AVR), the code and data spaces are separate and are accessed through different CPU instructions. The const keyword is not sufficient to place data in ROM, and various compilers often provide specific extended keywords to designate the code space for placing constant data, such as the "__code" extended keyword in the IAR 8051 compiler. The macro Q_ROM hides such non-standard extensions. If you don't define Q_ROM in qepn_port.h, it will be defined to nothing in the qepn.h platform-independent header file. The Q_ROM_VAR macro defines the compiler-specific directive for accessing a constant object in ROM. Many compilers for 8-bit MCUs provide different size pointers for accessing objects in various memories. Constant objects allocated in ROM often mandate the use of specific-size pointers (e.g., far pointers) to get access to ROM objects. The macro Q_ROM_VAR specifies the kind of the pointer to be used to access the ROM objects. An example of valid Q_ROM_VAR macro definition is: __far (Freescale HC(S)08 compiler).

Note:
The order or the active object control blocks in the QF_active[] array defines the priorities of active objects. This is the only place in the code where you assign active object priorities.
In QP-nano, QF_MAX_ACTIVE denotes the exact number of active objects used in the application, as opposed to the full-version QP, where QF_MAX_ACTIVE denotes just the configurable maximum number of active objects.

Note:
All active objects in QP-nano must be defined at compile time. This means that all active objects exist from the beginning and cannot be started (or stopped) later, as it is possible in the full-version QP.
The macro QF_MAX_ACTIVE must be defined in qpn_port.h header file, because QP-nano uses the macro to optimize the internal algorithms based on the number of active objects. The compile-time assertion in line (12) makes sure that the configured number of active objects indeed matches exactly the number of active object control blocks defined in the QF_active[] array.

Overall, the application startup is much simpler in QP-nano than in full-version QP. Neither event pools, nor publish-subscribe lists are supported, so you don't need to initialize them. You also don't start active objects explicitly. The QF-nano framework starts all active objects defined in the QF_active[] array automatically just after it gets control in QF_run().

The qpn_port.h header file

The qpn_port.h header file defines the QP-nano port and all configuration parameters for the particular application. Unlike in the full-version QP, QP-nano ports are typically defined at the application level. Typically also, the whole QP-nano port consists of just the qpn_port.h header file. Listing 3-2 shows the complete qpn_port.h file for the DOS version of the “Fly ‘n’ Shoot” game.

Listing 3-2 The qpn_port.h header file for the “Fly ‘n’ Shoot” game.

     #ifndef qpn_port_h
     #define qpn_port_h

 (1) #define Q_PARAM_SIZE            4
 (2) #define QF_TIMEEVT_CTR_SIZE     2
 (3) #define Q_NFSM

     /* maximum # active objects--must match EXACTLY the QF_active[] definition  */
 (4) #define QF_MAX_ACTIVE           3

                                      /* interrupt locking policy for task level */
 (5) #define QF_INT_LOCK()           disable()
 (6) #define QF_INT_UNLOCK()         enable()

     /* Exact-width types (WG14/N843 C99 Standard) for Turbo C++/large model     */
 (7) typedef signed   char  int8_t;
     typedef signed   int   int16_t;
     typedef signed   long  int32_t;
     typedef unsigned char  uint8_t;
     typedef unsigned int   uint16_t;
     typedef unsigned long  uint32_t;

     #include <dos.h>                                                 /* DOS API */
     #undef outportb /*don't use the macro because it has a bug in Turbo C++ 1.01*/

 (8) #include "qepn.h"              /* QEP-nano platform-independent header file */
 (9) #include "qfn.h"                /* QF-nano platform-independent header file */

     #endif                                                        /* qpn_port_h */

Note:
The qpn_port.h header file in Listing 3-2 implicitly configures QP-nano to use the built-in cooperative "vanilla" kernel. The other alternative, which is the preemptive QK-nano kernel, is configured automatically when you include the qkn.h QK-nano interface in the qpn_port.h header file.
Prev: 2. Let's Play
Next: 4. Designing an Event-Driven Application

logo_ql_TM.jpg
Copyright © 2002-2008 Quantum Leaps, LLC. All Rights Reserved.
http://www.quantum-leaps.com
Generated on Sat Dec 27 22:01:47 2008 for QP-nano by  doxygen 1.5.4